diff options
Diffstat (limited to 'source/tanya/container/slice.d')
| -rw-r--r-- | source/tanya/container/slice.d | 2549 |
1 files changed, 0 insertions, 2549 deletions
diff --git a/source/tanya/container/slice.d b/source/tanya/container/slice.d deleted file mode 100644 index 321acfb..0000000 --- a/source/tanya/container/slice.d +++ /dev/null @@ -1,2549 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Single-dimensioned array. - * - * 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.slice; - -import core.checkedint; -import core.exception; -import std.algorithm.comparison; -import std.algorithm.mutation; -import std.conv; -import std.range.primitives; -import std.meta; -import std.traits; -import tanya.memory; - -/** - * Random-access range for the $(D_PSYMBOL Vector). - * - * Params: - * T = Element type. - */ -struct Slice(T) - if (!is(Unqual!T == char)) -{ - private T* begin, end; - private alias ContainerType = CopyConstness!(T, Vector!(Unqual!T)); - private ContainerType* vector; - - invariant - { - assert(this.begin <= this.end); - assert(this.vector !is null); - assert(this.begin >= this.vector.data); - assert(this.end <= this.vector.data + this.vector.length); - } - - private this(ref ContainerType vector, T* begin, T* end) @trusted - in - { - assert(begin <= end); - assert(begin >= vector.data); - assert(end <= vector.data + vector.length); - } - body - { - this.vector = &vector; - this.begin = begin; - this.end = end; - } - - @disable this(); - - @property Slice save() - { - return this; - } - - @property bool empty() const - { - return this.begin == this.end; - } - - @property size_t length() const - { - return this.end - this.begin; - } - - alias opDollar = length; - - @property ref inout(T) front() inout - in - { - assert(!empty); - } - body - { - return *this.begin; - } - - @property ref inout(T) back() inout @trusted - in - { - assert(!empty); - } - body - { - return *(this.end - 1); - } - - void popFront() @trusted - in - { - assert(!empty); - } - body - { - ++this.begin; - } - - void popBack() @trusted - in - { - assert(!empty); - } - body - { - --this.end; - } - - ref inout(T) opIndex(const size_t i) inout @trusted - in - { - assert(i < length); - } - body - { - return *(this.begin + i); - } - - Slice opIndex() - { - return typeof(return)(*this.vector, this.begin, this.end); - } - - Slice!(const T) opIndex() const - { - return typeof(return)(*this.vector, this.begin, this.end); - } - - Slice opSlice(const size_t i, const size_t j) @trusted - in - { - assert(i <= j); - assert(j <= length); - } - body - { - return typeof(return)(*this.vector, this.begin + i, this.begin + j); - } - - Slice!(const T) opSlice(const size_t i, const size_t j) const @trusted - in - { - assert(i <= j); - assert(j <= length); - } - body - { - return typeof(return)(*this.vector, this.begin + i, this.begin + j); - } - - inout(T[]) get() inout @trusted - { - return this.begin[0 .. length]; - } -} - -/** - * One dimensional array. - * - * Params: - * T = Content type. - */ -struct Vector(T) - if (!is(T == char)) -{ - private size_t length_; - private T* data; - private size_t capacity_; - - invariant - { - assert(this.length_ <= this.capacity_); - assert(this.capacity_ == 0 || this.data !is null); - } - - /** - * Creates a new $(D_PSYMBOL Vector) with the elements from a static array. - * - * Params: - * R = Static array size. - * init = Values to initialize the vector with. - * allocator = Allocator. - */ - this(size_t R)(T[R] init, shared Allocator allocator = defaultAllocator) - { - this(allocator); - insertBack!(T[])(init[]); - } - - /** - * Creates a new $(D_PSYMBOL Vector) with the elements from an input range. - * - * Params: - * R = Type of the initial range. - * init = Values to initialize the vector with. - * allocator = Allocator. - */ - 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)(const 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. - this.data = init.data; - this.length_ = init.length_; - this.capacity_ = init.capacity_; - - // Reset the source vector, so it can't destroy the moved storage. - init.length_ = init.capacity_ = 0; - init.data = null; - } - else - { - // Move each element. - reserve(init.length_); - moveEmplaceAll(init.data[0 .. init.length_], this.data[0 .. init.length_]); - this.length_ = init.length_; - // Destructor of init should destroy it here. - } - } - - /// - @trusted @nogc unittest - { - auto v1 = Vector!int([1, 2, 3]); - auto v2 = Vector!int(v1); - assert(v1 == v2); - - auto v3 = Vector!int(Vector!int([1, 2, 3])); - assert(v1 == v3); - assert(v3.length == 3); - assert(v3.capacity == 3); - } - - private @trusted @nogc unittest // const constructor tests - { - auto v1 = const Vector!int([1, 2, 3]); - auto v2 = Vector!int(v1); - assert(v1.data !is v2.data); - 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. - * init = Initial value to fill the vector with. - * allocator = Allocator. - */ - this(const size_t len, T init, shared Allocator allocator = defaultAllocator) @trusted - { - this(allocator); - reserve(len); - uninitializedFill(this.data[0 .. len], init); - length_ = len; - } - - /// Ditto. - this(const size_t len, shared Allocator allocator = defaultAllocator) - { - this(allocator); - 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(this.data[0 .. capacity]); - } - - /** - * Copies the vector. - */ - this(this) - { - auto buf = this.data[0 .. this.length_]; - this.length_ = capacity_ = 0; - this.data = 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(const size_t len) @trusted - { - if (len == length) - { - return; - } - else if (len > length) - { - reserve(len); - initializeAll(this.data[length_ .. len]); - } - else - { - static if (hasElaborateDestructor!T) - { - const T* end = this.data + length_ - 1; - for (T* e = this.data + 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(const size_t size) @trusted - { - if (capacity_ >= size) - { - return; - } - bool overflow; - immutable byteSize = mulu(size, T.sizeof, overflow); - assert(!overflow); - - void[] buf = this.data[0 .. this.capacity_]; - if (!allocator.reallocateInPlace(buf, byteSize)) - { - buf = allocator.allocate(byteSize); - if (buf is null) - { - onOutOfMemoryErrorNoGC(); - } - scope (failure) - { - allocator.deallocate(buf); - } - const T* end = this.data + this.length_; - for (T* src = this.data, dest = cast(T*) buf; src != end; ++src, ++dest) - { - moveEmplace(*src, *dest); - static if (hasElaborateDestructor!T) - { - destroy(*src); - } - } - allocator.deallocate(this.data[0 .. this.capacity_]); - this.data = cast(T*) buf; - } - this.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(const size_t size) @trusted - { - if (capacity <= size) - { - return; - } - immutable n = max(length, size); - void[] buf = this.data[0 .. this.capacity_]; - if (allocator.reallocateInPlace(buf, n * T.sizeof)) - { - this.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); - } - - /** - * 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(const 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: A range spanning the remaining elements in the array that - * initially were right after $(D_PARAM r). - * - * Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this). - */ - Slice!T remove(Slice!T r) @trusted - in - { - assert(r.vector is &this); - assert(r.begin >= this.data); - assert(r.end <= this.data + length); - } - body - { - auto end = this.data + this.length; - moveAll(Slice!T(this, r.end, end), Slice!T(this, r.begin, end)); - length = length - r.length; - return Slice!T(this, r.begin, this.data + length); - } - - /// - @safe @nogc unittest - { - auto v = Vector!int([5, 18, 17, 2, 4, 6, 1]); - - assert(v.remove(v[1 .. 3]).length == 4); - 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 == 1); - 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 == 0); - assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6); - assert(v.length == 4); - - assert(v.remove(v[]).length == 0); - - } - - private void moveBack(R)(ref R el) @trusted - if (isImplicitlyConvertible!(R, T)) - { - reserve(this.length + 1); - moveEmplace(el, *(this.data + this.length_)); - ++this.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)(R el) - if (isImplicitlyConvertible!(R, T)) - { - moveBack(el); - return 1; - } - - /// Ditto. - size_t insertBack(R)(ref R el) @trusted - if (isImplicitlyConvertible!(R, T)) - { - reserve(this.length_ + 1); - emplace(this.data + this.length_, el); - ++this.length_; - 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. - * - * Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this). - */ - size_t insertAfter(R)(Slice!T r, R el) - if (!isInfinite!R - && isInputRange!R - && isImplicitlyConvertible!(ElementType!R, T)) - in - { - assert(r.vector is &this); - assert(r.begin >= this.data); - assert(r.end <= this.data + length); - } - body - { - immutable oldLen = length; - immutable offset = r.end - this.data; - immutable inserted = insertBack(el); - bringToFront(this.data[offset .. oldLen], this.data[oldLen .. length]); - return inserted; - } - - /// Ditto. - size_t insertAfter(size_t R)(Slice!T r, T[R] el) - in - { - assert(r.vector is &this); - assert(r.begin >= this.data); - assert(r.end <= this.data + length); - } - body - { - return insertAfter!(T[])(r, el[]); - } - - /// Ditto. - size_t insertAfter(R)(Slice!T r, auto ref R el) - if (isImplicitlyConvertible!(R, T)) - in - { - assert(r.vector is &this); - assert(r.begin >= this.data); - assert(r.end <= this.data + length); - } - body - { - immutable oldLen = length; - immutable offset = r.end - this.data; - - static if (__traits(isRef, el)) - { - insertBack(el); - } - else - { - moveBack(el); - } - bringToFront(this.data[offset .. oldLen], this.data[oldLen .. length]); - - return 1; - } - - /// Ditto. - size_t insertBefore(R)(Slice!T r, R el) - if (!isInfinite!R - && isInputRange!R - && isImplicitlyConvertible!(ElementType!R, T)) - in - { - assert(r.vector is &this); - assert(r.begin >= this.data); - assert(r.end <= this.data + length); - } - body - { - return insertAfter(Slice!T(this, this.data, r.begin), el); - } - - /// Ditto. - size_t insertBefore(size_t R)(Slice!T r, T[R] el) - in - { - assert(r.vector is &this); - assert(r.begin >= this.data); - assert(r.end <= this.data + length); - } - body - { - return insertBefore!(T[])(r, el[]); - } - - /// Ditto. - size_t insertBefore(R)(Slice!T r, auto ref R el) - if (isImplicitlyConvertible!(R, T)) - in - { - assert(r.vector is &this); - assert(r.begin >= this.data); - assert(r.end <= this.data + length); - } - body - { - immutable oldLen = length; - immutable offset = r.begin - this.data; - - static if (__traits(isRef, el)) - { - insertBack(el); - } - else - { - moveBack(el); - } - bringToFront(this.data[offset .. oldLen], this.data[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, const 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, const size_t pos) - { - return opIndexAssign(value, pos); - } - - /// Ditto. - Slice!T opIndexAssign(T value) - { - return opSliceAssign(value, 0, length); - } - - /// Ditto. - Slice!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). - */ - Slice!T opIndexAssign(R)(R value) - if (!isInfinite!R && isInputRange!R - && isImplicitlyConvertible!(ElementType!R, T)) - { - return opSliceAssign!R(value, 0, length); - } - - /// Ditto. - Slice!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(const size_t pos) inout @trusted - in - { - assert(length > pos); - } - body - { - return *(this.data + pos); - } - - /** - * Returns: Random access range that iterates over elements of the vector, in - * forward order. - */ - Slice!T opIndex() @trusted - { - return typeof(return)(this, this.data, this.data + length); - } - - /// Ditto. - Slice!(const T) opIndex() const @trusted - { - return typeof(return)(this, this.data, this.data + 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(this.data[0 .. length], that.data[0 .. that.length]); - } - - /// Ditto. - bool opEquals()(const auto ref typeof(this) that) const @trusted - { - return equal(this.data[0 .. length], that.data[0 .. that.length]); - } - - /// Ditto. - bool opEquals(Slice!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)(Slice!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); - } - - /** - * Returns: The first element. - * - * Precondition: $(D_INLINECODE !empty). - */ - @property ref inout(T) front() inout - in - { - assert(!empty); - } - body - { - return *this.data; - } - - /// - @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 *(this.data + 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). - */ - Slice!T opSlice(const size_t i, const size_t j) @trusted - in - { - assert(i <= j); - assert(j <= length); - } - body - { - return typeof(return)(this, this.data + i, this.data + j); - } - - /// Ditto. - Slice!(const T) opSlice(const size_t i, const size_t j) const @trusted - in - { - assert(i <= j); - assert(j <= length); - } - body - { - return typeof(return)(this, this.data + i, this.data + 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) - */ - Slice!T opSliceAssign(R)(R value, const size_t i, const 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, this.data[i .. j]); - return opSlice(i, j); - } - - /// Ditto. - Slice!T opSliceAssign(size_t R)(T[R] value, const size_t i, const size_t j) - { - return opSliceAssign!(T[])(value[], i, j); - } - - /// Ditto. - Slice!T opSliceAssign(ref T value, const size_t i, const size_t j) @trusted - in - { - assert(i <= j); - assert(j <= length); - } - body - { - fill(this.data[i .. j], value); - return opSlice(i, j); - } - - /// Ditto. - Slice!T opSliceAssign(T value, const size_t i, const 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 this.data[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); - } - - /** - * Assigns another vector. - * - * If $(D_PARAM that) is passed by value, it won't be copied, but moved. - * This vector will take the ownership over $(D_PARAM that)'s storage and - * the allocator. - * - * If $(D_PARAM that) is passed by reference, it will be copied. - * - * Params: - * R = Content type. - * that = The value should be assigned. - * - * Returns: $(D_KEYWORD this). - */ - ref typeof(this) opAssign(R)(const ref R that) - if (is(Unqual!R == Vector)) - { - return this = that[]; - } - - /// Ditto. - ref typeof(this) opAssign(R)(R that) @trusted - if (is(R == Vector)) - { - swap(this.data, that.data); - swap(this.length_, that.length_); - swap(this.capacity_, that.capacity_); - swap(this.allocator_, that.allocator_); - return this; - } - - /** - * Assigns a range to the vector. - * - * Params: - * R = Content type. - * that = The value should be assigned. - * - * Returns: $(D_KEYWORD this). - */ - ref typeof(this) opAssign(R)(R that) - if (!isInfinite!R - && isInputRange!R - && isImplicitlyConvertible!(ElementType!R, T)) - { - length = 0; - insertBack(that); - return this; - } - - /// - @safe @nogc unittest - { - auto v1 = const Vector!int([5, 15, 8]); - Vector!int v2; - v2 = v1; - assert(v1 == v2); - } - - /// - @safe @nogc unittest - { - auto v1 = const Vector!int([5, 15, 8]); - Vector!int v2; - v2 = v1[0 .. 2]; - assert(equal(v1[0 .. 2], v2[])); - } - - // Move assignment. - private @safe @nogc unittest - { - Vector!int v1; - v1 = Vector!int([5, 15, 8]); - } - - /** - * Assigns a static array. - * - * Params: - * R = Static array size. - * that = Values to initialize the vector with. - * - * Returns: $(D_KEYWORD this). - */ - ref typeof(this) opAssign(size_t R)(T[R] that) - { - return opAssign!(T[])(that[]); - } - - /// - @safe @nogc unittest - { - auto v1 = Vector!int([5, 15, 8]); - Vector!int v2; - - v2 = [5, 15, 8]; - assert(v1 == v2); - } - - mixin DefaultAllocator; -} - -/// -unittest -{ - auto v = Vector!int([5, 15, 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); -} - -@nogc unittest -{ - const v1 = Vector!int(); - const Vector!int v2; - const v3 = Vector!int([1, 5, 8]); - static assert(is(PointerTarget!(typeof(v3.data)) == const(int))); -} - -@nogc unittest -{ - // 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); - - static assert(!is(typeof(r1[0] = 5))); - static assert(!is(typeof(v[0] = 5))); - - const r2 = r1[]; - static assert(is(typeof(r2[]))); -} - -@nogc unittest -{ - Vector!int v1; - const Vector!int v2; - - auto r1 = v1[]; - auto r2 = v1[]; - - 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[])); -} - -@nogc unittest -{ - struct MutableEqualsStruct - { - int opEquals(typeof(this) that) @nogc - { - return true; - } - } - struct ConstEqualsStruct - { - int opEquals(const 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 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. -} - -private unittest -{ - class A - { - } - A a1, a2; - auto v1 = Vector!A([a1, a2]); -} - -private @safe @nogc unittest -{ - auto v = Vector!int([5, 15, 8]); - { - size_t i; - - foreach (e; v) - { - assert(i != 0 || e == 5); - assert(i != 1 || e == 15); - assert(i != 2 || e == 8); - ++i; - } - assert(i == 3); - } - { - size_t i = 3; - - foreach_reverse (e; v) - { - --i; - assert(i != 2 || e == 8); - assert(i != 1 || e == 15); - assert(i != 0 || e == 5); - } - assert(i == 0); - } -} - -private ref const(wchar) front(const wchar[] str) -pure nothrow @safe @nogc -in -{ - assert(str.length > 0); -} -body -{ - return str[0]; -} - -private void popFront(ref const(wchar)[] str, const size_t s = 1) -pure nothrow @safe @nogc -in -{ - assert(str.length >= s); -} -body -{ - str = str[s .. $]; -} - -/** - * Thrown on encoding errors. - */ -class UTFException : Exception -{ - /** - * Params: - * msg = The message for the exception. - * file = The file where the exception occurred. - * line = The line number where the exception occurred. - * next = The previous exception in the chain of exceptions, if any. - */ - this(string msg, - string file = __FILE__, - size_t line = __LINE__, - Throwable next = null) @nogc @safe pure nothrow - { - super(msg, file, line, next); - } -} - -/** - * Iterates $(D_PSYMBOL String) by UTF-8 code unit. - * - * Params: - * E = Element type ($(D_KEYWORD char) or $(D_INLINECODE const(char))). - */ -struct ByCodeUnit(E) - if (is(Unqual!E == char)) -{ - private E* begin, end; - private alias ContainerType = CopyConstness!(E, Slice!char); - private ContainerType* container; - - invariant - { - assert(this.begin <= this.end); - assert(this.container !is null); - assert(this.begin >= this.container.data); - assert(this.end <= this.container.data + this.container.length); - } - - private this(ref ContainerType container, E* begin, E* end) @trusted - in - { - assert(begin <= end); - assert(begin >= container.data); - assert(end <= container.data + container.length); - } - body - { - this.container = &container; - this.begin = begin; - this.end = end; - } - - @disable this(); - - @property ByCodeUnit save() - { - return this; - } - - @property bool empty() const - { - return this.begin == this.end; - } - - @property size_t length() const - { - return this.end - this.begin; - } - - alias opDollar = length; - - @property ref inout(E) front() inout - in - { - assert(!empty); - } - body - { - return *this.begin; - } - - @property ref inout(E) back() inout @trusted - in - { - assert(!empty); - } - body - { - return *(this.end - 1); - } - - void popFront() @trusted - in - { - assert(!empty); - } - body - { - ++this.begin; - } - - void popBack() @trusted - in - { - assert(!empty); - } - body - { - --this.end; - } - - ref inout(E) opIndex(const size_t i) inout @trusted - in - { - assert(i < length); - } - body - { - return *(this.begin + i); - } - - ByCodeUnit opIndex() - { - return typeof(return)(*this.container, this.begin, this.end); - } - - ByCodeUnit!(const E) opIndex() const - { - return typeof(return)(*this.container, this.begin, this.end); - } - - ByCodeUnit opSlice(const size_t i, const size_t j) @trusted - in - { - assert(i <= j); - assert(j <= length); - } - body - { - return typeof(return)(*this.container, this.begin + i, this.begin + j); - } - - ByCodeUnit!(const E) opSlice(const size_t i, const size_t j) const @trusted - in - { - assert(i <= j); - assert(j <= length); - } - body - { - return typeof(return)(*this.container, this.begin + i, this.begin + j); - } - - inout(E[]) get() inout @trusted - { - return this.begin[0 .. length]; - } -} - -/// UTF-8 string. -alias String = Slice!char; - -/** - * UTF-8 string. - * - * Params: - * T = $(D_KEYWORD char). - */ -struct Slice(T) - if (is(T == char)) -{ - private size_t length_; - private char* data; - private size_t capacity_; - - pure nothrow @safe @nogc invariant - { - assert(this.length_ <= this.capacity_); - } - - /** - * Constructs the string from a stringish range. - * - * Params: - * R = String type. - * str = Initial string. - * allocator = Allocator. - * - * Throws: $(D_PSYMBOL UTFException). - * - * Precondition: $(D_INLINECODE allocator is null). - */ - this(R)(const R str, shared Allocator allocator = defaultAllocator) - if (!isInfinite!R - && isInputRange!R - && isSomeChar!(ElementEncodingType!R)) - { - this(allocator); - insertBack(str); - } - - /// - @safe @nogc unittest - { - auto s = String("\u10437"w); - assert("\u10437" == s.get()); - } - - /// - @safe @nogc unittest - { - auto s = String("Отказаться от вина - в этом страшная вина."d); - assert("Отказаться от вина - в этом страшная вина." == s.get()); - } - - /** - * Initializes this string 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). $(D_PARAM init) will be destroyed at the end. - * - * If $(D_PARAM init) is passed by reference, it will be copied. - * - * Params: - * init = Source string. - * allocator = Allocator. - * - * Precondition: $(D_INLINECODE allocator is null). - */ - this(Slice!char init, shared Allocator allocator = defaultAllocator) - nothrow @trusted @nogc - { - this(allocator); - if (allocator !is init.allocator) - { - // Just steal all references and the allocator. - this.data = init.data; - this.length_ = init.length_; - this.capacity_ = init.capacity_; - - // Reset the source string, so it can't destroy the moved storage. - init.length_ = init.capacity_ = 0; - init.data = null; - } - else - { - reserve(init.length); - init.data[0 .. init.length].copy(this.data[0 .. init.length]); - this.length_ = init.length; - } - } - - /// Ditto. - this(ref const Slice!char init, shared Allocator allocator = defaultAllocator) - nothrow @trusted @nogc - { - this(allocator); - reserve(init.length); - init.data[0 .. init.length].copy(this.data[0 .. init.length]); - this.length_ = init.length; - } - - /// Ditto. - this(shared Allocator allocator) pure nothrow @safe @nogc - in - { - assert(allocator !is null); - } - body - { - this.allocator_ = allocator; - } - - /** - * Fills the string with $(D_PARAM n) consecutive copies of character $(D_PARAM chr). - * - * Params: - * C = Type of the character to fill the string with. - * n = Number of characters to copy. - * chr = Character to fill the string with. - */ - this(C)(const size_t n, const C chr, - shared Allocator allocator = defaultAllocator) @trusted - if (isSomeChar!C) - { - this(allocator); - if (n == 0) - { - return; - } - insertBack(chr); - - // insertBack should validate the character, so we can just copy it - // n - 1 times. - auto remaining = length * n; - - reserve(remaining); - - // Use a quick copy. - for (auto i = this.length_ * 2; i <= remaining; i *= 2) - { - this.data[0 .. this.length_].copy(this.data[this.length_ .. i]); - this.length_ = i; - } - remaining -= length; - copy(this.data[this.length_ - remaining .. this.length_], - this.data[this.length_ .. this.length_ + remaining]); - this.length_ += remaining; - } - - private unittest - { - { - auto s = String(1, 'О'); - assert(s.length == 2); - } - { - auto s = String(3, 'О'); - assert(s.length == 6); - } - { - auto s = String(8, 'О'); - assert(s.length == 16); - } - } - - /** - * Destroys the string. - */ - ~this() nothrow @trusted @nogc - { - allocator.deallocate(this.data[0 .. this.capacity_]); - } - - private void write4Bytes(ref const dchar src) - pure nothrow @trusted @nogc - in - { - assert(capacity - length >= 4); - assert(src - 0x10000 < 0x100000); - } - body - { - auto dst = this.data + length; - - *dst++ = 0xf0 | (src >> 18); - *dst++ = 0x80 | ((src >> 12) & 0x3f); - *dst++ = 0x80 | ((src >> 6) & 0x3f); - *dst = 0x80 | (src & 0x3f); - - this.length_ += 4; - } - - private size_t insertWideChar(C)(auto ref const C chr) @trusted - if (is(C == wchar) || is(C == dchar)) - in - { - assert(capacity - length >= C.sizeof); - } - body - { - auto dst = this.data + length; - if (chr < 0x80) - { - *dst = chr & 0x7f; - this.length_ += 1; - return 1; - } - else if (chr < 0x800) - { - *dst++ = 0xc0 | (chr >> 6) & 0xff; - *dst = 0x80 | (chr & 0x3f); - this.length_ += 2; - return 2; - } - else if (chr < 0xd800 || chr - 0xe000 < 0x2000) - { - *dst++ = 0xe0 | (chr >> 12) & 0xff; - *dst++ = 0x80 | ((chr >> 6) & 0x3f); - *dst = 0x80 | (chr & 0x3f); - this.length_ += 3; - return 3; - } - return 0; - } - - /** - * Inserts a single character at the end of the string. - * - * Params: - * chr = The character should be inserted. - * - * Returns: The number of bytes inserted. - * - * Throws: $(D_PSYMBOL UTFException). - */ - size_t insertBack(const char chr) @trusted @nogc - { - if ((chr & 0x80) != 0) - { - throw defaultAllocator.make!UTFException("Invalid UTF-8 character"); - } - reserve(length + 1); - - *(data + length) = chr; - ++this.length_; - - return 1; - } - - /// Ditto. - size_t insertBack(const wchar chr) @trusted @nogc - { - reserve(length + wchar.sizeof); - - auto ret = insertWideChar(chr); - if (ret == 0) - { - throw defaultAllocator.make!UTFException("Invalid UTF-16 sequeunce"); - } - return ret; - } - - /// Ditto. - size_t insertBack(const dchar chr) @trusted @nogc - { - reserve(length + dchar.sizeof); - - auto ret = insertWideChar(chr); - if (ret > 0) - { - return ret; - } - else if (chr - 0x10000 < 0x100000) - { - write4Bytes(chr); - return 4; - } - else - { - throw defaultAllocator.make!UTFException("Invalid UTF-32 sequeunce"); - } - } - - /** - * Inserts a stringish range at the end of the string. - * - * Params: - * R = Type of the inserted string. - * str = String should be inserted. - * - * Returns: The number of bytes inserted. - * - * Throws: $(D_PSYMBOL UTFException). - */ - size_t insertBack(R)(R str) @trusted - if (!isInfinite!R - && isInputRange!R - && is(Unqual!(ElementEncodingType!R) == char)) - { - size_t size; - static if (hasLength!R || isNarrowString!R) - { - size = str.length + length; - reserve(size); - } - - static if (isNarrowString!R) - { - str.copy(this.data[length .. size]); - this.length_ = size; - return str.length; - } - else - { - size_t insertedLength; - while (!str.empty) - { - ubyte expectedLength; - if ((str.front & 0x80) == 0x00) - { - expectedLength = 1; - } - else if ((str.front & 0xe0) == 0xc0) - { - expectedLength = 2; - } - else if ((str.front & 0xf0) == 0xe0) - { - expectedLength = 3; - } - else if ((str.front & 0xf8) == 0xf0) - { - expectedLength = 4; - } - else - { - throw defaultAllocator.make!UTFException("Invalid UTF-8 sequeunce"); - } - size = length + expectedLength; - reserve(size); - - for (; expectedLength > 0; --expectedLength) - { - if (str.empty) - { - throw defaultAllocator.make!UTFException("Invalid UTF-8 sequeunce"); - } - *(data + length) = str.front; - str.popFront(); - } - insertedLength += expectedLength; - this.length_ = size; - } - return insertedLength; - } - } - - /// Ditto. - size_t insertBack(R)(R str) @trusted - if (!isInfinite!R - && isInputRange!R - && is(Unqual!(ElementEncodingType!R) == wchar)) - { - static if (hasLength!R || isNarrowString!R) - { - reserve(length + str.length * wchar.sizeof); - } - - static if (isNarrowString!R) - { - const(wchar)[] range = str; - } - else - { - alias range = str; - } - - auto oldLength = length; - - while (!range.empty) - { - reserve(length + 4); - - auto ret = insertWideChar(range.front); - if (ret > 0) - { - range.popFront(); - } - else if (range.front - 0xd800 < 2048) - { // Surrogate pair. - static if (isNarrowString!R) - { - if (range.length < 2 || range[1] - 0xdc00 >= 0x400) - { - throw defaultAllocator.make!UTFException("Invalid UTF-16 sequeunce"); - } - dchar d = (range[0] - 0xd800) | ((range[1] - 0xdc00) >> 10); - - range.popFront(2); - } - else - { - dchar d = range.front - 0xd800; - range.popFront(); - - if (range.empty || range.front - 0xdc00 >= 0x400) - { - throw defaultAllocator.make!UTFException("Invalid UTF-16 sequeunce"); - } - d |= (range.front - 0xdc00) >> 10; - - range.popFront(); - } - write4Bytes(d); - } - else - { - throw defaultAllocator.make!UTFException("Invalid UTF-16 sequeunce"); - } - } - return this.length_ - oldLength; - } - - /// Ditto. - size_t insertBack(R)(R str) @trusted - if (!isInfinite!R - && isInputRange!R - && is(Unqual!(ElementEncodingType!R) == dchar)) - { - static if (hasLength!R || isSomeString!R) - { - reserve(length + str.length * 4); - } - - size_t insertedLength; - foreach (const dchar c; str) - { - insertedLength += insertBack(c); - } - return insertedLength; - } - - /// Ditto. - alias insert = insertBack; - - /** - * Reserves $(D_PARAM size) bytes for the string. - * - * If $(D_PARAM size) is less than or equal to the $(D_PSYMBOL capacity), the - * function call does not cause a reallocation and the string capacity is not - * affected. - * - * Params: - * size = Desired size in bytes. - */ - void reserve(const size_t size) nothrow @trusted @nogc - { - if (this.capacity_ >= size) - { - return; - } - - this.data = allocator.resize(this.data[0 .. this.capacity_], size).ptr; - this.capacity_ = size; - } - - /// - @nogc @safe unittest - { - String s; - assert(s.capacity == 0); - - s.reserve(3); - assert(s.capacity == 3); - - s.reserve(3); - assert(s.capacity == 3); - - s.reserve(1); - assert(s.capacity == 3); - } - - /** - * Requests the string to reduce its capacity to fit the $(D_PARAM size). - * - * The request is non-binding. The string won't become smaller than the - * string byte length. - * - * Params: - * size = Desired size. - */ - void shrink(const size_t size) nothrow @trusted @nogc - { - if (this.capacity_ <= size) - { - return; - } - - const n = max(this.length_, size); - void[] buf = this.data[0 .. this.capacity_]; - if (allocator.reallocate(buf, n)) - { - this.capacity_ = n; - this.data = cast(char*) buf; - } - } - - /// - @nogc @safe unittest - { - auto s = String("Die Alten lasen laut."); - assert(s.capacity == 21); - - s.reserve(30); - s.shrink(25); - assert(s.capacity == 25); - - s.shrink(18); - assert(s.capacity == 21); - } - - /** - * Returns: String capacity in bytes. - */ - @property size_t capacity() const pure nothrow @safe @nogc - { - return this.capacity_; - } - - /// - unittest - { - auto s = String("In allem Schreiben ist Schamlosigkeit."); - assert(s.capacity == 38); - } - - /** - * Returns an array used internally by the string. - * The length of the returned array may be smaller than the size of the - * reserved memory for the string. - * - * Returns: The array representing the string. - */ - inout(char[]) get() inout pure nothrow @trusted @nogc - { - return this.data[0 .. this.length_]; - } - - /** - * Returns: The number of code units that are required to encode the string. - */ - @property size_t length() const pure nothrow @safe @nogc - { - return this.length_; - } - - /// - alias opDollar = length; - - /// - unittest - { - auto s = String("Piscis primuin a capite foetat."); - assert(s.length == 31); - assert(s[$ - 1] == '.'); - } - - /** - * Params: - * pos = Position. - * - * Returns: Byte at $(D_PARAM pos). - * - * Precondition: $(D_INLINECODE length > pos). - */ - ref inout(char) opIndex(const size_t pos) inout pure nothrow @trusted @nogc - in - { - assert(length > pos); - } - body - { - return *(this.data + pos); - } - - /// - unittest - { - auto s = String("Alea iacta est."); - assert(s[0] == 'A'); - assert(s[4] == ' '); - } - - /** - * Returns: Random access range that iterates over the string by bytes, in - * forward order. - */ - ByCodeUnit!char opIndex() pure nothrow @trusted @nogc - { - return typeof(return)(this, this.data, this.data + length); - } - - /// Ditto. - ByCodeUnit!(const char) opIndex() const pure nothrow @trusted @nogc - { - return typeof(return)(this, this.data, this.data + length); - } - - /// - unittest - { - auto s = String("Plutarchus"); - auto r = s[]; - assert(r.front == 'P'); - assert(r.back == 's'); - - r.popFront(); - assert(r.front == 'l'); - assert(r.back == 's'); - - r.popBack(); - assert(r.front == 'l'); - assert(r.back == 'u'); - - assert(r.length == 8); - } - - /** - * Returns: $(D_KEYWORD true) if the vector is empty. - */ - @property bool empty() const pure nothrow @safe @nogc - { - return length == 0; - } - - /** - * Params: - * i = Slice start. - * j = Slice end. - * - * Returns: A range that iterates over the string by bytes from - * index $(D_PARAM i) up to (excluding) index $(D_PARAM j). - * - * Precondition: $(D_INLINECODE i <= j && j <= length). - */ - ByCodeUnit!char opSlice(const size_t i, const size_t j) - pure nothrow @trusted @nogc - in - { - assert(i <= j); - assert(j <= length); - } - body - { - return typeof(return)(this, this.data + i, this.data + j); - } - - /// Ditto. - ByCodeUnit!(const char) opSlice(const size_t i, const size_t j) - const pure nothrow @trusted @nogc - in - { - assert(i <= j); - assert(j <= length); - } - body - { - return typeof(return)(this, this.data + i, this.data + j); - } - - /// - unittest - { - auto s = String("Vladimir Soloviev"); - auto r = s[9 .. $]; - - assert(r.front == 'S'); - assert(r.back == 'v'); - - r.popFront(); - r.popBack(); - assert(r.front == 'o'); - assert(r.back == 'e'); - - r.popFront(); - r.popBack(); - assert(r.front == 'l'); - assert(r.back == 'i'); - - r.popFront(); - r.popBack(); - assert(r.front == 'o'); - assert(r.back == 'v'); - - r.popFront(); - r.popBack(); - assert(r.empty); - } - - mixin DefaultAllocator; -} |
