From 3c23aca6a67f87e37bd858a824aa14d192f2977f Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Mon, 20 Feb 2017 12:03:49 +0100 Subject: [PATCH 01/26] Improve Vector module and reserve documentation --- source/tanya/container/vector.d | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/source/tanya/container/vector.d b/source/tanya/container/vector.d index b38c0a4..954ca20 100644 --- a/source/tanya/container/vector.d +++ b/source/tanya/container/vector.d @@ -3,6 +3,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** + * One-dimensional array. + * * Copyright: Eugene Wissner 2016-2017. * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * Mozilla Public License, v. 2.0). @@ -372,6 +374,13 @@ struct Vector(T) return capacity_; } + /// + @safe @nogc unittest + { + auto v = Vector!int(4); + assert(v.capacity == 4); + } + /** * Returns: Vector length. */ @@ -442,6 +451,10 @@ struct Vector(T) /** * 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. */ @@ -483,7 +496,7 @@ struct Vector(T) } /// - unittest + @nogc @safe unittest { Vector!int v; assert(v.capacity == 0); @@ -518,7 +531,7 @@ struct Vector(T) } /// - unittest + @nogc @safe unittest { Vector!int v; assert(v.capacity == 0); From c6a99b114e7093cc050b1d84fad8ff8bcd267261 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Wed, 1 Mar 2017 19:23:54 +0100 Subject: [PATCH 02/26] 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]); } From f90a03501bd7f0b25898f12297583dab79204c8d Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Thu, 2 Mar 2017 11:27:26 +0100 Subject: [PATCH 03/26] Move BitVector from the crypto branch --- source/tanya/container/bitvector.d | 510 +++++++++++++ source/tanya/container/buffer.d | 1116 ++++++++++++++-------------- source/tanya/container/entry.d | 10 +- source/tanya/container/package.d | 5 +- source/tanya/container/queue.d | 446 +++++------ source/tanya/container/vector.d | 78 +- 6 files changed, 1340 insertions(+), 825 deletions(-) create mode 100644 source/tanya/container/bitvector.d diff --git a/source/tanya/container/bitvector.d b/source/tanya/container/bitvector.d new file mode 100644 index 0000000..3597a73 --- /dev/null +++ b/source/tanya/container/bitvector.d @@ -0,0 +1,510 @@ +/* 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 bit 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.bitvector; + +/** + * Wrapper that allows bit manipulation on $(D_KEYWORD ubyte[]) array. + */ +struct BitVector +{ + protected ubyte[] vector; + + /** + * Params: + * array = Array should be manipulated on. + */ + this(inout(ubyte[]) array) inout pure nothrow @safe @nogc + in + { + assert(array.length <= size_t.max / 8); + assert(array !is null); + } + body + { + vector = array; + } + + /// + unittest + { + ubyte[5] array1 = [234, 3, 252, 10, 18]; + ubyte[3] array2 = [65, 13, 173]; + auto bits = BitVector(array1); + + assert(bits[] is array1); + assert(bits[] !is array2); + + bits = BitVector(array2); + assert(bits[] is array2); + } + + /** + * Returns: Number of bits in the vector. + */ + @property inout(size_t) length() inout const pure nothrow @safe @nogc + { + return vector.length * 8; + } + + /// Ditto. + inout(size_t) opDollar() inout const pure nothrow @safe @nogc + { + return vector.length * 8; + } + + /// + unittest + { + // [01000001, 00001101, 10101101] + ubyte[3] arr = [65, 13, 173]; + auto bits = BitVector(arr); + + assert(bits.length == 24); + } + + /** + * Params: + * bit = Bit position. + * + * Returns: $(D_KEYWORD true) if the bit on position $(D_PARAM bit) is set, + * $(D_KEYWORD false) if not set. + */ + inout(bool) opIndex(size_t bit) inout const pure nothrow @safe @nogc + in + { + assert(bit / 8 <= vector.length); + } + body + { + return (vector[bit / 8] & (0x80 >> (bit % 8))) != 0; + } + + /// + unittest + { + // [01000001, 00001101, 10101101] + ubyte[3] arr = [65, 13, 173]; + auto bits = BitVector(arr); + + assert(!bits[0]); + assert(bits[1]); + assert(bits[7]); + assert(!bits[8]); + assert(!bits[11]); + assert(bits[12]); + assert(bits[20]); + assert(bits[23]); + } + + /** + * Returns: Underlying array. + */ + inout(ubyte[]) opIndex() inout pure nothrow @safe @nogc + { + return vector; + } + + /// + unittest + { + // [01000001, 00001101, 10101101] + ubyte[3] arr = [65, 13, 173]; + auto bits = BitVector(arr); + + assert(bits[] is arr); + } + + /** + * Params: + * value = $(D_KEYWORD true) if the bit should be set, + * $(D_KEYWORD false) if cleared. + * bit = Bit position. + * + * Returns: $(D_PSYMBOL this). + */ + bool opIndexAssign(bool value, size_t bit) pure nothrow @safe @nogc + in + { + assert(bit / 8 <= vector.length); + } + body + { + if (value) + { + vector[bit / 8] |= (0x80 >> (bit % 8)); + } + else + { + vector[bit / 8] &= ~(0x80 >> (bit % 8)); + } + return value; + } + + /// + unittest + { + // [01000001, 00001101, 10101101] + ubyte[3] arr = [65, 13, 173]; + auto bits = BitVector(arr); + + bits[5] = bits[6] = true; + assert(bits[][0] == 71); + + bits[14] = true; + bits[15] = false; + assert(bits[][1] == 14); + + bits[16] = bits[23] = false; + assert(bits[][2] == 44); + } + + /** + * Copies bits from $(D_PARAM vector) into this $(D_PSYMBOL BitVector). + * + * The array that should be assigned, can be smaller (but not larger) than + * the underlying array of this $(D_PSYMBOL BitVector), leading zeros will + * be added in this case to the left. + * + * Params: + * vector = $(D_KEYWORD ubyte[]) array not larger than + * `$(D_PSYMBOL length) / 8`. + * + * Returns: $(D_KEYWORD this). + */ + BitVector opAssign(ubyte[] vector) pure nothrow @safe @nogc + in + { + assert(vector.length <= this.vector.length); + } + body + { + immutable delta = this.vector.length - vector.length; + if (delta > 0) + { + this.vector[0..delta] = 0; + } + this.vector[delta..$] = vector[0..$]; + return this; + } + + /// + unittest + { + ubyte[5] array1 = [234, 3, 252, 10, 18]; + ubyte[3] array2 = [65, 13, 173]; + auto bits = BitVector(array1); + + bits = array2; + assert(bits[][0] == 0); + assert(bits[][1] == 0); + assert(bits[][2] == 65); + assert(bits[][3] == 13); + assert(bits[][4] == 173); + + bits = array2[0..2]; + assert(bits[][0] == 0); + assert(bits[][1] == 0); + assert(bits[][2] == 0); + assert(bits[][3] == 65); + assert(bits[][4] == 13); + } + + /** + * Support for bitwise operations. + * + * Params: + * that = Another bit vector. + * + * Returns: $(D_KEYWORD this). + */ + BitVector opOpAssign(string op)(BitVector that) pure nothrow @safe @nogc + if ((op == "^") || (op == "|") || (op == "&")) + { + return opOpAssign(op)(that.vector); + } + + /// Ditto. + BitVector opOpAssign(string op)(ubyte[] that) pure nothrow @safe @nogc + if ((op == "^") || (op == "|") || (op == "&")) + in + { + assert(that.length <= vector.length); + } + body + { + for (int i = cast(int) vector.length - 1; i >= 0; --i) + { + mixin("vector[i] " ~ op ~ "= " ~ "that[i];"); + } + immutable delta = vector.length - that.length; + if (delta) + { + static if (op == "&") + { + vector[0..delta] = 0; + } + } + return this; + } + + /// + unittest + { + // [01000001, 00001101, 10101101] + ubyte[3] array1 = [65, 13, 173]; + ubyte[3] array2 = [0b01010010, 0b10111110, 0b10111110]; + auto bits = BitVector(array1); + + bits |= array2; + assert(bits[][0] == 0b01010011); + assert(bits[][1] == 0b10111111); + assert(bits[][2] == 0b10111111); + + bits &= array2; + assert(bits[][0] == array2[0]); + assert(bits[][1] == array2[1]); + assert(bits[][2] == array2[2]); + + bits ^= array2; + assert(bits[][0] == 0); + assert(bits[][1] == 0); + assert(bits[][2] == 0); + } + + /** + * Support for shift operations. + * + * Params: + * n = Number of bits. + * + * Returns: $(D_KEYWORD this). + */ + BitVector opOpAssign(string op)(in size_t n) pure nothrow @safe @nogc + if ((op == "<<") || (op == ">>")) + { + if (n >= length) + { + vector[0..$] = 0; + } + else if (n != 0) + { + immutable bit = n % 8, step = n / 8; + immutable delta = 8 - bit; + size_t i, j; + + static if (op == "<<") + { + for (j = step; j < vector.length - 1; ++i) + { + vector[i] = cast(ubyte)((vector[j] << bit) + | vector[++j] >> delta); + } + vector[i] = cast(ubyte)(vector[j] << bit); + vector[$ - step ..$] = 0; + } + else static if (op == ">>") + { + for (i = vector.length - 1, j = i - step; j > 0; --i) + { + vector[i] = cast(ubyte)((vector[j] >> bit) + | vector[--j] << delta); + } + vector[i] = cast(ubyte)(vector[j] >> bit); + vector[0..step] = 0; + } + } + return this; + } + + /// + nothrow @safe @nogc unittest + { + ubyte[4] arr = [0b10111110, 0b11110010, 0b01010010, 0b01010011]; + auto bits = BitVector(arr); + + bits <<= 0; + assert(bits[][0] == 0b10111110 && bits[][1] == 0b11110010 + && bits[][2] == 0b01010010 && bits[][3] == 0b01010011); + + bits <<= 2; + assert(bits[][0] == 0b11111011 && bits[][1] == 0b11001001 + && bits[][2] == 0b01001001 && bits[][3] == 0b01001100); + + bits <<= 4; + assert(bits[][0] == 0b10111100 && bits[][1] == 0b10010100 + && bits[][2] == 0b10010100 && bits[][3] == 0b11000000); + + bits <<= 8; + assert(bits[][0] == 0b10010100 && bits[][1] == 0b10010100 + && bits[][2] == 0b11000000 && bits[][3] == 0b00000000); + + bits <<= 7; + assert(bits[][0] == 0b01001010 && bits[][1] == 0b01100000 + && bits[][2] == 0b00000000 && bits[][3] == 0b00000000); + + bits <<= 25; + assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000 + && bits[][2] == 0b00000000 && bits[][3] == 0b00000000); + + arr = [0b00110011, 0b11001100, 0b11111111, 0b01010101]; + bits <<= 24; + assert(bits[][0] == 0b01010101 && bits[][1] == 0b00000000 + && bits[][2] == 0b00000000 && bits[][3] == 0b00000000); + + arr[1] = 0b11001100; + arr[2] = 0b11111111; + arr[3] = 0b01010101; + bits <<= 12; + assert(bits[][0] == 0b11001111 && bits[][1] == 0b11110101 + && bits[][2] == 0b01010000 && bits[][3] == 0b00000000); + + bits <<= 100; + assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000 + && bits[][2] == 0b00000000 && bits[][3] == 0b00000000); + + arr = [0b10111110, 0b11110010, 0b01010010, 0b01010011]; + bits >>= 0; + assert(bits[][0] == 0b10111110 && bits[][1] == 0b11110010 + && bits[][2] == 0b01010010 && bits[][3] == 0b01010011); + + bits >>= 2; + assert(bits[][0] == 0b00101111 && bits[][1] == 0b10111100 + && bits[][2] == 0b10010100 && bits[][3] == 0b10010100); + + bits >>= 4; + assert(bits[][0] == 0b00000010 && bits[][1] == 0b11111011 + && bits[][2] == 0b11001001 && bits[][3] == 0b01001001); + + bits >>= 8; + assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000010 + && bits[][2] == 0b11111011 && bits[][3] == 0b11001001); + + bits >>= 7; + assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000 + && bits[][2] == 0b00000101 && bits[][3] == 0b11110111); + + bits >>= 25; + assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000 + && bits[][2] == 0b00000000 && bits[][3] == 0b00000000); + + arr = [0b00110011, 0b11001100, 0b11111111, 0b01010101]; + bits >>= 24; + assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000 + && bits[][2] == 0b00000000 && bits[][3] == 0b00110011); + + arr[1] = 0b11001100; + arr[2] = 0b11111111; + arr[3] = 0b01010101; + bits >>= 12; + assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000 + && bits[][2] == 0b00001100 && bits[][3] == 0b11001111); + + bits >>= 100; + assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000 + && bits[][2] == 0b00000000 && bits[][3] == 0b00000000); + } + + /** + * Negates all bits. + * + * Returns: $(D_KEYWORD this). + */ + BitVector opUnary(string op)() pure nothrow @safe @nogc + if (op == "~") + { + foreach (ref b; vector) + { + b = ~b; + } + return this; + } + + /// + unittest + { + // [01000001, 00001101, 10101101] + ubyte[3] arr = [65, 13, 173]; + auto bits = BitVector(arr); + + ~bits; + assert(bits[][0] == 0b10111110); + assert(bits[][1] == 0b11110010); + assert(bits[][2] == 0b01010010); + } + + /** + * Iterates through all bits. + * + * Params: + * dg = $(D_KEYWORD foreach) delegate. + * + * Returns: By $(D_PARAM dg) returned value. + */ + int opApply(int delegate(size_t, bool) dg) + { + int result; + foreach (i, ref v; vector) + { + foreach (c; 0..8) + { + result = dg(i * 8 + c, (v & (0x80 >> c)) != 0); + if (result) + { + return result; + } + } + } + return result; + } + + /// Ditto. + int opApply(int delegate(bool) dg) + { + int result; + foreach (ref v; vector) + { + foreach (c; 0..8) + { + result = dg((v & (0x80 >> c)) != 0); + if (result) + { + return result; + } + } + } + return result; + } + + /// + unittest + { + ubyte[2] arr = [0b01000001, 0b00001101]; + auto bits = BitVector(arr); + size_t c; + + foreach (i, v; bits) + { + assert(i == c); + if (i == 1 || i == 7 || i == 15 || i == 13 || i == 12) + { + assert(v); + } + else + { + assert(!v); + } + ++c; + } + assert(c == 16); + } +} diff --git a/source/tanya/container/buffer.d b/source/tanya/container/buffer.d index 4d01ad5..9dde930 100644 --- a/source/tanya/container/buffer.d +++ b/source/tanya/container/buffer.d @@ -7,7 +7,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.buffer; import std.traits; @@ -15,23 +15,23 @@ import tanya.memory; version (unittest) { - private int fillBuffer(ubyte[] buffer, - in size_t size, - int start = 0, - int end = 10) @nogc pure nothrow - in - { - assert(start < end); - } - body - { - auto numberRead = end - start; - for (ubyte i; i < numberRead; ++i) - { - buffer[i] = cast(ubyte) (start + i); - } - return numberRead; - } + private int fillBuffer(ubyte[] buffer, + in size_t size, + int start = 0, + int end = 10) @nogc pure nothrow + in + { + assert(start < end); + } + body + { + auto numberRead = end - start; + for (ubyte i; i < numberRead; ++i) + { + buffer[i] = cast(ubyte) (start + i); + } + return numberRead; + } } /** @@ -45,253 +45,253 @@ version (unittest) * of the pended asynchronous call. * * Params: - * T = Buffer type. + * T = Buffer type. */ struct ReadBuffer(T = ubyte) - if (isScalarType!T) + if (isScalarType!T) { - /// Internal buffer. - private T[] buffer_; + /// Internal buffer. + private T[] buffer_; - /// Filled buffer length. - private size_t length_; + /// Filled buffer length. + private size_t length_; - /// Start of available data. - private size_t start; + /// Start of available data. + private size_t start; - /// Last position returned with $(D_KEYWORD []). - private size_t ring; + /// Last position returned with $(D_KEYWORD []). + private size_t ring; - /// Available space. - private immutable size_t minAvailable = 1024; + /// Available space. + private immutable size_t minAvailable = 1024; - /// Size by which the buffer will grow. - private immutable size_t blockSize = 8192; + /// Size by which the buffer will grow. + private immutable size_t blockSize = 8192; - invariant - { - assert(length_ <= buffer_.length); - assert(blockSize > 0); - assert(minAvailable > 0); - } + invariant + { + assert(length_ <= buffer_.length); + assert(blockSize > 0); + assert(minAvailable > 0); + } - /** - * Creates a new read buffer. - * - * Params: - * size = Initial buffer size and the size by which the buffer - * will grow. - * minAvailable = minimal size should be always available to fill. - * So it will reallocate if $(D_INLINECODE - * $(D_PSYMBOL free) < $(D_PARAM minAvailable)). - * allocator = Allocator. - */ - this(in size_t size, - in size_t minAvailable = 1024, - shared Allocator allocator = defaultAllocator) @trusted - { - this(allocator); - this.minAvailable = minAvailable; - this.blockSize = size; - buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof); - } + /** + * Creates a new read buffer. + * + * Params: + * size = Initial buffer size and the size by which the buffer + * will grow. + * minAvailable = minimal size should be always available to fill. + * So it will reallocate if $(D_INLINECODE + * $(D_PSYMBOL free) < $(D_PARAM minAvailable)). + * allocator = Allocator. + */ + this(in size_t size, + in size_t minAvailable = 1024, + shared Allocator allocator = defaultAllocator) @trusted + { + this(allocator); + this.minAvailable = minAvailable; + this.blockSize = size; + buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof); + } - /// Ditto. - this(shared Allocator allocator) - in - { - assert(allocator_ is null); - } - body - { - allocator_ = allocator; - } + /// Ditto. + this(shared Allocator allocator) + in + { + assert(allocator_ is null); + } + body + { + allocator_ = allocator; + } - /** - * Deallocates the internal buffer. - */ - ~this() @trusted - { - allocator.deallocate(buffer_); - } + /** + * Deallocates the internal buffer. + */ + ~this() @trusted + { + allocator.deallocate(buffer_); + } - /// - unittest - { - ReadBuffer!ubyte b; - assert(b.capacity == 0); - assert(b.length == 0); - } + /// + unittest + { + ReadBuffer!ubyte b; + assert(b.capacity == 0); + assert(b.length == 0); + } - /** - * Returns: The size of the internal buffer. - */ - @property size_t capacity() const - { - return buffer_.length; - } + /** + * Returns: The size of the internal buffer. + */ + @property size_t capacity() const + { + return buffer_.length; + } - /** - * Returns: Data size. - */ - @property size_t length() const - { - return length_ - start; - } + /** + * Returns: Data size. + */ + @property size_t length() const + { + return length_ - start; + } - /// Ditto. - alias opDollar = length; + /// Ditto. + alias opDollar = length; - /** - * Clears the buffer. - * - * Returns: $(D_KEYWORD this). - */ - void clear() - { - start = length_ = ring; - } + /** + * Clears the buffer. + * + * Returns: $(D_KEYWORD this). + */ + void clear() + { + start = length_ = ring; + } - /** - * Returns: Available space. - */ - @property size_t free() const - { - return length > ring ? capacity - length : capacity - ring; - } + /** + * Returns: Available space. + */ + @property size_t free() const + { + return length > ring ? capacity - length : capacity - ring; + } - /// - unittest - { - ReadBuffer!ubyte b; - size_t numberRead; + /// + unittest + { + ReadBuffer!ubyte b; + size_t numberRead; - assert(b.free == 0); + assert(b.free == 0); - // Fills the buffer with values 0..10 - numberRead = fillBuffer(b[], b.free, 0, 10); - b += numberRead; - assert(b.free == b.blockSize - numberRead); - b.clear(); - assert(b.free == b.blockSize); - } + // Fills the buffer with values 0..10 + numberRead = fillBuffer(b[], b.free, 0, 10); + b += numberRead; + assert(b.free == b.blockSize - numberRead); + b.clear(); + assert(b.free == b.blockSize); + } - /** - * Appends some data to the buffer. - * - * Params: - * length = Number of the bytes read. - * - * Returns: $(D_KEYWORD this). - */ - ref ReadBuffer opOpAssign(string op)(in size_t length) - if (op == "+") - { - length_ += length; - ring = start; - return this; - } + /** + * Appends some data to the buffer. + * + * Params: + * length = Number of the bytes read. + * + * Returns: $(D_KEYWORD this). + */ + ref ReadBuffer opOpAssign(string op)(in size_t length) + if (op == "+") + { + length_ += length; + ring = start; + return this; + } - /// - unittest - { - ReadBuffer!ubyte b; - size_t numberRead; - ubyte[] result; + /// + unittest + { + ReadBuffer!ubyte b; + size_t numberRead; + ubyte[] result; - // Fills the buffer with values 0..10 - numberRead = fillBuffer(b[], b.free, 0, 10); - b += numberRead; + // Fills the buffer with values 0..10 + numberRead = fillBuffer(b[], b.free, 0, 10); + b += numberRead; - result = b[0 .. $]; - assert(result[0] == 0); - assert(result[1] == 1); - assert(result[9] == 9); - b.clear(); + result = b[0 .. $]; + assert(result[0] == 0); + assert(result[1] == 1); + assert(result[9] == 9); + b.clear(); - // It shouldn't overwrite, but append another 5 bytes to the buffer - numberRead = fillBuffer(b[], b.free, 0, 10); - b += numberRead; + // It shouldn't overwrite, but append another 5 bytes to the buffer + numberRead = fillBuffer(b[], b.free, 0, 10); + b += numberRead; - numberRead = fillBuffer(b[], b.free, 20, 25); - b += numberRead; + numberRead = fillBuffer(b[], b.free, 20, 25); + b += numberRead; - result = b[0..$]; - assert(result[0] == 0); - assert(result[1] == 1); - assert(result[9] == 9); - assert(result[10] == 20); - assert(result[14] == 24); - } + result = b[0..$]; + assert(result[0] == 0); + assert(result[1] == 1); + assert(result[9] == 9); + assert(result[10] == 20); + assert(result[14] == 24); + } - /** - * Params: - * start = Start position. - * end = End position. - * - * Returns: Array between $(D_PARAM start) and $(D_PARAM end). - */ - T[] opSlice(in size_t start, in size_t end) - { - return buffer_[this.start + start .. this.start + end]; - } + /** + * Params: + * start = Start position. + * end = End position. + * + * Returns: Array between $(D_PARAM start) and $(D_PARAM end). + */ + T[] opSlice(in size_t start, in size_t end) + { + return buffer_[this.start + start .. this.start + end]; + } - /** - * Returns a free chunk of the buffer. - * - * Add ($(D_KEYWORD +=)) the number of the read bytes after using it. - * - * Returns: A free chunk of the buffer. - */ - T[] opIndex() - { - if (start > 0) - { - auto ret = buffer_[0 .. start]; - ring = 0; - return ret; - } - else - { - if (capacity - length < minAvailable) - { - void[] buf = buffer_; - immutable cap = capacity; - () @trusted { - allocator.reallocate(buf, (cap + blockSize) * T.sizeof); - buffer_ = cast(T[]) buf; - }(); - } - ring = length_; - return buffer_[length_ .. $]; - } - } + /** + * Returns a free chunk of the buffer. + * + * Add ($(D_KEYWORD +=)) the number of the read bytes after using it. + * + * Returns: A free chunk of the buffer. + */ + T[] opIndex() + { + if (start > 0) + { + auto ret = buffer_[0 .. start]; + ring = 0; + return ret; + } + else + { + if (capacity - length < minAvailable) + { + void[] buf = buffer_; + immutable cap = capacity; + () @trusted { + allocator.reallocate(buf, (cap + blockSize) * T.sizeof); + buffer_ = cast(T[]) buf; + }(); + } + ring = length_; + return buffer_[length_ .. $]; + } + } - /// - unittest - { - ReadBuffer!ubyte b; - size_t numberRead; - ubyte[] result; + /// + unittest + { + ReadBuffer!ubyte b; + size_t numberRead; + ubyte[] result; - // Fills the buffer with values 0..10 - numberRead = fillBuffer(b[], b.free, 0, 10); - b += numberRead; + // Fills the buffer with values 0..10 + numberRead = fillBuffer(b[], b.free, 0, 10); + b += numberRead; - assert(b.length == 10); - result = b[0 .. $]; - assert(result[0] == 0); - assert(result[9] == 9); - b.clear(); - assert(b.length == 0); - } + assert(b.length == 10); + result = b[0 .. $]; + assert(result[0] == 0); + assert(result[9] == 9); + b.clear(); + assert(b.length == 0); + } - mixin DefaultAllocator; + mixin DefaultAllocator; } private unittest { - static assert(is(ReadBuffer!int)); + static assert(is(ReadBuffer!int)); } /** @@ -304,385 +304,385 @@ private unittest * reading, because it may allocate and move elements. * * Params: - * T = Buffer type. + * T = Buffer type. */ struct WriteBuffer(T = ubyte) - if (isScalarType!T) + if (isScalarType!T) { - /// Internal buffer. - private T[] buffer_; + /// Internal buffer. + private T[] buffer_; - /// Buffer start position. - private size_t start; + /// Buffer start position. + private size_t start; - /// Buffer ring area size. After this position begins buffer overflow area. - private size_t ring; + /// Buffer ring area size. After this position begins buffer overflow area. + private size_t ring; - /// Size by which the buffer will grow. - private immutable size_t blockSize; + /// Size by which the buffer will grow. + private immutable size_t blockSize; - /// The position of the free area in the buffer. - private size_t position; + /// The position of the free area in the buffer. + private size_t position; - invariant - { - assert(blockSize > 0); - // Position can refer to an element outside the buffer if the buffer is full. - assert(position <= buffer_.length); - } + invariant + { + assert(blockSize > 0); + // Position can refer to an element outside the buffer if the buffer is full. + assert(position <= buffer_.length); + } - /** - * Params: - * size = Initial buffer size and the size by which the buffer will - * grow. - * allocator = Allocator. - * - * Precondition: $(D_INLINECODE size > 0 && allocator !is null) - */ - this(in size_t size, shared Allocator allocator = defaultAllocator) @trusted - in - { - assert(size > 0); - assert(allocator !is null); - } - body - { - blockSize = size; - ring = size - 1; - allocator_ = allocator; - buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof); - } + /** + * Params: + * size = Initial buffer size and the size by which the buffer will + * grow. + * allocator = Allocator. + * + * Precondition: $(D_INLINECODE size > 0 && allocator !is null) + */ + this(in size_t size, shared Allocator allocator = defaultAllocator) @trusted + in + { + assert(size > 0); + assert(allocator !is null); + } + body + { + blockSize = size; + ring = size - 1; + allocator_ = allocator; + buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof); + } - @disable this(); + @disable this(); - /** - * Deallocates the internal buffer. - */ - ~this() - { - allocator.deallocate(buffer_); - } + /** + * Deallocates the internal buffer. + */ + ~this() + { + allocator.deallocate(buffer_); + } - /** - * Returns: The size of the internal buffer. - */ - @property size_t capacity() const - { - return buffer_.length; - } + /** + * Returns: The size of the internal buffer. + */ + @property size_t capacity() const + { + return buffer_.length; + } - /** - * Note that $(D_PSYMBOL length) doesn't return the real length of the data, - * but only the array length that will be returned with $(D_PSYMBOL opIndex) - * next time. Be sure to call $(D_PSYMBOL opIndex) and set $(D_KEYWORD +=) - * until $(D_PSYMBOL length) returns 0. - * - * Returns: Data size. - */ - @property size_t length() const - { - if (position > ring || position < start) // Buffer overflowed - { - return ring - start + 1; - } - else - { - return position - start; - } - } + /** + * Note that $(D_PSYMBOL length) doesn't return the real length of the data, + * but only the array length that will be returned with $(D_PSYMBOL opIndex) + * next time. Be sure to call $(D_PSYMBOL opIndex) and set $(D_KEYWORD +=) + * until $(D_PSYMBOL length) returns 0. + * + * Returns: Data size. + */ + @property size_t length() const + { + if (position > ring || position < start) // Buffer overflowed + { + return ring - start + 1; + } + else + { + return position - start; + } + } - /// Ditto. - alias opDollar = length; + /// Ditto. + alias opDollar = length; - /// - unittest - { - auto b = WriteBuffer!ubyte(4); - ubyte[3] buf = [48, 23, 255]; + /// + unittest + { + auto b = WriteBuffer!ubyte(4); + ubyte[3] buf = [48, 23, 255]; - b ~= buf; - assert(b.length == 3); - b += 2; - assert(b.length == 1); + b ~= buf; + assert(b.length == 3); + b += 2; + assert(b.length == 1); - b ~= buf; - assert(b.length == 2); - b += 2; - assert(b.length == 2); + b ~= buf; + assert(b.length == 2); + b += 2; + assert(b.length == 2); - b ~= buf; - assert(b.length == 5); - b += b.length; - assert(b.length == 0); - } + b ~= buf; + assert(b.length == 5); + b += b.length; + assert(b.length == 0); + } - /** - * Returns: Available space. - */ - @property size_t free() const - { - return capacity - length; - } + /** + * Returns: Available space. + */ + @property size_t free() const + { + return capacity - length; + } - /** - * Appends data to the buffer. - * - * Params: - * buffer = Buffer chunk got with $(D_PSYMBOL opIndex). - */ - ref WriteBuffer opOpAssign(string op)(in T[] buffer) - if (op == "~") - { - size_t end, start; + /** + * Appends data to the buffer. + * + * Params: + * buffer = Buffer chunk got with $(D_PSYMBOL opIndex). + */ + ref WriteBuffer opOpAssign(string op)(in T[] buffer) + if (op == "~") + { + size_t end, start; - if (position >= this.start && position <= ring) - { - auto afterRing = ring + 1; + if (position >= this.start && position <= ring) + { + auto afterRing = ring + 1; - end = position + buffer.length; - if (end > afterRing) - { - end = afterRing; - } - start = end - position; - buffer_[position .. end] = buffer[0 .. start]; - if (end == afterRing) - { - position = this.start == 0 ? afterRing : 0; - } - else - { - position = end; - } - } + end = position + buffer.length; + if (end > afterRing) + { + end = afterRing; + } + start = end - position; + buffer_[position .. end] = buffer[0 .. start]; + if (end == afterRing) + { + position = this.start == 0 ? afterRing : 0; + } + else + { + position = end; + } + } - // Check if we have some free space at the beginning - if (start < buffer.length && position < this.start) - { - end = position + buffer.length - start; - if (end > this.start) - { - end = this.start; - } - auto areaEnd = end - position + start; - buffer_[position .. end] = buffer[start .. areaEnd]; - position = end == this.start ? ring + 1 : end - position; - start = areaEnd; - } + // Check if we have some free space at the beginning + if (start < buffer.length && position < this.start) + { + end = position + buffer.length - start; + if (end > this.start) + { + end = this.start; + } + auto areaEnd = end - position + start; + buffer_[position .. end] = buffer[start .. areaEnd]; + position = end == this.start ? ring + 1 : end - position; + start = areaEnd; + } - // And if we still haven't found any place, save the rest in the overflow area - if (start < buffer.length) - { - end = position + buffer.length - start; - if (end > capacity) - { - auto newSize = (end / blockSize * blockSize + blockSize) * T.sizeof; - () @trusted { - void[] buf = buffer_; - allocator.reallocate(buf, newSize); - buffer_ = cast(T[]) buf; - }(); - } - buffer_[position .. end] = buffer[start .. $]; - position = end; - if (this.start == 0) - { - ring = capacity - 1; - } - } + // And if we still haven't found any place, save the rest in the overflow area + if (start < buffer.length) + { + end = position + buffer.length - start; + if (end > capacity) + { + auto newSize = (end / blockSize * blockSize + blockSize) * T.sizeof; + () @trusted { + void[] buf = buffer_; + allocator.reallocate(buf, newSize); + buffer_ = cast(T[]) buf; + }(); + } + buffer_[position .. end] = buffer[start .. $]; + position = end; + if (this.start == 0) + { + ring = capacity - 1; + } + } - return this; - } + return this; + } - /// - unittest - { - auto b = WriteBuffer!ubyte(4); - ubyte[3] buf = [48, 23, 255]; + /// + unittest + { + auto b = WriteBuffer!ubyte(4); + ubyte[3] buf = [48, 23, 255]; - b ~= buf; - assert(b.capacity == 4); - assert(b.buffer_[0] == 48 && b.buffer_[1] == 23 && b.buffer_[2] == 255); + b ~= buf; + assert(b.capacity == 4); + assert(b.buffer_[0] == 48 && b.buffer_[1] == 23 && b.buffer_[2] == 255); - b += 2; - b ~= buf; - assert(b.capacity == 4); - assert(b.buffer_[0] == 23 && b.buffer_[1] == 255 - && b.buffer_[2] == 255 && b.buffer_[3] == 48); + b += 2; + b ~= buf; + assert(b.capacity == 4); + assert(b.buffer_[0] == 23 && b.buffer_[1] == 255 + && b.buffer_[2] == 255 && b.buffer_[3] == 48); - b += 2; - b ~= buf; - assert(b.capacity == 8); - assert(b.buffer_[0] == 23 && b.buffer_[1] == 255 - && b.buffer_[2] == 48 && b.buffer_[3] == 23 && b.buffer_[4] == 255); - } + b += 2; + b ~= buf; + assert(b.capacity == 8); + assert(b.buffer_[0] == 23 && b.buffer_[1] == 255 + && b.buffer_[2] == 48 && b.buffer_[3] == 23 && b.buffer_[4] == 255); + } - /// - unittest - { - auto b = WriteBuffer!ubyte(2); - ubyte[3] buf = [48, 23, 255]; + /// + unittest + { + auto b = WriteBuffer!ubyte(2); + ubyte[3] buf = [48, 23, 255]; - b ~= buf; - assert(b.start == 0); - assert(b.capacity == 4); - assert(b.ring == 3); - assert(b.position == 3); - } + b ~= buf; + assert(b.start == 0); + assert(b.capacity == 4); + assert(b.ring == 3); + assert(b.position == 3); + } - /** - * Sets how many bytes were written. It will shrink the buffer - * appropriately. Always call it after $(D_PSYMBOL opIndex). - * - * Params: - * length = Length of the written data. - * - * Returns: $(D_KEYWORD this). - */ - ref WriteBuffer opOpAssign(string op)(in size_t length) - if (op == "+") - in - { - assert(length <= this.length); - } - body - { - auto afterRing = ring + 1; - auto oldStart = start; + /** + * Sets how many bytes were written. It will shrink the buffer + * appropriately. Always call it after $(D_PSYMBOL opIndex). + * + * Params: + * length = Length of the written data. + * + * Returns: $(D_KEYWORD this). + */ + ref WriteBuffer opOpAssign(string op)(in size_t length) + if (op == "+") + in + { + assert(length <= this.length); + } + body + { + auto afterRing = ring + 1; + auto oldStart = start; - if (length <= 0) - { - return this; - } - else if (position <= afterRing) - { - start += length; - if (start > 0 && position == afterRing) - { - position = oldStart; - } - } - else - { - auto overflow = position - afterRing; + if (length <= 0) + { + return this; + } + else if (position <= afterRing) + { + start += length; + if (start > 0 && position == afterRing) + { + position = oldStart; + } + } + else + { + auto overflow = position - afterRing; - if (overflow > length) - { - immutable afterLength = afterRing + length; - buffer_[start .. start + length] = buffer_[afterRing .. afterLength]; - buffer_[afterRing .. afterLength] = buffer_[afterLength .. position]; - position -= length; - } - else if (overflow == length) - { - buffer_[start .. start + overflow] = buffer_[afterRing .. position]; - position -= overflow; - } - else - { - buffer_[start .. start + overflow] = buffer_[afterRing .. position]; - position = overflow; - } - start += length; + if (overflow > length) + { + immutable afterLength = afterRing + length; + buffer_[start .. start + length] = buffer_[afterRing .. afterLength]; + buffer_[afterRing .. afterLength] = buffer_[afterLength .. position]; + position -= length; + } + else if (overflow == length) + { + buffer_[start .. start + overflow] = buffer_[afterRing .. position]; + position -= overflow; + } + else + { + buffer_[start .. start + overflow] = buffer_[afterRing .. position]; + position = overflow; + } + start += length; - if (start == position) - { - if (position != afterRing) - { - position = 0; - } - start = 0; - ring = capacity - 1; - } - } - if (start > ring) - { - start = 0; - } - return this; - } + if (start == position) + { + if (position != afterRing) + { + position = 0; + } + start = 0; + ring = capacity - 1; + } + } + if (start > ring) + { + start = 0; + } + return this; + } - /// - unittest - { - auto b = WriteBuffer!ubyte(6); - ubyte[6] buf = [23, 23, 255, 128, 127, 9]; + /// + unittest + { + auto b = WriteBuffer!ubyte(6); + ubyte[6] buf = [23, 23, 255, 128, 127, 9]; - b ~= buf; - assert(b.length == 6); - b += 2; - assert(b.length == 4); - b += 4; - assert(b.length == 0); - } + b ~= buf; + assert(b.length == 6); + b += 2; + assert(b.length == 4); + b += 4; + assert(b.length == 0); + } - /** - * Returns a chunk with data. - * - * After calling it, set $(D_KEYWORD +=) to the length could be - * written. - * - * $(D_PSYMBOL opIndex) may return only part of the data. You may need - * to call it and set $(D_KEYWORD +=) several times until - * $(D_PSYMBOL length) is 0. If all the data can be written, - * maximally 3 calls are required. - * - * Returns: A chunk of data buffer. - */ - T[] opSlice(in size_t start, in size_t end) - { - immutable internStart = this.start + start; + /** + * Returns a chunk with data. + * + * After calling it, set $(D_KEYWORD +=) to the length could be + * written. + * + * $(D_PSYMBOL opIndex) may return only part of the data. You may need + * to call it and set $(D_KEYWORD +=) several times until + * $(D_PSYMBOL length) is 0. If all the data can be written, + * maximally 3 calls are required. + * + * Returns: A chunk of data buffer. + */ + T[] opSlice(in size_t start, in size_t end) + { + immutable internStart = this.start + start; - if (position > ring || position < start) // Buffer overflowed - { - return buffer_[this.start .. ring + 1 - length + end]; - } - else - { - return buffer_[this.start .. this.start + end]; - } - } + if (position > ring || position < start) // Buffer overflowed + { + return buffer_[this.start .. ring + 1 - length + end]; + } + else + { + return buffer_[this.start .. this.start + end]; + } + } - /// - unittest - { - auto b = WriteBuffer!ubyte(6); - ubyte[6] buf = [23, 23, 255, 128, 127, 9]; + /// + unittest + { + auto b = WriteBuffer!ubyte(6); + ubyte[6] buf = [23, 23, 255, 128, 127, 9]; - b ~= buf; - assert(b[0 .. $] == buf[0 .. 6]); - b += 2; + b ~= buf; + assert(b[0 .. $] == buf[0 .. 6]); + b += 2; - assert(b[0 .. $] == buf[2 .. 6]); + assert(b[0 .. $] == buf[2 .. 6]); - b ~= buf; - assert(b[0 .. $] == buf[2 .. 6]); - b += b.length; + b ~= buf; + assert(b[0 .. $] == buf[2 .. 6]); + b += b.length; - assert(b[0 .. $] == buf[0 .. 6]); - b += b.length; - } + assert(b[0 .. $] == buf[0 .. 6]); + b += b.length; + } - /** - * After calling it, set $(D_KEYWORD +=) to the length could be - * written. - * - * $(D_PSYMBOL opIndex) may return only part of the data. You may need - * to call it and set $(D_KEYWORD +=) several times until - * $(D_PSYMBOL length) is 0. If all the data can be written, - * maximally 3 calls are required. - * - * Returns: A chunk of data buffer. - */ - T[] opIndex() - { - return opSlice(0, length); - } + /** + * After calling it, set $(D_KEYWORD +=) to the length could be + * written. + * + * $(D_PSYMBOL opIndex) may return only part of the data. You may need + * to call it and set $(D_KEYWORD +=) several times until + * $(D_PSYMBOL length) is 0. If all the data can be written, + * maximally 3 calls are required. + * + * Returns: A chunk of data buffer. + */ + T[] opIndex() + { + return opSlice(0, length); + } - mixin DefaultAllocator; + mixin DefaultAllocator; } private unittest { - static assert(is(typeof(WriteBuffer!int(5)))); + static assert(is(typeof(WriteBuffer!int(5)))); } diff --git a/source/tanya/container/entry.d b/source/tanya/container/entry.d index 1194f6b..cc74204 100644 --- a/source/tanya/container/entry.d +++ b/source/tanya/container/entry.d @@ -9,14 +9,14 @@ * 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.entry; package struct SEntry(T) { - /// Item content. - T content; + /// Item content. + T content; - /// Next item. - SEntry* next; + /// Next item. + SEntry* next; } diff --git a/source/tanya/container/package.d b/source/tanya/container/package.d index db8f025..7ba9f20 100644 --- a/source/tanya/container/package.d +++ b/source/tanya/container/package.d @@ -3,13 +3,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** + * Abstract data types whose instances are collections of other objects. + * * 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; +public import tanya.container.bitvector; public import tanya.container.buffer; public import tanya.container.list; public import tanya.container.vector; diff --git a/source/tanya/container/queue.d b/source/tanya/container/queue.d index 6388013..48cc483 100644 --- a/source/tanya/container/queue.d +++ b/source/tanya/container/queue.d @@ -3,11 +3,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** + * FIFO queue. + * * 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.queue; import core.exception; @@ -20,267 +22,267 @@ import tanya.memory; * FIFO queue. * * Params: - * T = Content type. + * T = Content type. */ struct Queue(T) { - /** - * Removes all elements from the queue. - */ - ~this() - { - while (!empty) - { - dequeue(); - } - } + /** + * Removes all elements from the queue. + */ + ~this() + { + while (!empty) + { + dequeue(); + } + } - /** - * Returns how many elements are in the queue. It iterates through the queue - * to count the elements. - * - * Returns: How many elements are in the queue. - */ - size_t length() const - { - size_t len; - for (const(SEntry!T)* i = first; i !is null; i = i.next) - { - ++len; - } - return len; - } + /** + * Returns how many elements are in the queue. It iterates through the queue + * to count the elements. + * + * Returns: How many elements are in the queue. + */ + size_t length() const + { + size_t len; + for (const(SEntry!T)* i = first; i !is null; i = i.next) + { + ++len; + } + return len; + } - /// - unittest - { - Queue!int q; + /// + unittest + { + Queue!int q; - assert(q.length == 0); - q.enqueue(5); - assert(q.length == 1); - q.enqueue(4); - assert(q.length == 2); - q.enqueue(9); - assert(q.length == 3); + assert(q.length == 0); + q.enqueue(5); + assert(q.length == 1); + q.enqueue(4); + assert(q.length == 2); + q.enqueue(9); + assert(q.length == 3); - q.dequeue(); - assert(q.length == 2); - q.dequeue(); - assert(q.length == 1); - q.dequeue(); - assert(q.length == 0); - } + q.dequeue(); + assert(q.length == 2); + q.dequeue(); + assert(q.length == 1); + q.dequeue(); + assert(q.length == 0); + } - private void enqueueEntry(ref SEntry!T* entry) - { - if (empty) - { - first = rear = entry; - } - else - { - rear.next = entry; - rear = rear.next; - } - } + private void enqueueEntry(ref SEntry!T* entry) + { + if (empty) + { + first = rear = entry; + } + else + { + rear.next = entry; + rear = rear.next; + } + } - private SEntry!T* allocateEntry() - { - auto temp = cast(SEntry!T*) allocator.allocate(SEntry!T.sizeof); - if (temp is null) - { - onOutOfMemoryError(); - } - return temp; - } + private SEntry!T* allocateEntry() + { + auto temp = cast(SEntry!T*) allocator.allocate(SEntry!T.sizeof); + if (temp is null) + { + onOutOfMemoryError(); + } + return temp; + } - /** - * Inserts a new element. - * - * Params: - * x = New element. - */ - void enqueue(ref T x) - { - auto temp = allocateEntry(); + /** + * Inserts a new element. + * + * Params: + * x = New element. + */ + void enqueue(ref T x) + { + auto temp = allocateEntry(); - *temp = SEntry!T.init; - temp.content = x; + *temp = SEntry!T.init; + temp.content = x; - enqueueEntry(temp); - } + enqueueEntry(temp); + } - /// Ditto. - void enqueue(T x) - { - auto temp = allocateEntry(); + /// Ditto. + void enqueue(T x) + { + auto temp = allocateEntry(); - moveEmplace(x, (*temp).content); - (*temp).next = null; + moveEmplace(x, (*temp).content); + (*temp).next = null; - enqueueEntry(temp); - } + enqueueEntry(temp); + } - /// - unittest - { - Queue!int q; + /// + unittest + { + Queue!int q; - assert(q.empty); - q.enqueue(8); - q.enqueue(9); - assert(q.dequeue() == 8); - assert(q.dequeue() == 9); - } + assert(q.empty); + q.enqueue(8); + q.enqueue(9); + assert(q.dequeue() == 8); + assert(q.dequeue() == 9); + } - /** - * Returns: $(D_KEYWORD true) if the queue is empty. - */ - @property bool empty() const - { - return first is null; - } + /** + * Returns: $(D_KEYWORD true) if the queue is empty. + */ + @property bool empty() const + { + return first is null; + } - /// - unittest - { - Queue!int q; - int value = 7; + /// + unittest + { + Queue!int q; + int value = 7; - assert(q.empty); - q.enqueue(value); - assert(!q.empty); - } + assert(q.empty); + q.enqueue(value); + assert(!q.empty); + } - /** - * Move the position to the next element. - * - * Returns: Dequeued element. - */ - T dequeue() - in - { - assert(!empty); - } - body - { - auto n = first.next; - T ret = move(first.content); + /** + * Move the position to the next element. + * + * Returns: Dequeued element. + */ + T dequeue() + in + { + assert(!empty); + } + body + { + auto n = first.next; + T ret = move(first.content); - allocator.dispose(first); - first = n; - return ret; - } + allocator.dispose(first); + first = n; + return ret; + } - /// - unittest - { - Queue!int q; + /// + unittest + { + Queue!int q; - q.enqueue(8); - q.enqueue(9); - assert(q.dequeue() == 8); - assert(q.dequeue() == 9); - } + q.enqueue(8); + q.enqueue(9); + assert(q.dequeue() == 8); + assert(q.dequeue() == 9); + } - /** - * $(D_KEYWORD foreach) iteration. The elements will be automatically - * dequeued. - * - * 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; + /** + * $(D_KEYWORD foreach) iteration. The elements will be automatically + * dequeued. + * + * 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; - for (size_t i = 0; !empty; ++i) - { - auto e = dequeue(); - if ((result = dg(i, e)) != 0) - { - return result; - } - } - return result; - } + for (size_t i = 0; !empty; ++i) + { + auto e = dequeue(); + if ((result = dg(i, e)) != 0) + { + return result; + } + } + return result; + } - /// Ditto. - int opApply(scope int delegate(ref T) @nogc dg) - { - int result; + /// Ditto. + int opApply(scope int delegate(ref T) @nogc dg) + { + int result; - while (!empty) - { - auto e = dequeue(); - if ((result = dg(e)) != 0) - { - return result; - } - } - return result; - } + while (!empty) + { + auto e = dequeue(); + if ((result = dg(e)) != 0) + { + return result; + } + } + return result; + } - /// - unittest - { - Queue!int q; + /// + unittest + { + Queue!int q; - size_t j; - q.enqueue(5); - q.enqueue(4); - q.enqueue(9); - foreach (i, e; q) - { - assert(i != 2 || e == 9); - assert(i != 1 || e == 4); - assert(i != 0 || e == 5); - ++j; - } - assert(j == 3); - assert(q.empty); + size_t j; + q.enqueue(5); + q.enqueue(4); + q.enqueue(9); + foreach (i, e; q) + { + assert(i != 2 || e == 9); + assert(i != 1 || e == 4); + assert(i != 0 || e == 5); + ++j; + } + assert(j == 3); + assert(q.empty); - j = 0; - q.enqueue(5); - q.enqueue(4); - q.enqueue(9); - foreach (e; q) - { - assert(j != 2 || e == 9); - assert(j != 1 || e == 4); - assert(j != 0 || e == 5); - ++j; - } - assert(j == 3); - assert(q.empty); - } + j = 0; + q.enqueue(5); + q.enqueue(4); + q.enqueue(9); + foreach (e; q) + { + assert(j != 2 || e == 9); + assert(j != 1 || e == 4); + assert(j != 0 || e == 5); + ++j; + } + assert(j == 3); + assert(q.empty); + } - private SEntry!T* first; - private SEntry!T* rear; + private SEntry!T* first; + private SEntry!T* rear; - mixin DefaultAllocator; + mixin DefaultAllocator; } /// unittest { - Queue!int q; + Queue!int q; - q.enqueue(5); - assert(!q.empty); + q.enqueue(5); + assert(!q.empty); - q.enqueue(4); - q.enqueue(9); + q.enqueue(4); + q.enqueue(9); - assert(q.dequeue() == 5); + assert(q.dequeue() == 5); - foreach (i, ref e; q) - { - assert(i != 0 || e == 4); - assert(i != 1 || e == 9); - } - assert(q.empty); + foreach (i, ref e; q) + { + assert(i != 0 || e == 4); + assert(i != 1 || e == 9); + } + assert(q.empty); } diff --git a/source/tanya/container/vector.d b/source/tanya/container/vector.d index c14e060..0f3dc00 100644 --- a/source/tanya/container/vector.d +++ b/source/tanya/container/vector.d @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** - * One-dimensional array. + * Single-dimensioned array. * * Copyright: Eugene Wissner 2016-2017. * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, @@ -166,10 +166,10 @@ struct Vector(T) * 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. + * 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) { @@ -200,9 +200,9 @@ struct Vector(T) * If $(D_PARAM init) is passed by reference, it will be copied. * * Params: - * R = Vector type. - * init = Source vector. - * allocator = Allocator. + * R = Vector type. + * init = Source vector. + * allocator = Allocator. */ this(R)(ref R init, shared Allocator allocator = defaultAllocator) if (is(Unqual!R == Vector)) @@ -268,8 +268,8 @@ struct Vector(T) * Creates a new $(D_PSYMBOL Vector). * * Params: - * len = Initial length of the vector. - * allocator = Allocator. + * len = Initial length of the vector. + * allocator = Allocator. */ this(in size_t len, shared Allocator allocator = defaultAllocator) { @@ -281,9 +281,9 @@ struct Vector(T) * Creates a new $(D_PSYMBOL Vector). * * Params: - * len = Initial length of the vector. - * init = Initial value to fill the vector with. - * allocator = Allocator. + * 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 { @@ -399,7 +399,7 @@ struct Vector(T) * Expands/shrinks the vector. * * Params: - * len = New length. + * len = New length. */ @property void length(in size_t len) @trusted { @@ -456,7 +456,7 @@ struct Vector(T) * affected. * * Params: - * size = Desired size. + * size = Desired size. */ void reserve(in size_t size) @trusted { @@ -514,7 +514,7 @@ struct Vector(T) * $(D_PARAM length). * * Params: - * size = Desired size. + * size = Desired size. */ void shrink(in size_t size) @trusted { @@ -585,7 +585,7 @@ struct Vector(T) * length, all elements are removed. * * Params: - * howMany = How many elements should be removed. + * howMany = How many elements should be removed. * * Returns: The number of elements removed */ @@ -618,7 +618,7 @@ struct Vector(T) * Remove all elements beloning to $(D_PARAM r). * * Params: - * r = Range originally obtained from this vector. + * r = Range originally obtained from this vector. * * Returns: Elements in $(D_PARAM r) after removing. * @@ -674,8 +674,8 @@ struct Vector(T) * 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. + * 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. */ @@ -767,9 +767,9 @@ struct Vector(T) * 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. + * 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. */ @@ -946,8 +946,8 @@ struct Vector(T) * Assigns a value to the element with the index $(D_PARAM pos). * * Params: - * value = Value. - * pos = Position. + * value = Value. + * pos = Position. * * Returns: Assigned value. * @@ -987,8 +987,8 @@ struct Vector(T) * Assigns a range or a static array. * * Params: - * R = Range type or static array length. - * value = Value. + * R = Range type or static array length. + * value = Value. * * Returns: Assigned value. * @@ -1025,7 +1025,7 @@ struct Vector(T) /** * Params: - * pos = Index. + * pos = Index. * * Returns: The value at a specified index. * @@ -1073,7 +1073,7 @@ struct Vector(T) * Comparison for equality. * * Params: - * that = The vector to compare with. + * that = The vector to compare with. * * Returns: $(D_KEYWORD true) if the vectors are equal, $(D_KEYWORD false) * otherwise. @@ -1099,8 +1099,8 @@ struct Vector(T) * Comparison for equality. * * Params: - * R = Right hand side type. - * that = Right hand side vector range. + * 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. @@ -1135,7 +1135,7 @@ struct Vector(T) * $(D_KEYWORD foreach) iteration. * * Params: - * dg = $(D_KEYWORD foreach) body. + * dg = $(D_KEYWORD foreach) body. * * Returns: The value returned from $(D_PARAM dg). */ @@ -1301,8 +1301,8 @@ struct Vector(T) /** * Params: - * i = Slice start. - * j = Slice end. + * 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). @@ -1380,11 +1380,11 @@ struct Vector(T) * 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. + * 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. * From 093d499729c23ca64d0742092993a8849992cc4b Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Wed, 8 Mar 2017 07:12:23 +0100 Subject: [PATCH 04/26] Fix element order inserted from a range into list --- source/tanya/container/list.d | 149 ++++++++++++++++++++++++++++++-- source/tanya/container/vector.d | 37 ++++---- 2 files changed, 159 insertions(+), 27 deletions(-) diff --git a/source/tanya/container/list.d b/source/tanya/container/list.d index 34a20a1..e318a27 100644 --- a/source/tanya/container/list.d +++ b/source/tanya/container/list.d @@ -91,6 +91,105 @@ struct SList(T) // 0th element of the list. private Entry* head; + /** + * Creates a new $(D_PSYMBOL SList) with the elements from a static array. + * + * Params: + * R = Static array size. + * init = Values to initialize the list with. + * allocator = Allocator. + */ + this(size_t R)(T[R] init, shared Allocator allocator = defaultAllocator) + { + this(allocator); + insertFront(init[]); + } + + /// + @safe @nogc unittest + { + auto l = SList!int([5, 8, 15]); + assert(l.front == 5); + } + + /** + * Creates a new $(D_PSYMBOL SList) with the elements from an input range. + * + * Params: + * R = Type of the initial range. + * init = Values to initialize the list with. + * allocator = Allocator. + */ + this(R)(R init, shared Allocator allocator = defaultAllocator) + if (!isInfinite!R + && isInputRange!R + && isImplicitlyConvertible!(ElementType!R, T)) + { + this(allocator); + insertFront(init); + } + + /** + * Creates a new $(D_PSYMBOL SList). + * + * Params: + * len = Initial length of the list. + * init = Initial value to fill the list with. + * allocator = Allocator. + */ + this(in size_t len, T init, shared Allocator allocator = defaultAllocator) @trusted + { + this(allocator); + + Entry* next; + foreach (i; 0 .. len) + { + if (next is null) + { + next = allocator.make!Entry(init); + head = next; + } + else + { + next.next = allocator.make!Entry(init); + next = next.next; + } + } + } + + /// + @safe @nogc unittest + { + auto l = SList!int(2, 3); + assert(l.length == 2); + assert(l.front == 3); + } + + /// Ditto. + this(in size_t len, shared Allocator allocator = defaultAllocator) + { + this(len, T.init, allocator); + } + + /// + @safe @nogc unittest + { + auto l = SList!int(2); + assert(l.length == 2); + assert(l.front == 0); + } + + /// Ditto. + this(shared Allocator allocator) + in + { + assert(allocator !is null); + } + body + { + this.allocator_ = allocator; + } + /** * Removes all elements from the list. */ @@ -99,6 +198,24 @@ struct SList(T) clear(); } + /** + * Copies the list. + */ + this(this) + { + auto buf = opIndex(); + head = null; + insertFront(buf); + } + + /// + unittest + { + auto l1 = SList!int([5, 1, 234]); + auto l2 = l1; + assert(l1 == l2); + } + /** * Removes all contents from the list. */ @@ -113,10 +230,9 @@ struct SList(T) /// unittest { - SList!int l; + SList!int l = SList!int([8, 5]); - l.insertFront(8); - l.insertFront(5); + assert(!l.empty); l.clear(); assert(l.empty); } @@ -164,15 +280,32 @@ struct SList(T) } /// Ditto. - size_t insertFront(R)(R el) + size_t insertFront(R)(R el) @trusted if (!isInfinite!R && isInputRange!R && isImplicitlyConvertible!(ElementType!R, T)) { size_t retLength; - foreach (e; el) + Entry* next, newHead; + + foreach (ref e; el) { - retLength += insertFront(e); + if (next is null) + { + next = allocator.make!Entry(e); + newHead = next; + } + else + { + next.next = allocator.make!Entry(e); + next = next.next; + } + ++retLength; + } + if (newHead !is null) + { + next.next = head; + head = newHead; } return retLength; } @@ -198,11 +331,11 @@ struct SList(T) SList!int l2; assert(l2.insertFront([25, 30, 15]) == 3); - assert(l2.front == 15); + assert(l2.front == 25); l2.insertFront(l1[]); assert(l2.length == 5); - assert(l2.front == 8); + assert(l2.front == 9); } /** diff --git a/source/tanya/container/vector.d b/source/tanya/container/vector.d index 0f3dc00..3010955 100644 --- a/source/tanya/container/vector.d +++ b/source/tanya/container/vector.d @@ -162,13 +162,11 @@ struct Vector(T) } /** - * Creates a new $(D_PSYMBOL Vector) with the elements from another input - * range or a static array $(D_PARAM init). + * Creates a new $(D_PSYMBOL Vector) with the elements from a static array. * * Params: - * R = Type of the initial range or size of the static array. - * init = Values to initialize the array with. - * to generate a list. + * R = Static array size. + * init = Values to initialize the vector with. * allocator = Allocator. */ this(size_t R)(T[R] init, shared Allocator allocator = defaultAllocator) @@ -177,7 +175,14 @@ struct Vector(T) insertBack!(T[])(init[]); } - /// Ditto. + /** + * 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 @@ -264,19 +269,6 @@ struct Vector(T) 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). * @@ -293,6 +285,13 @@ struct Vector(T) length_ = len; } + /// Ditto. + this(in size_t len, shared Allocator allocator = defaultAllocator) + { + this(allocator); + length = len; + } + /// Ditto. this(shared Allocator allocator) in From f4145abfd1f46f07c0da58b029b29c294f394882 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Thu, 9 Mar 2017 06:07:23 +0100 Subject: [PATCH 05/26] Add SList constructors --- source/tanya/container/list.d | 57 +++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/source/tanya/container/list.d b/source/tanya/container/list.d index e318a27..52706ac 100644 --- a/source/tanya/container/list.d +++ b/source/tanya/container/list.d @@ -190,6 +190,63 @@ struct SList(T) this.allocator_ = allocator; } + /** + * Initializes this list 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: + * init = Source list. + * allocator = Allocator. + */ + this(ref SList init, shared Allocator allocator = defaultAllocator) + { + this(init[], allocator); + } + + /// Ditto. + this(SList init, shared Allocator allocator = defaultAllocator) @trusted + { + this(allocator); + if (allocator is init.allocator) + { + head = init.head; + init.head = null; + } + else + { + Entry* next; + for (auto current = init.head; current !is null; current = current.next) + { + if (head is null) + { + head = allocator.make!Entry(move(current.content)); + next = head; + } + else + { + next.next = allocator.make!Entry(move(current.content)); + next = next.next; + } + } + } + } + + /// + @safe @nogc unittest + { + auto l1 = SList!int([5, 1, 234]); + auto l2 = SList!int(l1); + assert(l1 == l2); + } + /** * Removes all elements from the list. */ From d0ada39fa752abf7f8ad24e1353d03ea5d296e82 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sat, 18 Mar 2017 08:07:01 +0100 Subject: [PATCH 06/26] Add Mallocator as an alternative allocator --- source/tanya/container/vector.d | 48 ++++---- source/tanya/memory/mallocator.d | 185 +++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+), 28 deletions(-) create mode 100644 source/tanya/memory/mallocator.d diff --git a/source/tanya/container/vector.d b/source/tanya/container/vector.d index 3010955..da79929 100644 --- a/source/tanya/container/vector.d +++ b/source/tanya/container/vector.d @@ -95,7 +95,7 @@ private struct Range(E) --end; } - ref inout(E) opIndex(in size_t i) inout @trusted + ref inout(E) opIndex(const size_t i) inout @trusted in { assert(i < length); @@ -115,7 +115,7 @@ private struct Range(E) return typeof(return)(begin, end); } - Range opSlice(in size_t i, in size_t j) @trusted + Range opSlice(const size_t i, const size_t j) @trusted in { assert(i <= j); @@ -126,7 +126,7 @@ private struct Range(E) return typeof(return)(begin + i, begin + j); } - Range!(const E) opSlice(in size_t i, in size_t j) const @trusted + Range!(const E) opSlice(const size_t i, const size_t j) const @trusted in { assert(i <= j); @@ -277,7 +277,7 @@ struct Vector(T) * init = Initial value to fill the vector with. * allocator = Allocator. */ - this(in size_t len, T init, shared Allocator allocator = defaultAllocator) @trusted + this(const size_t len, T init, shared Allocator allocator = defaultAllocator) @trusted { this(allocator); reserve(len); @@ -286,7 +286,7 @@ struct Vector(T) } /// Ditto. - this(in size_t len, shared Allocator allocator = defaultAllocator) + this(const size_t len, shared Allocator allocator = defaultAllocator) { this(allocator); length = len; @@ -400,7 +400,7 @@ struct Vector(T) * Params: * len = New length. */ - @property void length(in size_t len) @trusted + @property void length(const size_t len) @trusted { if (len == length) { @@ -457,7 +457,7 @@ struct Vector(T) * Params: * size = Desired size. */ - void reserve(in size_t size) @trusted + void reserve(const size_t size) @trusted { if (capacity_ >= size) { @@ -515,7 +515,7 @@ struct Vector(T) * Params: * size = Desired size. */ - void shrink(in size_t size) @trusted + void shrink(const size_t size) @trusted { if (capacity_ <= size) { @@ -541,14 +541,6 @@ struct Vector(T) 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); } /** @@ -588,7 +580,7 @@ struct Vector(T) * * Returns: The number of elements removed */ - size_t removeBack(in size_t howMany) + size_t removeBack(const size_t howMany) out (removed) { assert(removed <= howMany); @@ -952,7 +944,7 @@ struct Vector(T) * * Precondition: $(D_INLINECODE length > pos) */ - ref T opIndexAssign(ref T value, in size_t pos) + ref T opIndexAssign(ref T value, const size_t pos) { return opIndex(pos) = value; } @@ -965,7 +957,7 @@ struct Vector(T) } /// Ditto. - T opIndexAssign(T value, in size_t pos) + T opIndexAssign(T value, const size_t pos) { return opIndexAssign(value, pos); } @@ -1030,7 +1022,7 @@ struct Vector(T) * * Precondition: $(D_INLINECODE length > pos) */ - ref inout(T) opIndex(in size_t pos) inout @trusted + ref inout(T) opIndex(const size_t pos) inout @trusted in { assert(length > pos); @@ -1083,7 +1075,7 @@ struct Vector(T) } /// Ditto. - bool opEquals()(in auto ref typeof(this) that) const @trusted + bool opEquals()(const auto ref typeof(this) that) const @trusted { return equal(vector[0 .. length_], that.vector[0 .. that.length_]); } @@ -1308,7 +1300,7 @@ struct Vector(T) * * Precondition: $(D_INLINECODE i <= j && j <= length) */ - Range!T opSlice(in size_t i, in size_t j) @trusted + Range!T opSlice(const size_t i, const size_t j) @trusted in { assert(i <= j); @@ -1320,7 +1312,7 @@ struct Vector(T) } /// Ditto. - Range!(const T) opSlice(in size_t i, in size_t j) const @trusted + Range!(const T) opSlice(const size_t i, const size_t j) const @trusted in { assert(i <= j); @@ -1390,7 +1382,7 @@ struct Vector(T) * 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 + Range!T opSliceAssign(R)(R value, const size_t i, const size_t j) @trusted if (!isInfinite!R && isInputRange!R && isImplicitlyConvertible!(ElementType!R, T)) @@ -1407,13 +1399,13 @@ struct Vector(T) } /// Ditto. - Range!T opSliceAssign(size_t R)(T[R] value, in size_t i, in size_t j) + Range!T opSliceAssign(size_t R)(T[R] value, const size_t i, const 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 + Range!T opSliceAssign(ref T value, const size_t i, const size_t j) @trusted in { assert(i <= j); @@ -1426,7 +1418,7 @@ struct Vector(T) } /// Ditto. - Range!T opSliceAssign(T value, in size_t i, in size_t j) + Range!T opSliceAssign(T value, const size_t i, const size_t j) { return opSliceAssign(value, i, j); } @@ -1561,7 +1553,7 @@ unittest } struct ConstEqualsStruct { - int opEquals(in typeof(this) that) const @nogc + int opEquals(const typeof(this) that) const @nogc { return true; } diff --git a/source/tanya/memory/mallocator.d b/source/tanya/memory/mallocator.d new file mode 100644 index 0000000..0e4ac7f --- /dev/null +++ b/source/tanya/memory/mallocator.d @@ -0,0 +1,185 @@ +/* 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/. */ + +/** + * Copyright: Eugene Wissner 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.memory.mallocator; + +import core.stdc.stdlib; +import std.algorithm.comparison; +import tanya.memory.allocator; + +/** + * Wrapper for malloc/realloc/free from the C standard library. + */ +final class Mallocator : Allocator +{ + /** + * Allocates $(D_PARAM size) bytes of memory. + * + * Params: + * size = Amount of memory to allocate. + * + * Returns: The pointer to the new allocated memory. + */ + void[] allocate(in size_t size) shared nothrow @nogc + { + if (!size) + { + return null; + } + auto p = malloc(size + psize); + + return p is null ? null : p[psize .. psize + size]; + } + + /// + @nogc nothrow unittest + { + auto p = Mallocator.instance.allocate(20); + + assert(p.length == 20); + + Mallocator.instance.deallocate(p); + } + + /** + * Deallocates a memory block. + * + * Params: + * p = A pointer to the memory block to be freed. + * + * Returns: Whether the deallocation was successful. + */ + bool deallocate(void[] p) shared nothrow @nogc + { + if (p !is null) + { + free(p.ptr - psize); + } + return true; + } + + /// + @nogc nothrow unittest + { + void[] p; + assert(Mallocator.instance.deallocate(p)); + + p = Mallocator.instance.allocate(10); + assert(Mallocator.instance.deallocate(p)); + } + + /** + * Reallocating in place isn't supported. + * + * Params: + * p = A pointer to the memory block. + * size = Size of the reallocated block. + * + * Returns: $(D_KEYWORD false). + */ + bool reallocateInPlace(ref void[] p, const size_t size) shared nothrow @nogc + { + return false; + } + + /** + * Increases or decreases the size of a memory block. + * + * Params: + * p = A pointer to the memory block. + * size = Size of the reallocated block. + * + * Returns: Whether the reallocation was successful. + */ + bool reallocate(ref void[] p, const size_t size) shared nothrow @nogc + { + if (size == 0) + { + if (deallocate(p)) + { + p = null; + return true; + } + } + else if (p is null) + { + p = allocate(size); + return p is null ? false : true; + } + else + { + auto r = realloc(p.ptr - psize, size + psize); + + if (r !is null) + { + p = r[psize .. psize + size]; + return true; + } + } + return false; + } + + /// + @nogc nothrow unittest + { + void[] p; + + assert(Mallocator.instance.reallocate(p, 20)); + assert(p.length == 20); + + assert(Mallocator.instance.reallocate(p, 30)); + assert(p.length == 30); + + assert(Mallocator.instance.reallocate(p, 10)); + assert(p.length == 10); + + assert(Mallocator.instance.reallocate(p, 0)); + assert(p is null); + } + + /** + * Returns: The alignment offered. + */ + @property uint alignment() shared const pure nothrow @safe @nogc + { + return cast(uint) max(double.alignof, real.alignof); + } + + /** + * Static allocator instance and initializer. + * + * Returns: The global $(D_PSYMBOL Allocator) instance. + */ + static @property ref shared(Mallocator) instance() @nogc nothrow + { + if (instance_ is null) + { + immutable size = __traits(classInstanceSize, Mallocator) + psize; + void* p = malloc(size); + + if (p !is null) + { + p[psize .. size] = typeid(Mallocator).initializer[]; + instance_ = cast(shared Mallocator) p[psize .. size].ptr; + } + } + return instance_; + } + + /// + @nogc nothrow unittest + { + assert(instance is instance); + } + + private enum psize = 8; + + private shared static Mallocator instance_; +} From b90c56395cb8a3c4cabde4acc7654dd9ceffc87e Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sun, 19 Mar 2017 06:10:27 +0100 Subject: [PATCH 07/26] Remove resizeArray alias --- source/tanya/math/random.d | 474 +++++++++++++++++----------------- source/tanya/memory/package.d | 379 +++++++++++++-------------- 2 files changed, 423 insertions(+), 430 deletions(-) diff --git a/source/tanya/math/random.d b/source/tanya/math/random.d index e58d209..76295f7 100644 --- a/source/tanya/math/random.d +++ b/source/tanya/math/random.d @@ -8,8 +8,8 @@ * Copyright: Eugene Wissner 2016. * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:belka@caraus.de, Eugene Wissner) - */ + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + */ module tanya.math.random; import std.digest.sha; @@ -27,20 +27,20 @@ enum maxGather = 128; */ class EntropyException : Exception { - /** - * Params: - * msg = Message to output. - * 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) pure @safe nothrow const @nogc - { - super(msg, file, line, next); - } + /** + * Params: + * msg = Message to output. + * 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) pure @safe nothrow const @nogc + { + super(msg, file, line, next); + } } /** @@ -48,103 +48,103 @@ class EntropyException : Exception */ abstract class EntropySource { - /// Amount of already generated entropy. - protected ushort size_; + /// Amount of already generated entropy. + protected ushort size_; - /** - * Returns: Minimum bytes required from the entropy source. - */ - @property immutable(ubyte) threshold() const @safe pure nothrow; + /** + * Returns: Minimum bytes required from the entropy source. + */ + @property immutable(ubyte) threshold() const @safe pure nothrow; - /** - * Returns: Whether this entropy source is strong. - */ - @property immutable(bool) strong() const @safe pure nothrow; + /** + * Returns: Whether this entropy source is strong. + */ + @property immutable(bool) strong() const @safe pure nothrow; - /** - * Returns: Amount of already generated entropy. - */ - @property ushort size() const @safe pure nothrow - { - return size_; - } + /** + * Returns: Amount of already generated entropy. + */ + @property ushort size() const @safe pure nothrow + { + return size_; + } - /** - * Params: - * size = Amount of already generated entropy. Cannot be smaller than the - * already set value. - */ - @property void size(ushort size) @safe pure nothrow - { - size_ = size; - } + /** + * Params: + * size = Amount of already generated entropy. Cannot be smaller than the + * already set value. + */ + @property void size(ushort size) @safe pure nothrow + { + size_ = size; + } - /** - * Poll the entropy source. - * - * Params: - * output = Buffer to save the generate random sequence (the method will - * to fill the buffer). - * - * Returns: Number of bytes that were copied to the $(D_PARAM output) - * or $(D_PSYMBOL Nullable!ubyte.init) on error. - */ - Nullable!ubyte poll(out ubyte[maxGather] output); + /** + * Poll the entropy source. + * + * Params: + * output = Buffer to save the generate random sequence (the method will + * to fill the buffer). + * + * Returns: Number of bytes that were copied to the $(D_PARAM output) + * or $(D_PSYMBOL Nullable!ubyte.init) on error. + */ + Nullable!ubyte poll(out ubyte[maxGather] output); } version (linux) { - extern (C) long syscall(long number, ...) nothrow; + extern (C) long syscall(long number, ...) nothrow; - /** - * Uses getrandom system call. - */ - class PlatformEntropySource : EntropySource - { - /** - * Returns: Minimum bytes required from the entropy source. - */ - override @property immutable(ubyte) threshold() const @safe pure nothrow - { - return 32; - } + /** + * Uses getrandom system call. + */ + class PlatformEntropySource : EntropySource + { + /** + * Returns: Minimum bytes required from the entropy source. + */ + override @property immutable(ubyte) threshold() const @safe pure nothrow + { + return 32; + } - /** - * Returns: Whether this entropy source is strong. - */ - override @property immutable(bool) strong() const @safe pure nothrow - { - return true; - } + /** + * Returns: Whether this entropy source is strong. + */ + override @property immutable(bool) strong() const @safe pure nothrow + { + return true; + } - /** - * Poll the entropy source. - * - * Params: - * output = Buffer to save the generate random sequence (the method will - * to fill the buffer). - * - * Returns: Number of bytes that were copied to the $(D_PARAM output) - * or $(D_PSYMBOL Nullable!ubyte.init) on error. - */ - override Nullable!ubyte poll(out ubyte[maxGather] output) nothrow - out (length) - { - assert(length <= maxGather); - } - body - { - // int getrandom(void *buf, size_t buflen, unsigned int flags); - auto length = syscall(318, output.ptr, output.length, 0); - Nullable!ubyte ret; + /** + * Poll the entropy source. + * + * Params: + * output = Buffer to save the generate random sequence (the method will + * to fill the buffer). + * + * Returns: Number of bytes that were copied to the $(D_PARAM output) + * or $(D_PSYMBOL Nullable!ubyte.init) on error. + */ + override Nullable!ubyte poll(out ubyte[maxGather] output) nothrow + out (length) + { + assert(length <= maxGather); + } + body + { + // int getrandom(void *buf, size_t buflen, unsigned int flags); + auto length = syscall(318, output.ptr, output.length, 0); + Nullable!ubyte ret; - if (length >= 0) - { - ret = cast(ubyte) length; - } - return ret; - } - } + if (length >= 0) + { + ret = cast(ubyte) length; + } + return ret; + } + } } /** @@ -156,165 +156,165 @@ version (linux) * * output = entropy.random; * - * defaultAllocator.finalize(entropy); + * defaultAllocator.dispose(entropy); * --- */ class Entropy { - /// Entropy sources. - protected EntropySource[] sources; + /// Entropy sources. + protected EntropySource[] sources; - private ubyte sourceCount_; + private ubyte sourceCount_; - private shared Allocator allocator; + private shared Allocator allocator; - /// Entropy accumulator. - protected SHA!(maxGather * 8, 512) accumulator; + /// Entropy accumulator. + protected SHA!(maxGather * 8, 512) accumulator; - /** - * Params: - * maxSources = Maximum amount of entropy sources can be set. - * allocator = Allocator to allocate entropy sources available on the - * system. - */ - this(size_t maxSources = 20, shared Allocator allocator = defaultAllocator) - in - { - assert(maxSources > 0 && maxSources <= ubyte.max); - assert(allocator !is null); - } - body - { - allocator.resizeArray(sources, maxSources); + /** + * Params: + * maxSources = Maximum amount of entropy sources can be set. + * allocator = Allocator to allocate entropy sources available on the + * system. + */ + this(size_t maxSources = 20, shared Allocator allocator = defaultAllocator) + in + { + assert(maxSources > 0 && maxSources <= ubyte.max); + assert(allocator !is null); + } + body + { + allocator.resize(sources, maxSources); - version (linux) - { - this ~= allocator.make!PlatformEntropySource; - } - } + version (linux) + { + this ~= allocator.make!PlatformEntropySource; + } + } - /** - * Returns: Amount of the registered entropy sources. - */ - @property ubyte sourceCount() const @safe pure nothrow - { - return sourceCount_; - } + /** + * Returns: Amount of the registered entropy sources. + */ + @property ubyte sourceCount() const @safe pure nothrow + { + return sourceCount_; + } - /** - * Add an entropy source. - * - * Params: - * source = Entropy source. - * - * Returns: $(D_PSYMBOL this). - * - * See_Also: - * $(D_PSYMBOL EntropySource) - */ - Entropy opOpAssign(string Op)(EntropySource source) @safe pure nothrow - if (Op == "~") - in - { - assert(sourceCount_ <= sources.length); - } - body - { - sources[sourceCount_++] = source; - return this; - } + /** + * Add an entropy source. + * + * Params: + * source = Entropy source. + * + * Returns: $(D_PSYMBOL this). + * + * See_Also: + * $(D_PSYMBOL EntropySource) + */ + Entropy opOpAssign(string Op)(EntropySource source) @safe pure nothrow + if (Op == "~") + in + { + assert(sourceCount_ <= sources.length); + } + body + { + sources[sourceCount_++] = source; + return this; + } - /** - * Returns: Generated random sequence. - * - * Throws: $(D_PSYMBOL EntropyException) if no strong entropy source was - * registered or it failed. - */ - @property ubyte[blockSize] random() - in - { - assert(sourceCount_ > 0, "No entropy sources defined."); - } - body - { - bool haveStrong; - ushort done; - ubyte[blockSize] output; + /** + * Returns: Generated random sequence. + * + * Throws: $(D_PSYMBOL EntropyException) if no strong entropy source was + * registered or it failed. + */ + @property ubyte[blockSize] random() + in + { + assert(sourceCount_ > 0, "No entropy sources defined."); + } + body + { + bool haveStrong; + ushort done; + ubyte[blockSize] output; - do - { - ubyte[maxGather] buffer; + do + { + ubyte[maxGather] buffer; - // Run through our entropy sources - for (ubyte i; i < sourceCount; ++i) - { - auto outputLength = sources[i].poll(buffer); + // Run through our entropy sources + for (ubyte i; i < sourceCount; ++i) + { + auto outputLength = sources[i].poll(buffer); - if (!outputLength.isNull) - { - if (outputLength > 0) - { - update(i, buffer, outputLength); - sources[i].size = cast(ushort) (sources[i].size + outputLength); - } - if (sources[i].size < sources[i].threshold) - { - continue; - } - else if (sources[i].strong) - { - haveStrong = true; - } - } - done = 257; - } - } - while (++done < 256); + if (!outputLength.isNull) + { + if (outputLength > 0) + { + update(i, buffer, outputLength); + sources[i].size = cast(ushort) (sources[i].size + outputLength); + } + if (sources[i].size < sources[i].threshold) + { + continue; + } + else if (sources[i].strong) + { + haveStrong = true; + } + } + done = 257; + } + } + while (++done < 256); - if (!haveStrong) - { - throw allocator.make!EntropyException("No strong entropy source defined."); - } + if (!haveStrong) + { + throw allocator.make!EntropyException("No strong entropy source defined."); + } - output = accumulator.finish(); + output = accumulator.finish(); - // Reset accumulator and counters and recycle existing entropy - accumulator.start(); + // Reset accumulator and counters and recycle existing entropy + accumulator.start(); - // Perform second SHA-512 on entropy - output = sha512Of(output); + // Perform second SHA-512 on entropy + output = sha512Of(output); - for (ubyte i = 0; i < sourceCount; ++i) - { - sources[i].size = 0; - } - return output; - } + for (ubyte i = 0; i < sourceCount; ++i) + { + sources[i].size = 0; + } + return output; + } - /** - * Update entropy accumulator. - * - * Params: - * sourceId = Entropy source index in $(D_PSYMBOL sources). - * data = Data got from the entropy source. - * length = Length of the received data. - */ - protected void update(in ubyte sourceId, - ref ubyte[maxGather] data, - ubyte length) @safe pure nothrow - { - ubyte[2] header; + /** + * Update entropy accumulator. + * + * Params: + * sourceId = Entropy source index in $(D_PSYMBOL sources). + * data = Data got from the entropy source. + * length = Length of the received data. + */ + protected void update(in ubyte sourceId, + ref ubyte[maxGather] data, + ubyte length) @safe pure nothrow + { + ubyte[2] header; - if (length > blockSize) - { - data[0..64] = sha512Of(data); - length = blockSize; - } + if (length > blockSize) + { + data[0..64] = sha512Of(data); + length = blockSize; + } - header[0] = sourceId; - header[1] = length; + header[0] = sourceId; + header[1] = length; - accumulator.put(header); - accumulator.put(data[0..length]); - } + accumulator.put(header); + accumulator.put(data[0..length]); + } } diff --git a/source/tanya/memory/package.d b/source/tanya/memory/package.d index 9ecb45b..da53ecf 100644 --- a/source/tanya/memory/package.d +++ b/source/tanya/memory/package.d @@ -23,59 +23,61 @@ public import tanya.memory.allocator; */ mixin template DefaultAllocator() { - /// Allocator. - protected shared Allocator allocator_; + /// Allocator. + protected shared Allocator allocator_; - /** - * Params: - * allocator = The allocator should be used. - */ - this(shared Allocator allocator) - in - { - assert(allocator !is null); - } - body - { - this.allocator_ = allocator; - } + /** + * Params: + * allocator = The allocator should be used. + * + * Precondition: $(D_INLINECODE allocator_ !is null) + */ + this(shared Allocator allocator) + in + { + assert(allocator !is null); + } + body + { + this.allocator_ = allocator; + } - /** - * This property checks if the allocator was set in the constructor - * and sets it to the default one, if not. - * - * Returns: Used allocator. - * - * Postcondition: $(D_INLINECODE allocator_ !is null) - */ - protected @property shared(Allocator) allocator() nothrow @safe @nogc - out (allocator) - { - assert(allocator !is null); - } - body - { - if (allocator_ is null) - { - allocator_ = defaultAllocator; - } - return allocator_; - } + /** + * This property checks if the allocator was set in the constructor + * and sets it to the default one, if not. + * + * Returns: Used allocator. + * + * Postcondition: $(D_INLINECODE allocator !is null) + */ + protected @property shared(Allocator) allocator() nothrow @safe @nogc + out (allocator) + { + assert(allocator !is null); + } + body + { + if (allocator_ is null) + { + allocator_ = defaultAllocator; + } + return allocator_; + } - /// Ditto. - @property shared(Allocator) allocator() const nothrow @trusted @nogc - out (allocator) - { - assert(allocator !is null); - } - body - { - if (allocator_ is null) - { - return defaultAllocator; - } - return cast(shared Allocator) allocator_; - } + /// Ditto. + @property shared(Allocator) allocator() const nothrow @trusted @nogc + out (allocator) + { + assert(allocator !is null); + } + body + { + if (allocator_ is null) + { + return defaultAllocator; + } + return cast(shared Allocator) allocator_; + } } // From druntime @@ -85,28 +87,28 @@ shared Allocator allocator; shared static this() nothrow @trusted @nogc { - import tanya.memory.mmappool; - allocator = MmapPool.instance; + import tanya.memory.mmappool; + allocator = MmapPool.instance; } @property ref shared(Allocator) defaultAllocator() nothrow @safe @nogc out (allocator) { - assert(allocator !is null); + assert(allocator !is null); } body { - return allocator; + return allocator; } @property void defaultAllocator(shared(Allocator) allocator) nothrow @safe @nogc in { - assert(allocator !is null); + assert(allocator !is null); } body { - .allocator = allocator; + .allocator = allocator; } /** @@ -114,101 +116,92 @@ body * object of type $(D_PARAM T). * * Params: - * T = Object type. + * T = Object type. */ template stateSize(T) { - static if (is(T == class) || is(T == interface)) - { - enum stateSize = __traits(classInstanceSize, T); - } - else - { - enum stateSize = T.sizeof; - } + static if (is(T == class) || is(T == interface)) + { + enum stateSize = __traits(classInstanceSize, T); + } + else + { + enum stateSize = T.sizeof; + } } /** * Params: - * size = Raw size. - * alignment = Alignment. + * size = Raw size. + * alignment = Alignment. * * Returns: Aligned size. */ size_t alignedSize(in size_t size, in size_t alignment = 8) pure nothrow @safe @nogc { - return (size - 1) / alignment * alignment + alignment; + return (size - 1) / alignment * alignment + alignment; } /** * Internal function used to create, resize or destroy a dynamic array. It - * throws $(D_PSYMBOL OutOfMemoryError) if $(D_PARAM Throws) is set. The new + * may throw $(D_PSYMBOL OutOfMemoryError). The new * allocated part of the array is initialized only if $(D_PARAM Init) * is set. This function can be trusted only in the data structures that * can ensure that the array is allocated/rellocated/deallocated with the * same allocator. * * Params: - * T = Element type of the array being created. - * Init = If should be initialized. - * Throws = If $(D_PSYMBOL OutOfMemoryError) should be throwsn. - * allocator = The allocator used for getting memory. - * array = A reference to the array being changed. - * length = New array length. + * T = Element type of the array being created. + * Init = If should be initialized. + * allocator = The allocator used for getting memory. + * array = A reference to the array being changed. + * length = New array length. * - * Returns: $(D_KEYWORD true) upon success, $(D_KEYWORD false) if memory could - * not be reallocated. In the latter + * Returns: $(D_PARAM array). */ -package(tanya) bool resize(T, - bool Init = true, - bool Throws = true) - (shared Allocator allocator, - ref T[] array, - in size_t length) @trusted +package(tanya) T[] resize(T, + bool Init = true) + (shared Allocator allocator, + auto ref T[] array, + const size_t length) @trusted { - void[] buf = array; - static if (Init) - { - immutable oldLength = array.length; - } - if (!allocator.reallocate(buf, length * T.sizeof)) - { - static if (Throws) - { - onOutOfMemoryError; - } - return false; - } - // Casting from void[] is unsafe, but we know we cast to the original type. - array = cast(T[]) buf; + void[] buf = array; + static if (Init) + { + const oldLength = array.length; + } + if (!allocator.reallocate(buf, length * T.sizeof)) + { + onOutOfMemoryError; + } + // Casting from void[] is unsafe, but we know we cast to the original type. + array = cast(T[]) buf; - static if (Init) - { - if (oldLength < length) - { - array[oldLength .. $] = T.init; - } - } - return true; + static if (Init) + { + if (oldLength < length) + { + array[oldLength .. $] = T.init; + } + } + return array; } -package(tanya) alias resizeArray = resize; -/// -unittest +private unittest { - int[] p; + int[] p; - defaultAllocator.resizeArray(p, 20); - assert(p.length == 20); + p = defaultAllocator.resize(p, 20); + assert(p.length == 20); - defaultAllocator.resizeArray(p, 30); - assert(p.length == 30); + p = defaultAllocator.resize(p, 30); + assert(p.length == 30); - defaultAllocator.resizeArray(p, 10); - assert(p.length == 10); + p = defaultAllocator.resize(p, 10); + assert(p.length == 10); - defaultAllocator.resizeArray(p, 0); - assert(p is null); + p = defaultAllocator.resize(p, 0); + assert(p is null); } /** @@ -217,101 +210,101 @@ unittest * allocator. * * Params: - * T = Type of $(D_PARAM p). - * allocator = Allocator the $(D_PARAM p) was allocated with. - * p = Object or array to be destroyed. + * T = Type of $(D_PARAM p). + * allocator = Allocator the $(D_PARAM p) was allocated with. + * p = Object or array to be destroyed. */ void dispose(T)(shared Allocator allocator, auto ref T* p) { - static if (hasElaborateDestructor!T) - { - destroy(*p); - } - () @trusted { allocator.deallocate((cast(void*) p)[0 .. T.sizeof]); }(); - p = null; + static if (hasElaborateDestructor!T) + { + destroy(*p); + } + () @trusted { allocator.deallocate((cast(void*) p)[0 .. T.sizeof]); }(); + p = null; } /// Ditto. void dispose(T)(shared Allocator allocator, auto ref T p) - if (is(T == class) || is(T == interface)) + if (is(T == class) || is(T == interface)) { - if (p is null) - { - return; - } - static if (is(T == interface)) - { - version(Windows) - { - import core.sys.windows.unknwn : IUnknown; - static assert(!is(T: IUnknown), "COM interfaces can't be destroyed in " - ~ __PRETTY_FUNCTION__); - } - auto ob = cast(Object) p; - } - else - { - alias ob = p; - } - auto ptr = cast(void *) ob; + if (p is null) + { + return; + } + static if (is(T == interface)) + { + version(Windows) + { + import core.sys.windows.unknwn : IUnknown; + static assert(!is(T: IUnknown), "COM interfaces can't be destroyed in " + ~ __PRETTY_FUNCTION__); + } + auto ob = cast(Object) p; + } + else + { + alias ob = p; + } + auto ptr = cast(void *) ob; - auto support = ptr[0 .. typeid(ob).initializer.length]; - scope (success) - { - () @trusted { allocator.deallocate(support); }(); - p = null; - } + auto support = ptr[0 .. typeid(ob).initializer.length]; + scope (success) + { + () @trusted { allocator.deallocate(support); }(); + p = null; + } - auto ppv = cast(void**) ptr; - if (!*ppv) - { - return; - } - auto pc = cast(ClassInfo*) *ppv; - scope (exit) - { - *ppv = null; - } + auto ppv = cast(void**) ptr; + if (!*ppv) + { + return; + } + auto pc = cast(ClassInfo*) *ppv; + scope (exit) + { + *ppv = null; + } - auto c = *pc; - do - { - // Assume the destructor is @nogc. Leave it nothrow since the destructor - // shouldn't throw and if it does, it is an error anyway. - if (c.destructor) - { - (cast(void function (Object) nothrow @safe @nogc) c.destructor)(ob); - } - } - while ((c = c.base) !is null); + auto c = *pc; + do + { + // Assume the destructor is @nogc. Leave it nothrow since the destructor + // shouldn't throw and if it does, it is an error anyway. + if (c.destructor) + { + (cast(void function (Object) nothrow @safe @nogc) c.destructor)(ob); + } + } + while ((c = c.base) !is null); - if (ppv[1]) // if monitor is not null - { - _d_monitordelete(cast(Object) ptr, true); - } + if (ppv[1]) // if monitor is not null + { + _d_monitordelete(cast(Object) ptr, true); + } } /// Ditto. void dispose(T)(shared Allocator allocator, auto ref T[] p) { - static if (hasElaborateDestructor!(typeof(p[0]))) - { - import std.algorithm.iteration; - p.each!(e => destroy(e)); - } - () @trusted { allocator.deallocate(p); }(); - p = null; + static if (hasElaborateDestructor!(typeof(p[0]))) + { + import std.algorithm.iteration; + p.each!(e => destroy(e)); + } + () @trusted { allocator.deallocate(p); }(); + p = null; } unittest { - struct S - { - ~this() - { - } - } - auto p = cast(S[]) defaultAllocator.allocate(S.sizeof); + struct S + { + ~this() + { + } + } + auto p = cast(S[]) defaultAllocator.allocate(S.sizeof); - defaultAllocator.dispose(p); + defaultAllocator.dispose(p); } From 85380ac3fc700817562042b659f8dfa4774e96cf Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sun, 19 Mar 2017 06:54:59 +0100 Subject: [PATCH 08/26] Remove makeArray import --- source/tanya/memory/allocator.d | 90 ++-- source/tanya/memory/package.d | 2 +- source/tanya/memory/types.d | 698 ++++++++++++++++---------------- 3 files changed, 395 insertions(+), 395 deletions(-) diff --git a/source/tanya/memory/allocator.d b/source/tanya/memory/allocator.d index c942568..560d9a1 100644 --- a/source/tanya/memory/allocator.d +++ b/source/tanya/memory/allocator.d @@ -15,53 +15,53 @@ module tanya.memory.allocator; */ interface Allocator { - /** - * Returns: Alignment offered. - */ - @property uint alignment() const shared pure nothrow @safe @nogc; + /** + * Returns: Alignment offered. + */ + @property uint alignment() const shared pure nothrow @safe @nogc; - /** - * Allocates $(D_PARAM size) bytes of memory. - * - * Params: - * size = Amount of memory to allocate. - * - * Returns: Pointer to the new allocated memory. - */ - void[] allocate(in size_t size) shared nothrow @nogc; + /** + * Allocates $(D_PARAM size) bytes of memory. + * + * Params: + * size = Amount of memory to allocate. + * + * Returns: Pointer to the new allocated memory. + */ + void[] allocate(in size_t size) shared nothrow @nogc; - /** - * Deallocates a memory block. - * - * Params: - * p = A pointer to the memory block to be freed. - * - * Returns: Whether the deallocation was successful. - */ - bool deallocate(void[] p) shared nothrow @nogc; + /** + * Deallocates a memory block. + * + * Params: + * p = A pointer to the memory block to be freed. + * + * Returns: Whether the deallocation was successful. + */ + bool deallocate(void[] p) shared nothrow @nogc; - /** - * Increases or decreases the size of a memory block. - * - * Params: - * p = A pointer to the memory block. - * size = Size of the reallocated block. - * - * Returns: Pointer to the allocated memory. - */ - bool reallocate(ref void[] p, in size_t size) shared nothrow @nogc; + /** + * Increases or decreases the size of a memory block. + * + * Params: + * p = A pointer to the memory block. + * size = Size of the reallocated block. + * + * Returns: Pointer to the allocated memory. + */ + bool reallocate(ref void[] p, in size_t size) shared nothrow @nogc; - /** - * Reallocates a memory block in place if possible or returns - * $(D_KEYWORD false). This function cannot be used to allocate or - * deallocate memory, so if $(D_PARAM p) is $(D_KEYWORD null) or - * $(D_PARAM size) is `0`, it should return $(D_KEYWORD false). - * - * Params: - * p = A pointer to the memory block. - * size = Size of the reallocated block. - * - * Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise. - */ - bool reallocateInPlace(ref void[] p, in size_t size) shared nothrow @nogc; + /** + * Reallocates a memory block in place if possible or returns + * $(D_KEYWORD false). This function cannot be used to allocate or + * deallocate memory, so if $(D_PARAM p) is $(D_KEYWORD null) or + * $(D_PARAM size) is `0`, it should return $(D_KEYWORD false). + * + * Params: + * p = A pointer to the memory block. + * size = Size of the reallocated block. + * + * Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise. + */ + bool reallocateInPlace(ref void[] p, in size_t size) shared nothrow @nogc; } diff --git a/source/tanya/memory/package.d b/source/tanya/memory/package.d index da53ecf..1959dbd 100644 --- a/source/tanya/memory/package.d +++ b/source/tanya/memory/package.d @@ -11,7 +11,7 @@ module tanya.memory; import core.exception; -public import std.experimental.allocator : make, makeArray; +public import std.experimental.allocator : make; import std.traits; public import tanya.memory.allocator; diff --git a/source/tanya/memory/types.d b/source/tanya/memory/types.d index be9c8cd..1207d73 100644 --- a/source/tanya/memory/types.d +++ b/source/tanya/memory/types.d @@ -3,10 +3,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** - * Copyright: Eugene Wissner 2016. + * 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:belka@caraus.de, Eugene Wissner) + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) */ module tanya.memory.types; @@ -23,345 +23,345 @@ import tanya.memory; * when the reference count goes down to zero, frees the underlying store. * * Params: - * T = Type of the reference-counted value. + * T = Type of the reference-counted value. */ struct RefCounted(T) { - static if (is(T == class) || is(T == interface)) - { - private alias Payload = T; - } - else - { - private alias Payload = T*; - } + static if (is(T == class) || is(T == interface)) + { + private alias Payload = T; + } + else + { + private alias Payload = T*; + } - private class Storage - { - private Payload payload; - private size_t counter = 1; + private class Storage + { + private Payload payload; + private size_t counter = 1; - private final size_t opUnary(string op)() pure nothrow @safe @nogc - if (op == "--" || op == "++") - in - { - assert(counter > 0); - } - body - { - mixin("return " ~ op ~ "counter;"); - } + private final size_t opUnary(string op)() pure nothrow @safe @nogc + if (op == "--" || op == "++") + in + { + assert(counter > 0); + } + body + { + mixin("return " ~ op ~ "counter;"); + } - private final int opCmp(size_t counter) const pure nothrow @safe @nogc - { - if (this.counter > counter) - { - return 1; - } - else if (this.counter < counter) - { - return -1; - } - else - { - return 0; - } - } + private final int opCmp(size_t counter) const pure nothrow @safe @nogc + { + if (this.counter > counter) + { + return 1; + } + else if (this.counter < counter) + { + return -1; + } + else + { + return 0; + } + } - private final int opEquals(size_t counter) const pure nothrow @safe @nogc - { - return this.counter == counter; - } - } + private final int opEquals(size_t counter) const pure nothrow @safe @nogc + { + return this.counter == counter; + } + } - private final class RefCountedStorage : Storage - { - private shared Allocator allocator; + private final class RefCountedStorage : Storage + { + private shared Allocator allocator; - this(shared Allocator allocator) pure nothrow @safe @nogc - in - { - assert(allocator !is null); - } - body - { - this.allocator = allocator; - } + this(shared Allocator allocator) pure nothrow @safe @nogc + in + { + assert(allocator !is null); + } + body + { + this.allocator = allocator; + } - ~this() nothrow @nogc - { - allocator.dispose(payload); - } - } + ~this() nothrow @nogc + { + allocator.dispose(payload); + } + } - private Storage storage; + private Storage storage; - invariant - { - assert(storage is null || allocator_ !is null); - } + invariant + { + assert(storage is null || allocator_ !is null); + } - /** - * Takes ownership over $(D_PARAM value), setting the counter to 1. - * $(D_PARAM value) may be a pointer, an object or a dynamic array. - * - * Params: - * value = Value whose ownership is taken over. - * allocator = Allocator used to destroy the $(D_PARAM value) and to - * allocate/deallocate internal storage. - * - * Precondition: $(D_INLINECODE allocator !is null) - */ - this(Payload value, shared Allocator allocator = defaultAllocator) - { - this(allocator); - storage = allocator.make!RefCountedStorage(allocator); - move(value, storage.payload); - } + /** + * Takes ownership over $(D_PARAM value), setting the counter to 1. + * $(D_PARAM value) may be a pointer, an object or a dynamic array. + * + * Params: + * value = Value whose ownership is taken over. + * allocator = Allocator used to destroy the $(D_PARAM value) and to + * allocate/deallocate internal storage. + * + * Precondition: $(D_INLINECODE allocator !is null) + */ + this(Payload value, shared Allocator allocator = defaultAllocator) + { + this(allocator); + storage = allocator.make!RefCountedStorage(allocator); + move(value, storage.payload); + } - /// Ditto. - this(shared Allocator allocator) - in - { - assert(allocator !is null); - } - body - { - this.allocator_ = allocator; - } + /// Ditto. + this(shared Allocator allocator) + in + { + assert(allocator !is null); + } + body + { + this.allocator_ = allocator; + } - /** - * Increases the reference counter by one. - */ - this(this) - { - if (count != 0) - { - ++storage; - } - } + /** + * Increases the reference counter by one. + */ + this(this) + { + if (count != 0) + { + ++storage; + } + } - /** - * Decreases the reference counter by one. - * - * If the counter reaches 0, destroys the owned object. - */ - ~this() - { - if (storage !is null && !(storage.counter && --storage)) - { - allocator_.dispose(storage); - } - } + /** + * Decreases the reference counter by one. + * + * If the counter reaches 0, destroys the owned object. + */ + ~this() + { + if (storage !is null && !(storage.counter && --storage)) + { + allocator_.dispose(storage); + } + } - /** - * Takes ownership over $(D_PARAM rhs). Initializes this - * $(D_PSYMBOL RefCounted) if needed. - * - * If it is the last reference of the previously owned object, - * it will be destroyed. - * - * To reset the $(D_PSYMBOL RefCounted) assign $(D_KEYWORD null). - * - * If the allocator wasn't set before, $(D_PSYMBOL defaultAllocator) will - * be used. If you need a different allocator, create a new - * $(D_PSYMBOL RefCounted) and assign it. - * - * Params: - * rhs = $(D_KEYWORD this). - */ - ref typeof(this) opAssign(Payload rhs) - { - if (storage is null) - { - storage = allocator.make!RefCountedStorage(allocator); - } - else if (storage > 1) - { - --storage; - storage = allocator.make!RefCountedStorage(allocator); - } - else if (cast(RefCountedStorage) storage is null) - { - // Created with refCounted. Always destroyed togethter with the pointer. - assert(storage.counter != 0); - allocator.dispose(storage); - storage = allocator.make!RefCountedStorage(allocator); - } - else - { - allocator.dispose(storage.payload); - } - move(rhs, storage.payload); - return this; - } + /** + * Takes ownership over $(D_PARAM rhs). Initializes this + * $(D_PSYMBOL RefCounted) if needed. + * + * If it is the last reference of the previously owned object, + * it will be destroyed. + * + * To reset the $(D_PSYMBOL RefCounted) assign $(D_KEYWORD null). + * + * If the allocator wasn't set before, $(D_PSYMBOL defaultAllocator) will + * be used. If you need a different allocator, create a new + * $(D_PSYMBOL RefCounted) and assign it. + * + * Params: + * rhs = $(D_KEYWORD this). + */ + ref typeof(this) opAssign(Payload rhs) + { + if (storage is null) + { + storage = allocator.make!RefCountedStorage(allocator); + } + else if (storage > 1) + { + --storage; + storage = allocator.make!RefCountedStorage(allocator); + } + else if (cast(RefCountedStorage) storage is null) + { + // Created with refCounted. Always destroyed togethter with the pointer. + assert(storage.counter != 0); + allocator.dispose(storage); + storage = allocator.make!RefCountedStorage(allocator); + } + else + { + allocator.dispose(storage.payload); + } + move(rhs, storage.payload); + return this; + } - /// Ditto. - ref typeof(this) opAssign(typeof(null)) - { - if (storage is null) - { - return this; - } - else if (storage > 1) - { - --storage; - storage = null; - } - else if (cast(RefCountedStorage) storage is null) - { - // Created with refCounted. Always destroyed togethter with the pointer. - assert(storage.counter != 0); - allocator.dispose(storage); - return this; - } - else - { - allocator.dispose(storage.payload); - } - return this; - } + /// Ditto. + ref typeof(this) opAssign(typeof(null)) + { + if (storage is null) + { + return this; + } + else if (storage > 1) + { + --storage; + storage = null; + } + else if (cast(RefCountedStorage) storage is null) + { + // Created with refCounted. Always destroyed togethter with the pointer. + assert(storage.counter != 0); + allocator.dispose(storage); + return this; + } + else + { + allocator.dispose(storage.payload); + } + return this; + } - /// Ditto. - ref typeof(this) opAssign(typeof(this) rhs) - { - swap(allocator_, rhs.allocator_); - swap(storage, rhs.storage); - return this; - } + /// Ditto. + ref typeof(this) opAssign(typeof(this) rhs) + { + swap(allocator_, rhs.allocator_); + swap(storage, rhs.storage); + return this; + } - /** - * Returns: Reference to the owned object. - */ - inout(Payload) get() inout pure nothrow @safe @nogc - in - { - assert(count > 0, "Attempted to access an uninitialized reference."); - } - body - { - return storage.payload; - } + /** + * Returns: Reference to the owned object. + */ + inout(Payload) get() inout pure nothrow @safe @nogc + in + { + assert(count > 0, "Attempted to access an uninitialized reference."); + } + body + { + return storage.payload; + } - static if (isPointer!Payload) - { - /** - * Params: - * op = Operation. - * - * Dereferences the pointer. It is defined only for pointers, not for - * reference types like classes, that can be accessed directly. - * - * Returns: Reference to the pointed value. - */ - ref T opUnary(string op)() - if (op == "*") - { - return *storage.payload; - } - } + static if (isPointer!Payload) + { + /** + * Params: + * op = Operation. + * + * Dereferences the pointer. It is defined only for pointers, not for + * reference types like classes, that can be accessed directly. + * + * Returns: Reference to the pointed value. + */ + ref T opUnary(string op)() + if (op == "*") + { + return *storage.payload; + } + } - /** - * Returns: Whether this $(D_PSYMBOL RefCounted) already has an internal - * storage. - */ - @property bool isInitialized() const - { - return storage !is null; - } + /** + * Returns: Whether this $(D_PSYMBOL RefCounted) already has an internal + * storage. + */ + @property bool isInitialized() const + { + return storage !is null; + } - /** - * Returns: The number of $(D_PSYMBOL RefCounted) instances that share - * ownership over the same pointer (including $(D_KEYWORD this)). - * If this $(D_PSYMBOL RefCounted) isn't initialized, returns `0`. - */ - @property size_t count() const - { - return storage is null ? 0 : storage.counter; - } + /** + * Returns: The number of $(D_PSYMBOL RefCounted) instances that share + * ownership over the same pointer (including $(D_KEYWORD this)). + * If this $(D_PSYMBOL RefCounted) isn't initialized, returns `0`. + */ + @property size_t count() const + { + return storage is null ? 0 : storage.counter; + } - mixin DefaultAllocator; - alias get this; + mixin DefaultAllocator; + alias get this; } /// unittest { - auto rc = RefCounted!int(defaultAllocator.make!int(5), defaultAllocator); - auto val = rc.get; + auto rc = RefCounted!int(defaultAllocator.make!int(5), defaultAllocator); + auto val = rc.get; - *val = 8; - assert(*rc.storage.payload == 8); + *val = 8; + assert(*rc.storage.payload == 8); - val = null; - assert(rc.storage.payload !is null); - assert(*rc.storage.payload == 8); + val = null; + assert(rc.storage.payload !is null); + assert(*rc.storage.payload == 8); - *rc = 9; - assert(*rc.storage.payload == 9); + *rc = 9; + assert(*rc.storage.payload == 9); } version (unittest) { - private class A - { - uint *destroyed; + private class A + { + uint *destroyed; - this(ref uint destroyed) @nogc - { - this.destroyed = &destroyed; - } + this(ref uint destroyed) @nogc + { + this.destroyed = &destroyed; + } - ~this() @nogc - { - ++(*destroyed); - } - } + ~this() @nogc + { + ++(*destroyed); + } + } - private struct B - { - int prop; - @disable this(); - this(int param1) @nogc - { - prop = param1; - } - } + private struct B + { + int prop; + @disable this(); + this(int param1) @nogc + { + prop = param1; + } + } } private unittest { - uint destroyed; - auto a = defaultAllocator.make!A(destroyed); + uint destroyed; + auto a = defaultAllocator.make!A(destroyed); - assert(destroyed == 0); - { - auto rc = RefCounted!A(a, defaultAllocator); - assert(rc.count == 1); + assert(destroyed == 0); + { + auto rc = RefCounted!A(a, defaultAllocator); + assert(rc.count == 1); - void func(RefCounted!A rc) - { - assert(rc.count == 2); - } - func(rc); + void func(RefCounted!A rc) + { + assert(rc.count == 2); + } + func(rc); - assert(rc.count == 1); - } - assert(destroyed == 1); + assert(rc.count == 1); + } + assert(destroyed == 1); - RefCounted!int rc; - assert(rc.count == 0); - rc = defaultAllocator.make!int(8); - assert(rc.count == 1); + RefCounted!int rc; + assert(rc.count == 0); + rc = defaultAllocator.make!int(8); + assert(rc.count == 1); } private unittest { - static assert(is(typeof(RefCounted!int.storage.payload) == int*)); - static assert(is(typeof(RefCounted!A.storage.payload) == A)); + static assert(is(typeof(RefCounted!int.storage.payload) == int*)); + static assert(is(typeof(RefCounted!A.storage.payload) == A)); - static assert(is(RefCounted!B)); - static assert(is(RefCounted!A)); + static assert(is(RefCounted!B)); + static assert(is(RefCounted!A)); } /** @@ -374,85 +374,85 @@ private unittest * object). * * Params: - * T = Type of the constructed object. - * A = Types of the arguments to the constructor of $(D_PARAM T). - * allocator = Allocator. - * args = Constructor arguments of $(D_PARAM T). + * T = Type of the constructed object. + * A = Types of the arguments to the constructor of $(D_PARAM T). + * allocator = Allocator. + * args = Constructor arguments of $(D_PARAM T). * * Returns: Newly created $(D_PSYMBOL RefCounted!T). */ RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args) - if (!is(T == interface) && !isAbstractClass!T + if (!is(T == interface) && !isAbstractClass!T && !isArray!T && !isAssociativeArray!T) { - auto rc = typeof(return)(allocator); + auto rc = typeof(return)(allocator); - immutable storageSize = alignedSize(stateSize!(RefCounted!T.Storage)); - immutable size = alignedSize(stateSize!T + storageSize); + immutable storageSize = alignedSize(stateSize!(RefCounted!T.Storage)); + immutable size = alignedSize(stateSize!T + storageSize); - auto mem = (() @trusted => allocator.allocate(size))(); - if (mem is null) - { - onOutOfMemoryError(); - } - scope (failure) - { - () @trusted { allocator.deallocate(mem); }(); - } - rc.storage = emplace!(RefCounted!T.Storage)(mem[0 .. storageSize]); + auto mem = (() @trusted => allocator.allocate(size))(); + if (mem is null) + { + onOutOfMemoryError(); + } + scope (failure) + { + () @trusted { allocator.deallocate(mem); }(); + } + rc.storage = emplace!(RefCounted!T.Storage)(mem[0 .. storageSize]); - static if (is(T == class)) - { - rc.storage.payload = emplace!T(mem[storageSize .. $], args); - } - else - { - auto ptr = (() @trusted => (cast(T*) mem[storageSize .. $].ptr))(); - rc.storage.payload = emplace!T(ptr, args); - } - return rc; + static if (is(T == class)) + { + rc.storage.payload = emplace!T(mem[storageSize .. $], args); + } + else + { + auto ptr = (() @trusted => (cast(T*) mem[storageSize .. $].ptr))(); + rc.storage.payload = emplace!T(ptr, args); + } + return rc; } /// unittest { - auto rc = defaultAllocator.refCounted!int(5); - assert(rc.count == 1); + auto rc = defaultAllocator.refCounted!int(5); + assert(rc.count == 1); - void func(RefCounted!int param) - { - if (param.count == 2) - { - func(param); - } - else - { - assert(param.count == 3); - } - } - func(rc); + void func(RefCounted!int param) + { + if (param.count == 2) + { + func(param); + } + else + { + assert(param.count == 3); + } + } + func(rc); - assert(rc.count == 1); + assert(rc.count == 1); } private @nogc unittest { - struct E - { - } - auto b = defaultAllocator.refCounted!B(15); - static assert(is(typeof(b.storage.payload) == B*)); - static assert(is(typeof(b.prop) == int)); - static assert(!is(typeof(defaultAllocator.refCounted!B()))); + struct E + { + } + auto b = defaultAllocator.refCounted!B(15); + static assert(is(typeof(b.storage.payload) == B*)); + static assert(is(typeof(b.prop) == int)); + static assert(!is(typeof(defaultAllocator.refCounted!B()))); - static assert(is(typeof(defaultAllocator.refCounted!E()))); - static assert(!is(typeof(defaultAllocator.refCounted!E(5)))); - { - auto rc = defaultAllocator.refCounted!B(3); - assert(rc.get.prop == 3); - } - { - auto rc = defaultAllocator.refCounted!E(); - assert(rc.count); - } + static assert(is(typeof(defaultAllocator.refCounted!E()))); + static assert(!is(typeof(defaultAllocator.refCounted!E(5)))); + { + auto rc = defaultAllocator.refCounted!B(3); + assert(rc.get.prop == 3); + } + { + auto rc = defaultAllocator.refCounted!E(); + assert(rc.count); + } } From b90517580e8ef94524433719743b9c95fb1bc149 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Tue, 21 Mar 2017 19:25:12 +0100 Subject: [PATCH 09/26] Merge math.mp.Integer changes from the crypto branch --- source/tanya/math/mp.d | 2300 +++++++++++++++++++---------------- source/tanya/math/package.d | 218 ++-- 2 files changed, 1363 insertions(+), 1155 deletions(-) diff --git a/source/tanya/math/mp.d b/source/tanya/math/mp.d index d36ec18..50efb64 100644 --- a/source/tanya/math/mp.d +++ b/source/tanya/math/mp.d @@ -1,1060 +1,1240 @@ -/* 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/. */ - -/** - * Copyright: Eugene Wissner 2016. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:belka@caraus.de, Eugene Wissner) - */ -module tanya.math.mp; - -import std.algorithm.iteration; -import std.algorithm.searching; -import std.algorithm.mutation; -import std.math; -import std.range; -import std.traits; -import tanya.memory; - -/** - * Mutliple precision integer. - */ -struct Integer -{ - package ubyte[] rep; - private bool sign; - - /** - * Creates a multiple precision integer. - * - * Params: - * T = Value type. - * value = Initial value. - * allocator = Allocator. - * - * Precondition: $(D_INLINECODE allocator !is null) - */ - this(T)(in auto ref T value, shared Allocator allocator = defaultAllocator) - nothrow @safe @nogc - if (isIntegral!T || is(T == Integer)) - { - this(allocator); - static if (isIntegral!T) - { - assignInt(value); - } - else if (value.length > 0) - { - allocator.resize!(ubyte, false)(rep, value.length); - rep[] = value.rep[]; - sign = value.sign; - } - } - - /// Ditto. - this(shared Allocator allocator) pure nothrow @safe @nogc - in - { - assert(allocator !is null); - } - body - { - allocator_ = allocator; - } - - private @nogc unittest - { - auto h1 = Integer(79); - assert(h1.length == 1); - assert(h1.rep[0] == 79); - assert(!h1.sign); - - auto h2 = Integer(-2); - assert(h2.length == 1); - assert(h2.rep[0] == 2); - assert(h2.sign); - } - - @disable this(this); - - /** - * Destroys the internal representation. - */ - ~this() nothrow @safe @nogc - { - allocator.dispose(rep); - } - - /* - * Figures out the minimum amount of space this value will take - * up in bytes and resizes the internal storage. Sets the sign. - */ - private void assignInt(T)(in ref T value) - nothrow @safe @nogc - in - { - static assert(isIntegral!T); - } - body - { - ubyte size = ulong.sizeof; - ulong mask; - - static if (isSigned!T) - { - sign = value < 0 ? true : false; - immutable absolute = value.abs; - } - else - { - sign = false; - alias absolute = value; - } - for (mask = 0xff00000000000000; mask >= 0xff; mask >>= 8) - { - if (absolute & mask) - { - break; - } - --size; - } - allocator.resize!(ubyte, false)(rep, size); - - /* Work backward through the int, masking off each byte (up to the - first 0 byte) and copy it into the internal representation in - big-endian format. */ - mask = 0xff; - ushort shift; - for (auto i = rep.length; i; --i, mask <<= 8, shift += 8) - { - rep[i - 1] = cast(ubyte) ((absolute & mask) >> shift); - } - } - - /** - * Assigns a new value. - * - * Params: - * T = Value type. - * value = Value. - * - * Returns: $(D_KEYWORD this). - */ - ref Integer opAssign(T)(in auto ref T value) nothrow @safe @nogc - if (isIntegral!T || is(T == Integer)) - { - static if (isIntegral!T) - { - assignInt(value); - } - else - { - allocator.resize!(ubyte, false)(rep, value.length); - rep[0 .. $] = value.rep[0 .. $]; - sign = value.sign; - } - return this; - } - - private @nogc unittest - { - auto h = Integer(1019); - assert(h.length == 2); - assert(h.rep[0] == 0b00000011 && h.rep[1] == 0b11111011); - - h = 3337; - assert(h.length == 2); - assert(h.rep[0] == 0b00001101 && h.rep[1] == 0b00001001); - - h = 688; - assert(h.length == 2); - assert(h.rep[0] == 0b00000010 && h.rep[1] == 0b10110000); - - h = 0; - assert(h.length == 0); - } - - /** - * Returns: Integer size. - */ - @property size_t length() const pure nothrow @safe @nogc - { - return rep.length; - } - - /** - * Params: - * h = The second integer. - * - * Returns: Whether the two integers are equal. - */ - bool opEquals(in Integer h) const nothrow @safe @nogc - { - return rep == h.rep; - } - - /// Ditto. - bool opEquals(in ref Integer h) const nothrow @safe @nogc - { - return rep == h.rep; - } - - /// - unittest - { - auto h1 = Integer(1019); - - assert(h1 == Integer(1019)); - assert(h1 != Integer(109)); - } - - /** - * Params: - * h = The second integer. - * - * Returns: A positive number if $(D_INLINECODE this > h), a negative - * number if $(D_INLINECODE this > h), `0` otherwise. - */ - int opCmp(in ref Integer h) const nothrow @safe @nogc - { - if (length > h.length) - { - return 1; - } - if (length < h.length) - { - return -1; - } - // Otherwise, keep searching through the representational integers - // until one is bigger than another - once we've found one, it's - // safe to stop, since the lower order bytes can't affect the - // comparison - for (size_t i, j; i < length && j < h.length; ++i, ++j) - { - if (rep[i] < h.rep[j]) - { - return -1; - } - else if (rep[i] > h.rep[j]) - { - return 1; - } - } - // if we got all the way to the end without a comparison, the - // two are equal - return 0; - } - - /// Ditto. - int opCmp(in Integer h) const nothrow @safe @nogc - { - return opCmp(h); - } - - /// - unittest - { - auto h1 = Integer(1019); - auto h2 = Integer(1019); - assert(h1 == h2); - - h2 = 3337; - assert(h1 < h2); - - h2 = 688; - assert(h1 > h2); - } - - private void add(in ref ubyte[] h) nothrow @safe @nogc - { - uint sum; - uint carry = 0; - ubyte[] tmp; - - if (h.length > length) - { - allocator.resize!(ubyte, false)(tmp, h.length); - tmp[0 .. h.length] = 0; - tmp[h.length - length .. $] = rep[0 .. length]; - swap(rep, tmp); - } - - auto i = length; - auto j = h.length; - - do - { - --i; - if (j) - { - --j; - sum = rep[i] + h[j] + carry; - } - else - { - sum = rep[i] + carry; - } - carry = sum > 0xff; - rep[i] = cast(ubyte) sum; - } - while (i); - - if (carry) - { - // Still overflowed; allocate more space - allocator.resize!(ubyte, false)(tmp, length + 1); - tmp[1 .. $] = rep[0 .. length]; - tmp[0] = 0x01; - swap(rep, tmp); - } - allocator.dispose(tmp); - } - - private void subtract(in ref ubyte[] h) nothrow @trusted @nogc - { - auto i = rep.length; - auto j = h.length; - uint borrow = 0; - - do - { - int difference; - --i; - - if (j) - { - --j; - difference = rep[i] - h[j] - borrow; - } - else - { - difference = rep[i] - borrow; - } - borrow = difference < 0; - rep[i] = cast(ubyte) difference; - } - while (i); - - if (borrow && i && rep[i - 1]) - { - --rep[i - 1]; - } - // Go through the representation array and see how many of the - // left-most bytes are unused. Remove them and resize the array. - immutable offset = rep.countUntil!((const ref a) => a != 0); - if (offset > 0) - { - ubyte[] tmp = cast(ubyte[]) allocator.allocate(rep.length - offset); - tmp[0 .. $] = rep[offset .. $]; - allocator.deallocate(rep); - rep = tmp; - } - else if (offset == -1) - { - allocator.dispose(rep); - } - } - - /** - * Assignment operators with another $(D_PSYMBOL Integer). - * - * Params: - * op = Operation. - * h = The second integer. - * - * Returns: $(D_KEYWORD this). - */ - ref Integer opOpAssign(string op)(in auto ref Integer h) nothrow @safe @nogc - if ((op == "+") || (op == "-")) - out - { - assert(rep.length || !sign, "0 should be positive."); - assert(!rep.length || rep[0]); - } - body - { - static if (op == "+") - { - if (h.sign == sign) - { - add(h.rep); - } - else - { - if (this >= h) - { - subtract(h.rep); - } - else - { - auto tmp = Integer(h); - tmp.subtract(rep); - swap(rep, tmp.rep); - sign = length == 0 ? false : h.sign; - } - } - } - else - { - if (h.sign == sign) - { - if (this >= h) - { - subtract(h.rep); - } - else - { - auto tmp = Integer(h); - tmp.subtract(rep); - swap(rep, tmp.rep); - sign = length == 0 ? false : !sign; - } - } - else - { - add(h.rep); - } - } - return this; - } - - private @nogc unittest - { - { - auto h1 = Integer(1019); - auto h2 = Integer(3337); - h1 += h2; - assert(h1.length == 2); - assert(h1.rep[0] == 0x11 && h1.rep[1] == 0x04); - } - { - auto h1 = Integer(4356); - auto h2 = Integer(2_147_483_647); - ubyte[4] expected = [0x80, 0x00, 0x11, 0x03]; - h1 += h2; - assert(h1.rep == expected); - } - { - auto h1 = Integer(2147488003L); - auto h2 = Integer(2_147_483_647); - ubyte[5] expected = [0x01, 0x00, 0x00, 0x11, 0x02]; - h1 += h2; - assert(h1.rep == expected); - } - { - auto h1 = Integer(3); - auto h2 = Integer(4); - h1 -= h2; - assert(h1.length == 1); - assert(h1.rep[0] == 0x01); - assert(h1.sign); - } - } - - private @nogc unittest - { - { - auto h1 = Integer(8589934590L); - auto h2 = Integer(2147483647); - ubyte[5] expected = [0x01, 0x7f, 0xff, 0xff, 0xff]; - - h1 -= h2; - assert(h1.rep == expected); - } - { - auto h1 = Integer(6442450943); - auto h2 = Integer(4294967294); - ubyte[4] expected = [0x80, 0x00, 0x00, 0x01]; - h1 -= h2; - assert(h1.rep == expected); - } - { - auto h1 = Integer(2147483649); - auto h2 = Integer(h1); - h1 -= h2; - assert(h1.length == 0); - } - } - - /// Ditto. - ref Integer opOpAssign(string op)(in auto ref Integer h) nothrow @safe @nogc - if (op == "*") - out - { - assert(rep.length || !sign, "0 should be positive."); - assert(!rep.length || rep[0]); - } - body - { - auto i = h.rep.length; - if (length == 0) - { - return this; - } - else if (i == 0) - { - opAssign(0); - return this; - } - auto temp = Integer(this, allocator); - immutable sign = sign == h.sign ? false : true; - - opAssign(0); - do - { - --i; - for (ubyte mask = 0x01; mask; mask <<= 1) - { - if (mask & h.rep[i]) - { - opOpAssign!"+"(temp); - } - temp <<= 1; - } - } - while (i); - this.sign = sign; - - return this; - } - - /// - unittest - { - auto h1 = Integer(123); - auto h2 = Integer(456); - h1 *= h2; - assert(cast(long) h1 == 56088); - } - - private unittest - { - assert((Integer(1) * Integer()).length == 0); - } - - /// Ditto. - ref Integer opOpAssign(string op)(in auto ref Integer h) nothrow @safe @nogc - if ((op == "/") || (op == "%")) - in - { - assert(h.length > 0, "Division by zero."); - } - body - { - auto divisor = Integer(h, allocator); - size_t bitSize; - - // First, left-shift divisor until it's >= than the dividend - for (; opCmp(divisor) > 0; ++bitSize) - { - divisor <<= 1; - } - static if (op == "/") - { - ubyte[] quotient; - allocator.resize!(ubyte, false)(quotient, bitSize / 8 + 1); - } - - // "bitPosition" keeps track of which bit, of the quotient, - // is being set or cleared on the current operation. - auto bitPosition = 8 - (bitSize % 8) - 1; - do - { - if (opCmp(divisor) >= 0) - { - opOpAssign!"-"(divisor); - static if (op == "/") - { - quotient[bitPosition / 8] |= (0x80 >> (bitPosition % 8)); - } - } - if (bitSize) - { - divisor >>= 1; - } - else - { - break; - } - ++bitPosition, --bitSize; - } - while (true); - - static if (op == "/") - { - allocator.dispose(rep); - rep = quotient; - sign = sign == h.sign ? false : true; - } - return this; - } - - private @nogc unittest - { - auto h1 = Integer(18); - auto h2 = Integer(4); - h1 %= h2; - assert(h1.length == 1); - assert(h1.rep[0] == 0x02); - - h1 = 8; - h1 %= h2; - assert(h1.length == 0); - - h1 = 7; - h1 %= h2; - assert(h1.length == 1); - assert(h1.rep[0] == 0x03); - - h1 = 56088; - h2 = 456; - h1 /= h2; - assert(h1.length == 1); - assert(h1.rep[0] == 0x7b); - } - - /// Ditto. - ref Integer opOpAssign(string op)(in auto ref Integer exp) nothrow @safe @nogc - if (op == "^^") - out - { - assert(rep.length || !sign, "0 should be positive."); - assert(!rep.length || rep[0]); - } - body - { - auto i = exp.rep.length; - auto tmp1 = Integer(this, allocator); - auto tmp2 = Integer(allocator); - - opAssign(1); - - do - { - --i; - for (ubyte mask = 0x01; mask; mask <<= 1) - { - if (exp.rep[i] & mask) - { - opOpAssign!"*"(tmp1); - } - // Square tmp1 - tmp2 = tmp1; - tmp1 *= tmp2; - } - } - while (i); - - return this; - } - - private @nogc unittest - { - auto h1 = Integer(2); - auto h2 = Integer(4); - - h1 ^^= h2; - assert(h1.length == 1); - assert(h1.rep[0] == 0x10); - - h1 = Integer(2342); - h1 ^^= h2; - ubyte[6] expected = [0x1b, 0x5c, 0xab, 0x9c, 0x31, 0x10]; - assert(h1.rep == expected); - } - - /** - * Unary operators. - * - * Params: - * op = Operation. - * - * Returns: New $(D_PSYMBOL Integer). - */ - Integer opUnary(string op)() nothrow @safe @nogc - if ((op == "+") || (op == "-") || (op == "~")) - { - auto h = Integer(this, allocator); - static if (op == "-") - { - h.sign = !h.sign; - } - else static if (op == "~") - { - h.rep.each!((ref a) => a = ~a); - } - return h; - } - - private @nogc unittest - { - auto h1 = Integer(79); - Integer h2; - - h2 = +h1; - assert(h2.length == 1); - assert(h2.rep[0] == 79); - assert(!h2.sign); - assert(h2 !is h1); - - h2 = -h1; - assert(h2.length == 1); - assert(h2.rep[0] == 79); - assert(h2.sign); - - h1 = -h2; - assert(h2.length == 1); - assert(h2.rep[0] == 79); - assert(h2.sign); - assert(h2 !is h1); - - h1 = -h2; - assert(h1.length == 1); - assert(h1.rep[0] == 79); - assert(!h1.sign); - - h2 = ~h1; - assert(h2.rep[0] == ~cast(ubyte) 79); - } - - private void decrement() nothrow @safe @nogc - { - immutable size = rep.retro.countUntil!((const ref a) => a != 0); - if (rep[0] == 1) - { - allocator.resize!(ubyte, false)(rep, rep.length - 1); - rep[0 .. $] = typeof(rep[0]).max; - } - else - { - --rep[$ - size - 1]; - rep[$ - size .. $] = typeof(rep[0]).max; - } - } - - private void increment() nothrow @safe @nogc - { - auto size = rep.retro.countUntil!((const ref a) => a != typeof(rep[0]).max); - if (size == -1) - { - size = length; - allocator.resize!(ubyte, false)(rep, rep.length + 1); - rep[0] = 1; - } - else - { - ++rep[$ - size - 1]; - } - rep[$ - size .. $] = 0; - } - - /** - * Increment/decrement. - * - * Params: - * op = Operation. - * - * Returns: $(D_KEYWORD this). - */ - ref Integer opUnary(string op)() nothrow @safe @nogc - if ((op == "++") || (op == "--")) - out - { - assert(rep.length || !sign, "0 should be positive."); - assert(!rep.length || rep[0]); - } - body - { - static if (op == "++") - { - if (sign) - { - decrement(); - if (length == 0) - { - sign = false; - } - } - else - { - increment(); - } - } - else if (sign) - { - increment(); - } - else - { - decrement(); - } - return this; - } - - private @nogc unittest - { - Integer h; - - ++h; - assert(h.length == 1); - assert(h.rep[0] == 0x01); - - --h; - assert(h.length == 0); - - h = 511; - ++h; - assert(h.length == 2); - assert(h.rep[0] == 0x02 && h.rep[1] == 0x00); - - --h; - assert(h.length == 2); - assert(h.rep[0] == 0x01 && h.rep[1] == 0xff); - - h = 79; - ++h; - assert(h.length == 1); - assert(h.rep[0] == 0x50); - - --h; - assert(h.length == 1); - assert(h.rep[0] == 0x4f); - - h = 65535; - ++h; - assert(h.length == 3); - assert(h.rep[0] == 0x01 && h.rep[1] == 0x00 && h.rep[2] == 0x00); - - --h; - assert(h.length == 2); - assert(h.rep[0] == 0xff && h.rep[1] == 0xff); - - h = -2; - ++h; - assert(h.length == 1); - assert(h.rep[0] == 0x01); - } - - /** - * Casting. - * - * Params: - * T = Target type. - * - * Returns: $(D_KEYWORD false) if the $(D_PSYMBOL Integer) is 0, - * $(D_KEYWORD true) otherwise. - */ - T opCast(T : bool)() const pure nothrow @safe @nogc - { - return length == 0 ? false : true; - } - - /// Ditto. - T opCast(T)() const pure nothrow @safe @nogc - if (isIntegral!T && isSigned!T) - { - ulong ret; - for (size_t i = length, j; i > 0 && j <= T.sizeof * 4; --i, j += 8) - { - ret |= cast(T) (rep[i - 1]) << j; - } - return sign ? -ret : ret; - } - - /// Ditto. - T opCast(T)() const pure nothrow @safe @nogc - if (isIntegral!T && isUnsigned!T) - { - ulong ret; - for (size_t i = length, j; i > 0 && j <= T.sizeof * 8; --i, j += 8) - { - ret |= cast(T) (rep[i - 1]) << j; - } - return ret; - } - - /// - unittest - { - auto h = Integer(79); - assert(cast(long) h == 79); - - h = -79; - assert(cast(long) h == -79); - - h = 4294967295; - assert(cast(long) h == 4294967295); - - h = -4294967295; - assert(cast(long) h == -4294967295); - } - - /** - * Shift operations. - * - * Params: - * op = Left or right shift. - * n = Number of bits to shift by. - * - * Returns: An $(D_PSYMBOL Integer) shifted by $(D_PARAM n) bits. - */ - ref Integer opOpAssign(string op)(in auto ref size_t n) nothrow @safe @nogc - if (op == ">>") - out - { - assert(rep.length || !sign, "0 should be positive."); - assert(!rep.length || rep[0]); - } - body - { - immutable step = n / 8; - - if (step >= rep.length) - { - allocator.resize!(ubyte, false)(rep, 0); - return this; - } - - size_t i, j; - ubyte carry; - immutable bit = n % 8; - immutable delta = 8 - bit; - - carry = cast(ubyte) (rep[0] << delta); - rep[0] = (rep[0] >> bit); - if (rep[0]) - { - ++j; - } - for (i = 1; i < rep.length; ++i) - { - immutable oldCarry = carry; - carry = cast(ubyte) (rep[i] << delta); - rep[j] = (rep[i] >> bit | oldCarry); - ++j; - } - allocator.resize!(ubyte, false)(rep, rep.length - n / 8 - (i == j ? 0 : 1)); - - return this; - } - - private @nogc unittest - { - auto h1 = Integer(4294967294); - h1 >>= 10; - assert(h1.length == 3); - assert(h1.rep[0] == 0x3f && h1.rep[1] == 0xff && h1.rep[2] == 0xff); - - h1 = 27336704; - h1 >>= 1; - assert(h1.length == 3); - assert(h1.rep[0] == 0xd0 && h1.rep[1] == 0x90 && h1.rep[2] == 0x00); - - h1 = 4294967294; - h1 >>= 20; - assert(h1.length == 2); - assert(h1.rep[0] == 0x0f && h1.rep[1] == 0xff); - - h1 >>= 0; - assert(h1.length == 2); - assert(h1.rep[0] == 0x0f && h1.rep[1] == 0xff); - - h1 >>= 20; - assert(h1.length == 0); - - h1 >>= 2; - assert(h1.length == 0); - - h1 = 1431655765; - h1 >>= 16; - assert(h1.length == 2); - assert(h1.rep[0] == 0x55 && h1.rep[1] == 0x55); - - h1 >>= 16; - assert(h1.length == 0); - } - - /// Ditto. - ref Integer opOpAssign(string op)(in auto ref size_t n) nothrow @safe @nogc - if (op == "<<") - out - { - assert(rep.length || !sign, "0 should be positive."); - assert(!rep.length || rep[0]); - } - body - { - ubyte carry; - auto i = rep.length; - size_t j; - immutable bit = n % 8; - immutable delta = 8 - bit; - - if (cast(ubyte) (rep[0] >> delta)) - { - allocator.resize!(ubyte, false)(rep, i + n / 8 + 1); - j = i + 1; - } - else - { - allocator.resize!(ubyte, false)(rep, i + n / 8); - j = i; - } - do - { - --i, --j; - immutable oldCarry = carry; - carry = rep[i] >> delta; - rep[j] = cast(ubyte) ((rep[i] << bit) | oldCarry); - } - while (i); - if (carry) - { - rep[0] = carry; - } - return this; - } - - private @nogc unittest - { - auto h1 = Integer(4294967295); - ubyte[5] expected = [0x01, 0xff, 0xff, 0xff, 0xfe]; - h1 <<= 1; - assert(h1.rep == expected); - } - - /// Ditto. - Integer opBinary(string op)(in auto ref size_t n) nothrow @safe @nogc - if (op == "<<" || op == ">>" || op == "+" || op == "-" || op == "/" - || op == "*" || op == "^^" || op == "%") - { - auto ret = Integer(this, allocator); - mixin("ret " ~ op ~ "= n;"); - return ret; - } - - /// - unittest - { - auto h1 = Integer(425); - auto h2 = h1 << 1; - assert(cast(long) h2 == 850); - - h2 = h1 >> 1; - assert(cast(long) h2 == 212); - } - - /// Ditto. - Integer opBinary(string op)(in auto ref Integer h) nothrow @safe @nogc - if (op == "+" || op == "-" || op == "/" - || op == "*" || op == "^^" || op == "%") - { - auto ret = Integer(this, allocator); - mixin("ret " ~ op ~ "= h;"); - return ret; - } - - mixin DefaultAllocator; -} +/* 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/. */ + +/** + * Arbitrary precision arithmetic. + * + * 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.math.mp; + +import core.exception; +import std.algorithm; +import std.range; +import std.traits; +import tanya.math; +import tanya.memory; + +/** + * Algebraic sign. + */ +enum Sign : bool +{ + /// The value is positive or `0`. + positive = false, + + /// The value is negative. + negative = true, +} + +/** + * Mutliple precision integer. + */ +struct Integer +{ + private size_t size; + package ubyte* rep; + package Sign sign; + + pure nothrow @safe @nogc invariant + { + assert(this.size > 0 || !this.sign, "0 should be positive."); + } + + /** + * Creates a multiple precision integer. + * + * Params: + * T = Value type. + * value = Initial value. + * allocator = Allocator. + * + * Precondition: $(D_INLINECODE allocator !is null) + */ + this(T)(const T value, shared Allocator allocator = defaultAllocator) + if (isIntegral!T) + { + this(allocator); + assign(value); + } + + /// Ditto. + this(const ref Integer value, shared Allocator allocator = defaultAllocator) + nothrow @safe @nogc + { + this(allocator); + assign(value); + } + + /// Ditto. + this(Integer value, shared Allocator allocator = defaultAllocator) + nothrow @safe @nogc + { + this(allocator); + if (allocator is value.allocator) + { + this.rep = value.rep; + this.size = value.size; + this.sign = value.sign; + value.rep = null; + value.size = 0; + value.sign = Sign.positive; + } + else + { + assign(value); + } + } + + /// Ditto. + this(shared Allocator allocator) pure nothrow @safe @nogc + in + { + assert(allocator !is null); + } + body + { + this.allocator_ = allocator; + } + + private @nogc unittest + { + { + auto integer = Integer(79); + assert(integer.length == 1); + assert(integer.rep[0] == 79); + assert(!integer.sign); + } + { + auto integer = Integer(-2); + assert(integer.length == 1); + assert(integer.rep[0] == 2); + assert(integer.sign); + } + } + + /** + * Constructs the integer from a sign-magnitude $(D_KEYWORD ubyte) range. + * + * Params: + * R = Range type. + * sign = Sign. + * value = Range. + * allocator = Allocator. + * + * Precondition: $(D_INLINECODE allocator !is null) + */ + this(R)(const Sign sign, R value, shared Allocator allocator = defaultAllocator) + if (isInputRange!R && hasLength!R && is(Unqual!(ElementType!R) == ubyte)) + out + { + assert(this.size == 0 || this.rep[0] > 0); + } + body + { + this(allocator); + while (!value.empty && value.front == 0) + { + value.popFront(); + } + this.rep = allocator.resize(this.rep[0 .. this.size], value.length).ptr; + this.size = value.length; + this.sign = sign; + value.copy(this.rep[0 .. this.size]); + } + + /** + * Copies the integer. + */ + this(this) nothrow @trusted @nogc + { + auto tmp = allocator.resize!ubyte(null, this.size); + this.rep[0 .. this.size].copy(tmp); + this.rep = tmp.ptr; + } + + /** + * Destroys the integer. + */ + ~this() nothrow @trusted @nogc + { + allocator.deallocate(this.rep[0 .. this.size]); + } + + /* + * Figures out the minimum amount of space this value will take + * up in bytes and resizes the internal storage. Sets the sign. + */ + private void assign(T)(const ref T value) @trusted + if (isIntegral!T) + { + ubyte size = ulong.sizeof; + ulong mask; + + this.sign = Sign.positive; // Reset the sign. + static if (isSigned!T) + { + const absolute = value.abs; + } + else + { + alias absolute = value; + } + for (mask = 0xff00000000000000; mask >= 0xff; mask >>= 8) + { + if (absolute & mask) + { + break; + } + --size; + } + + this.rep = allocator.resize(this.rep[0 .. this.size], size).ptr; + this.size = size; + static if (isSigned!T) + { + this.sign = value < 0 ? Sign.negative : Sign.positive; + } + + /* Work backward through the int, masking off each byte (up to the + first 0 byte) and copy it into the internal representation in + big-endian format. */ + mask = 0xff; + ubyte shift; + for (auto i = this.size; i; --i, mask <<= 8, shift += 8) + { + this.rep[i - 1] = cast(ubyte) ((absolute & mask) >> shift); + } + } + + private void assign(const ref Integer value) nothrow @trusted @nogc + { + this.rep = allocator.resize(this.rep[0 .. this.size], value.size).ptr; + this.size = value.size; + this.sign = value.sign; + value.rep[0 .. value.size].copy(this.rep[0 .. this.size]); + } + + /** + * Returns: Integer size. + */ + @property size_t length() const pure nothrow @safe @nogc + { + return this.size; + } + + /** + * Assigns a new value. + * + * Params: + * T = Value type. + * value = Value. + * + * Returns: $(D_KEYWORD this). + */ + ref Integer opAssign(T)(const T value) + if (isIntegral!T) + { + assign(value); + return this; + } + + /// Ditto. + ref Integer opAssign(const ref Integer value) nothrow @safe @nogc + { + assign(value); + return this; + } + + /// Ditto. + ref Integer opAssign(Integer value) nothrow @safe @nogc + { + swap(this.rep, value.rep); + swap(this.sign, value.sign); + swap(this.size, value.size); + return this; + } + + private @nogc unittest + { + auto integer = Integer(1019); + assert(integer.length == 2); + assert(integer.rep[0] == 0b00000011 && integer.rep[1] == 0b11111011); + + integer = 3337; + assert(integer.length == 2); + assert(integer.rep[0] == 0b00001101 && integer.rep[1] == 0b00001001); + + integer = 688; + assert(integer.length == 2); + assert(integer.rep[0] == 0b00000010 && integer.rep[1] == 0b10110000); + + integer = 0; + assert(integer.length == 0); + } + + /* + * Extend the space for this by 1 byte and set the LSB to 1. + */ + private void expand() nothrow @trusted @nogc + { + rep = allocator.resize(this.rep[0 .. this.size], this.size + 1).ptr; + ++this.size; + auto target = this.rep[1 .. this.size].retro; + this.rep[0 .. this.size - 1].retro.copy(target); + this.rep[0] = 0x01; + } + + /* + * Go through this and see how many of the left-most bytes are unused. + * Remove them and resize this appropriately. + */ + private void contract() nothrow @trusted @nogc + { + const i = this.rep[0 .. this.size].countUntil!((const ref a) => a != 0); + + if (i > 0) + { + this.rep[i .. this.size].copy(this.rep[0 .. this.size - i]); + this.rep = allocator.resize(this.rep[0 .. this.size], this.size - i).ptr; + this.size -= i; + } + else if (i == -1) + { + this.sign = Sign.positive; + this.rep = allocator.resize(this.rep[0 .. this.size], 0).ptr; + this.size = 0; + } + } + + private void add(const ref Integer summand) nothrow @trusted @nogc + { + if (summand.length > this.length) + { + auto tmp = this.rep[0 .. this.size]; + this.rep = allocator.resize!ubyte(null, summand.size).ptr; + tmp.copy(this.rep[summand.size - this.size .. summand.size]); + allocator.resize(tmp[0 .. this.size], 0); + this.size = summand.size; + } + + bool carry; + size_t i = this.size; + size_t j = summand.size; + do + { + uint sum; + --i; + if (j) + { + --j; + sum = this.rep[i] + summand.rep[j] + carry; + } + else + { + sum = this.rep[i] + carry; + } + + carry = sum > 0xff; + this.rep[i] = cast(ubyte) sum; + } + while (i); + + if (carry) + { + // Still overflowed; allocate more space + expand(); + } + } + + private void subtract(const ref Integer subtrahend) nothrow @trusted @nogc + { + size_t i = this.size; + size_t j = subtrahend.size; + bool borrow; + + while (i) + { + int difference; + --i; + + if (j) + { + --j; + difference = this.rep[i] - subtrahend.rep[j] - borrow; + } + else + { + difference = this.rep[i] - borrow; + } + borrow = difference < 0; + this.rep[i] = cast(ubyte) difference; + } + + if (borrow && i > 0) + { + if (this.rep[i - 1]) // Don't borrow i + { + --this.rep[i - 1]; + } + } + contract(); + } + + private int compare(const ref Integer that) const nothrow @trusted @nogc + { + if (this.length > that.length) + { + return 1; + } + else if (this.length < that.length) + { + return -1; + } + return this.rep[0 .. this.size].cmp(that.rep[0 .. that.size]); + } + + /** + * Comparison. + * + * Params: + * that = The second integer. + * + * Returns: A positive number if $(D_INLINECODE this > that), a negative + * number if $(D_INLINECODE this < that), `0` otherwise. + */ + int opCmp(I : Integer)(const auto ref I that) const + { + if (this.sign != that.sign) + { + return this.sign ? -1 : 1; + } + return compare(that); + } + + /// + @safe @nogc unittest + { + auto integer1 = Integer(1019); + auto integer2 = Integer(1019); + assert(integer1 == integer2); + + integer2 = 3337; + assert(integer1 < integer2); + + integer2 = 688; + assert(integer1 > integer2); + + integer2 = -3337; + assert(integer1 > integer2); + } + + /// Ditto. + int opCmp(I)(const auto ref I that) const + if (isIntegral!I) + { + if (that < 0 && !this.sign) + { + return 1; + } + else if (that > 0 && this.sign) + { + return -1; + } + else if (this.length > I.sizeof) + { + return this.sign ? -1 : 1; + } + + const diff = (cast(I) this) - that; + if (diff > 0) + { + return 1; + } + else if (diff < 0) + { + return -1; + } + return 0; + } + + /// + @safe @nogc unittest + { + auto integer = Integer(1019); + + assert(integer == 1019); + assert(integer < 3337); + assert(integer > 688); + assert(integer > -3337); + } + + /** + * Params: + * that = The second integer. + * + * Returns: Whether the two integers are equal. + */ + bool opEquals(I)(const auto ref I that) const + if (is(I : Integer) || isIntegral!I) + { + return opCmp!I(that) == 0; + } + + /// + @safe @nogc unittest + { + auto integer = Integer(1019); + + assert(integer == Integer(1019)); + assert(integer != Integer(109)); + } + + /** + * Assignment operators with another $(D_PSYMBOL Integer). + * + * Params: + * op = Operation. + * operand = The second operand. + * + * Returns: $(D_KEYWORD this). + */ + ref Integer opOpAssign(string op : "+")(const auto ref Integer operand) + out + { + assert(this.size == 0 || this.rep[0] > 0); + } + body + { + if (this.sign == operand.sign) + { + add(operand); + } + else + { + if (this >= operand) + { + subtract(operand); + } + else + { + // Swap the operands. + auto tmp = Integer(this, this.allocator); + this = Integer(operand, this.allocator); + subtract(tmp); + this.sign = operand.sign; + } + + } + return this; + } + + private @nogc unittest + { + { + auto h1 = Integer(1019); + auto h2 = Integer(3337); + h1 += h2; + assert(h1.length == 2); + assert(h1.rep[0] == 0x11 && h1.rep[1] == 0x04); + } + { + auto h1 = Integer(4356); + auto h2 = Integer(2_147_483_647); + ubyte[4] expected = [0x80, 0x00, 0x11, 0x03]; + h1 += h2; + assert(h1.rep[0 .. h1.length] == expected); + } + { + auto h1 = Integer(2147488003L); + auto h2 = Integer(2_147_483_647); + ubyte[5] expected = [0x01, 0x00, 0x00, 0x11, 0x02]; + h1 += h2; + assert(h1.rep[0 .. h1.length] == expected); + } + } + + /// Ditto. + ref Integer opOpAssign(string op : "-")(const auto ref Integer operand) + out + { + assert(this.size == 0 || this.rep[0] > 0); + } + body + { + if (operand.sign == this.sign) + { + if (this >= operand) + { + subtract(operand); + } + else + { + // Swap the operands. + auto tmp = Integer(this, this.allocator); + this = Integer(operand, this.allocator); + subtract(tmp); + + if (operand.sign || this.size == 0) + { + this.sign = Sign.positive; + } + else + { + this.sign = Sign.negative; + } + } + } + else + { + add(operand); + } + return this; + } + + private @nogc unittest + { + { + auto h1 = Integer(3); + auto h2 = Integer(4); + h1 -= h2; + assert(h1.length == 1); + assert(h1.rep[0] == 0x01); + assert(h1.sign); + } + { + auto h1 = Integer(8589934590L); + auto h2 = Integer(2147483647); + ubyte[5] expected = [0x01, 0x7f, 0xff, 0xff, 0xff]; + + h1 -= h2; + assert(h1.rep[0 .. h1.size] == expected); + } + { + auto h1 = Integer(6442450943); + auto h2 = Integer(4294967294); + ubyte[4] expected = [0x80, 0x00, 0x00, 0x01]; + h1 -= h2; + assert(h1.rep[0 .. h1.size] == expected); + } + { + auto h1 = Integer(2147483649); + auto h2 = Integer(h1); + h1 -= h2; + assert(h1.length == 0); + } + } + + /// Ditto. + ref Integer opOpAssign(string op : "*")(const auto ref Integer operand) @trusted + out + { + assert(this.size == 0 || this.rep[0] > 0); + } + body + { + size_t i = operand.size; + if (this.length == 0) + { + return this; + } + else if (i == 0) + { + this = 0; + return this; + } + auto temp = Integer(this, this.allocator); + auto sign = this.sign != operand.sign; + + this = 0; + do + { + --i; + for (ubyte mask = 0x01; mask; mask <<= 1) + { + if (mask & operand.rep[i]) + { + add(temp); + } + temp <<= 1; + } + } + while (i); + + this.sign = sign ? Sign.negative : Sign.positive; + + return this; + } + + /// + @safe @nogc unittest + { + auto h1 = Integer(123); + auto h2 = Integer(456); + h1 *= h2; + assert(h1 == 56088); + } + + private @nogc unittest + { + assert((Integer(1) * Integer()).length == 0); + } + + /// Ditto. + ref Integer opOpAssign(string op : "^^")(const auto ref Integer operand) + @trusted + out + { + assert(this.size == 0 || this.rep[0] > 0); + } + body + { + size_t i = operand.size; + + auto tmp1 = Integer(this, this.allocator); + this = 1; + + do + { + --i; + for (ubyte mask = 0x01; mask; mask <<= 1) + { + if (operand.rep[i] & mask) + { + this *= tmp1; + } + // Square tmp1 + auto tmp2 = tmp1; + tmp1 *= tmp2; + } + } + while (i); + + return this; + } + + private @nogc unittest + { + auto h1 = Integer(2); + auto h2 = Integer(4); + + h1 ^^= h2; + assert(h1.length == 1); + assert(h1.rep[0] == 0x10); + + h1 = Integer(2342); + h1 ^^= h2; + ubyte[6] expected = [0x1b, 0x5c, 0xab, 0x9c, 0x31, 0x10]; + assert(h1.rep[0 .. h1.size] == expected); + } + + /// Ditto. + ref Integer opOpAssign(string op)(const auto ref Integer operand) @trusted + if ((op == "%") || (op == "/")) + in + { + assert(operand.length > 0, "Division by zero."); + } + out + { + assert(this.size == 0 || this.rep[0] > 0); + } + body + { + auto divisor = Integer(operand, this.allocator); + size_t bitSize; + + // First, left-shift divisor until it's >= than the dividend + while (compare(divisor) > 0) + { + divisor <<= 1; + ++bitSize; + } + static if (op == "/") + { + auto quotient = allocator.resize!ubyte(null, bitSize / 8 + 1); + } + + // bitPosition keeps track of which bit, of the quotient, + // is being set or cleared on the current operation. + auto bitPosition = 8 - (bitSize % 8) - 1; + do + { + if (compare(divisor) >= 0) + { + subtract(divisor); // dividend -= divisor + static if (op == "/") + { + quotient[bitPosition / 8] |= 0x80 >> (bitPosition % 8); + } + } + if (bitSize != 0) + { + divisor >>= 1; + } + ++bitPosition; + } + while (bitSize--); + + static if (op == "/") + { + allocator.resize(this.rep[0 .. this.size], 0); + this.rep = quotient.ptr; + this.size = quotient.length; + this.sign = this.sign == divisor.sign ? Sign.positive : Sign.negative; + contract(); + } + + return this; + } + + private @nogc unittest + { + auto h1 = Integer(18); + auto h2 = Integer(4); + h1 %= h2; + assert(h1.length == 1); + assert(h1.rep[0] == 0x02); + + h1 = 8; + h1 %= h2; + assert(h1.length == 0); + + h1 = 7; + h1 %= h2; + assert(h1.length == 1); + assert(h1.rep[0] == 0x03); + + h1 = 56088; + h2 = 456; + h1 /= h2; + assert(h1.length == 1); + assert(h1.rep[0] == 0x7b); + } + + /// Ditto. + ref Integer opOpAssign(string op : ">>")(const size_t operand) @trusted + out + { + assert(this.size == 0 || this.rep[0] > 0); + } + body + { + const step = operand / 8; + + if (step >= this.length) + { + this.rep = allocator.resize(this.rep[0 .. this.size], 0).ptr; + this.size = 0; + return this; + } + + size_t i, j; + ubyte carry; + const bit = operand % 8; + const delta = 8 - bit; + + carry = cast(ubyte) (this.rep[0] << delta); + this.rep[0] = this.rep[0] >> bit; + if (rep[0]) + { + ++j; + } + for (i = 1; i < this.length; ++i) + { + immutable oldCarry = carry; + carry = cast(ubyte) (this.rep[i] << delta); + this.rep[j] = this.rep[i] >> bit | oldCarry; + ++j; + } + const newSize = this.length - operand / 8 - (i == j ? 0 : 1); + this.rep = allocator.resize(this.rep[0 .. this.size], newSize).ptr; + this.size = newSize; + + return this; + } + + private @nogc unittest + { + auto integer = Integer(4294967294); + integer >>= 10; + assert(integer.length == 3); + assert(integer.rep[0] == 0x3f && integer.rep[1] == 0xff && integer.rep[2] == 0xff); + + integer = 27336704; + integer >>= 1; + assert(integer.length == 3); + assert(integer.rep[0] == 0xd0 && integer.rep[1] == 0x90 && integer.rep[2] == 0x00); + + integer = 4294967294; + integer >>= 20; + assert(integer.length == 2); + assert(integer.rep[0] == 0x0f && integer.rep[1] == 0xff); + + integer >>= 0; + assert(integer.length == 2); + assert(integer.rep[0] == 0x0f && integer.rep[1] == 0xff); + + integer >>= 20; + assert(integer.length == 0); + + integer >>= 2; + assert(integer.length == 0); + + integer = 1431655765; + integer >>= 16; + assert(integer.length == 2); + assert(integer.rep[0] == 0x55 && integer.rep[1] == 0x55); + + integer >>= 16; + assert(integer.length == 0); + } + + /// Ditto. + ref Integer opOpAssign(string op : "<<")(const size_t operand) @trusted + out + { + assert(this.size == 0 || this.rep[0] > 0); + } + body + { + ubyte carry; + auto i = this.length; + size_t j; + const bit = operand % 8; + const delta = 8 - bit; + + if (cast(ubyte) (this.rep[0] >> delta)) + { + const newSize = i + operand / 8 + 1; + this.rep = allocator.resize(this.rep[0 .. this.size], newSize).ptr; + this.size = newSize; + j = i + 1; + } + else + { + const newSize = i + operand / 8; + this.rep = allocator.resize(this.rep[0 .. this.size], newSize).ptr; + j = i; + } + do + { + --i, --j; + const oldCarry = carry; + carry = rep[i] >> delta; + rep[j] = cast(ubyte) ((this.rep[i] << bit) | oldCarry); + } + while (i); + if (carry) + { + rep[0] = carry; + } + return this; + } + + private @nogc unittest + { + auto integer = Integer(4294967295); + ubyte[5] expected = [0x01, 0xff, 0xff, 0xff, 0xfe]; + integer <<= 1; + assert(integer.rep[0 .. integer.size] == expected); + } + + private void decrement() nothrow @trusted @nogc + in + { + assert(this.length > 0); + } + body + { + auto p = this.rep + this.size; + while (p != this.rep) + { + --p, --*p; + if (*p != ubyte.max) + { + break; + } + } + contract(); + } + + private void increment() nothrow @trusted @nogc + { + auto p = this.rep + this.size; + while (p != this.rep) + { + --p, ++*p; + if (*p > 0) + { + return; + } + } + if (p == this.rep) + { + expand(); + } + } + + /** + * Unary operators. + * + * Params: + * op = Operation. + * + * Returns: New $(D_PSYMBOL Integer). + */ + Integer opUnary(string op : "~")() const + { + auto ret = Integer(this, this.allocator); + ret.rep[0 .. ret.size].each!((ref a) => a = ~a); + return ret; + } + + /// Ditto. + Integer opUnary(string op : "-")() const + { + auto ret = Integer(this, this.allocator); + ret.sign = ret.sign ? Sign.positive : Sign.negative; + return ret; + } + + /** + * Unary operators. + * + * Params: + * op = Operation. + * + * Returns: $(D_KEYWORD this). + */ + ref inout(Integer) opUnary(string op : "+")() inout + { + return this; + } + + private @nogc unittest + { + auto h1 = Integer(79); + Integer h2; + + h2 = +h1; + assert(h2.length == 1); + assert(h2.rep[0] == 79); + assert(!h2.sign); + assert(h2 !is h1); + + h2 = -h1; + assert(h2.length == 1); + assert(h2.rep[0] == 79); + assert(h2.sign); + + h1 = -h2; + assert(h2.length == 1); + assert(h2.rep[0] == 79); + assert(h2.sign); + assert(h2 !is h1); + + h1 = -h2; + assert(h1.length == 1); + assert(h1.rep[0] == 79); + assert(!h1.sign); + + h2 = ~h1; + assert(h2.rep[0] == ~cast(ubyte) 79); + } + + /// Ditto. + ref Integer opUnary(string op : "++")() + out + { + assert(this.size == 0 || this.rep[0] > 0); + } + body + { + if (this.sign) + { + decrement(); + if (this.length == 0) + { + this.sign = Sign.positive; + } + } + else + { + increment(); + } + return this; + } + + /// Ditto. + ref Integer opUnary(string op : "--")() + out + { + assert(this.size == 0 || this.rep[0] > 0); + } + body + { + if (this.sign) + { + increment(); + } + else + { + decrement(); + } + return this; + } + + private @nogc unittest + { + Integer integer; + + ++integer; + assert(integer.length == 1); + assert(integer.rep[0] == 0x01); + + --integer; + assert(integer.length == 0); + + integer = 511; + ++integer; + assert(integer.length == 2); + assert(integer.rep[0] == 0x02 && integer.rep[1] == 0x00); + + --integer; + assert(integer.length == 2); + assert(integer.rep[0] == 0x01 && integer.rep[1] == 0xff); + + integer = 79; + ++integer; + assert(integer.length == 1); + assert(integer.rep[0] == 0x50); + + --integer; + assert(integer.length == 1); + assert(integer.rep[0] == 0x4f); + + integer = 65535; + ++integer; + assert(integer.length == 3); + assert(integer.rep[0] == 0x01 && integer.rep[1] == 0x00 && integer.rep[2] == 0x00); + + --integer; + assert(integer.length == 2); + assert(integer.rep[0] == 0xff && integer.rep[1] == 0xff); + + integer = -2; + ++integer; + assert(integer.length == 1); + assert(integer.rep[0] == 0x01); + } + + /** + * Implements binary operators. + * + * Params: + * op = Operation. + * operand = The second operand. + */ + Integer opBinary(string op)(const auto ref Integer operand) const + if (op == "+" || op == "-" || op == "*" || op == "^^") + { + auto ret = Integer(this, this.allocator); + mixin("ret " ~ op ~ "= operand;"); + return ret; + } + + /// Ditto. + Integer opBinary(string op)(const auto ref Integer operand) const + if (op == "/" || op == "%") + in + { + assert(operand.length > 0, "Division by zero."); + } + body + { + auto ret = Integer(this, this.allocator); + mixin("ret " ~ op ~ "= operand;"); + return ret; + } + + /// Ditto. + Integer opBinary(string op)(const auto ref size_t operand) const + if (op == "<<" || op == ">>") + { + auto ret = Integer(this, this.allocator); + mixin("ret " ~ op ~ "= operand;"); + return ret; + } + + /// + @safe @nogc unittest + { + auto integer1 = Integer(425); + auto integer2 = integer1 << 1; + assert(integer2 == 850); + + integer2 = integer1 >> 1; + assert(integer2 == 212); + } + + /** + * Casting. + * + * Params: + * T = Target type. + * + * Returns: $(D_KEYWORD false) if the $(D_PSYMBOL Integer) is 0, + * $(D_KEYWORD true) otherwise. + */ + T opCast(T : bool)() const + { + return this.length > 0; + } + + /// Ditto. + T opCast(T)() const + if (isIntegral!T && isSigned!T) + { + return this.sign ? -(cast(Unsigned!T) this) : cast(Unsigned!T) this; + } + + /// Ditto. + T opCast(T)() const @trusted + if (isIntegral!T && isUnsigned!T) + { + if (this.length == 0) + { + return 0; + } + T ret; + const(ubyte)* src = this.rep + this.size - 1; + for (ubyte shift; src >= this.rep && shift <= T.sizeof * 8; --src, shift += 8) + { + ret |= cast(T) (*src) << shift; + } + return ret; + } + + /// + @safe @nogc unittest + { + auto integer = Integer(79); + assert(cast(long) integer == 79); + + integer = -79; + assert(cast(long) integer == -79); + + integer = 4294967295; + assert(cast(long) integer == 4294967295); + + integer = -4294967295; + assert(cast(long) integer == -4294967295); + } + + mixin DefaultAllocator; +} diff --git a/source/tanya/math/package.d b/source/tanya/math/package.d index 8449c34..994d5c7 100644 --- a/source/tanya/math/package.d +++ b/source/tanya/math/package.d @@ -3,11 +3,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** - * Copyright: Eugene Wissner 2016. + * 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:belka@caraus.de, Eugene Wissner) - */ + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + */ module tanya.math; import std.traits; @@ -16,7 +16,7 @@ public import tanya.math.random; version (unittest) { - import std.algorithm.iteration; + import std.algorithm.iteration; } /** @@ -26,12 +26,12 @@ version (unittest) * is used to allocate the result. * * Params: - * I = Base type. - * G = Exponent type. - * H = Divisor type: - * x = Base. - * y = Exponent. - * z = Divisor. + * I = Base type. + * G = Exponent type. + * H = Divisor type: + * x = Base. + * y = Exponent. + * z = Divisor. * * Returns: Reminder of the division of $(D_PARAM x) to the power $(D_PARAM y) * by $(D_PARAM z). @@ -39,134 +39,162 @@ version (unittest) * Precondition: $(D_INLINECODE z > 0) */ H pow(I, G, H)(in auto ref I x, in auto ref G y, in auto ref H z) - if (isIntegral!I && isIntegral!G && isIntegral!H) + if (isIntegral!I && isIntegral!G && isIntegral!H) in { - assert(z > 0, "Division by zero."); + assert(z > 0, "Division by zero."); } body { - G mask = G.max / 2 + 1; - H result; + G mask = G.max / 2 + 1; + H result; - if (y == 0) - { - return 1 % z; - } - else if (y == 1) - { - return x % z; - } - do - { - immutable bit = y & mask; - if (!result && bit) - { - result = x; - continue; - } + if (y == 0) + { + return 1 % z; + } + else if (y == 1) + { + return x % z; + } + do + { + immutable bit = y & mask; + if (!result && bit) + { + result = x; + continue; + } - result *= result; - if (bit) - { - result *= x; - } - result %= z; - } - while (mask >>= 1); + result *= result; + if (bit) + { + result *= x; + } + result %= z; + } + while (mask >>= 1); - return result; + return result; } /// Ditto. -I pow(I)(in auto ref I x, in auto ref I y, in auto ref I z) - if (is(I == Integer)) +I pow(I)(const auto ref I x, const auto ref I y, const auto ref I z) + if (is(I == Integer)) in { - assert(z.length > 0, "Division by zero."); + assert(z.length > 0, "Division by zero."); } body { - size_t i = y.length; - auto tmp2 = Integer(x.allocator), tmp1 = Integer(x, x.allocator); - Integer result = Integer(x.allocator); + size_t i = y.length; + auto tmp1 = Integer(x, x.allocator); + auto result = Integer(x.allocator); - if (x.length == 0 && i != 0) - { - i = 0; - } - else - { - result = 1; - } - while (i) - { - --i; - for (ubyte mask = 0x01; mask; mask <<= 1) - { - if (y.rep[i] & mask) - { - result *= tmp1; - result %= z; - } - tmp2 = tmp1; - tmp1 *= tmp2; - tmp1 %= z; - } - } - return result; + if (x.length == 0 && i != 0) + { + i = 0; + } + else + { + result = 1; + } + while (i) + { + --i; + for (ubyte mask = 0x01; mask; mask <<= 1) + { + if (y.rep[i] & mask) + { + result *= tmp1; + result %= z; + } + auto tmp2 = tmp1; + tmp1 *= tmp2; + tmp1 %= z; + } + } + return result; } /// pure nothrow @safe @nogc unittest { - assert(pow(3, 5, 7) == 5); - assert(pow(2, 2, 1) == 0); - assert(pow(3, 3, 3) == 0); - assert(pow(7, 4, 2) == 1); - assert(pow(53, 0, 2) == 1); - assert(pow(53, 1, 3) == 2); - assert(pow(53, 2, 5) == 4); - assert(pow(0, 0, 5) == 1); - assert(pow(0, 5, 5) == 0); + assert(pow(3, 5, 7) == 5); + assert(pow(2, 2, 1) == 0); + assert(pow(3, 3, 3) == 0); + assert(pow(7, 4, 2) == 1); + assert(pow(53, 0, 2) == 1); + assert(pow(53, 1, 3) == 2); + assert(pow(53, 2, 5) == 4); + assert(pow(0, 0, 5) == 1); + assert(pow(0, 5, 5) == 0); } /// unittest { - assert(cast(long) pow(Integer(3), Integer(5), Integer(7)) == 5); - assert(cast(long) pow(Integer(2), Integer(2), Integer(1)) == 0); - assert(cast(long) pow(Integer(3), Integer(3), Integer(3)) == 0); - assert(cast(long) pow(Integer(7), Integer(4), Integer(2)) == 1); - assert(cast(long) pow(Integer(53), Integer(0), Integer(2)) == 1); - assert(cast(long) pow(Integer(53), Integer(1), Integer(3)) == 2); - assert(cast(long) pow(Integer(53), Integer(2), Integer(5)) == 4); - assert(cast(long) pow(Integer(0), Integer(0), Integer(5)) == 1); - assert(cast(long) pow(Integer(0), Integer(5), Integer(5)) == 0); + assert(pow(Integer(3), Integer(5), Integer(7)) == 5); + assert(pow(Integer(2), Integer(2), Integer(1)) == 0); + assert(pow(Integer(3), Integer(3), Integer(3)) == 0); + assert(pow(Integer(7), Integer(4), Integer(2)) == 1); + assert(pow(Integer(53), Integer(0), Integer(2)) == 1); + assert(pow(Integer(53), Integer(1), Integer(3)) == 2); + assert(pow(Integer(53), Integer(2), Integer(5)) == 4); + assert(pow(Integer(0), Integer(0), Integer(5)) == 1); + assert(pow(Integer(0), Integer(5), Integer(5)) == 0); } /** * Checks if $(D_PARAM x) is a prime. * * Params: - * x = The number should be checked. + * x = The number should be checked. * * Returns: $(D_KEYWORD true) if $(D_PARAM x) is a prime number, * $(D_KEYWORD false) otherwise. */ bool isPseudoprime(ulong x) nothrow pure @safe @nogc { - return pow(2, x - 1, x) == 1; + return pow(2, x - 1, x) == 1; } /// unittest { - uint[30] known = [74623, 74653, 74687, 74699, 74707, 74713, 74717, 74719, - 74843, 74747, 74759, 74761, 74771, 74779, 74797, 74821, - 74827, 9973, 104729, 15485867, 49979693, 104395303, - 593441861, 104729, 15485867, 49979693, 104395303, - 593441861, 899809363, 982451653]; + uint[30] known = [74623, 74653, 74687, 74699, 74707, 74713, 74717, 74719, + 74843, 74747, 74759, 74761, 74771, 74779, 74797, 74821, + 74827, 9973, 104729, 15485867, 49979693, 104395303, + 593441861, 104729, 15485867, 49979693, 104395303, + 593441861, 899809363, 982451653]; - known.each!((ref x) => assert(isPseudoprime(x))); + known.each!((ref x) => assert(isPseudoprime(x))); +} + +/** + * Params: + * I = Value type. + * x = Value. + * + * Returns: The absolute value of a number. + */ +I abs(I : Integer)(const auto ref I x) +{ + auto result = Integer(x, x.allocator); + result.sign = Sign.positive; + return result; +} + +/// Ditto. +I abs(I : Integer)(I x) +{ + x.sign = Sign.positive; + return x; +} + +/// Ditto. +I abs(I)(const I x) + if (isIntegral!I) +{ + return x >= 0 ? x : -x; } From 7892c1a930f95ff113a8de2b00b808ac0f8c9218 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Wed, 22 Mar 2017 08:51:00 +0100 Subject: [PATCH 10/26] Remove Init template parameter from memory.resize() --- source/tanya/math/mp.d | 14 +++++++++----- source/tanya/memory/package.d | 31 +++++++++---------------------- 2 files changed, 18 insertions(+), 27 deletions(-) diff --git a/source/tanya/math/mp.d b/source/tanya/math/mp.d index 50efb64..8c7c9a1 100644 --- a/source/tanya/math/mp.d +++ b/source/tanya/math/mp.d @@ -315,10 +315,14 @@ struct Integer { if (summand.length > this.length) { + const delta = summand.size - this.size; auto tmp = this.rep[0 .. this.size]; + this.rep = allocator.resize!ubyte(null, summand.size).ptr; - tmp.copy(this.rep[summand.size - this.size .. summand.size]); - allocator.resize(tmp[0 .. this.size], 0); + tmp.copy(this.rep[delta .. summand.size]); + this.rep[0 .. delta].initializeAll(); + + allocator.deallocate(tmp[0 .. this.size]); this.size = summand.size; } @@ -533,7 +537,7 @@ struct Integer return this; } - private @nogc unittest + private unittest { { auto h1 = Integer(1019); @@ -547,14 +551,14 @@ struct Integer auto h2 = Integer(2_147_483_647); ubyte[4] expected = [0x80, 0x00, 0x11, 0x03]; h1 += h2; - assert(h1.rep[0 .. h1.length] == expected); + assert(h1.rep[0 .. h1.size] == expected); } { auto h1 = Integer(2147488003L); auto h2 = Integer(2_147_483_647); ubyte[5] expected = [0x01, 0x00, 0x00, 0x11, 0x02]; h1 += h2; - assert(h1.rep[0 .. h1.length] == expected); + assert(h1.rep[0 .. h1.size] == expected); } } diff --git a/source/tanya/memory/package.d b/source/tanya/memory/package.d index 1959dbd..0595073 100644 --- a/source/tanya/memory/package.d +++ b/source/tanya/memory/package.d @@ -137,7 +137,8 @@ template stateSize(T) * * Returns: Aligned size. */ -size_t alignedSize(in size_t size, in size_t alignment = 8) pure nothrow @safe @nogc +size_t alignedSize(const size_t size, const size_t alignment = 8) +pure nothrow @safe @nogc { return (size - 1) / alignment * alignment + alignment; } @@ -145,31 +146,24 @@ size_t alignedSize(in size_t size, in size_t alignment = 8) pure nothrow @safe @ /** * Internal function used to create, resize or destroy a dynamic array. It * may throw $(D_PSYMBOL OutOfMemoryError). The new - * allocated part of the array is initialized only if $(D_PARAM Init) - * is set. This function can be trusted only in the data structures that - * can ensure that the array is allocated/rellocated/deallocated with the - * same allocator. + * allocated part of the array isn't initialized. This function can be trusted + * only in the data structures that can ensure that the array is + * allocated/rellocated/deallocated with the same allocator. * * Params: * T = Element type of the array being created. - * Init = If should be initialized. * allocator = The allocator used for getting memory. * array = A reference to the array being changed. * length = New array length. * * Returns: $(D_PARAM array). */ -package(tanya) T[] resize(T, - bool Init = true) - (shared Allocator allocator, - auto ref T[] array, - const size_t length) @trusted +package(tanya) T[] resize(T)(shared Allocator allocator, + auto ref T[] array, + const size_t length) @trusted { void[] buf = array; - static if (Init) - { - const oldLength = array.length; - } + if (!allocator.reallocate(buf, length * T.sizeof)) { onOutOfMemoryError; @@ -177,13 +171,6 @@ package(tanya) T[] resize(T, // Casting from void[] is unsafe, but we know we cast to the original type. array = cast(T[]) buf; - static if (Init) - { - if (oldLength < length) - { - array[oldLength .. $] = T.init; - } - } return array; } From 402fdfae898ddecf71fac3b62aee3c7e356c5f5a Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Thu, 23 Mar 2017 15:36:17 +0100 Subject: [PATCH 11/26] math.mp: Fix initialization issues after resizing --- source/tanya/math/mp.d | 2489 +++++++++++++++++---------------- source/tanya/memory/package.d | 19 +- 2 files changed, 1260 insertions(+), 1248 deletions(-) diff --git a/source/tanya/math/mp.d b/source/tanya/math/mp.d index 8c7c9a1..57a8d5c 100644 --- a/source/tanya/math/mp.d +++ b/source/tanya/math/mp.d @@ -1,1244 +1,1245 @@ -/* 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/. */ - -/** - * Arbitrary precision arithmetic. - * - * 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.math.mp; - -import core.exception; -import std.algorithm; -import std.range; -import std.traits; -import tanya.math; -import tanya.memory; - -/** - * Algebraic sign. - */ -enum Sign : bool -{ - /// The value is positive or `0`. - positive = false, - - /// The value is negative. - negative = true, -} - -/** - * Mutliple precision integer. - */ -struct Integer -{ - private size_t size; - package ubyte* rep; - package Sign sign; - - pure nothrow @safe @nogc invariant - { - assert(this.size > 0 || !this.sign, "0 should be positive."); - } - - /** - * Creates a multiple precision integer. - * - * Params: - * T = Value type. - * value = Initial value. - * allocator = Allocator. - * - * Precondition: $(D_INLINECODE allocator !is null) - */ - this(T)(const T value, shared Allocator allocator = defaultAllocator) - if (isIntegral!T) - { - this(allocator); - assign(value); - } - - /// Ditto. - this(const ref Integer value, shared Allocator allocator = defaultAllocator) - nothrow @safe @nogc - { - this(allocator); - assign(value); - } - - /// Ditto. - this(Integer value, shared Allocator allocator = defaultAllocator) - nothrow @safe @nogc - { - this(allocator); - if (allocator is value.allocator) - { - this.rep = value.rep; - this.size = value.size; - this.sign = value.sign; - value.rep = null; - value.size = 0; - value.sign = Sign.positive; - } - else - { - assign(value); - } - } - - /// Ditto. - this(shared Allocator allocator) pure nothrow @safe @nogc - in - { - assert(allocator !is null); - } - body - { - this.allocator_ = allocator; - } - - private @nogc unittest - { - { - auto integer = Integer(79); - assert(integer.length == 1); - assert(integer.rep[0] == 79); - assert(!integer.sign); - } - { - auto integer = Integer(-2); - assert(integer.length == 1); - assert(integer.rep[0] == 2); - assert(integer.sign); - } - } - - /** - * Constructs the integer from a sign-magnitude $(D_KEYWORD ubyte) range. - * - * Params: - * R = Range type. - * sign = Sign. - * value = Range. - * allocator = Allocator. - * - * Precondition: $(D_INLINECODE allocator !is null) - */ - this(R)(const Sign sign, R value, shared Allocator allocator = defaultAllocator) - if (isInputRange!R && hasLength!R && is(Unqual!(ElementType!R) == ubyte)) - out - { - assert(this.size == 0 || this.rep[0] > 0); - } - body - { - this(allocator); - while (!value.empty && value.front == 0) - { - value.popFront(); - } - this.rep = allocator.resize(this.rep[0 .. this.size], value.length).ptr; - this.size = value.length; - this.sign = sign; - value.copy(this.rep[0 .. this.size]); - } - - /** - * Copies the integer. - */ - this(this) nothrow @trusted @nogc - { - auto tmp = allocator.resize!ubyte(null, this.size); - this.rep[0 .. this.size].copy(tmp); - this.rep = tmp.ptr; - } - - /** - * Destroys the integer. - */ - ~this() nothrow @trusted @nogc - { - allocator.deallocate(this.rep[0 .. this.size]); - } - - /* - * Figures out the minimum amount of space this value will take - * up in bytes and resizes the internal storage. Sets the sign. - */ - private void assign(T)(const ref T value) @trusted - if (isIntegral!T) - { - ubyte size = ulong.sizeof; - ulong mask; - - this.sign = Sign.positive; // Reset the sign. - static if (isSigned!T) - { - const absolute = value.abs; - } - else - { - alias absolute = value; - } - for (mask = 0xff00000000000000; mask >= 0xff; mask >>= 8) - { - if (absolute & mask) - { - break; - } - --size; - } - - this.rep = allocator.resize(this.rep[0 .. this.size], size).ptr; - this.size = size; - static if (isSigned!T) - { - this.sign = value < 0 ? Sign.negative : Sign.positive; - } - - /* Work backward through the int, masking off each byte (up to the - first 0 byte) and copy it into the internal representation in - big-endian format. */ - mask = 0xff; - ubyte shift; - for (auto i = this.size; i; --i, mask <<= 8, shift += 8) - { - this.rep[i - 1] = cast(ubyte) ((absolute & mask) >> shift); - } - } - - private void assign(const ref Integer value) nothrow @trusted @nogc - { - this.rep = allocator.resize(this.rep[0 .. this.size], value.size).ptr; - this.size = value.size; - this.sign = value.sign; - value.rep[0 .. value.size].copy(this.rep[0 .. this.size]); - } - - /** - * Returns: Integer size. - */ - @property size_t length() const pure nothrow @safe @nogc - { - return this.size; - } - - /** - * Assigns a new value. - * - * Params: - * T = Value type. - * value = Value. - * - * Returns: $(D_KEYWORD this). - */ - ref Integer opAssign(T)(const T value) - if (isIntegral!T) - { - assign(value); - return this; - } - - /// Ditto. - ref Integer opAssign(const ref Integer value) nothrow @safe @nogc - { - assign(value); - return this; - } - - /// Ditto. - ref Integer opAssign(Integer value) nothrow @safe @nogc - { - swap(this.rep, value.rep); - swap(this.sign, value.sign); - swap(this.size, value.size); - return this; - } - - private @nogc unittest - { - auto integer = Integer(1019); - assert(integer.length == 2); - assert(integer.rep[0] == 0b00000011 && integer.rep[1] == 0b11111011); - - integer = 3337; - assert(integer.length == 2); - assert(integer.rep[0] == 0b00001101 && integer.rep[1] == 0b00001001); - - integer = 688; - assert(integer.length == 2); - assert(integer.rep[0] == 0b00000010 && integer.rep[1] == 0b10110000); - - integer = 0; - assert(integer.length == 0); - } - - /* - * Extend the space for this by 1 byte and set the LSB to 1. - */ - private void expand() nothrow @trusted @nogc - { - rep = allocator.resize(this.rep[0 .. this.size], this.size + 1).ptr; - ++this.size; - auto target = this.rep[1 .. this.size].retro; - this.rep[0 .. this.size - 1].retro.copy(target); - this.rep[0] = 0x01; - } - - /* - * Go through this and see how many of the left-most bytes are unused. - * Remove them and resize this appropriately. - */ - private void contract() nothrow @trusted @nogc - { - const i = this.rep[0 .. this.size].countUntil!((const ref a) => a != 0); - - if (i > 0) - { - this.rep[i .. this.size].copy(this.rep[0 .. this.size - i]); - this.rep = allocator.resize(this.rep[0 .. this.size], this.size - i).ptr; - this.size -= i; - } - else if (i == -1) - { - this.sign = Sign.positive; - this.rep = allocator.resize(this.rep[0 .. this.size], 0).ptr; - this.size = 0; - } - } - - private void add(const ref Integer summand) nothrow @trusted @nogc - { - if (summand.length > this.length) - { - const delta = summand.size - this.size; - auto tmp = this.rep[0 .. this.size]; - - this.rep = allocator.resize!ubyte(null, summand.size).ptr; - tmp.copy(this.rep[delta .. summand.size]); - this.rep[0 .. delta].initializeAll(); - - allocator.deallocate(tmp[0 .. this.size]); - this.size = summand.size; - } - - bool carry; - size_t i = this.size; - size_t j = summand.size; - do - { - uint sum; - --i; - if (j) - { - --j; - sum = this.rep[i] + summand.rep[j] + carry; - } - else - { - sum = this.rep[i] + carry; - } - - carry = sum > 0xff; - this.rep[i] = cast(ubyte) sum; - } - while (i); - - if (carry) - { - // Still overflowed; allocate more space - expand(); - } - } - - private void subtract(const ref Integer subtrahend) nothrow @trusted @nogc - { - size_t i = this.size; - size_t j = subtrahend.size; - bool borrow; - - while (i) - { - int difference; - --i; - - if (j) - { - --j; - difference = this.rep[i] - subtrahend.rep[j] - borrow; - } - else - { - difference = this.rep[i] - borrow; - } - borrow = difference < 0; - this.rep[i] = cast(ubyte) difference; - } - - if (borrow && i > 0) - { - if (this.rep[i - 1]) // Don't borrow i - { - --this.rep[i - 1]; - } - } - contract(); - } - - private int compare(const ref Integer that) const nothrow @trusted @nogc - { - if (this.length > that.length) - { - return 1; - } - else if (this.length < that.length) - { - return -1; - } - return this.rep[0 .. this.size].cmp(that.rep[0 .. that.size]); - } - - /** - * Comparison. - * - * Params: - * that = The second integer. - * - * Returns: A positive number if $(D_INLINECODE this > that), a negative - * number if $(D_INLINECODE this < that), `0` otherwise. - */ - int opCmp(I : Integer)(const auto ref I that) const - { - if (this.sign != that.sign) - { - return this.sign ? -1 : 1; - } - return compare(that); - } - - /// - @safe @nogc unittest - { - auto integer1 = Integer(1019); - auto integer2 = Integer(1019); - assert(integer1 == integer2); - - integer2 = 3337; - assert(integer1 < integer2); - - integer2 = 688; - assert(integer1 > integer2); - - integer2 = -3337; - assert(integer1 > integer2); - } - - /// Ditto. - int opCmp(I)(const auto ref I that) const - if (isIntegral!I) - { - if (that < 0 && !this.sign) - { - return 1; - } - else if (that > 0 && this.sign) - { - return -1; - } - else if (this.length > I.sizeof) - { - return this.sign ? -1 : 1; - } - - const diff = (cast(I) this) - that; - if (diff > 0) - { - return 1; - } - else if (diff < 0) - { - return -1; - } - return 0; - } - - /// - @safe @nogc unittest - { - auto integer = Integer(1019); - - assert(integer == 1019); - assert(integer < 3337); - assert(integer > 688); - assert(integer > -3337); - } - - /** - * Params: - * that = The second integer. - * - * Returns: Whether the two integers are equal. - */ - bool opEquals(I)(const auto ref I that) const - if (is(I : Integer) || isIntegral!I) - { - return opCmp!I(that) == 0; - } - - /// - @safe @nogc unittest - { - auto integer = Integer(1019); - - assert(integer == Integer(1019)); - assert(integer != Integer(109)); - } - - /** - * Assignment operators with another $(D_PSYMBOL Integer). - * - * Params: - * op = Operation. - * operand = The second operand. - * - * Returns: $(D_KEYWORD this). - */ - ref Integer opOpAssign(string op : "+")(const auto ref Integer operand) - out - { - assert(this.size == 0 || this.rep[0] > 0); - } - body - { - if (this.sign == operand.sign) - { - add(operand); - } - else - { - if (this >= operand) - { - subtract(operand); - } - else - { - // Swap the operands. - auto tmp = Integer(this, this.allocator); - this = Integer(operand, this.allocator); - subtract(tmp); - this.sign = operand.sign; - } - - } - return this; - } - - private unittest - { - { - auto h1 = Integer(1019); - auto h2 = Integer(3337); - h1 += h2; - assert(h1.length == 2); - assert(h1.rep[0] == 0x11 && h1.rep[1] == 0x04); - } - { - auto h1 = Integer(4356); - auto h2 = Integer(2_147_483_647); - ubyte[4] expected = [0x80, 0x00, 0x11, 0x03]; - h1 += h2; - assert(h1.rep[0 .. h1.size] == expected); - } - { - auto h1 = Integer(2147488003L); - auto h2 = Integer(2_147_483_647); - ubyte[5] expected = [0x01, 0x00, 0x00, 0x11, 0x02]; - h1 += h2; - assert(h1.rep[0 .. h1.size] == expected); - } - } - - /// Ditto. - ref Integer opOpAssign(string op : "-")(const auto ref Integer operand) - out - { - assert(this.size == 0 || this.rep[0] > 0); - } - body - { - if (operand.sign == this.sign) - { - if (this >= operand) - { - subtract(operand); - } - else - { - // Swap the operands. - auto tmp = Integer(this, this.allocator); - this = Integer(operand, this.allocator); - subtract(tmp); - - if (operand.sign || this.size == 0) - { - this.sign = Sign.positive; - } - else - { - this.sign = Sign.negative; - } - } - } - else - { - add(operand); - } - return this; - } - - private @nogc unittest - { - { - auto h1 = Integer(3); - auto h2 = Integer(4); - h1 -= h2; - assert(h1.length == 1); - assert(h1.rep[0] == 0x01); - assert(h1.sign); - } - { - auto h1 = Integer(8589934590L); - auto h2 = Integer(2147483647); - ubyte[5] expected = [0x01, 0x7f, 0xff, 0xff, 0xff]; - - h1 -= h2; - assert(h1.rep[0 .. h1.size] == expected); - } - { - auto h1 = Integer(6442450943); - auto h2 = Integer(4294967294); - ubyte[4] expected = [0x80, 0x00, 0x00, 0x01]; - h1 -= h2; - assert(h1.rep[0 .. h1.size] == expected); - } - { - auto h1 = Integer(2147483649); - auto h2 = Integer(h1); - h1 -= h2; - assert(h1.length == 0); - } - } - - /// Ditto. - ref Integer opOpAssign(string op : "*")(const auto ref Integer operand) @trusted - out - { - assert(this.size == 0 || this.rep[0] > 0); - } - body - { - size_t i = operand.size; - if (this.length == 0) - { - return this; - } - else if (i == 0) - { - this = 0; - return this; - } - auto temp = Integer(this, this.allocator); - auto sign = this.sign != operand.sign; - - this = 0; - do - { - --i; - for (ubyte mask = 0x01; mask; mask <<= 1) - { - if (mask & operand.rep[i]) - { - add(temp); - } - temp <<= 1; - } - } - while (i); - - this.sign = sign ? Sign.negative : Sign.positive; - - return this; - } - - /// - @safe @nogc unittest - { - auto h1 = Integer(123); - auto h2 = Integer(456); - h1 *= h2; - assert(h1 == 56088); - } - - private @nogc unittest - { - assert((Integer(1) * Integer()).length == 0); - } - - /// Ditto. - ref Integer opOpAssign(string op : "^^")(const auto ref Integer operand) - @trusted - out - { - assert(this.size == 0 || this.rep[0] > 0); - } - body - { - size_t i = operand.size; - - auto tmp1 = Integer(this, this.allocator); - this = 1; - - do - { - --i; - for (ubyte mask = 0x01; mask; mask <<= 1) - { - if (operand.rep[i] & mask) - { - this *= tmp1; - } - // Square tmp1 - auto tmp2 = tmp1; - tmp1 *= tmp2; - } - } - while (i); - - return this; - } - - private @nogc unittest - { - auto h1 = Integer(2); - auto h2 = Integer(4); - - h1 ^^= h2; - assert(h1.length == 1); - assert(h1.rep[0] == 0x10); - - h1 = Integer(2342); - h1 ^^= h2; - ubyte[6] expected = [0x1b, 0x5c, 0xab, 0x9c, 0x31, 0x10]; - assert(h1.rep[0 .. h1.size] == expected); - } - - /// Ditto. - ref Integer opOpAssign(string op)(const auto ref Integer operand) @trusted - if ((op == "%") || (op == "/")) - in - { - assert(operand.length > 0, "Division by zero."); - } - out - { - assert(this.size == 0 || this.rep[0] > 0); - } - body - { - auto divisor = Integer(operand, this.allocator); - size_t bitSize; - - // First, left-shift divisor until it's >= than the dividend - while (compare(divisor) > 0) - { - divisor <<= 1; - ++bitSize; - } - static if (op == "/") - { - auto quotient = allocator.resize!ubyte(null, bitSize / 8 + 1); - } - - // bitPosition keeps track of which bit, of the quotient, - // is being set or cleared on the current operation. - auto bitPosition = 8 - (bitSize % 8) - 1; - do - { - if (compare(divisor) >= 0) - { - subtract(divisor); // dividend -= divisor - static if (op == "/") - { - quotient[bitPosition / 8] |= 0x80 >> (bitPosition % 8); - } - } - if (bitSize != 0) - { - divisor >>= 1; - } - ++bitPosition; - } - while (bitSize--); - - static if (op == "/") - { - allocator.resize(this.rep[0 .. this.size], 0); - this.rep = quotient.ptr; - this.size = quotient.length; - this.sign = this.sign == divisor.sign ? Sign.positive : Sign.negative; - contract(); - } - - return this; - } - - private @nogc unittest - { - auto h1 = Integer(18); - auto h2 = Integer(4); - h1 %= h2; - assert(h1.length == 1); - assert(h1.rep[0] == 0x02); - - h1 = 8; - h1 %= h2; - assert(h1.length == 0); - - h1 = 7; - h1 %= h2; - assert(h1.length == 1); - assert(h1.rep[0] == 0x03); - - h1 = 56088; - h2 = 456; - h1 /= h2; - assert(h1.length == 1); - assert(h1.rep[0] == 0x7b); - } - - /// Ditto. - ref Integer opOpAssign(string op : ">>")(const size_t operand) @trusted - out - { - assert(this.size == 0 || this.rep[0] > 0); - } - body - { - const step = operand / 8; - - if (step >= this.length) - { - this.rep = allocator.resize(this.rep[0 .. this.size], 0).ptr; - this.size = 0; - return this; - } - - size_t i, j; - ubyte carry; - const bit = operand % 8; - const delta = 8 - bit; - - carry = cast(ubyte) (this.rep[0] << delta); - this.rep[0] = this.rep[0] >> bit; - if (rep[0]) - { - ++j; - } - for (i = 1; i < this.length; ++i) - { - immutable oldCarry = carry; - carry = cast(ubyte) (this.rep[i] << delta); - this.rep[j] = this.rep[i] >> bit | oldCarry; - ++j; - } - const newSize = this.length - operand / 8 - (i == j ? 0 : 1); - this.rep = allocator.resize(this.rep[0 .. this.size], newSize).ptr; - this.size = newSize; - - return this; - } - - private @nogc unittest - { - auto integer = Integer(4294967294); - integer >>= 10; - assert(integer.length == 3); - assert(integer.rep[0] == 0x3f && integer.rep[1] == 0xff && integer.rep[2] == 0xff); - - integer = 27336704; - integer >>= 1; - assert(integer.length == 3); - assert(integer.rep[0] == 0xd0 && integer.rep[1] == 0x90 && integer.rep[2] == 0x00); - - integer = 4294967294; - integer >>= 20; - assert(integer.length == 2); - assert(integer.rep[0] == 0x0f && integer.rep[1] == 0xff); - - integer >>= 0; - assert(integer.length == 2); - assert(integer.rep[0] == 0x0f && integer.rep[1] == 0xff); - - integer >>= 20; - assert(integer.length == 0); - - integer >>= 2; - assert(integer.length == 0); - - integer = 1431655765; - integer >>= 16; - assert(integer.length == 2); - assert(integer.rep[0] == 0x55 && integer.rep[1] == 0x55); - - integer >>= 16; - assert(integer.length == 0); - } - - /// Ditto. - ref Integer opOpAssign(string op : "<<")(const size_t operand) @trusted - out - { - assert(this.size == 0 || this.rep[0] > 0); - } - body - { - ubyte carry; - auto i = this.length; - size_t j; - const bit = operand % 8; - const delta = 8 - bit; - - if (cast(ubyte) (this.rep[0] >> delta)) - { - const newSize = i + operand / 8 + 1; - this.rep = allocator.resize(this.rep[0 .. this.size], newSize).ptr; - this.size = newSize; - j = i + 1; - } - else - { - const newSize = i + operand / 8; - this.rep = allocator.resize(this.rep[0 .. this.size], newSize).ptr; - j = i; - } - do - { - --i, --j; - const oldCarry = carry; - carry = rep[i] >> delta; - rep[j] = cast(ubyte) ((this.rep[i] << bit) | oldCarry); - } - while (i); - if (carry) - { - rep[0] = carry; - } - return this; - } - - private @nogc unittest - { - auto integer = Integer(4294967295); - ubyte[5] expected = [0x01, 0xff, 0xff, 0xff, 0xfe]; - integer <<= 1; - assert(integer.rep[0 .. integer.size] == expected); - } - - private void decrement() nothrow @trusted @nogc - in - { - assert(this.length > 0); - } - body - { - auto p = this.rep + this.size; - while (p != this.rep) - { - --p, --*p; - if (*p != ubyte.max) - { - break; - } - } - contract(); - } - - private void increment() nothrow @trusted @nogc - { - auto p = this.rep + this.size; - while (p != this.rep) - { - --p, ++*p; - if (*p > 0) - { - return; - } - } - if (p == this.rep) - { - expand(); - } - } - - /** - * Unary operators. - * - * Params: - * op = Operation. - * - * Returns: New $(D_PSYMBOL Integer). - */ - Integer opUnary(string op : "~")() const - { - auto ret = Integer(this, this.allocator); - ret.rep[0 .. ret.size].each!((ref a) => a = ~a); - return ret; - } - - /// Ditto. - Integer opUnary(string op : "-")() const - { - auto ret = Integer(this, this.allocator); - ret.sign = ret.sign ? Sign.positive : Sign.negative; - return ret; - } - - /** - * Unary operators. - * - * Params: - * op = Operation. - * - * Returns: $(D_KEYWORD this). - */ - ref inout(Integer) opUnary(string op : "+")() inout - { - return this; - } - - private @nogc unittest - { - auto h1 = Integer(79); - Integer h2; - - h2 = +h1; - assert(h2.length == 1); - assert(h2.rep[0] == 79); - assert(!h2.sign); - assert(h2 !is h1); - - h2 = -h1; - assert(h2.length == 1); - assert(h2.rep[0] == 79); - assert(h2.sign); - - h1 = -h2; - assert(h2.length == 1); - assert(h2.rep[0] == 79); - assert(h2.sign); - assert(h2 !is h1); - - h1 = -h2; - assert(h1.length == 1); - assert(h1.rep[0] == 79); - assert(!h1.sign); - - h2 = ~h1; - assert(h2.rep[0] == ~cast(ubyte) 79); - } - - /// Ditto. - ref Integer opUnary(string op : "++")() - out - { - assert(this.size == 0 || this.rep[0] > 0); - } - body - { - if (this.sign) - { - decrement(); - if (this.length == 0) - { - this.sign = Sign.positive; - } - } - else - { - increment(); - } - return this; - } - - /// Ditto. - ref Integer opUnary(string op : "--")() - out - { - assert(this.size == 0 || this.rep[0] > 0); - } - body - { - if (this.sign) - { - increment(); - } - else - { - decrement(); - } - return this; - } - - private @nogc unittest - { - Integer integer; - - ++integer; - assert(integer.length == 1); - assert(integer.rep[0] == 0x01); - - --integer; - assert(integer.length == 0); - - integer = 511; - ++integer; - assert(integer.length == 2); - assert(integer.rep[0] == 0x02 && integer.rep[1] == 0x00); - - --integer; - assert(integer.length == 2); - assert(integer.rep[0] == 0x01 && integer.rep[1] == 0xff); - - integer = 79; - ++integer; - assert(integer.length == 1); - assert(integer.rep[0] == 0x50); - - --integer; - assert(integer.length == 1); - assert(integer.rep[0] == 0x4f); - - integer = 65535; - ++integer; - assert(integer.length == 3); - assert(integer.rep[0] == 0x01 && integer.rep[1] == 0x00 && integer.rep[2] == 0x00); - - --integer; - assert(integer.length == 2); - assert(integer.rep[0] == 0xff && integer.rep[1] == 0xff); - - integer = -2; - ++integer; - assert(integer.length == 1); - assert(integer.rep[0] == 0x01); - } - - /** - * Implements binary operators. - * - * Params: - * op = Operation. - * operand = The second operand. - */ - Integer opBinary(string op)(const auto ref Integer operand) const - if (op == "+" || op == "-" || op == "*" || op == "^^") - { - auto ret = Integer(this, this.allocator); - mixin("ret " ~ op ~ "= operand;"); - return ret; - } - - /// Ditto. - Integer opBinary(string op)(const auto ref Integer operand) const - if (op == "/" || op == "%") - in - { - assert(operand.length > 0, "Division by zero."); - } - body - { - auto ret = Integer(this, this.allocator); - mixin("ret " ~ op ~ "= operand;"); - return ret; - } - - /// Ditto. - Integer opBinary(string op)(const auto ref size_t operand) const - if (op == "<<" || op == ">>") - { - auto ret = Integer(this, this.allocator); - mixin("ret " ~ op ~ "= operand;"); - return ret; - } - - /// - @safe @nogc unittest - { - auto integer1 = Integer(425); - auto integer2 = integer1 << 1; - assert(integer2 == 850); - - integer2 = integer1 >> 1; - assert(integer2 == 212); - } - - /** - * Casting. - * - * Params: - * T = Target type. - * - * Returns: $(D_KEYWORD false) if the $(D_PSYMBOL Integer) is 0, - * $(D_KEYWORD true) otherwise. - */ - T opCast(T : bool)() const - { - return this.length > 0; - } - - /// Ditto. - T opCast(T)() const - if (isIntegral!T && isSigned!T) - { - return this.sign ? -(cast(Unsigned!T) this) : cast(Unsigned!T) this; - } - - /// Ditto. - T opCast(T)() const @trusted - if (isIntegral!T && isUnsigned!T) - { - if (this.length == 0) - { - return 0; - } - T ret; - const(ubyte)* src = this.rep + this.size - 1; - for (ubyte shift; src >= this.rep && shift <= T.sizeof * 8; --src, shift += 8) - { - ret |= cast(T) (*src) << shift; - } - return ret; - } - - /// - @safe @nogc unittest - { - auto integer = Integer(79); - assert(cast(long) integer == 79); - - integer = -79; - assert(cast(long) integer == -79); - - integer = 4294967295; - assert(cast(long) integer == 4294967295); - - integer = -4294967295; - assert(cast(long) integer == -4294967295); - } - - mixin DefaultAllocator; -} +/* 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/. */ + +/** + * Arbitrary precision arithmetic. + * + * 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.math.mp; + +import core.exception; +import std.algorithm; +import std.range; +import std.traits; +import tanya.math; +import tanya.memory; + +/** + * Algebraic sign. + */ +enum Sign : bool +{ + /// The value is positive or `0`. + positive = false, + + /// The value is negative. + negative = true, +} + +/** + * Mutliple precision integer. + */ +struct Integer +{ + private size_t size; + package ubyte* rep; + package Sign sign; + + pure nothrow @safe @nogc invariant + { + assert(this.size > 0 || !this.sign, "0 should be positive."); + } + + /** + * Creates a multiple precision integer. + * + * Params: + * T = Value type. + * value = Initial value. + * allocator = Allocator. + * + * Precondition: $(D_INLINECODE allocator !is null) + */ + this(T)(const T value, shared Allocator allocator = defaultAllocator) + if (isIntegral!T) + { + this(allocator); + assign(value); + } + + /// Ditto. + this(const ref Integer value, shared Allocator allocator = defaultAllocator) + nothrow @safe @nogc + { + this(allocator); + assign(value); + } + + /// Ditto. + this(Integer value, shared Allocator allocator = defaultAllocator) + nothrow @safe @nogc + { + this(allocator); + if (allocator is value.allocator) + { + this.rep = value.rep; + this.size = value.size; + this.sign = value.sign; + value.rep = null; + value.size = 0; + value.sign = Sign.positive; + } + else + { + assign(value); + } + } + + /// Ditto. + this(shared Allocator allocator) pure nothrow @safe @nogc + in + { + assert(allocator !is null); + } + body + { + this.allocator_ = allocator; + } + + private @nogc unittest + { + { + auto integer = Integer(79); + assert(integer.length == 1); + assert(integer.rep[0] == 79); + assert(!integer.sign); + } + { + auto integer = Integer(-2); + assert(integer.length == 1); + assert(integer.rep[0] == 2); + assert(integer.sign); + } + } + + /** + * Constructs the integer from a sign-magnitude $(D_KEYWORD ubyte) range. + * + * Params: + * R = Range type. + * sign = Sign. + * value = Range. + * allocator = Allocator. + * + * Precondition: $(D_INLINECODE allocator !is null) + */ + this(R)(const Sign sign, R value, shared Allocator allocator = defaultAllocator) + if (isInputRange!R && hasLength!R && is(Unqual!(ElementType!R) == ubyte)) + out + { + assert(this.size == 0 || this.rep[0] > 0); + } + body + { + this(allocator); + while (!value.empty && value.front == 0) + { + value.popFront(); + } + this.rep = allocator.resize(this.rep[0 .. this.size], value.length).ptr; + this.size = value.length; + this.sign = sign; + value.copy(this.rep[0 .. this.size]); + } + + /** + * Copies the integer. + */ + this(this) nothrow @trusted @nogc + { + auto tmp = allocator.resize!ubyte(null, this.size); + this.rep[0 .. this.size].copy(tmp); + this.rep = tmp.ptr; + } + + /** + * Destroys the integer. + */ + ~this() nothrow @trusted @nogc + { + allocator.deallocate(this.rep[0 .. this.size]); + } + + /* + * Figures out the minimum amount of space this value will take + * up in bytes and resizes the internal storage. Sets the sign. + */ + private void assign(T)(const ref T value) @trusted + if (isIntegral!T) + { + ubyte size = ulong.sizeof; + ulong mask; + + this.sign = Sign.positive; // Reset the sign. + static if (isSigned!T) + { + const absolute = value.abs; + } + else + { + alias absolute = value; + } + for (mask = 0xff00000000000000; mask >= 0xff; mask >>= 8) + { + if (absolute & mask) + { + break; + } + --size; + } + + this.rep = allocator.resize(this.rep[0 .. this.size], size).ptr; + this.size = size; + static if (isSigned!T) + { + this.sign = value < 0 ? Sign.negative : Sign.positive; + } + + /* Work backward through the int, masking off each byte (up to the + first 0 byte) and copy it into the internal representation in + big-endian format. */ + mask = 0xff; + ubyte shift; + for (auto i = this.size; i; --i, mask <<= 8, shift += 8) + { + this.rep[i - 1] = cast(ubyte) ((absolute & mask) >> shift); + } + } + + private void assign(const ref Integer value) nothrow @trusted @nogc + { + this.rep = allocator.resize(this.rep[0 .. this.size], value.size).ptr; + this.size = value.size; + this.sign = value.sign; + value.rep[0 .. value.size].copy(this.rep[0 .. this.size]); + } + + /** + * Returns: Integer size. + */ + @property size_t length() const pure nothrow @safe @nogc + { + return this.size; + } + + /** + * Assigns a new value. + * + * Params: + * T = Value type. + * value = Value. + * + * Returns: $(D_KEYWORD this). + */ + ref Integer opAssign(T)(const T value) + if (isIntegral!T) + { + assign(value); + return this; + } + + /// Ditto. + ref Integer opAssign(const ref Integer value) nothrow @safe @nogc + { + assign(value); + return this; + } + + /// Ditto. + ref Integer opAssign(Integer value) nothrow @safe @nogc + { + swap(this.rep, value.rep); + swap(this.sign, value.sign); + swap(this.size, value.size); + return this; + } + + private @nogc unittest + { + auto integer = Integer(1019); + assert(integer.length == 2); + assert(integer.rep[0] == 0b00000011 && integer.rep[1] == 0b11111011); + + integer = 3337; + assert(integer.length == 2); + assert(integer.rep[0] == 0b00001101 && integer.rep[1] == 0b00001001); + + integer = 688; + assert(integer.length == 2); + assert(integer.rep[0] == 0b00000010 && integer.rep[1] == 0b10110000); + + integer = 0; + assert(integer.length == 0); + } + + /* + * Extend the space for this by 1 byte and set the LSB to 1. + */ + private void expand() nothrow @trusted @nogc + { + rep = allocator.resize(this.rep[0 .. this.size], this.size + 1).ptr; + ++this.size; + auto target = this.rep[1 .. this.size].retro; + this.rep[0 .. this.size - 1].retro.copy(target); + this.rep[0] = 0x01; + } + + /* + * Go through this and see how many of the left-most bytes are unused. + * Remove them and resize this appropriately. + */ + private void contract() nothrow @trusted @nogc + { + const i = this.rep[0 .. this.size].countUntil!((const ref a) => a != 0); + + if (i > 0) + { + this.rep[i .. this.size].copy(this.rep[0 .. this.size - i]); + this.rep = allocator.resize(this.rep[0 .. this.size], this.size - i).ptr; + this.size -= i; + } + else if (i == -1) + { + this.sign = Sign.positive; + this.rep = allocator.resize(this.rep[0 .. this.size], 0).ptr; + this.size = 0; + } + } + + private void add(const ref Integer summand) nothrow @trusted @nogc + { + if (summand.length > this.length) + { + const delta = summand.size - this.size; + auto tmp = this.rep[0 .. this.size]; + + this.rep = allocator.resize!ubyte(null, summand.size).ptr; + tmp.copy(this.rep[delta .. summand.size]); + this.rep[0 .. delta].initializeAll(); + + allocator.deallocate(tmp[0 .. this.size]); + this.size = summand.size; + } + + bool carry; + size_t i = this.size; + size_t j = summand.size; + do + { + uint sum; + --i; + if (j) + { + --j; + sum = this.rep[i] + summand.rep[j] + carry; + } + else + { + sum = this.rep[i] + carry; + } + + carry = sum > 0xff; + this.rep[i] = cast(ubyte) sum; + } + while (i); + + if (carry) + { + // Still overflowed; allocate more space + expand(); + } + } + + private void subtract(const ref Integer subtrahend) nothrow @trusted @nogc + { + size_t i = this.size; + size_t j = subtrahend.size; + bool borrow; + + while (i) + { + int difference; + --i; + + if (j) + { + --j; + difference = this.rep[i] - subtrahend.rep[j] - borrow; + } + else + { + difference = this.rep[i] - borrow; + } + borrow = difference < 0; + this.rep[i] = cast(ubyte) difference; + } + + if (borrow && i > 0) + { + if (this.rep[i - 1]) // Don't borrow i + { + --this.rep[i - 1]; + } + } + contract(); + } + + private int compare(const ref Integer that) const nothrow @trusted @nogc + { + if (this.length > that.length) + { + return 1; + } + else if (this.length < that.length) + { + return -1; + } + return this.rep[0 .. this.size].cmp(that.rep[0 .. that.size]); + } + + /** + * Comparison. + * + * Params: + * that = The second integer. + * + * Returns: A positive number if $(D_INLINECODE this > that), a negative + * number if $(D_INLINECODE this < that), `0` otherwise. + */ + int opCmp(I : Integer)(const auto ref I that) const + { + if (this.sign != that.sign) + { + return this.sign ? -1 : 1; + } + return compare(that); + } + + /// + @safe @nogc unittest + { + auto integer1 = Integer(1019); + auto integer2 = Integer(1019); + assert(integer1 == integer2); + + integer2 = 3337; + assert(integer1 < integer2); + + integer2 = 688; + assert(integer1 > integer2); + + integer2 = -3337; + assert(integer1 > integer2); + } + + /// Ditto. + int opCmp(I)(const auto ref I that) const + if (isIntegral!I) + { + if (that < 0 && !this.sign) + { + return 1; + } + else if (that > 0 && this.sign) + { + return -1; + } + else if (this.length > I.sizeof) + { + return this.sign ? -1 : 1; + } + + const diff = (cast(I) this) - that; + if (diff > 0) + { + return 1; + } + else if (diff < 0) + { + return -1; + } + return 0; + } + + /// + @safe @nogc unittest + { + auto integer = Integer(1019); + + assert(integer == 1019); + assert(integer < 3337); + assert(integer > 688); + assert(integer > -3337); + } + + /** + * Params: + * that = The second integer. + * + * Returns: Whether the two integers are equal. + */ + bool opEquals(I)(const auto ref I that) const + if (is(I : Integer) || isIntegral!I) + { + return opCmp!I(that) == 0; + } + + /// + @safe @nogc unittest + { + auto integer = Integer(1019); + + assert(integer == Integer(1019)); + assert(integer != Integer(109)); + } + + /** + * Assignment operators with another $(D_PSYMBOL Integer). + * + * Params: + * op = Operation. + * operand = The second operand. + * + * Returns: $(D_KEYWORD this). + */ + ref Integer opOpAssign(string op : "+")(const auto ref Integer operand) + out + { + assert(this.size == 0 || this.rep[0] > 0); + } + body + { + if (this.sign == operand.sign) + { + add(operand); + } + else + { + if (this >= operand) + { + subtract(operand); + } + else + { + // Swap the operands. + auto tmp = Integer(this, this.allocator); + this = Integer(operand, this.allocator); + subtract(tmp); + this.sign = operand.sign; + } + + } + return this; + } + + private @nogc unittest + { + { + auto h1 = Integer(1019); + auto h2 = Integer(3337); + h1 += h2; + assert(h1.length == 2); + assert(h1.rep[0] == 0x11 && h1.rep[1] == 0x04); + } + { + auto h1 = Integer(4356); + auto h2 = Integer(2_147_483_647); + ubyte[4] expected = [0x80, 0x00, 0x11, 0x03]; + h1 += h2; + assert(h1.rep[0 .. h1.size] == expected); + } + { + auto h1 = Integer(2147488003L); + auto h2 = Integer(2_147_483_647); + ubyte[5] expected = [0x01, 0x00, 0x00, 0x11, 0x02]; + h1 += h2; + assert(h1.rep[0 .. h1.size] == expected); + } + } + + /// Ditto. + ref Integer opOpAssign(string op : "-")(const auto ref Integer operand) + out + { + assert(this.size == 0 || this.rep[0] > 0); + } + body + { + if (operand.sign == this.sign) + { + if (this >= operand) + { + subtract(operand); + } + else + { + // Swap the operands. + auto tmp = Integer(this, this.allocator); + this = Integer(operand, this.allocator); + subtract(tmp); + + if (operand.sign || this.size == 0) + { + this.sign = Sign.positive; + } + else + { + this.sign = Sign.negative; + } + } + } + else + { + add(operand); + } + return this; + } + + private @nogc unittest + { + { + auto h1 = Integer(3); + auto h2 = Integer(4); + h1 -= h2; + assert(h1.length == 1); + assert(h1.rep[0] == 0x01); + assert(h1.sign); + } + { + auto h1 = Integer(8589934590L); + auto h2 = Integer(2147483647); + ubyte[5] expected = [0x01, 0x7f, 0xff, 0xff, 0xff]; + + h1 -= h2; + assert(h1.rep[0 .. h1.size] == expected); + } + { + auto h1 = Integer(6442450943); + auto h2 = Integer(4294967294); + ubyte[4] expected = [0x80, 0x00, 0x00, 0x01]; + h1 -= h2; + assert(h1.rep[0 .. h1.size] == expected); + } + { + auto h1 = Integer(2147483649); + auto h2 = Integer(h1); + h1 -= h2; + assert(h1.length == 0); + } + } + + /// Ditto. + ref Integer opOpAssign(string op : "*")(const auto ref Integer operand) @trusted + out + { + assert(this.size == 0 || this.rep[0] > 0); + } + body + { + size_t i = operand.size; + if (this.length == 0) + { + return this; + } + else if (i == 0) + { + this = 0; + return this; + } + auto temp = Integer(this, this.allocator); + auto sign = this.sign != operand.sign; + + this = 0; + do + { + --i; + for (ubyte mask = 0x01; mask; mask <<= 1) + { + if (mask & operand.rep[i]) + { + add(temp); + } + temp <<= 1; + } + } + while (i); + + this.sign = sign ? Sign.negative : Sign.positive; + + return this; + } + + /// + @safe @nogc unittest + { + auto h1 = Integer(123); + auto h2 = Integer(456); + h1 *= h2; + assert(h1 == 56088); + } + + private @nogc unittest + { + assert((Integer(1) * Integer()).length == 0); + } + + /// Ditto. + ref Integer opOpAssign(string op : "^^")(const auto ref Integer operand) + @trusted + out + { + assert(this.size == 0 || this.rep[0] > 0); + } + body + { + size_t i = operand.size; + + auto tmp1 = Integer(this, this.allocator); + this = 1; + + do + { + --i; + for (ubyte mask = 0x01; mask; mask <<= 1) + { + if (operand.rep[i] & mask) + { + this *= tmp1; + } + // Square tmp1 + auto tmp2 = tmp1; + tmp1 *= tmp2; + } + } + while (i); + + return this; + } + + private @nogc unittest + { + auto h1 = Integer(2); + auto h2 = Integer(4); + + h1 ^^= h2; + assert(h1.length == 1); + assert(h1.rep[0] == 0x10); + + h1 = Integer(2342); + h1 ^^= h2; + ubyte[6] expected = [0x1b, 0x5c, 0xab, 0x9c, 0x31, 0x10]; + assert(h1.rep[0 .. h1.size] == expected); + } + + /// Ditto. + ref Integer opOpAssign(string op)(const auto ref Integer operand) @trusted + if ((op == "%") || (op == "/")) + in + { + assert(operand.length > 0, "Division by zero."); + } + out + { + assert(this.size == 0 || this.rep[0] > 0); + } + body + { + auto divisor = Integer(operand, this.allocator); + size_t bitSize; + + // First, left-shift divisor until it's >= than the dividend + while (compare(divisor) > 0) + { + divisor <<= 1; + ++bitSize; + } + static if (op == "/") + { + auto quotient = allocator.resize!ubyte(null, bitSize / 8 + 1); + quotient.initializeAll(); + } + + // bitPosition keeps track of which bit, of the quotient, + // is being set or cleared on the current operation. + auto bitPosition = 8 - (bitSize % 8) - 1; + do + { + if (compare(divisor) >= 0) + { + subtract(divisor); // dividend -= divisor + static if (op == "/") + { + quotient[bitPosition / 8] |= 0x80 >> (bitPosition % 8); + } + } + if (bitSize != 0) + { + divisor >>= 1; + } + ++bitPosition; + } + while (bitSize--); + + static if (op == "/") + { + allocator.resize(this.rep[0 .. this.size], 0); + this.rep = quotient.ptr; + this.size = quotient.length; + this.sign = this.sign == divisor.sign ? Sign.positive : Sign.negative; + contract(); + } + + return this; + } + + private @nogc unittest + { + auto h1 = Integer(18); + auto h2 = Integer(4); + h1 %= h2; + assert(h1.length == 1); + assert(h1.rep[0] == 0x02); + + h1 = 8; + h1 %= h2; + assert(h1.length == 0); + + h1 = 7; + h1 %= h2; + assert(h1.length == 1); + assert(h1.rep[0] == 0x03); + + h1 = 56088; + h2 = 456; + h1 /= h2; + assert(h1.length == 1); + assert(h1.rep[0] == 0x7b); + } + + /// Ditto. + ref Integer opOpAssign(string op : ">>")(const size_t operand) @trusted + out + { + assert(this.size == 0 || this.rep[0] > 0); + } + body + { + const step = operand / 8; + + if (step >= this.length) + { + this.rep = allocator.resize(this.rep[0 .. this.size], 0).ptr; + this.size = 0; + return this; + } + + size_t i, j; + ubyte carry; + const bit = operand % 8; + const delta = 8 - bit; + + carry = cast(ubyte) (this.rep[0] << delta); + this.rep[0] = this.rep[0] >> bit; + if (rep[0]) + { + ++j; + } + for (i = 1; i < this.length; ++i) + { + immutable oldCarry = carry; + carry = cast(ubyte) (this.rep[i] << delta); + this.rep[j] = this.rep[i] >> bit | oldCarry; + ++j; + } + const newSize = this.length - operand / 8 - (i == j ? 0 : 1); + this.rep = allocator.resize(this.rep[0 .. this.size], newSize).ptr; + this.size = newSize; + + return this; + } + + private @nogc unittest + { + auto integer = Integer(4294967294); + integer >>= 10; + assert(integer.length == 3); + assert(integer.rep[0] == 0x3f && integer.rep[1] == 0xff && integer.rep[2] == 0xff); + + integer = 27336704; + integer >>= 1; + assert(integer.length == 3); + assert(integer.rep[0] == 0xd0 && integer.rep[1] == 0x90 && integer.rep[2] == 0x00); + + integer = 4294967294; + integer >>= 20; + assert(integer.length == 2); + assert(integer.rep[0] == 0x0f && integer.rep[1] == 0xff); + + integer >>= 0; + assert(integer.length == 2); + assert(integer.rep[0] == 0x0f && integer.rep[1] == 0xff); + + integer >>= 20; + assert(integer.length == 0); + + integer >>= 2; + assert(integer.length == 0); + + integer = 1431655765; + integer >>= 16; + assert(integer.length == 2); + assert(integer.rep[0] == 0x55 && integer.rep[1] == 0x55); + + integer >>= 16; + assert(integer.length == 0); + } + + /// Ditto. + ref Integer opOpAssign(string op : "<<")(const size_t operand) @trusted + out + { + assert(this.size == 0 || this.rep[0] > 0); + } + body + { + ubyte carry; + auto i = this.length; + size_t j; + const bit = operand % 8; + const delta = 8 - bit; + + if (cast(ubyte) (this.rep[0] >> delta)) + { + const newSize = i + operand / 8 + 1; + this.rep = allocator.resize(this.rep[0 .. this.size], newSize).ptr; + this.size = newSize; + j = i + 1; + } + else + { + const newSize = i + operand / 8; + this.rep = allocator.resize(this.rep[0 .. this.size], newSize).ptr; + j = i; + } + do + { + --i, --j; + const oldCarry = carry; + carry = rep[i] >> delta; + rep[j] = cast(ubyte) ((this.rep[i] << bit) | oldCarry); + } + while (i); + if (carry) + { + rep[0] = carry; + } + return this; + } + + private @nogc unittest + { + auto integer = Integer(4294967295); + ubyte[5] expected = [0x01, 0xff, 0xff, 0xff, 0xfe]; + integer <<= 1; + assert(integer.rep[0 .. integer.size] == expected); + } + + private void decrement() nothrow @trusted @nogc + in + { + assert(this.length > 0); + } + body + { + auto p = this.rep + this.size; + while (p != this.rep) + { + --p, --*p; + if (*p != ubyte.max) + { + break; + } + } + contract(); + } + + private void increment() nothrow @trusted @nogc + { + auto p = this.rep + this.size; + while (p != this.rep) + { + --p, ++*p; + if (*p > 0) + { + return; + } + } + if (p == this.rep) + { + expand(); + } + } + + /** + * Unary operators. + * + * Params: + * op = Operation. + * + * Returns: New $(D_PSYMBOL Integer). + */ + Integer opUnary(string op : "~")() const + { + auto ret = Integer(this, this.allocator); + ret.rep[0 .. ret.size].each!((ref a) => a = ~a); + return ret; + } + + /// Ditto. + Integer opUnary(string op : "-")() const + { + auto ret = Integer(this, this.allocator); + ret.sign = ret.sign ? Sign.positive : Sign.negative; + return ret; + } + + /** + * Unary operators. + * + * Params: + * op = Operation. + * + * Returns: $(D_KEYWORD this). + */ + ref inout(Integer) opUnary(string op : "+")() inout + { + return this; + } + + private @nogc unittest + { + auto h1 = Integer(79); + Integer h2; + + h2 = +h1; + assert(h2.length == 1); + assert(h2.rep[0] == 79); + assert(!h2.sign); + assert(h2 !is h1); + + h2 = -h1; + assert(h2.length == 1); + assert(h2.rep[0] == 79); + assert(h2.sign); + + h1 = -h2; + assert(h2.length == 1); + assert(h2.rep[0] == 79); + assert(h2.sign); + assert(h2 !is h1); + + h1 = -h2; + assert(h1.length == 1); + assert(h1.rep[0] == 79); + assert(!h1.sign); + + h2 = ~h1; + assert(h2.rep[0] == ~cast(ubyte) 79); + } + + /// Ditto. + ref Integer opUnary(string op : "++")() + out + { + assert(this.size == 0 || this.rep[0] > 0); + } + body + { + if (this.sign) + { + decrement(); + if (this.length == 0) + { + this.sign = Sign.positive; + } + } + else + { + increment(); + } + return this; + } + + /// Ditto. + ref Integer opUnary(string op : "--")() + out + { + assert(this.size == 0 || this.rep[0] > 0); + } + body + { + if (this.sign) + { + increment(); + } + else + { + decrement(); + } + return this; + } + + private @nogc unittest + { + Integer integer; + + ++integer; + assert(integer.length == 1); + assert(integer.rep[0] == 0x01); + + --integer; + assert(integer.length == 0); + + integer = 511; + ++integer; + assert(integer.length == 2); + assert(integer.rep[0] == 0x02 && integer.rep[1] == 0x00); + + --integer; + assert(integer.length == 2); + assert(integer.rep[0] == 0x01 && integer.rep[1] == 0xff); + + integer = 79; + ++integer; + assert(integer.length == 1); + assert(integer.rep[0] == 0x50); + + --integer; + assert(integer.length == 1); + assert(integer.rep[0] == 0x4f); + + integer = 65535; + ++integer; + assert(integer.length == 3); + assert(integer.rep[0] == 0x01 && integer.rep[1] == 0x00 && integer.rep[2] == 0x00); + + --integer; + assert(integer.length == 2); + assert(integer.rep[0] == 0xff && integer.rep[1] == 0xff); + + integer = -2; + ++integer; + assert(integer.length == 1); + assert(integer.rep[0] == 0x01); + } + + /** + * Implements binary operators. + * + * Params: + * op = Operation. + * operand = The second operand. + */ + Integer opBinary(string op)(const auto ref Integer operand) const + if (op == "+" || op == "-" || op == "*" || op == "^^") + { + auto ret = Integer(this, this.allocator); + mixin("ret " ~ op ~ "= operand;"); + return ret; + } + + /// Ditto. + Integer opBinary(string op)(const auto ref Integer operand) const + if (op == "/" || op == "%") + in + { + assert(operand.length > 0, "Division by zero."); + } + body + { + auto ret = Integer(this, this.allocator); + mixin("ret " ~ op ~ "= operand;"); + return ret; + } + + /// Ditto. + Integer opBinary(string op)(const auto ref size_t operand) const + if (op == "<<" || op == ">>") + { + auto ret = Integer(this, this.allocator); + mixin("ret " ~ op ~ "= operand;"); + return ret; + } + + /// + @safe @nogc unittest + { + auto integer1 = Integer(425); + auto integer2 = integer1 << 1; + assert(integer2 == 850); + + integer2 = integer1 >> 1; + assert(integer2 == 212); + } + + /** + * Casting. + * + * Params: + * T = Target type. + * + * Returns: $(D_KEYWORD false) if the $(D_PSYMBOL Integer) is 0, + * $(D_KEYWORD true) otherwise. + */ + T opCast(T : bool)() const + { + return this.length > 0; + } + + /// Ditto. + T opCast(T)() const + if (isIntegral!T && isSigned!T) + { + return this.sign ? -(cast(Unsigned!T) this) : cast(Unsigned!T) this; + } + + /// Ditto. + T opCast(T)() const @trusted + if (isIntegral!T && isUnsigned!T) + { + if (this.length == 0) + { + return 0; + } + T ret; + const(ubyte)* src = this.rep + this.size - 1; + for (ubyte shift; src >= this.rep && shift <= T.sizeof * 8; --src, shift += 8) + { + ret |= cast(T) (*src) << shift; + } + return ret; + } + + /// + @safe @nogc unittest + { + auto integer = Integer(79); + assert(cast(long) integer == 79); + + integer = -79; + assert(cast(long) integer == -79); + + integer = 4294967295; + assert(cast(long) integer == 4294967295); + + integer = -4294967295; + assert(cast(long) integer == -4294967295); + } + + mixin DefaultAllocator; +} diff --git a/source/tanya/memory/package.d b/source/tanya/memory/package.d index 0595073..5e05943 100644 --- a/source/tanya/memory/package.d +++ b/source/tanya/memory/package.d @@ -87,8 +87,8 @@ shared Allocator allocator; shared static this() nothrow @trusted @nogc { - import tanya.memory.mmappool; - allocator = MmapPool.instance; + import tanya.memory.mallocator; + allocator = Mallocator.instance; } @property ref shared(Allocator) defaultAllocator() nothrow @safe @nogc @@ -162,11 +162,22 @@ package(tanya) T[] resize(T)(shared Allocator allocator, auto ref T[] array, const size_t length) @trusted { - void[] buf = array; + if (length == 0) + { + if (allocator.deallocate(array)) + { + return null; + } + else + { + onOutOfMemoryErrorNoGC(); + } + } + void[] buf = array; if (!allocator.reallocate(buf, length * T.sizeof)) { - onOutOfMemoryError; + onOutOfMemoryErrorNoGC(); } // Casting from void[] is unsafe, but we know we cast to the original type. array = cast(T[]) buf; From 49cae886456c68bd99388eb5df6bf1740e8953a2 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Fri, 24 Mar 2017 20:54:28 +0100 Subject: [PATCH 12/26] Add insertBefore and remove to SList --- source/tanya/container/list.d | 266 +++++++++++++++++++++++++--------- 1 file changed, 201 insertions(+), 65 deletions(-) diff --git a/source/tanya/container/list.d b/source/tanya/container/list.d index 52706ac..491b31b 100644 --- a/source/tanya/container/list.d +++ b/source/tanya/container/list.d @@ -20,16 +20,22 @@ import std.traits; import tanya.container.entry; import tanya.memory; -private struct Range(E) - if (__traits(isSame, TemplateOf!E, SEntry)) +private struct Range(Entry) + if (__traits(isSame, TemplateOf!Entry, SEntry)) { private alias T = typeof(E.content); + private alias E = CopyConstness!(Entry, Entry*); private E* head; - private this(E* head) + invariant { - this.head = head; + assert(head !is null); + } + + private this(ref E head) @trusted + { + this.head = &head; } @property Range save() @@ -44,7 +50,7 @@ private struct Range(E) @property bool empty() const { - return head is null; + return *head is null; } @property ref inout(T) front() inout @@ -54,27 +60,27 @@ private struct Range(E) } body { - return head.content; + return (*head).content; } - void popFront() + void popFront() @trusted in { assert(!empty); } body { - head = head.next; + head = &(*head).next; } Range opIndex() { - return typeof(return)(head); + return typeof(return)(*head); } - Range!(const E) opIndex() const + Range!(const Entry) opIndex() const { - return typeof(return)(head); + return typeof(return)(*head); } } @@ -137,7 +143,7 @@ struct SList(T) * init = Initial value to fill the list with. * allocator = Allocator. */ - this(in size_t len, T init, shared Allocator allocator = defaultAllocator) @trusted + this(const size_t len, T init, shared Allocator allocator = defaultAllocator) @trusted { this(allocator); @@ -166,7 +172,7 @@ struct SList(T) } /// Ditto. - this(in size_t len, shared Allocator allocator = defaultAllocator) + this(const size_t len, shared Allocator allocator = defaultAllocator) { this(len, T.init, allocator); } @@ -260,13 +266,13 @@ struct SList(T) */ this(this) { - auto buf = opIndex(); - head = null; - insertFront(buf); + auto list = typeof(this)(this[], this.allocator); + this.head = list.head; + list.head = null; } /// - unittest + @safe @nogc unittest { auto l1 = SList!int([5, 1, 234]); auto l2 = l1; @@ -285,7 +291,7 @@ struct SList(T) } /// - unittest + @safe @nogc unittest { SList!int l = SList!int([8, 5]); @@ -307,35 +313,40 @@ struct SList(T) return head.content; } - /** - * 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. - size_t insertFront(R)(R x) @trusted + private size_t moveEntry(R)(ref Entry* head, ref R el) @trusted if (isImplicitlyConvertible!(R, T)) { auto temp = cast(Entry*) allocator.allocate(Entry.sizeof); - x.moveEmplace(temp.content); + el.moveEmplace(temp.content); temp.next = head; head = temp; return 1; } + /** + * Inserts a new element at the beginning. + * + * Params: + * R = Type of the inserted value(s). + * el = New element(s). + * + * Returns: The number of elements inserted. + */ + size_t insertFront(ref T el) @trusted + { + head = allocator.make!Entry(el, head); + return 1; + } + + /// Ditto. + size_t insertFront(R)(R el) + if (isImplicitlyConvertible!(R, T)) + { + return moveEntry(head, el); + } + /// Ditto. size_t insertFront(R)(R el) @trusted if (!isInfinite!R @@ -377,7 +388,7 @@ struct SList(T) alias insert = insertFront; /// - @nogc @safe unittest + @safe @nogc unittest { SList!int l1; @@ -395,16 +406,116 @@ struct SList(T) assert(l2.front == 9); } + private bool checkRangeBelonging(ref Range!Entry r) const + { + const(Entry*)* pos; + for (pos = &head; pos != r.head && *pos !is null; pos = &(*pos).next) + { + } + return pos == r.head; + } + + /** + * Inserts new elements before $(D_PARAM r). + * + * Params: + * R = Type of the inserted value(s). + * el = New element(s). + * + * Returns: The number of elements inserted. + * + * Precondition: $(D_PARAM r) is extracted from this list. + */ + size_t insertBefore(Range!Entry r, ref T el) @trusted + in + { + assert(checkRangeBelonging(r)); + } + body + { + *r.head = allocator.make!Entry(el, *r.head); + return 1; + } + + /// + @safe @nogc unittest + { + auto l1 = SList!int([234, 5, 1]); + auto l2 = SList!int([5, 1]); + int var = 234; + l2.insertBefore(l2[], var); + assert(l1 == l2); + } + + /// Ditto. + size_t insertBefore(R)(Range!Entry r, R el) + if (isImplicitlyConvertible!(R, T)) + in + { + assert(checkRangeBelonging(r)); + } + body + { + return moveEntry(*r.head, el); + } + + /// + @safe @nogc unittest + { + auto l1 = SList!int([234, 5, 1]); + auto l2 = SList!int([5, 1]); + l2.insertBefore(l2[], 234); + assert(l1 == l2); + } + + /// Ditto. + size_t insertBefore(R)(Range!Entry r, R el) + if (!isInfinite!R + && isInputRange!R + && isImplicitlyConvertible!(ElementType!R, T)) + in + { + assert(checkRangeBelonging(r)); + } + body + { + size_t inserted; + foreach (e; el) + { + inserted += insertBefore(r, e); + r.popFront(); + } + return inserted; + } + + /// + @safe @nogc unittest + { + auto l1 = SList!int([5, 234, 30, 1]); + auto l2 = SList!int([5, 1]); + auto l3 = SList!int([234, 30]); + auto r = l2[]; + r.popFront(); + l2.insertBefore(r, l3[]); + assert(l1 == l2); + } + + /// Ditto. + size_t insertBefore(size_t R)(Range!Entry r, T[R] el) + { + return insertFront!(T[])(el[]); + } + /** * Returns: How many elements are in the list. */ @property size_t length() const { - return count(opIndex()); + return count(this[]); } /// - unittest + @safe @nogc unittest { SList!int l; @@ -426,19 +537,13 @@ struct SList(T) * Returns: $(D_KEYWORD true) if the lists are equal, $(D_KEYWORD false) * otherwise. */ - bool opEquals()(auto ref typeof(this) that) @trusted + bool opEquals()(auto ref typeof(this) that) inout { - return equal(opIndex(), that[]); - } - - /// Ditto. - bool opEquals()(in auto ref typeof(this) that) const @trusted - { - return equal(opIndex(), that[]); + return equal(this[], that[]); } /// - unittest + @safe @nogc unittest { SList!int l1, l2; @@ -483,14 +588,14 @@ struct SList(T) } body { - auto n = head.next; + auto n = this.head.next; - allocator.dispose(head); - head = n; + this.allocator.dispose(this.head); + this.head = n; } /// - unittest + @safe @nogc unittest { SList!int l; @@ -515,7 +620,7 @@ struct SList(T) * * Returns: The number of elements removed. */ - size_t removeFront(in size_t howMany) + size_t removeFront(const size_t howMany) out (removed) { assert(removed <= howMany); @@ -530,23 +635,54 @@ struct SList(T) return i; } - /// Ditto. - alias remove = removeFront; - /// - unittest + @safe @nogc unittest { - SList!int l; + SList!int l = SList!int([8, 5, 4]); - 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); } + /** + * Removes $(D_PARAM r) from the list. + * + * Params: + * r = The range to remove. + * + * Returns: An empty range. + * + * Precondition: $(D_PARAM r) is extracted from this list. + */ + Range!Entry remove(Range!Entry r) + in + { + assert(checkRangeBelonging(r)); + } + body + { + typeof(this) outOfScopeList; + outOfScopeList.head = *r.head; + *r.head = null; + + return r; + } + + /// + @safe @nogc unittest + { + auto l1 = SList!int([5, 234, 30, 1]); + auto l2 = SList!int([5]); + auto r = l1[]; + + r.popFront(); + + assert(l1.remove(r).empty); + assert(l1 == l2); + } + /** * $(D_KEYWORD foreach) iteration. * @@ -590,7 +726,7 @@ struct SList(T) } /// - unittest + @nogc unittest { SList!int l; @@ -619,7 +755,7 @@ struct SList(T) } /// -unittest +@nogc unittest { SList!int l; size_t i; @@ -637,7 +773,7 @@ unittest assert(i == 3); } -unittest +@safe @nogc private unittest { interface Stuff { From 4d8b95812ebb3415bf77f4f2e32e7e58f77d6ab8 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Tue, 28 Mar 2017 20:42:42 +0200 Subject: [PATCH 13/26] Implement opAssign for the Vector --- source/tanya/container/vector.d | 125 +++++++++++++++++++++++++++----- 1 file changed, 105 insertions(+), 20 deletions(-) diff --git a/source/tanya/container/vector.d b/source/tanya/container/vector.d index da79929..b8b2bba 100644 --- a/source/tanya/container/vector.d +++ b/source/tanya/container/vector.d @@ -209,7 +209,7 @@ struct Vector(T) * init = Source vector. * allocator = Allocator. */ - this(R)(ref R init, shared Allocator allocator = defaultAllocator) + this(R)(const ref R init, shared Allocator allocator = defaultAllocator) if (is(Unqual!R == Vector)) { this(allocator); @@ -221,25 +221,7 @@ struct Vector(T) 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. - } + moveAssign(init); } /// @@ -1473,6 +1455,109 @@ struct Vector(T) assert(data.length == 1); } + private void moveAssign(ref Vector v) @trusted + { + if (allocator is v.allocator) + { + // Just steal all references and the allocator. + vector = v.vector; + length_ = v.length_; + capacity_ = v.capacity_; + + // Reset the source vector, so it can't destroy the moved storage. + v.length_ = v.capacity_ = 0; + v.vector = null; + } + else + { + // Move each element. + reserve(v.length); + moveEmplaceAll(v.vector[0 .. v.length_], vector[0 .. v.length_]); + length_ = v.length; + // Destructor of v should destroy it here. + } + } + + /** + * Assigns content to the vector. + * + * Params: + * R = Content type. + * init = The value should be assigned. + */ + ref typeof(this) opAssign(R)(const ref R init) + if (is(Unqual!R == Vector)) + { + insertBack(init[]); + return this; + } + + /// Ditto. + ref typeof(this) opAssign(R)(R init) + if (is(R == Vector)) + { + moveAssign(init); + return this; + } + + /// Ditto. + ref typeof(this) opAssign(R)(R init) + if (!isInfinite!R + && isInputRange!R + && isImplicitlyConvertible!(ElementType!R, T)) + { + insertBack(init); + 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. + * init = Values to initialize the vector with. + */ + ref typeof(this) opAssign(size_t R)(T[R] init) + { + insertBack!(T[])(init[]); + return this; + } + + /// + @safe @nogc unittest + { + auto v1 = Vector!int([5, 15, 8]); + Vector!int v2; + + v2 = [5, 15, 8]; + assert(v1 == v2); + } + mixin DefaultAllocator; } From aabb4fb534184497e48c2d7e050753d555615f47 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Wed, 29 Mar 2017 10:35:45 +0200 Subject: [PATCH 14/26] Add SList.opAssign --- source/tanya/container/list.d | 130 ++++++++++++++++++++++++++++---- source/tanya/container/vector.d | 95 +++++++++++++---------- 2 files changed, 171 insertions(+), 54 deletions(-) diff --git a/source/tanya/container/list.d b/source/tanya/container/list.d index 491b31b..47453ed 100644 --- a/source/tanya/container/list.d +++ b/source/tanya/container/list.d @@ -356,18 +356,17 @@ struct SList(T) size_t retLength; Entry* next, newHead; + if (!el.empty) + { + next = allocator.make!Entry(el.front); + newHead = next; + el.popFront(); + retLength = 1; + } foreach (ref e; el) { - if (next is null) - { - next = allocator.make!Entry(e); - newHead = next; - } - else - { - next.next = allocator.make!Entry(e); - next = next.next; - } + next.next = allocator.make!Entry(e); + next = next.next; ++retLength; } if (newHead !is null) @@ -406,13 +405,16 @@ struct SList(T) assert(l2.front == 9); } - private bool checkRangeBelonging(ref Range!Entry r) const + version (assert) { - const(Entry*)* pos; - for (pos = &head; pos != r.head && *pos !is null; pos = &(*pos).next) + private bool checkRangeBelonging(ref Range!Entry r) const { + const(Entry*)* pos; + for (pos = &head; pos != r.head && *pos !is null; pos = &(*pos).next) + { + } + return pos == r.head; } - return pos == r.head; } /** @@ -741,16 +743,116 @@ struct SList(T) } } + /** + * Returns: Range that iterates over all elements of the container, in + * forward order. + */ Range!Entry opIndex() { return typeof(return)(head); } + /// Ditto. Range!(const Entry) opIndex() const { return typeof(return)(head); } + /** + * Assigns another list. + * + * If $(D_PARAM that) is passed by value, it won't be copied, but moved. + * This list 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 == SList)) + { + return this = that[]; + } + + /// Ditto. + ref typeof(this) opAssign(R)(const ref R that) + if (is(Unqual!R == SList)) + { + swap(this.head, that.head); + swap(this.allocator_, that.allocator_); + } + + /** + * Assigns an input range. + * + * Params: + * R = Type of the initial range. + * that = Values to initialize the list with. + * + * Returns: $(D_KEYWORD this). + */ + ref typeof(this) opAssign(R)(R that) @trusted + if (!isInfinite!R + && isInputRange!R + && isImplicitlyConvertible!(ElementType!R, T)) + { + Entry** next = &head; + + foreach (ref e; that) + { + if (*next is null) + { + *next = allocator.make!Entry(e); + } + else + { + (*next).content = e; + } + next = &(*next).next; + } + remove(Range!Entry(*next)); + + return this; + } + + /// + @safe @nogc unittest + { + auto l1 = SList!int([5, 4, 9]); + auto l2 = SList!int([9, 4]); + l1 = l2[]; + assert(l1 == l2); + } + + /** + * 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 l1 = SList!int([5, 4, 9]); + auto l2 = SList!int([9, 4]); + l1 = [9, 4]; + assert(l1 == l2); + } + + mixin DefaultAllocator; } diff --git a/source/tanya/container/vector.d b/source/tanya/container/vector.d index b8b2bba..4a7b5c3 100644 --- a/source/tanya/container/vector.d +++ b/source/tanya/container/vector.d @@ -221,11 +221,29 @@ struct Vector(T) if (is(R == Vector)) { this(allocator); - moveAssign(init); + 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 + @trusted @nogc unittest { auto v1 = Vector!int([1, 2, 3]); auto v2 = Vector!int(v1); @@ -238,7 +256,7 @@ struct Vector(T) assert(v3.capacity == 3); } - unittest // const constructor tests + private @trusted @nogc unittest // const constructor tests { auto v1 = const Vector!int([1, 2, 3]); auto v2 = Vector!int(v1); @@ -1455,58 +1473,54 @@ struct Vector(T) assert(data.length == 1); } - private void moveAssign(ref Vector v) @trusted - { - if (allocator is v.allocator) - { - // Just steal all references and the allocator. - vector = v.vector; - length_ = v.length_; - capacity_ = v.capacity_; - - // Reset the source vector, so it can't destroy the moved storage. - v.length_ = v.capacity_ = 0; - v.vector = null; - } - else - { - // Move each element. - reserve(v.length); - moveEmplaceAll(v.vector[0 .. v.length_], vector[0 .. v.length_]); - length_ = v.length; - // Destructor of v should destroy it here. - } - } - /** - * Assigns content to the vector. + * 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. - * init = The value should be assigned. + * that = The value should be assigned. + * + * Returns: $(D_KEYWORD this). */ - ref typeof(this) opAssign(R)(const ref R init) + ref typeof(this) opAssign(R)(const ref R that) if (is(Unqual!R == Vector)) { - insertBack(init[]); - return this; + return this = that[]; } /// Ditto. - ref typeof(this) opAssign(R)(R init) + ref typeof(this) opAssign(R)(R that) @trusted if (is(R == Vector)) { - moveAssign(init); + swap(this.vector, that.vector); + swap(this.length_, that.length_); + swap(this.capacity_, that.capacity_); + swap(this.allocator_, that.allocator_); return this; } - /// Ditto. - ref typeof(this) opAssign(R)(R init) + /** + * 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)) { - insertBack(init); + this.length = 0; + insertBack(that); return this; } @@ -1540,12 +1554,13 @@ struct Vector(T) * * Params: * R = Static array size. - * init = Values to initialize the vector with. + * that = Values to initialize the vector with. + * + * Returns: $(D_KEYWORD this). */ - ref typeof(this) opAssign(size_t R)(T[R] init) + ref typeof(this) opAssign(size_t R)(T[R] that) { - insertBack!(T[])(init[]); - return this; + return opAssign!(T[])(that[]); } /// From b870179a35747d4676c538974a45a89589c3c9cc Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Wed, 29 Mar 2017 11:17:03 +0200 Subject: [PATCH 15/26] Move bitvector to another branch till it is finished --- source/tanya/container/bitvector.d | 510 ----------------------------- source/tanya/container/package.d | 1 - 2 files changed, 511 deletions(-) delete mode 100644 source/tanya/container/bitvector.d diff --git a/source/tanya/container/bitvector.d b/source/tanya/container/bitvector.d deleted file mode 100644 index 3597a73..0000000 --- a/source/tanya/container/bitvector.d +++ /dev/null @@ -1,510 +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 bit 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.bitvector; - -/** - * Wrapper that allows bit manipulation on $(D_KEYWORD ubyte[]) array. - */ -struct BitVector -{ - protected ubyte[] vector; - - /** - * Params: - * array = Array should be manipulated on. - */ - this(inout(ubyte[]) array) inout pure nothrow @safe @nogc - in - { - assert(array.length <= size_t.max / 8); - assert(array !is null); - } - body - { - vector = array; - } - - /// - unittest - { - ubyte[5] array1 = [234, 3, 252, 10, 18]; - ubyte[3] array2 = [65, 13, 173]; - auto bits = BitVector(array1); - - assert(bits[] is array1); - assert(bits[] !is array2); - - bits = BitVector(array2); - assert(bits[] is array2); - } - - /** - * Returns: Number of bits in the vector. - */ - @property inout(size_t) length() inout const pure nothrow @safe @nogc - { - return vector.length * 8; - } - - /// Ditto. - inout(size_t) opDollar() inout const pure nothrow @safe @nogc - { - return vector.length * 8; - } - - /// - unittest - { - // [01000001, 00001101, 10101101] - ubyte[3] arr = [65, 13, 173]; - auto bits = BitVector(arr); - - assert(bits.length == 24); - } - - /** - * Params: - * bit = Bit position. - * - * Returns: $(D_KEYWORD true) if the bit on position $(D_PARAM bit) is set, - * $(D_KEYWORD false) if not set. - */ - inout(bool) opIndex(size_t bit) inout const pure nothrow @safe @nogc - in - { - assert(bit / 8 <= vector.length); - } - body - { - return (vector[bit / 8] & (0x80 >> (bit % 8))) != 0; - } - - /// - unittest - { - // [01000001, 00001101, 10101101] - ubyte[3] arr = [65, 13, 173]; - auto bits = BitVector(arr); - - assert(!bits[0]); - assert(bits[1]); - assert(bits[7]); - assert(!bits[8]); - assert(!bits[11]); - assert(bits[12]); - assert(bits[20]); - assert(bits[23]); - } - - /** - * Returns: Underlying array. - */ - inout(ubyte[]) opIndex() inout pure nothrow @safe @nogc - { - return vector; - } - - /// - unittest - { - // [01000001, 00001101, 10101101] - ubyte[3] arr = [65, 13, 173]; - auto bits = BitVector(arr); - - assert(bits[] is arr); - } - - /** - * Params: - * value = $(D_KEYWORD true) if the bit should be set, - * $(D_KEYWORD false) if cleared. - * bit = Bit position. - * - * Returns: $(D_PSYMBOL this). - */ - bool opIndexAssign(bool value, size_t bit) pure nothrow @safe @nogc - in - { - assert(bit / 8 <= vector.length); - } - body - { - if (value) - { - vector[bit / 8] |= (0x80 >> (bit % 8)); - } - else - { - vector[bit / 8] &= ~(0x80 >> (bit % 8)); - } - return value; - } - - /// - unittest - { - // [01000001, 00001101, 10101101] - ubyte[3] arr = [65, 13, 173]; - auto bits = BitVector(arr); - - bits[5] = bits[6] = true; - assert(bits[][0] == 71); - - bits[14] = true; - bits[15] = false; - assert(bits[][1] == 14); - - bits[16] = bits[23] = false; - assert(bits[][2] == 44); - } - - /** - * Copies bits from $(D_PARAM vector) into this $(D_PSYMBOL BitVector). - * - * The array that should be assigned, can be smaller (but not larger) than - * the underlying array of this $(D_PSYMBOL BitVector), leading zeros will - * be added in this case to the left. - * - * Params: - * vector = $(D_KEYWORD ubyte[]) array not larger than - * `$(D_PSYMBOL length) / 8`. - * - * Returns: $(D_KEYWORD this). - */ - BitVector opAssign(ubyte[] vector) pure nothrow @safe @nogc - in - { - assert(vector.length <= this.vector.length); - } - body - { - immutable delta = this.vector.length - vector.length; - if (delta > 0) - { - this.vector[0..delta] = 0; - } - this.vector[delta..$] = vector[0..$]; - return this; - } - - /// - unittest - { - ubyte[5] array1 = [234, 3, 252, 10, 18]; - ubyte[3] array2 = [65, 13, 173]; - auto bits = BitVector(array1); - - bits = array2; - assert(bits[][0] == 0); - assert(bits[][1] == 0); - assert(bits[][2] == 65); - assert(bits[][3] == 13); - assert(bits[][4] == 173); - - bits = array2[0..2]; - assert(bits[][0] == 0); - assert(bits[][1] == 0); - assert(bits[][2] == 0); - assert(bits[][3] == 65); - assert(bits[][4] == 13); - } - - /** - * Support for bitwise operations. - * - * Params: - * that = Another bit vector. - * - * Returns: $(D_KEYWORD this). - */ - BitVector opOpAssign(string op)(BitVector that) pure nothrow @safe @nogc - if ((op == "^") || (op == "|") || (op == "&")) - { - return opOpAssign(op)(that.vector); - } - - /// Ditto. - BitVector opOpAssign(string op)(ubyte[] that) pure nothrow @safe @nogc - if ((op == "^") || (op == "|") || (op == "&")) - in - { - assert(that.length <= vector.length); - } - body - { - for (int i = cast(int) vector.length - 1; i >= 0; --i) - { - mixin("vector[i] " ~ op ~ "= " ~ "that[i];"); - } - immutable delta = vector.length - that.length; - if (delta) - { - static if (op == "&") - { - vector[0..delta] = 0; - } - } - return this; - } - - /// - unittest - { - // [01000001, 00001101, 10101101] - ubyte[3] array1 = [65, 13, 173]; - ubyte[3] array2 = [0b01010010, 0b10111110, 0b10111110]; - auto bits = BitVector(array1); - - bits |= array2; - assert(bits[][0] == 0b01010011); - assert(bits[][1] == 0b10111111); - assert(bits[][2] == 0b10111111); - - bits &= array2; - assert(bits[][0] == array2[0]); - assert(bits[][1] == array2[1]); - assert(bits[][2] == array2[2]); - - bits ^= array2; - assert(bits[][0] == 0); - assert(bits[][1] == 0); - assert(bits[][2] == 0); - } - - /** - * Support for shift operations. - * - * Params: - * n = Number of bits. - * - * Returns: $(D_KEYWORD this). - */ - BitVector opOpAssign(string op)(in size_t n) pure nothrow @safe @nogc - if ((op == "<<") || (op == ">>")) - { - if (n >= length) - { - vector[0..$] = 0; - } - else if (n != 0) - { - immutable bit = n % 8, step = n / 8; - immutable delta = 8 - bit; - size_t i, j; - - static if (op == "<<") - { - for (j = step; j < vector.length - 1; ++i) - { - vector[i] = cast(ubyte)((vector[j] << bit) - | vector[++j] >> delta); - } - vector[i] = cast(ubyte)(vector[j] << bit); - vector[$ - step ..$] = 0; - } - else static if (op == ">>") - { - for (i = vector.length - 1, j = i - step; j > 0; --i) - { - vector[i] = cast(ubyte)((vector[j] >> bit) - | vector[--j] << delta); - } - vector[i] = cast(ubyte)(vector[j] >> bit); - vector[0..step] = 0; - } - } - return this; - } - - /// - nothrow @safe @nogc unittest - { - ubyte[4] arr = [0b10111110, 0b11110010, 0b01010010, 0b01010011]; - auto bits = BitVector(arr); - - bits <<= 0; - assert(bits[][0] == 0b10111110 && bits[][1] == 0b11110010 - && bits[][2] == 0b01010010 && bits[][3] == 0b01010011); - - bits <<= 2; - assert(bits[][0] == 0b11111011 && bits[][1] == 0b11001001 - && bits[][2] == 0b01001001 && bits[][3] == 0b01001100); - - bits <<= 4; - assert(bits[][0] == 0b10111100 && bits[][1] == 0b10010100 - && bits[][2] == 0b10010100 && bits[][3] == 0b11000000); - - bits <<= 8; - assert(bits[][0] == 0b10010100 && bits[][1] == 0b10010100 - && bits[][2] == 0b11000000 && bits[][3] == 0b00000000); - - bits <<= 7; - assert(bits[][0] == 0b01001010 && bits[][1] == 0b01100000 - && bits[][2] == 0b00000000 && bits[][3] == 0b00000000); - - bits <<= 25; - assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000 - && bits[][2] == 0b00000000 && bits[][3] == 0b00000000); - - arr = [0b00110011, 0b11001100, 0b11111111, 0b01010101]; - bits <<= 24; - assert(bits[][0] == 0b01010101 && bits[][1] == 0b00000000 - && bits[][2] == 0b00000000 && bits[][3] == 0b00000000); - - arr[1] = 0b11001100; - arr[2] = 0b11111111; - arr[3] = 0b01010101; - bits <<= 12; - assert(bits[][0] == 0b11001111 && bits[][1] == 0b11110101 - && bits[][2] == 0b01010000 && bits[][3] == 0b00000000); - - bits <<= 100; - assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000 - && bits[][2] == 0b00000000 && bits[][3] == 0b00000000); - - arr = [0b10111110, 0b11110010, 0b01010010, 0b01010011]; - bits >>= 0; - assert(bits[][0] == 0b10111110 && bits[][1] == 0b11110010 - && bits[][2] == 0b01010010 && bits[][3] == 0b01010011); - - bits >>= 2; - assert(bits[][0] == 0b00101111 && bits[][1] == 0b10111100 - && bits[][2] == 0b10010100 && bits[][3] == 0b10010100); - - bits >>= 4; - assert(bits[][0] == 0b00000010 && bits[][1] == 0b11111011 - && bits[][2] == 0b11001001 && bits[][3] == 0b01001001); - - bits >>= 8; - assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000010 - && bits[][2] == 0b11111011 && bits[][3] == 0b11001001); - - bits >>= 7; - assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000 - && bits[][2] == 0b00000101 && bits[][3] == 0b11110111); - - bits >>= 25; - assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000 - && bits[][2] == 0b00000000 && bits[][3] == 0b00000000); - - arr = [0b00110011, 0b11001100, 0b11111111, 0b01010101]; - bits >>= 24; - assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000 - && bits[][2] == 0b00000000 && bits[][3] == 0b00110011); - - arr[1] = 0b11001100; - arr[2] = 0b11111111; - arr[3] = 0b01010101; - bits >>= 12; - assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000 - && bits[][2] == 0b00001100 && bits[][3] == 0b11001111); - - bits >>= 100; - assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000 - && bits[][2] == 0b00000000 && bits[][3] == 0b00000000); - } - - /** - * Negates all bits. - * - * Returns: $(D_KEYWORD this). - */ - BitVector opUnary(string op)() pure nothrow @safe @nogc - if (op == "~") - { - foreach (ref b; vector) - { - b = ~b; - } - return this; - } - - /// - unittest - { - // [01000001, 00001101, 10101101] - ubyte[3] arr = [65, 13, 173]; - auto bits = BitVector(arr); - - ~bits; - assert(bits[][0] == 0b10111110); - assert(bits[][1] == 0b11110010); - assert(bits[][2] == 0b01010010); - } - - /** - * Iterates through all bits. - * - * Params: - * dg = $(D_KEYWORD foreach) delegate. - * - * Returns: By $(D_PARAM dg) returned value. - */ - int opApply(int delegate(size_t, bool) dg) - { - int result; - foreach (i, ref v; vector) - { - foreach (c; 0..8) - { - result = dg(i * 8 + c, (v & (0x80 >> c)) != 0); - if (result) - { - return result; - } - } - } - return result; - } - - /// Ditto. - int opApply(int delegate(bool) dg) - { - int result; - foreach (ref v; vector) - { - foreach (c; 0..8) - { - result = dg((v & (0x80 >> c)) != 0); - if (result) - { - return result; - } - } - } - return result; - } - - /// - unittest - { - ubyte[2] arr = [0b01000001, 0b00001101]; - auto bits = BitVector(arr); - size_t c; - - foreach (i, v; bits) - { - assert(i == c); - if (i == 1 || i == 7 || i == 15 || i == 13 || i == 12) - { - assert(v); - } - else - { - assert(!v); - } - ++c; - } - assert(c == 16); - } -} diff --git a/source/tanya/container/package.d b/source/tanya/container/package.d index 7ba9f20..8012ffe 100644 --- a/source/tanya/container/package.d +++ b/source/tanya/container/package.d @@ -12,7 +12,6 @@ */ module tanya.container; -public import tanya.container.bitvector; public import tanya.container.buffer; public import tanya.container.list; public import tanya.container.vector; From 4cd6126d6bb8ebe0e41e16452d6c93c7bb10c978 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Wed, 29 Mar 2017 17:22:25 +0200 Subject: [PATCH 16/26] Fix SList documentation for insertFront and insertBefore --- source/tanya/container/list.d | 70 ++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/source/tanya/container/list.d b/source/tanya/container/list.d index 47453ed..2f7cdbb 100644 --- a/source/tanya/container/list.d +++ b/source/tanya/container/list.d @@ -334,13 +334,6 @@ struct SList(T) * * Returns: The number of elements inserted. */ - size_t insertFront(ref T el) @trusted - { - head = allocator.make!Entry(el, head); - return 1; - } - - /// Ditto. size_t insertFront(R)(R el) if (isImplicitlyConvertible!(R, T)) { @@ -383,6 +376,13 @@ struct SList(T) return insertFront!(T[])(el[]); } + /// Ditto. + size_t insertFront(ref T el) @trusted + { + head = allocator.make!Entry(el, head); + return 1; + } + /// Ditto. alias insert = insertFront; @@ -422,34 +422,13 @@ struct SList(T) * * Params: * R = Type of the inserted value(s). + * r = Range extracted from this list. * el = New element(s). * * Returns: The number of elements inserted. * * Precondition: $(D_PARAM r) is extracted from this list. */ - size_t insertBefore(Range!Entry r, ref T el) @trusted - in - { - assert(checkRangeBelonging(r)); - } - body - { - *r.head = allocator.make!Entry(el, *r.head); - return 1; - } - - /// - @safe @nogc unittest - { - auto l1 = SList!int([234, 5, 1]); - auto l2 = SList!int([5, 1]); - int var = 234; - l2.insertBefore(l2[], var); - assert(l1 == l2); - } - - /// Ditto. size_t insertBefore(R)(Range!Entry r, R el) if (isImplicitlyConvertible!(R, T)) in @@ -503,6 +482,39 @@ struct SList(T) } /// Ditto. + size_t insertBefore(Range!Entry r, ref T el) @trusted + in + { + assert(checkRangeBelonging(r)); + } + body + { + *r.head = allocator.make!Entry(el, *r.head); + return 1; + } + + /// + @safe @nogc unittest + { + auto l1 = SList!int([234, 5, 1]); + auto l2 = SList!int([5, 1]); + int var = 234; + l2.insertBefore(l2[], var); + assert(l1 == l2); + } + + /** + * Inserts elements from a static array before $(D_PARAM r). + * + * Params: + * R = Static array size. + * r = Range extracted from this list. + * el = New elements. + * + * Returns: The number of elements inserted. + * + * Precondition: $(D_PARAM r) is extracted from this list. + */ size_t insertBefore(size_t R)(Range!Entry r, T[R] el) { return insertFront!(T[])(el[]); From 647cfe03c21858e8da2c0cc3d5add706d6760ac7 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Wed, 29 Mar 2017 17:23:10 +0200 Subject: [PATCH 17/26] Update latest supported compiler --- .travis.yml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index d2c2f90..e307d73 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ os: language: d d: - - dmd-2.073.0 + - dmd-2.073.2 - dmd-2.072.2 - dmd-2.071.2 - dmd-2.070.2 diff --git a/README.md b/README.md index ffb554c..33f95d5 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ helper functions). ### Supported compilers -* dmd 2.073.0 +* dmd 2.073.2 * dmd 2.072.2 * dmd 2.071.2 * dmd 2.070.2 From 5e16fe98d6d36c97e46809169ba16d03a0f72e75 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sat, 1 Apr 2017 09:53:59 +0200 Subject: [PATCH 18/26] Add tanya.network package file --- source/tanya/network/package.d | 16 + source/tanya/network/socket.d | 2390 ++++++++++++++++---------------- source/tanya/network/url.d | 964 ++++++------- 3 files changed, 1697 insertions(+), 1673 deletions(-) create mode 100644 source/tanya/network/package.d diff --git a/source/tanya/network/package.d b/source/tanya/network/package.d new file mode 100644 index 0000000..96b987c --- /dev/null +++ b/source/tanya/network/package.d @@ -0,0 +1,16 @@ +/* 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/. */ + +/** + * Network programming. + * + * 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.network; + +public import tanya.network.socket; +public import tanya.network.url; diff --git a/source/tanya/network/socket.d b/source/tanya/network/socket.d index 17bcfa4..5dbbe41 100644 --- a/source/tanya/network/socket.d +++ b/source/tanya/network/socket.d @@ -3,7 +3,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** - * Copyright: Eugene Wissner 2016. + * Socket programming. + * + * 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) @@ -22,532 +24,532 @@ import std.typecons; version (Posix) { - import core.stdc.errno; - import core.sys.posix.fcntl; - import core.sys.posix.netdb; - import core.sys.posix.netinet.in_; - import core.sys.posix.sys.socket; - import core.sys.posix.sys.time; - import core.sys.posix.unistd; + import core.stdc.errno; + import core.sys.posix.fcntl; + import core.sys.posix.netdb; + import core.sys.posix.netinet.in_; + import core.sys.posix.sys.socket; + import core.sys.posix.sys.time; + import core.sys.posix.unistd; - private enum SOCKET_ERROR = -1; + private enum SOCKET_ERROR = -1; } else version (Windows) { - import tanya.async.iocp; - import core.sys.windows.basetyps; - import core.sys.windows.mswsock; - import core.sys.windows.winbase; - import core.sys.windows.windef; - import core.sys.windows.winsock2; + import tanya.async.iocp; + import core.sys.windows.basetyps; + import core.sys.windows.mswsock; + import core.sys.windows.winbase; + import core.sys.windows.windef; + import core.sys.windows.winsock2; - enum : uint - { - IOC_UNIX = 0x00000000, - IOC_WS2 = 0x08000000, - IOC_PROTOCOL = 0x10000000, - IOC_VOID = 0x20000000, /// No parameters. - IOC_OUT = 0x40000000, /// Copy parameters back. - IOC_IN = 0x80000000, /// Copy parameters into. - IOC_VENDOR = 0x18000000, - IOC_INOUT = (IOC_IN | IOC_OUT), /// Copy parameter into and get back. - } + enum : uint + { + IOC_UNIX = 0x00000000, + IOC_WS2 = 0x08000000, + IOC_PROTOCOL = 0x10000000, + IOC_VOID = 0x20000000, /// No parameters. + IOC_OUT = 0x40000000, /// Copy parameters back. + IOC_IN = 0x80000000, /// Copy parameters into. + IOC_VENDOR = 0x18000000, + IOC_INOUT = (IOC_IN | IOC_OUT), /// Copy parameter into and get back. + } - template _WSAIO(int x, int y) - { - enum _WSAIO = IOC_VOID | x | y; - } - template _WSAIOR(int x, int y) - { - enum _WSAIOR = IOC_OUT | x | y; - } - template _WSAIOW(int x, int y) - { - enum _WSAIOW = IOC_IN | x | y; - } - template _WSAIORW(int x, int y) - { - enum _WSAIORW = IOC_INOUT | x | y; - } + template _WSAIO(int x, int y) + { + enum _WSAIO = IOC_VOID | x | y; + } + template _WSAIOR(int x, int y) + { + enum _WSAIOR = IOC_OUT | x | y; + } + template _WSAIOW(int x, int y) + { + enum _WSAIOW = IOC_IN | x | y; + } + template _WSAIORW(int x, int y) + { + enum _WSAIORW = IOC_INOUT | x | y; + } - alias SIO_ASSOCIATE_HANDLE = _WSAIOW!(IOC_WS2, 1); - alias SIO_ENABLE_CIRCULAR_QUEUEING = _WSAIO!(IOC_WS2, 2); - alias SIO_FIND_ROUTE = _WSAIOR!(IOC_WS2, 3); - alias SIO_FLUSH = _WSAIO!(IOC_WS2, 4); - alias SIO_GET_BROADCAST_ADDRESS = _WSAIOR!(IOC_WS2, 5); - alias SIO_GET_EXTENSION_FUNCTION_POINTER = _WSAIORW!(IOC_WS2, 6); - alias SIO_GET_QOS = _WSAIORW!(IOC_WS2, 7); - alias SIO_GET_GROUP_QOS = _WSAIORW!(IOC_WS2, 8); - alias SIO_MULTIPOINT_LOOPBACK = _WSAIOW!(IOC_WS2, 9); - alias SIO_MULTICAST_SCOPE = _WSAIOW!(IOC_WS2, 10); - alias SIO_SET_QOS = _WSAIOW!(IOC_WS2, 11); - alias SIO_SET_GROUP_QOS = _WSAIOW!(IOC_WS2, 12); - alias SIO_TRANSLATE_HANDLE = _WSAIORW!(IOC_WS2, 13); - alias SIO_ROUTING_INTERFACE_QUERY = _WSAIORW!(IOC_WS2, 20); - alias SIO_ROUTING_INTERFACE_CHANGE = _WSAIOW!(IOC_WS2, 21); - alias SIO_ADDRESS_LIST_QUERY = _WSAIOR!(IOC_WS2, 22); - alias SIO_ADDRESS_LIST_CHANGE = _WSAIO!(IOC_WS2, 23); - alias SIO_QUERY_TARGET_PNP_HANDLE = _WSAIOR!(IOC_WS2, 24); - alias SIO_NSP_NOTIFY_CHANGE = _WSAIOW!(IOC_WS2, 25); + alias SIO_ASSOCIATE_HANDLE = _WSAIOW!(IOC_WS2, 1); + alias SIO_ENABLE_CIRCULAR_QUEUEING = _WSAIO!(IOC_WS2, 2); + alias SIO_FIND_ROUTE = _WSAIOR!(IOC_WS2, 3); + alias SIO_FLUSH = _WSAIO!(IOC_WS2, 4); + alias SIO_GET_BROADCAST_ADDRESS = _WSAIOR!(IOC_WS2, 5); + alias SIO_GET_EXTENSION_FUNCTION_POINTER = _WSAIORW!(IOC_WS2, 6); + alias SIO_GET_QOS = _WSAIORW!(IOC_WS2, 7); + alias SIO_GET_GROUP_QOS = _WSAIORW!(IOC_WS2, 8); + alias SIO_MULTIPOINT_LOOPBACK = _WSAIOW!(IOC_WS2, 9); + alias SIO_MULTICAST_SCOPE = _WSAIOW!(IOC_WS2, 10); + alias SIO_SET_QOS = _WSAIOW!(IOC_WS2, 11); + alias SIO_SET_GROUP_QOS = _WSAIOW!(IOC_WS2, 12); + alias SIO_TRANSLATE_HANDLE = _WSAIORW!(IOC_WS2, 13); + alias SIO_ROUTING_INTERFACE_QUERY = _WSAIORW!(IOC_WS2, 20); + alias SIO_ROUTING_INTERFACE_CHANGE = _WSAIOW!(IOC_WS2, 21); + alias SIO_ADDRESS_LIST_QUERY = _WSAIOR!(IOC_WS2, 22); + alias SIO_ADDRESS_LIST_CHANGE = _WSAIO!(IOC_WS2, 23); + alias SIO_QUERY_TARGET_PNP_HANDLE = _WSAIOR!(IOC_WS2, 24); + alias SIO_NSP_NOTIFY_CHANGE = _WSAIOW!(IOC_WS2, 25); - private alias GROUP = uint; + private alias GROUP = uint; - enum - { - WSA_FLAG_OVERLAPPED = 0x01, - MAX_PROTOCOL_CHAIN = 7, - WSAPROTOCOL_LEN = 255, - } + enum + { + WSA_FLAG_OVERLAPPED = 0x01, + MAX_PROTOCOL_CHAIN = 7, + WSAPROTOCOL_LEN = 255, + } - struct WSAPROTOCOLCHAIN - { - int ChainLen; - DWORD[MAX_PROTOCOL_CHAIN] ChainEntries; - } - alias LPWSAPROTOCOLCHAIN = WSAPROTOCOLCHAIN*; + struct WSAPROTOCOLCHAIN + { + int ChainLen; + DWORD[MAX_PROTOCOL_CHAIN] ChainEntries; + } + alias LPWSAPROTOCOLCHAIN = WSAPROTOCOLCHAIN*; - struct WSAPROTOCOL_INFO - { - DWORD dwServiceFlags1; - DWORD dwServiceFlags2; - DWORD dwServiceFlags3; - DWORD dwServiceFlags4; - DWORD dwProviderFlags; - GUID ProviderId; - DWORD dwCatalogEntryId; - WSAPROTOCOLCHAIN ProtocolChain; - int iVersion; - int iAddressFamily; - int iMaxSockAddr; - int iMinSockAddr; - int iSocketType; - int iProtocol; - int iProtocolMaxOffset; - int iNetworkByteOrder; - int iSecurityScheme; - DWORD dwMessageSize; - DWORD dwProviderReserved; - TCHAR[WSAPROTOCOL_LEN + 1] szProtocol; - } - alias LPWSAPROTOCOL_INFO = WSAPROTOCOL_INFO*; + struct WSAPROTOCOL_INFO + { + DWORD dwServiceFlags1; + DWORD dwServiceFlags2; + DWORD dwServiceFlags3; + DWORD dwServiceFlags4; + DWORD dwProviderFlags; + GUID ProviderId; + DWORD dwCatalogEntryId; + WSAPROTOCOLCHAIN ProtocolChain; + int iVersion; + int iAddressFamily; + int iMaxSockAddr; + int iMinSockAddr; + int iSocketType; + int iProtocol; + int iProtocolMaxOffset; + int iNetworkByteOrder; + int iSecurityScheme; + DWORD dwMessageSize; + DWORD dwProviderReserved; + TCHAR[WSAPROTOCOL_LEN + 1] szProtocol; + } + alias LPWSAPROTOCOL_INFO = WSAPROTOCOL_INFO*; - extern (Windows) @nogc nothrow - { - private SOCKET WSASocketW(int af, - int type, - int protocol, - LPWSAPROTOCOL_INFO lpProtocolInfo, - GROUP g, - DWORD dwFlags); - int WSARecv(SOCKET s, - LPWSABUF lpBuffers, - DWORD dwBufferCount, - LPDWORD lpNumberOfBytesRecvd, - LPDWORD lpFlags, - LPOVERLAPPED lpOverlapped, - LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); - int WSASend(SOCKET s, - LPWSABUF lpBuffers, - DWORD dwBufferCount, - LPDWORD lpNumberOfBytesRecvd, - DWORD lpFlags, - LPOVERLAPPED lpOverlapped, - LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); - int WSAIoctl(SOCKET s, - uint dwIoControlCode, - void* lpvInBuffer, - uint cbInBuffer, - void* lpvOutBuffer, - uint cbOutBuffer, - uint* lpcbBytesReturned, - LPWSAOVERLAPPED lpOverlapped, - LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); - alias LPFN_ACCEPTEX = BOOL function(SOCKET, - SOCKET, - PVOID, - DWORD, - DWORD, - DWORD, - LPDWORD, - LPOVERLAPPED); - } - alias WSASocket = WSASocketW; + extern (Windows) @nogc nothrow + { + private SOCKET WSASocketW(int af, + int type, + int protocol, + LPWSAPROTOCOL_INFO lpProtocolInfo, + GROUP g, + DWORD dwFlags); + int WSARecv(SOCKET s, + LPWSABUF lpBuffers, + DWORD dwBufferCount, + LPDWORD lpNumberOfBytesRecvd, + LPDWORD lpFlags, + LPOVERLAPPED lpOverlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); + int WSASend(SOCKET s, + LPWSABUF lpBuffers, + DWORD dwBufferCount, + LPDWORD lpNumberOfBytesRecvd, + DWORD lpFlags, + LPOVERLAPPED lpOverlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); + int WSAIoctl(SOCKET s, + uint dwIoControlCode, + void* lpvInBuffer, + uint cbInBuffer, + void* lpvOutBuffer, + uint cbOutBuffer, + uint* lpcbBytesReturned, + LPWSAOVERLAPPED lpOverlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); + alias LPFN_ACCEPTEX = BOOL function(SOCKET, + SOCKET, + PVOID, + DWORD, + DWORD, + DWORD, + LPDWORD, + LPOVERLAPPED); + } + alias WSASocket = WSASocketW; - alias LPFN_GETACCEPTEXSOCKADDRS = VOID function(PVOID, - DWORD, - DWORD, - DWORD, - SOCKADDR**, - LPINT, - SOCKADDR**, - LPINT); - const GUID WSAID_GETACCEPTEXSOCKADDRS = {0xb5367df2,0xcbac,0x11cf,[0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92]}; + alias LPFN_GETACCEPTEXSOCKADDRS = VOID function(PVOID, + DWORD, + DWORD, + DWORD, + SOCKADDR**, + LPINT, + SOCKADDR**, + LPINT); + const GUID WSAID_GETACCEPTEXSOCKADDRS = {0xb5367df2,0xcbac,0x11cf,[0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92]}; - struct WSABUF { - ULONG len; - CHAR* buf; - } - alias WSABUF* LPWSABUF; + struct WSABUF { + ULONG len; + CHAR* buf; + } + alias WSABUF* LPWSABUF; - struct WSAOVERLAPPED { - ULONG_PTR Internal; - ULONG_PTR InternalHigh; - union { - struct { - DWORD Offset; - DWORD OffsetHigh; - } - PVOID Pointer; - } - HANDLE hEvent; - } - alias LPWSAOVERLAPPED = WSAOVERLAPPED*; + struct WSAOVERLAPPED { + ULONG_PTR Internal; + ULONG_PTR InternalHigh; + union { + struct { + DWORD Offset; + DWORD OffsetHigh; + } + PVOID Pointer; + } + HANDLE hEvent; + } + alias LPWSAOVERLAPPED = WSAOVERLAPPED*; - enum SO_UPDATE_ACCEPT_CONTEXT = 0x700B; + enum SO_UPDATE_ACCEPT_CONTEXT = 0x700B; - enum OverlappedSocketEvent - { - accept = 1, - read = 2, - write = 3, - } + enum OverlappedSocketEvent + { + accept = 1, + read = 2, + write = 3, + } - class SocketState : State - { - private WSABUF buffer; - } + class SocketState : State + { + private WSABUF buffer; + } - /** - * Socket returned if a connection has been established. - */ - class OverlappedConnectedSocket : ConnectedSocket - { - /** - * Create a socket. - * - * Params: - * handle = Socket handle. - * af = Address family. - */ - this(socket_t handle, AddressFamily af) @nogc - { - super(handle, af); - } + /** + * Socket returned if a connection has been established. + */ + class OverlappedConnectedSocket : ConnectedSocket + { + /** + * Create a socket. + * + * Params: + * handle = Socket handle. + * af = Address family. + */ + this(socket_t handle, AddressFamily af) @nogc + { + super(handle, af); + } - /** - * Begins to asynchronously receive data from a connected socket. - * - * Params: - * buffer = Storage location for the received data. - * flags = Flags. - * overlapped = Unique operation identifier. - * - * Returns: $(D_KEYWORD true) if the operation could be finished synchronously. - * $(D_KEYWORD false) otherwise. - * - * Throws: $(D_PSYMBOL SocketException) if unable to receive. - */ - bool beginReceive(ubyte[] buffer, - SocketState overlapped, - Flags flags = Flags(Flag.none)) @nogc @trusted - { - auto receiveFlags = cast(DWORD) flags; + /** + * Begins to asynchronously receive data from a connected socket. + * + * Params: + * buffer = Storage location for the received data. + * flags = Flags. + * overlapped = Unique operation identifier. + * + * Returns: $(D_KEYWORD true) if the operation could be finished synchronously. + * $(D_KEYWORD false) otherwise. + * + * Throws: $(D_PSYMBOL SocketException) if unable to receive. + */ + bool beginReceive(ubyte[] buffer, + SocketState overlapped, + Flags flags = Flags(Flag.none)) @nogc @trusted + { + auto receiveFlags = cast(DWORD) flags; - overlapped.handle = cast(HANDLE) handle_; - overlapped.event = OverlappedSocketEvent.read; - overlapped.buffer.len = cast(ULONG) buffer.length; - overlapped.buffer.buf = cast(char*) buffer.ptr; + overlapped.handle = cast(HANDLE) handle_; + overlapped.event = OverlappedSocketEvent.read; + overlapped.buffer.len = cast(ULONG) buffer.length; + overlapped.buffer.buf = cast(char*) buffer.ptr; - auto result = WSARecv(handle_, - &overlapped.buffer, - 1u, - NULL, - &receiveFlags, - &overlapped.overlapped, - NULL); + auto result = WSARecv(handle_, + &overlapped.buffer, + 1u, + NULL, + &receiveFlags, + &overlapped.overlapped, + NULL); - if (result == SOCKET_ERROR && !wouldHaveBlocked) - { - throw defaultAllocator.make!SocketException("Unable to receive"); - } - return result == 0; - } + if (result == SOCKET_ERROR && !wouldHaveBlocked) + { + throw defaultAllocator.make!SocketException("Unable to receive"); + } + return result == 0; + } - /** - * Ends a pending asynchronous read. - * - * Params - * overlapped = Unique operation identifier. - * - * Returns: Number of bytes received. - * - * Throws: $(D_PSYMBOL SocketException) if unable to receive. - */ - int endReceive(SocketState overlapped) @nogc @trusted - out (count) - { - assert(count >= 0); - } - body - { - DWORD lpNumber; - BOOL result = GetOverlappedResult(overlapped.handle, - &overlapped.overlapped, - &lpNumber, - FALSE); - if (result == FALSE && !wouldHaveBlocked) - { - disconnected_ = true; - throw defaultAllocator.make!SocketException("Unable to receive"); - } - if (lpNumber == 0) - { - disconnected_ = true; - } - return lpNumber; - } + /** + * Ends a pending asynchronous read. + * + * Params + * overlapped = Unique operation identifier. + * + * Returns: Number of bytes received. + * + * Throws: $(D_PSYMBOL SocketException) if unable to receive. + */ + int endReceive(SocketState overlapped) @nogc @trusted + out (count) + { + assert(count >= 0); + } + body + { + DWORD lpNumber; + BOOL result = GetOverlappedResult(overlapped.handle, + &overlapped.overlapped, + &lpNumber, + FALSE); + if (result == FALSE && !wouldHaveBlocked) + { + disconnected_ = true; + throw defaultAllocator.make!SocketException("Unable to receive"); + } + if (lpNumber == 0) + { + disconnected_ = true; + } + return lpNumber; + } - /** - * Sends data asynchronously to a connected socket. - * - * Params: - * buffer = Data to be sent. - * flags = Flags. - * overlapped = Unique operation identifier. - * - * Returns: $(D_KEYWORD true) if the operation could be finished synchronously. - * $(D_KEYWORD false) otherwise. - * - * Throws: $(D_PSYMBOL SocketException) if unable to send. - */ - bool beginSend(ubyte[] buffer, - SocketState overlapped, - Flags flags = Flags(Flag.none)) @nogc @trusted - { - overlapped.handle = cast(HANDLE) handle_; - overlapped.event = OverlappedSocketEvent.write; - overlapped.buffer.len = cast(ULONG) buffer.length; - overlapped.buffer.buf = cast(char*) buffer.ptr; + /** + * Sends data asynchronously to a connected socket. + * + * Params: + * buffer = Data to be sent. + * flags = Flags. + * overlapped = Unique operation identifier. + * + * Returns: $(D_KEYWORD true) if the operation could be finished synchronously. + * $(D_KEYWORD false) otherwise. + * + * Throws: $(D_PSYMBOL SocketException) if unable to send. + */ + bool beginSend(ubyte[] buffer, + SocketState overlapped, + Flags flags = Flags(Flag.none)) @nogc @trusted + { + overlapped.handle = cast(HANDLE) handle_; + overlapped.event = OverlappedSocketEvent.write; + overlapped.buffer.len = cast(ULONG) buffer.length; + overlapped.buffer.buf = cast(char*) buffer.ptr; - auto result = WSASend(handle_, - &overlapped.buffer, - 1u, - NULL, - cast(DWORD) flags, - &overlapped.overlapped, - NULL); + auto result = WSASend(handle_, + &overlapped.buffer, + 1u, + NULL, + cast(DWORD) flags, + &overlapped.overlapped, + NULL); - if (result == SOCKET_ERROR && !wouldHaveBlocked) - { - disconnected_ = true; - throw defaultAllocator.make!SocketException("Unable to send"); - } - return result == 0; - } + if (result == SOCKET_ERROR && !wouldHaveBlocked) + { + disconnected_ = true; + throw defaultAllocator.make!SocketException("Unable to send"); + } + return result == 0; + } - /** - * Ends a pending asynchronous send. - * - * Params - * overlapped = Unique operation identifier. - * - * Returns: Number of bytes sent. - * - * Throws: $(D_PSYMBOL SocketException) if unable to receive. - */ - int endSend(SocketState overlapped) @nogc @trusted - out (count) - { - assert(count >= 0); - } - body - { - DWORD lpNumber; - BOOL result = GetOverlappedResult(overlapped.handle, - &overlapped.overlapped, - &lpNumber, - FALSE); - if (result == FALSE && !wouldHaveBlocked) - { - disconnected_ = true; - throw defaultAllocator.make!SocketException("Unable to receive"); - } - return lpNumber; - } - } + /** + * Ends a pending asynchronous send. + * + * Params + * overlapped = Unique operation identifier. + * + * Returns: Number of bytes sent. + * + * Throws: $(D_PSYMBOL SocketException) if unable to receive. + */ + int endSend(SocketState overlapped) @nogc @trusted + out (count) + { + assert(count >= 0); + } + body + { + DWORD lpNumber; + BOOL result = GetOverlappedResult(overlapped.handle, + &overlapped.overlapped, + &lpNumber, + FALSE); + if (result == FALSE && !wouldHaveBlocked) + { + disconnected_ = true; + throw defaultAllocator.make!SocketException("Unable to receive"); + } + return lpNumber; + } + } - class OverlappedStreamSocket : StreamSocket - { - /// Accept extension function pointer. - package LPFN_ACCEPTEX acceptExtension; + class OverlappedStreamSocket : StreamSocket + { + /// Accept extension function pointer. + package LPFN_ACCEPTEX acceptExtension; - /** - * Create a socket. - * - * Params: - * af = Address family. - * - * Throws: $(D_PSYMBOL SocketException) on errors. - */ - this(AddressFamily af) @nogc @trusted - { - super(af); - scope (failure) - { - this.close(); - } - blocking = false; + /** + * Create a socket. + * + * Params: + * af = Address family. + * + * Throws: $(D_PSYMBOL SocketException) on errors. + */ + this(AddressFamily af) @nogc @trusted + { + super(af); + scope (failure) + { + this.close(); + } + blocking = false; - GUID guidAcceptEx = WSAID_ACCEPTEX; - DWORD dwBytes; + GUID guidAcceptEx = WSAID_ACCEPTEX; + DWORD dwBytes; - auto result = WSAIoctl(handle_, - SIO_GET_EXTENSION_FUNCTION_POINTER, - &guidAcceptEx, - guidAcceptEx.sizeof, - &acceptExtension, - acceptExtension.sizeof, - &dwBytes, - NULL, - NULL); - if (!result == SOCKET_ERROR) - { - throw make!SocketException(defaultAllocator, - "Unable to retrieve an accept extension function pointer"); - } - } + auto result = WSAIoctl(handle_, + SIO_GET_EXTENSION_FUNCTION_POINTER, + &guidAcceptEx, + guidAcceptEx.sizeof, + &acceptExtension, + acceptExtension.sizeof, + &dwBytes, + NULL, + NULL); + if (!result == SOCKET_ERROR) + { + throw make!SocketException(defaultAllocator, + "Unable to retrieve an accept extension function pointer"); + } + } - /** - * Begins an asynchronous operation to accept an incoming connection attempt. - * - * Params: - * overlapped = Unique operation identifier. - * - * Returns: $(D_KEYWORD true) if the operation could be finished synchronously. - * $(D_KEYWORD false) otherwise. - * - * Throws: $(D_PSYMBOL SocketException) on accept errors. - */ - bool beginAccept(SocketState overlapped) @nogc @trusted - { - auto socket = cast(socket_t) socket(addressFamily, SOCK_STREAM, 0); - if (socket == socket_t.init) - { - throw defaultAllocator.make!SocketException("Unable to create socket"); - } - scope (failure) - { - closesocket(socket); - } - DWORD dwBytes; - overlapped.handle = cast(HANDLE) socket; - overlapped.event = OverlappedSocketEvent.accept; + /** + * Begins an asynchronous operation to accept an incoming connection attempt. + * + * Params: + * overlapped = Unique operation identifier. + * + * Returns: $(D_KEYWORD true) if the operation could be finished synchronously. + * $(D_KEYWORD false) otherwise. + * + * Throws: $(D_PSYMBOL SocketException) on accept errors. + */ + bool beginAccept(SocketState overlapped) @nogc @trusted + { + auto socket = cast(socket_t) socket(addressFamily, SOCK_STREAM, 0); + if (socket == socket_t.init) + { + throw defaultAllocator.make!SocketException("Unable to create socket"); + } + scope (failure) + { + closesocket(socket); + } + DWORD dwBytes; + overlapped.handle = cast(HANDLE) socket; + overlapped.event = OverlappedSocketEvent.accept; - immutable len = (sockaddr_in.sizeof + 16) * 2; - overlapped.buffer.len = len; - overlapped.buffer.buf = cast(char*) defaultAllocator.allocate(len).ptr; + immutable len = (sockaddr_in.sizeof + 16) * 2; + overlapped.buffer.len = len; + overlapped.buffer.buf = cast(char*) defaultAllocator.allocate(len).ptr; - // We don't want to get any data now, but only start to accept the connections - BOOL result = acceptExtension(handle_, - socket, - overlapped.buffer.buf, - 0u, - sockaddr_in.sizeof + 16, - sockaddr_in.sizeof + 16, - &dwBytes, - &overlapped.overlapped); - if (result == FALSE && !wouldHaveBlocked) - { - throw defaultAllocator.make!SocketException("Unable to accept socket connection"); - } - return result == TRUE; - } + // We don't want to get any data now, but only start to accept the connections + BOOL result = acceptExtension(handle_, + socket, + overlapped.buffer.buf, + 0u, + sockaddr_in.sizeof + 16, + sockaddr_in.sizeof + 16, + &dwBytes, + &overlapped.overlapped); + if (result == FALSE && !wouldHaveBlocked) + { + throw defaultAllocator.make!SocketException("Unable to accept socket connection"); + } + return result == TRUE; + } - /** - * Asynchronously accepts an incoming connection attempt and creates a - * new socket to handle remote host communication. - * - * Params: - * overlapped = Unique operation identifier. - * - * Returns: Connected socket. - * - * Throws: $(D_PSYMBOL SocketException) if unable to accept. - */ - OverlappedConnectedSocket endAccept(SocketState overlapped) @nogc @trusted - { - scope (exit) - { - defaultAllocator.dispose(overlapped.buffer.buf[0 .. overlapped.buffer.len]); - } - auto socket = make!OverlappedConnectedSocket(defaultAllocator, - cast(socket_t) overlapped.handle, - addressFamily); - scope (failure) - { - defaultAllocator.dispose(socket); - } - socket.setOption(SocketOptionLevel.SOCKET, - cast(SocketOption) SO_UPDATE_ACCEPT_CONTEXT, - cast(size_t) handle); - return socket; - } - } + /** + * Asynchronously accepts an incoming connection attempt and creates a + * new socket to handle remote host communication. + * + * Params: + * overlapped = Unique operation identifier. + * + * Returns: Connected socket. + * + * Throws: $(D_PSYMBOL SocketException) if unable to accept. + */ + OverlappedConnectedSocket endAccept(SocketState overlapped) @nogc @trusted + { + scope (exit) + { + defaultAllocator.dispose(overlapped.buffer.buf[0 .. overlapped.buffer.len]); + } + auto socket = make!OverlappedConnectedSocket(defaultAllocator, + cast(socket_t) overlapped.handle, + addressFamily); + scope (failure) + { + defaultAllocator.dispose(socket); + } + socket.setOption(SocketOptionLevel.SOCKET, + cast(SocketOption) SO_UPDATE_ACCEPT_CONTEXT, + cast(size_t) handle); + return socket; + } + } } version (linux) { - enum SOCK_NONBLOCK = O_NONBLOCK; - extern(C) int accept4(int, sockaddr*, socklen_t*, int flags) @nogc nothrow; + enum SOCK_NONBLOCK = O_NONBLOCK; + extern(C) int accept4(int, sockaddr*, socklen_t*, int flags) @nogc nothrow; } else version (OSX) { - version = MacBSD; + version = MacBSD; } else version (iOS) { - version = MacBSD; + version = MacBSD; } else version (FreeBSD) { - version = MacBSD; + version = MacBSD; } else version (OpenBSD) { - version = MacBSD; + version = MacBSD; } else version (DragonFlyBSD) { - version = MacBSD; + version = MacBSD; } version (MacBSD) { - enum ESOCKTNOSUPPORT = 44; /// Socket type not suppoted. + enum ESOCKTNOSUPPORT = 44; /// Socket type not suppoted. } private immutable { - typeof(&getaddrinfo) getaddrinfoPointer; - typeof(&freeaddrinfo) freeaddrinfoPointer; + typeof(&getaddrinfo) getaddrinfoPointer; + typeof(&freeaddrinfo) freeaddrinfoPointer; } shared static this() { - version (Windows) - { - auto ws2Lib = GetModuleHandle("ws2_32.dll"); + version (Windows) + { + auto ws2Lib = GetModuleHandle("ws2_32.dll"); - getaddrinfoPointer = cast(typeof(getaddrinfoPointer)) - GetProcAddress(ws2Lib, "getaddrinfo"); - freeaddrinfoPointer = cast(typeof(freeaddrinfoPointer)) - GetProcAddress(ws2Lib, "freeaddrinfo"); - } - else version (Posix) - { - getaddrinfoPointer = &getaddrinfo; - freeaddrinfoPointer = &freeaddrinfo; - } + getaddrinfoPointer = cast(typeof(getaddrinfoPointer)) + GetProcAddress(ws2Lib, "getaddrinfo"); + freeaddrinfoPointer = cast(typeof(freeaddrinfoPointer)) + GetProcAddress(ws2Lib, "freeaddrinfo"); + } + else version (Posix) + { + getaddrinfoPointer = &getaddrinfo; + freeaddrinfoPointer = &freeaddrinfo; + } } /** @@ -555,32 +557,32 @@ shared static this() */ enum SocketError : int { - /// Unknown error - unknown = 0, - /// Firewall rules forbid connection. - accessDenied = EPERM, - /// A socket operation was attempted on a non-socket. - notSocket = EBADF, - /// The network is not available. - networkDown = ECONNABORTED, - /// An invalid pointer address was detected by the underlying socket provider. - fault = EFAULT, - /// An invalid argument was supplied to a $(D_PSYMBOL Socket) member. - invalidArgument = EINVAL, - /// The limit on the number of open sockets has been reached. - tooManyOpenSockets = ENFILE, - /// No free buffer space is available for a Socket operation. - noBufferSpaceAvailable = ENOBUFS, - /// The address family is not supported by the protocol family. - operationNotSupported = EOPNOTSUPP, - /// The protocol is not implemented or has not been configured. - protocolNotSupported = EPROTONOSUPPORT, - /// Protocol error. - protocolError = EPROTOTYPE, - /// The connection attempt timed out, or the connected host has failed to respond. - timedOut = ETIMEDOUT, - /// The support for the specified socket type does not exist in this address family. - socketNotSupported = ESOCKTNOSUPPORT, + /// Unknown error + unknown = 0, + /// Firewall rules forbid connection. + accessDenied = EPERM, + /// A socket operation was attempted on a non-socket. + notSocket = EBADF, + /// The network is not available. + networkDown = ECONNABORTED, + /// An invalid pointer address was detected by the underlying socket provider. + fault = EFAULT, + /// An invalid argument was supplied to a $(D_PSYMBOL Socket) member. + invalidArgument = EINVAL, + /// The limit on the number of open sockets has been reached. + tooManyOpenSockets = ENFILE, + /// No free buffer space is available for a Socket operation. + noBufferSpaceAvailable = ENOBUFS, + /// The address family is not supported by the protocol family. + operationNotSupported = EOPNOTSUPP, + /// The protocol is not implemented or has not been configured. + protocolNotSupported = EPROTONOSUPPORT, + /// Protocol error. + protocolError = EPROTOTYPE, + /// The connection attempt timed out, or the connected host has failed to respond. + timedOut = ETIMEDOUT, + /// The support for the specified socket type does not exist in this address family. + socketNotSupported = ESOCKTNOSUPPORT, } /** @@ -590,53 +592,53 @@ enum SocketError : int */ class SocketException : Exception { - immutable SocketError error = SocketError.unknown; + immutable SocketError error = SocketError.unknown; - /** - * 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 nothrow - { - super(msg, file, line, next); + /** + * 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 nothrow + { + super(msg, file, line, next); - foreach (member; EnumMembers!SocketError) - { - if (member == lastError) - { - error = member; - return; - } - } - if (lastError == ENOMEM) - { - error = SocketError.noBufferSpaceAvailable; - } - else if (lastError == EMFILE) - { - error = SocketError.tooManyOpenSockets; - } - else version (linux) - { - if (lastError == ENOSR) - { - error = SocketError.networkDown; - } - } - else version (Posix) - { - if (lastError == EPROTO) - { - error = SocketError.networkDown; - } - } - } + foreach (member; EnumMembers!SocketError) + { + if (member == lastError) + { + error = member; + return; + } + } + if (lastError == ENOMEM) + { + error = SocketError.noBufferSpaceAvailable; + } + else if (lastError == EMFILE) + { + error = SocketError.tooManyOpenSockets; + } + else version (linux) + { + if (lastError == ENOSR) + { + error = SocketError.networkDown; + } + } + else version (Posix) + { + if (lastError == EPROTO) + { + error = SocketError.networkDown; + } + } + } } /** @@ -645,353 +647,353 @@ class SocketException : Exception */ abstract class Socket { - version (Posix) - { - /** - * How a socket is shutdown. - */ - enum Shutdown : int - { - receive = SHUT_RD, /// Socket receives are disallowed - send = SHUT_WR, /// Socket sends are disallowed - both = SHUT_RDWR, /// Both receive and send - } - } - else version (Windows) - { - /// Property to get or set whether the socket is blocking or nonblocking. - private bool blocking_ = true; + version (Posix) + { + /** + * How a socket is shutdown. + */ + enum Shutdown : int + { + receive = SHUT_RD, /// Socket receives are disallowed + send = SHUT_WR, /// Socket sends are disallowed + both = SHUT_RDWR, /// Both receive and send + } + } + else version (Windows) + { + /// Property to get or set whether the socket is blocking or nonblocking. + private bool blocking_ = true; - /** - * How a socket is shutdown. - */ - enum Shutdown : int - { - receive = SD_RECEIVE, /// Socket receives are disallowed. - send = SD_SEND, /// Socket sends are disallowed. - both = SD_BOTH, /// Both receive and send. - } + /** + * How a socket is shutdown. + */ + enum Shutdown : int + { + receive = SD_RECEIVE, /// Socket receives are disallowed. + send = SD_SEND, /// Socket sends are disallowed. + both = SD_BOTH, /// Both receive and send. + } - // The WinSock timeouts seem to be effectively skewed by a constant - // offset of about half a second (in milliseconds). - private enum WINSOCK_TIMEOUT_SKEW = 500; - } + // The WinSock timeouts seem to be effectively skewed by a constant + // offset of about half a second (in milliseconds). + private enum WINSOCK_TIMEOUT_SKEW = 500; + } - /// Socket handle. - protected socket_t handle_; + /// Socket handle. + protected socket_t handle_; - /// Address family. - protected AddressFamily family; + /// Address family. + protected AddressFamily family; - private @property void handle(socket_t handle) @nogc - in - { - assert(handle != socket_t.init); - assert(handle_ == socket_t.init, "Socket handle cannot be changed"); - } - body - { - handle_ = handle; + private @property void handle(socket_t handle) @nogc + in + { + assert(handle != socket_t.init); + assert(handle_ == socket_t.init, "Socket handle cannot be changed"); + } + body + { + handle_ = handle; - // Set the option to disable SIGPIPE on send() if the platform - // has it (e.g. on OS X). - static if (is(typeof(SO_NOSIGPIPE))) - { - setOption(SocketOptionLevel.SOCKET, cast(SocketOption)SO_NOSIGPIPE, true); - } - } + // Set the option to disable SIGPIPE on send() if the platform + // has it (e.g. on OS X). + static if (is(typeof(SO_NOSIGPIPE))) + { + setOption(SocketOptionLevel.SOCKET, cast(SocketOption)SO_NOSIGPIPE, true); + } + } - @property inout(socket_t) handle() inout const pure nothrow @safe @nogc - { - return handle_; - } + @property inout(socket_t) handle() inout const pure nothrow @safe @nogc + { + return handle_; + } - /** - * Create a socket. - * - * Params: - * handle = Socket. - * af = Address family. - */ - this(socket_t handle, AddressFamily af) @nogc - in - { - assert(handle != socket_t.init); - } - body - { - scope (failure) - { - this.close(); - } - this.handle = handle; - family = af; - } + /** + * Create a socket. + * + * Params: + * handle = Socket. + * af = Address family. + */ + this(socket_t handle, AddressFamily af) @nogc + in + { + assert(handle != socket_t.init); + } + body + { + scope (failure) + { + this.close(); + } + this.handle = handle; + family = af; + } - /** - * Closes the socket and calls the destructor on itself. - */ - ~this() nothrow @trusted @nogc - { - this.close(); - } + /** + * Closes the socket and calls the destructor on itself. + */ + ~this() nothrow @trusted @nogc + { + this.close(); + } - /** - * Get a socket option. - * - * Params: - * level = Protocol level at that the option exists. - * option = Option. - * result = Buffer to save the result. - * - * Returns: The number of bytes written to $(D_PARAM result). - * - * Throws: $(D_PSYMBOL SocketException) on error. - */ - protected int getOption(SocketOptionLevel level, - SocketOption option, - void[] result) const @trusted @nogc - { - auto length = cast(socklen_t) result.length; - if (getsockopt(handle_, - cast(int) level, - cast(int) option, - result.ptr, - &length) == SOCKET_ERROR) - { - throw defaultAllocator.make!SocketException("Unable to get socket option"); - } - return length; - } + /** + * Get a socket option. + * + * Params: + * level = Protocol level at that the option exists. + * option = Option. + * result = Buffer to save the result. + * + * Returns: The number of bytes written to $(D_PARAM result). + * + * Throws: $(D_PSYMBOL SocketException) on error. + */ + protected int getOption(SocketOptionLevel level, + SocketOption option, + void[] result) const @trusted @nogc + { + auto length = cast(socklen_t) result.length; + if (getsockopt(handle_, + cast(int) level, + cast(int) option, + result.ptr, + &length) == SOCKET_ERROR) + { + throw defaultAllocator.make!SocketException("Unable to get socket option"); + } + return length; + } - /// Ditto. - int getOption(SocketOptionLevel level, - SocketOption option, - out size_t result) const @trusted @nogc - { - return getOption(level, option, (&result)[0..1]); - } + /// Ditto. + int getOption(SocketOptionLevel level, + SocketOption option, + out size_t result) const @trusted @nogc + { + return getOption(level, option, (&result)[0..1]); + } - /// Ditto. - int getOption(SocketOptionLevel level, - SocketOption option, + /// Ditto. + int getOption(SocketOptionLevel level, + SocketOption option, out Linger result) const @trusted @nogc - { - return getOption(level, option, (&result.clinger)[0..1]); - } + { + return getOption(level, option, (&result.clinger)[0..1]); + } - /// Ditto. - int getOption(SocketOptionLevel level, - SocketOption option, - out Duration result) const @trusted @nogc - { - // WinSock returns the timeout values as a milliseconds DWORD, - // while Linux and BSD return a timeval struct. - version (Posix) - { - timeval tv; - auto ret = getOption(level, option, (&tv)[0..1]); - result = dur!"seconds"(tv.tv_sec) + dur!"usecs"(tv.tv_usec); - } - else version (Windows) - { - int msecs; - auto ret = getOption(level, option, (&msecs)[0 .. 1]); - if (option == SocketOption.RCVTIMEO) - { - msecs += WINSOCK_TIMEOUT_SKEW; - } - result = dur!"msecs"(msecs); - } - return ret; - } + /// Ditto. + int getOption(SocketOptionLevel level, + SocketOption option, + out Duration result) const @trusted @nogc + { + // WinSock returns the timeout values as a milliseconds DWORD, + // while Linux and BSD return a timeval struct. + version (Posix) + { + timeval tv; + auto ret = getOption(level, option, (&tv)[0..1]); + result = dur!"seconds"(tv.tv_sec) + dur!"usecs"(tv.tv_usec); + } + else version (Windows) + { + int msecs; + auto ret = getOption(level, option, (&msecs)[0 .. 1]); + if (option == SocketOption.RCVTIMEO) + { + msecs += WINSOCK_TIMEOUT_SKEW; + } + result = dur!"msecs"(msecs); + } + return ret; + } - /** - * Set a socket option. - * - * Params: - * level = Protocol level at that the option exists. - * option = Option. - * value = Option value. - * - * Throws: $(D_PSYMBOL SocketException) on error. - */ - protected void setOption(SocketOptionLevel level, - SocketOption option, - void[] value) const @trusted @nogc - { - if (setsockopt(handle_, - cast(int)level, - cast(int)option, - value.ptr, - cast(uint) value.length) == SOCKET_ERROR) - { - throw defaultAllocator.make!SocketException("Unable to set socket option"); - } - } + /** + * Set a socket option. + * + * Params: + * level = Protocol level at that the option exists. + * option = Option. + * value = Option value. + * + * Throws: $(D_PSYMBOL SocketException) on error. + */ + protected void setOption(SocketOptionLevel level, + SocketOption option, + void[] value) const @trusted @nogc + { + if (setsockopt(handle_, + cast(int)level, + cast(int)option, + value.ptr, + cast(uint) value.length) == SOCKET_ERROR) + { + throw defaultAllocator.make!SocketException("Unable to set socket option"); + } + } - /// Ditto. - void setOption(SocketOptionLevel level, SocketOption option, size_t value) - const @trusted @nogc - { - setOption(level, option, (&value)[0..1]); - } + /// Ditto. + void setOption(SocketOptionLevel level, SocketOption option, size_t value) + const @trusted @nogc + { + setOption(level, option, (&value)[0..1]); + } - /// Ditto. - void setOption(SocketOptionLevel level, SocketOption option, Linger value) - const @trusted @nogc - { - setOption(level, option, (&value.clinger)[0..1]); - } + /// Ditto. + void setOption(SocketOptionLevel level, SocketOption option, Linger value) + const @trusted @nogc + { + setOption(level, option, (&value.clinger)[0..1]); + } - /// Ditto. - void setOption(SocketOptionLevel level, SocketOption option, Duration value) - const @trusted @nogc - { - version (Posix) - { - timeval tv; - value.split!("seconds", "usecs")(tv.tv_sec, tv.tv_usec); - setOption(level, option, (&tv)[0..1]); - } - else version (Windows) - { - auto msecs = cast(int) value.total!"msecs"; - if (msecs > 0 && option == SocketOption.RCVTIMEO) - { - msecs = max(1, msecs - WINSOCK_TIMEOUT_SKEW); - } - setOption(level, option, msecs); - } - } + /// Ditto. + void setOption(SocketOptionLevel level, SocketOption option, Duration value) + const @trusted @nogc + { + version (Posix) + { + timeval tv; + value.split!("seconds", "usecs")(tv.tv_sec, tv.tv_usec); + setOption(level, option, (&tv)[0..1]); + } + else version (Windows) + { + auto msecs = cast(int) value.total!"msecs"; + if (msecs > 0 && option == SocketOption.RCVTIMEO) + { + msecs = max(1, msecs - WINSOCK_TIMEOUT_SKEW); + } + setOption(level, option, msecs); + } + } - /** - * Returns: Socket's blocking flag. - */ - @property inout(bool) blocking() inout const nothrow @nogc - { - version (Posix) - { - return !(fcntl(handle_, F_GETFL, 0) & O_NONBLOCK); - } - else version (Windows) - { - return blocking_; - } - } + /** + * Returns: Socket's blocking flag. + */ + @property inout(bool) blocking() inout const nothrow @nogc + { + version (Posix) + { + return !(fcntl(handle_, F_GETFL, 0) & O_NONBLOCK); + } + else version (Windows) + { + return blocking_; + } + } - /** - * Params: - * yes = Socket's blocking flag. - */ - @property void blocking(bool yes) @nogc - { - version (Posix) - { - int fl = fcntl(handle_, F_GETFL, 0); + /** + * Params: + * yes = Socket's blocking flag. + */ + @property void blocking(bool yes) @nogc + { + version (Posix) + { + int fl = fcntl(handle_, F_GETFL, 0); - if (fl != SOCKET_ERROR) - { - fl = yes ? fl & ~O_NONBLOCK : fl | O_NONBLOCK; - fl = fcntl(handle_, F_SETFL, fl); - } - if (fl == SOCKET_ERROR) - { - throw make!SocketException(defaultAllocator, - "Unable to set socket blocking"); - } - } - else version (Windows) - { - uint num = !yes; - if (ioctlsocket(handle_, FIONBIO, &num) == SOCKET_ERROR) - { - throw make!SocketException(defaultAllocator, - "Unable to set socket blocking"); - } - blocking_ = yes; - } - } + if (fl != SOCKET_ERROR) + { + fl = yes ? fl & ~O_NONBLOCK : fl | O_NONBLOCK; + fl = fcntl(handle_, F_SETFL, fl); + } + if (fl == SOCKET_ERROR) + { + throw make!SocketException(defaultAllocator, + "Unable to set socket blocking"); + } + } + else version (Windows) + { + uint num = !yes; + if (ioctlsocket(handle_, FIONBIO, &num) == SOCKET_ERROR) + { + throw make!SocketException(defaultAllocator, + "Unable to set socket blocking"); + } + blocking_ = yes; + } + } - /** - * Returns: The socket's address family. - */ - @property AddressFamily addressFamily() const @nogc @safe pure nothrow - { - return family; - } + /** + * Returns: The socket's address family. + */ + @property AddressFamily addressFamily() const @nogc @safe pure nothrow + { + return family; + } - /** - * Returns: $(D_KEYWORD true) if this is a valid, alive socket. - */ - @property bool isAlive() @trusted const nothrow @nogc - { - int type; - socklen_t typesize = cast(socklen_t) type.sizeof; - return !getsockopt(handle_, SOL_SOCKET, SO_TYPE, cast(char*)&type, &typesize); - } + /** + * Returns: $(D_KEYWORD true) if this is a valid, alive socket. + */ + @property bool isAlive() @trusted const nothrow @nogc + { + int type; + socklen_t typesize = cast(socklen_t) type.sizeof; + return !getsockopt(handle_, SOL_SOCKET, SO_TYPE, cast(char*)&type, &typesize); + } - /** - * Disables sends and/or receives. - * - * Params: - * how = What to disable. - * - * See_Also: - * $(D_PSYMBOL Shutdown) - */ - void shutdown(Shutdown how = Shutdown.both) @nogc @trusted const nothrow - { - .shutdown(handle_, cast(int)how); - } + /** + * Disables sends and/or receives. + * + * Params: + * how = What to disable. + * + * See_Also: + * $(D_PSYMBOL Shutdown) + */ + void shutdown(Shutdown how = Shutdown.both) @nogc @trusted const nothrow + { + .shutdown(handle_, cast(int)how); + } - /** - * Immediately drop any connections and release socket resources. - * Calling $(D_PSYMBOL shutdown) before $(D_PSYMBOL close) is recommended - * for connection-oriented sockets. The $(D_PSYMBOL Socket) object is no - * longer usable after $(D_PSYMBOL close). - */ - void close() nothrow @trusted @nogc - { - version(Windows) - { - .closesocket(handle_); - } - else version(Posix) - { - .close(handle_); - } - handle_ = socket_t.init; - } + /** + * Immediately drop any connections and release socket resources. + * Calling $(D_PSYMBOL shutdown) before $(D_PSYMBOL close) is recommended + * for connection-oriented sockets. The $(D_PSYMBOL Socket) object is no + * longer usable after $(D_PSYMBOL close). + */ + void close() nothrow @trusted @nogc + { + version(Windows) + { + .closesocket(handle_); + } + else version(Posix) + { + .close(handle_); + } + handle_ = socket_t.init; + } - /** - * Listen for an incoming connection. $(D_PSYMBOL bind) must be called before you - * can $(D_PSYMBOL listen). - * - * Params: - * backlog = Request of how many pending incoming connections are - * queued until $(D_PSYMBOL accept)ed. - */ - void listen(int backlog) const @trusted @nogc - { - if (.listen(handle_, backlog) == SOCKET_ERROR) - { - throw defaultAllocator.make!SocketException("Unable to listen on socket"); - } - } + /** + * Listen for an incoming connection. $(D_PSYMBOL bind) must be called before you + * can $(D_PSYMBOL listen). + * + * Params: + * backlog = Request of how many pending incoming connections are + * queued until $(D_PSYMBOL accept)ed. + */ + void listen(int backlog) const @trusted @nogc + { + if (.listen(handle_, backlog) == SOCKET_ERROR) + { + throw defaultAllocator.make!SocketException("Unable to listen on socket"); + } + } - /** - * Compare handles. - * - * Params: - * that = Another handle. - * - * Returns: Comparision result. - */ - int opCmp(size_t that) const pure nothrow @safe @nogc - { - return handle_ < that ? -1 : handle_ > that ? 1 : 0; - } + /** + * Compare handles. + * + * Params: + * that = Another handle. + * + * Returns: Comparision result. + */ + int opCmp(size_t that) const pure nothrow @safe @nogc + { + return handle_ < that ? -1 : handle_ > that ? 1 : 0; + } } /** @@ -999,119 +1001,123 @@ abstract class Socket */ interface ConnectionOrientedSocket { - /** - * Flags may be OR'ed together. - */ - enum Flag : int - { - none = 0, /// No flags specified - outOfBand = MSG_OOB, /// Out-of-band stream data - peek = MSG_PEEK, /// Peek at incoming data without removing it from the queue, only for receiving - dontRoute = MSG_DONTROUTE, /// Data should not be subject to routing; this flag may be ignored. Only for sending - } + /** + * Flags may be OR'ed together. + */ + enum Flag : int + { + /// No flags specified. + none = 0, + /// Out-of-band stream data. + outOfBand = MSG_OOB, + /// Peek at incoming data without removing it from the queue, only for receiving. + peek = MSG_PEEK, + /// Data should not be subject to routing; this flag may be ignored. Only for sending. + dontRoute = MSG_DONTROUTE, + } - alias Flags = BitFlags!Flag; + alias Flags = BitFlags!Flag; } class StreamSocket : Socket, ConnectionOrientedSocket { - /** - * Create a socket. - * - * Params: - * af = Address family. - */ - this(AddressFamily af) @trusted @nogc - { - auto handle = cast(socket_t) socket(af, SOCK_STREAM, 0); - if (handle == socket_t.init) - { - throw defaultAllocator.make!SocketException("Unable to create socket"); - } - super(handle, af); - } + /** + * Create a socket. + * + * Params: + * af = Address family. + */ + this(AddressFamily af) @trusted @nogc + { + auto handle = cast(socket_t) socket(af, SOCK_STREAM, 0); + if (handle == socket_t.init) + { + throw defaultAllocator.make!SocketException("Unable to create socket"); + } + super(handle, af); + } - /** - * Associate a local address with this socket. - * - * Params: - * address = Local address. - * - * Throws: $(D_PSYMBOL SocketException) if unable to bind. - */ - void bind(Address address) const @trusted @nogc - { - if (.bind(handle_, address.name, address.length) == SOCKET_ERROR) - { - throw defaultAllocator.make!SocketException("Unable to bind socket"); - } - } + /** + * Associate a local address with this socket. + * + * Params: + * address = Local address. + * + * Throws: $(D_PSYMBOL SocketException) if unable to bind. + */ + void bind(Address address) const @trusted @nogc + { + if (.bind(handle_, address.name, address.length) == SOCKET_ERROR) + { + throw defaultAllocator.make!SocketException("Unable to bind socket"); + } + } - /** - * Accept an incoming connection. - * - * The blocking mode is always inherited. - * - * Returns: $(D_PSYMBOL Socket) for the accepted connection or - * $(D_KEYWORD null) if the call would block on a - * non-blocking socket. - * - * Throws: $(D_PSYMBOL SocketException) if unable to accept. - */ - ConnectedSocket accept() @trusted @nogc - { - socket_t sock; + /** + * Accept an incoming connection. + * + * The blocking mode is always inherited. + * + * Returns: $(D_PSYMBOL Socket) for the accepted connection or + * $(D_KEYWORD null) if the call would block on a + * non-blocking socket. + * + * Throws: $(D_PSYMBOL SocketException) if unable to accept. + */ + ConnectedSocket accept() @trusted @nogc + { + socket_t sock; - version (linux) - { - int flags; - if (!blocking) - { - flags |= SOCK_NONBLOCK; - } - sock = cast(socket_t).accept4(handle_, null, null, flags); - } - else - { - sock = cast(socket_t).accept(handle_, null, null); - } + version (linux) + { + int flags; + if (!blocking) + { + flags |= SOCK_NONBLOCK; + } + sock = cast(socket_t).accept4(handle_, null, null, flags); + } + else + { + sock = cast(socket_t).accept(handle_, null, null); + } - if (sock == socket_t.init) - { - if (wouldHaveBlocked()) - { - return null; - } - throw make!SocketException(defaultAllocator, - "Unable to accept socket connection"); - } + if (sock == socket_t.init) + { + if (wouldHaveBlocked()) + { + return null; + } + throw make!SocketException(defaultAllocator, + "Unable to accept socket connection"); + } - auto newSocket = defaultAllocator.make!ConnectedSocket(sock, addressFamily); + auto newSocket = defaultAllocator.make!ConnectedSocket(sock, addressFamily); - version (linux) - { // Blocking mode already set - } - else version (Posix) - { - if (!blocking) - { - try - { - newSocket.blocking = blocking; - } - catch (SocketException e) - { - defaultAllocator.dispose(newSocket); - throw e; - } - } - } - else version (Windows) - { // Inherits blocking mode - newSocket.blocking_ = blocking; - } - return newSocket; - } + version (linux) + { // Blocking mode already set + } + else version (Posix) + { + if (!blocking) + { + try + { + newSocket.blocking = blocking; + } + catch (SocketException e) + { + defaultAllocator.dispose(newSocket); + throw e; + } + } + } + else version (Windows) + { // Inherits blocking mode + newSocket.blocking_ = blocking; + } + return newSocket; + } } /** @@ -1119,124 +1125,124 @@ class StreamSocket : Socket, ConnectionOrientedSocket */ class ConnectedSocket : Socket, ConnectionOrientedSocket { - /** - * $(D_KEYWORD true) if the stream socket peer has performed an orderly - * shutdown. - */ - protected bool disconnected_; + /** + * $(D_KEYWORD true) if the stream socket peer has performed an orderly + * shutdown. + */ + protected bool disconnected_; - /** - * Returns: $(D_KEYWORD true) if the stream socket peer has performed an orderly - * shutdown. - */ - @property inout(bool) disconnected() inout const pure nothrow @safe @nogc - { - return disconnected_; - } + /** + * Returns: $(D_KEYWORD true) if the stream socket peer has performed an orderly + * shutdown. + */ + @property inout(bool) disconnected() inout const pure nothrow @safe @nogc + { + return disconnected_; + } - /** - * Create a socket. - * - * Params: - * handle = Socket. - * af = Address family. - */ - this(socket_t handle, AddressFamily af) @nogc - { - super(handle, af); - } + /** + * Create a socket. + * + * Params: + * handle = Socket. + * af = Address family. + */ + this(socket_t handle, AddressFamily af) @nogc + { + super(handle, af); + } - version (Windows) - { - private static int capToMaxBuffer(size_t size) pure nothrow @safe @nogc - { - // Windows uses int instead of size_t for length arguments. - // Luckily, the send/recv functions make no guarantee that - // all the data is sent, so we use that to send at most - // int.max bytes. - return size > size_t (int.max) ? int.max : cast(int) size; - } - } - else - { - private static size_t capToMaxBuffer(size_t size) pure nothrow @safe @nogc - { - return size; - } - } + version (Windows) + { + private static int capToMaxBuffer(size_t size) pure nothrow @safe @nogc + { + // Windows uses int instead of size_t for length arguments. + // Luckily, the send/recv functions make no guarantee that + // all the data is sent, so we use that to send at most + // int.max bytes. + return size > size_t (int.max) ? int.max : cast(int) size; + } + } + else + { + private static size_t capToMaxBuffer(size_t size) pure nothrow @safe @nogc + { + return size; + } + } - /** - * Receive data on the connection. - * - * Params: - * buf = Buffer to save received data. - * flags = Flags. - * - * Returns: The number of bytes received or 0 if nothing received - * because the call would block. - * - * Throws: $(D_PSYMBOL SocketException) if unable to receive. - */ - ptrdiff_t receive(ubyte[] buf, Flags flags = Flag.none) @trusted @nogc - { - ptrdiff_t ret; - if (!buf.length) - { - return 0; - } + /** + * Receive data on the connection. + * + * Params: + * buf = Buffer to save received data. + * flags = Flags. + * + * Returns: The number of bytes received or 0 if nothing received + * because the call would block. + * + * Throws: $(D_PSYMBOL SocketException) if unable to receive. + */ + ptrdiff_t receive(ubyte[] buf, Flags flags = Flag.none) @trusted @nogc + { + ptrdiff_t ret; + if (!buf.length) + { + return 0; + } - ret = recv(handle_, buf.ptr, capToMaxBuffer(buf.length), cast(int) flags); - if (ret == 0) - { - disconnected_ = true; - } - else if (ret == SOCKET_ERROR) - { - if (wouldHaveBlocked()) - { - return 0; - } - disconnected_ = true; - throw defaultAllocator.make!SocketException("Unable to receive"); - } - return ret; - } + ret = recv(handle_, buf.ptr, capToMaxBuffer(buf.length), cast(int) flags); + if (ret == 0) + { + disconnected_ = true; + } + else if (ret == SOCKET_ERROR) + { + if (wouldHaveBlocked()) + { + return 0; + } + disconnected_ = true; + throw defaultAllocator.make!SocketException("Unable to receive"); + } + return ret; + } - /** - * Send data on the connection. If the socket is blocking and there is no - * buffer space left, $(D_PSYMBOL send) waits, non-blocking socket returns - * 0 in this case. - * - * Params: - * buf = Data to be sent. - * flags = Flags. - * - * Returns: The number of bytes actually sent. - * - * Throws: $(D_PSYMBOL SocketException) if unable to send. - */ - ptrdiff_t send(const(ubyte)[] buf, Flags flags = Flag.none) - const @trusted @nogc - { - int sendFlags = cast(int) flags; - ptrdiff_t sent; + /** + * Send data on the connection. If the socket is blocking and there is no + * buffer space left, $(D_PSYMBOL send) waits, non-blocking socket returns + * 0 in this case. + * + * Params: + * buf = Data to be sent. + * flags = Flags. + * + * Returns: The number of bytes actually sent. + * + * Throws: $(D_PSYMBOL SocketException) if unable to send. + */ + ptrdiff_t send(const(ubyte)[] buf, Flags flags = Flag.none) + const @trusted @nogc + { + int sendFlags = cast(int) flags; + ptrdiff_t sent; - static if (is(typeof(MSG_NOSIGNAL))) - { - sendFlags |= MSG_NOSIGNAL; - } + static if (is(typeof(MSG_NOSIGNAL))) + { + sendFlags |= MSG_NOSIGNAL; + } - sent = .send(handle_, buf.ptr, capToMaxBuffer(buf.length), sendFlags); - if (sent != SOCKET_ERROR) - { - return sent; - } - else if (wouldHaveBlocked()) - { - return 0; - } - throw defaultAllocator.make!SocketException("Unable to send"); - } + sent = .send(handle_, buf.ptr, capToMaxBuffer(buf.length), sendFlags); + if (sent != SOCKET_ERROR) + { + return sent; + } + else if (wouldHaveBlocked()) + { + return 0; + } + throw defaultAllocator.make!SocketException("Unable to send"); + } } /** @@ -1244,132 +1250,132 @@ class ConnectedSocket : Socket, ConnectionOrientedSocket */ abstract class Address { - /** - * Returns: Pointer to underlying $(D_PSYMBOL sockaddr) structure. - */ - abstract @property inout(sockaddr)* name() inout pure nothrow @nogc; + /** + * Returns: Pointer to underlying $(D_PSYMBOL sockaddr) structure. + */ + abstract @property inout(sockaddr)* name() inout pure nothrow @nogc; - /** - * Returns: Actual size of underlying $(D_PSYMBOL sockaddr) structure. - */ - abstract @property inout(socklen_t) length() inout const pure nothrow @nogc; + /** + * Returns: Actual size of underlying $(D_PSYMBOL sockaddr) structure. + */ + abstract @property inout(socklen_t) length() inout const pure nothrow @nogc; } class InternetAddress : Address { - version (Windows) - { - /// Internal internet address representation. - protected SOCKADDR_STORAGE storage; - } - else version (Posix) - { - /// Internal internet address representation. - protected sockaddr_storage storage; - } - immutable ushort port_; + version (Windows) + { + /// Internal internet address representation. + protected SOCKADDR_STORAGE storage; + } + else version (Posix) + { + /// Internal internet address representation. + protected sockaddr_storage storage; + } + immutable ushort port_; - enum - { - anyPort = 0, - } + enum + { + anyPort = 0, + } - this(in string host, ushort port = anyPort) @nogc - { - if (getaddrinfoPointer is null || freeaddrinfoPointer is null) - { - throw make!SocketException(defaultAllocator, - "Address info lookup is not available on this system"); - } - addrinfo* ai_res; - port_ = port; + this(in string host, ushort port = anyPort) @nogc + { + if (getaddrinfoPointer is null || freeaddrinfoPointer is null) + { + throw make!SocketException(defaultAllocator, + "Address info lookup is not available on this system"); + } + addrinfo* ai_res; + port_ = port; - // Make C-string from host. - auto node = cast(char[]) allocator.allocate(host.length + 1); - node[0.. $ - 1] = host; - node[$ - 1] = '\0'; - scope (exit) - { - allocator.deallocate(node); - } + // Make C-string from host. + auto node = cast(char[]) allocator.allocate(host.length + 1); + node[0.. $ - 1] = host; + node[$ - 1] = '\0'; + scope (exit) + { + allocator.deallocate(node); + } - // Convert port to a C-string. - char[6] service = [0, 0, 0, 0, 0, 0]; - const(char)* servicePointer; - if (port) - { - ushort start; - for (ushort j = 10, i = 4; i > 0; j *= 10, --i) - { - ushort rest = port % 10; - if (rest != 0) - { - service[i] = cast(char) (rest + '0'); - start = i; - } - port /= 10; - } - servicePointer = service[start..$].ptr; - } + // Convert port to a C-string. + char[6] service = [0, 0, 0, 0, 0, 0]; + const(char)* servicePointer; + if (port) + { + ushort start; + for (ushort j = 10, i = 4; i > 0; j *= 10, --i) + { + ushort rest = port % 10; + if (rest != 0) + { + service[i] = cast(char) (rest + '0'); + start = i; + } + port /= 10; + } + servicePointer = service[start..$].ptr; + } - auto ret = getaddrinfoPointer(node.ptr, servicePointer, null, &ai_res); - if (ret) - { - throw defaultAllocator.make!SocketException("Address info lookup failed"); - } - scope (exit) - { - freeaddrinfoPointer(ai_res); - } - - ubyte* dp = cast(ubyte*) &storage, sp = cast(ubyte*) ai_res.ai_addr; - for (auto i = ai_res.ai_addrlen; i > 0; --i, *dp++, *sp++) - { - *dp = *sp; - } - if (ai_res.ai_family != AddressFamily.INET && ai_res.ai_family != AddressFamily.INET6) - { - throw defaultAllocator.make!SocketException("Wrong address family"); - } - } + auto ret = getaddrinfoPointer(node.ptr, servicePointer, null, &ai_res); + if (ret) + { + throw defaultAllocator.make!SocketException("Address info lookup failed"); + } + scope (exit) + { + freeaddrinfoPointer(ai_res); + } + + ubyte* dp = cast(ubyte*) &storage, sp = cast(ubyte*) ai_res.ai_addr; + for (auto i = ai_res.ai_addrlen; i > 0; --i, *dp++, *sp++) + { + *dp = *sp; + } + if (ai_res.ai_family != AddressFamily.INET && ai_res.ai_family != AddressFamily.INET6) + { + throw defaultAllocator.make!SocketException("Wrong address family"); + } + } - /** - * Returns: Pointer to underlying $(D_PSYMBOL sockaddr) structure. - */ - override @property inout(sockaddr)* name() inout pure nothrow @nogc - { - return cast(sockaddr*) &storage; - } + /** + * Returns: Pointer to underlying $(D_PSYMBOL sockaddr) structure. + */ + override @property inout(sockaddr)* name() inout pure nothrow @nogc + { + return cast(sockaddr*) &storage; + } - /** - * Returns: Actual size of underlying $(D_PSYMBOL sockaddr) structure. - */ - override @property inout(socklen_t) length() inout const pure nothrow @nogc - { - // FreeBSD wants to know the exact length of the address on bind. - switch (family) - { - case AddressFamily.INET: - return sockaddr_in.sizeof; - case AddressFamily.INET6: - return sockaddr_in6.sizeof; - default: - assert(false); - } - } + /** + * Returns: Actual size of underlying $(D_PSYMBOL sockaddr) structure. + */ + override @property inout(socklen_t) length() inout const pure nothrow @nogc + { + // FreeBSD wants to know the exact length of the address on bind. + switch (family) + { + case AddressFamily.INET: + return sockaddr_in.sizeof; + case AddressFamily.INET6: + return sockaddr_in6.sizeof; + default: + assert(false); + } + } - /** - * Returns: Family of this address. - */ - @property inout(AddressFamily) family() inout const pure nothrow @nogc - { - return cast(AddressFamily) storage.ss_family; - } + /** + * Returns: Family of this address. + */ + @property inout(AddressFamily) family() inout const pure nothrow @nogc + { + return cast(AddressFamily) storage.ss_family; + } - @property inout(ushort) port() inout const pure nothrow @nogc - { - return port_; - } + @property inout(ushort) port() inout const pure nothrow @nogc + { + return port_; + } } /** @@ -1383,16 +1389,16 @@ class InternetAddress : Address */ bool wouldHaveBlocked() nothrow @trusted @nogc { - version (Posix) - { - return errno == EAGAIN || errno == EWOULDBLOCK; - } - else version (Windows) - { - return WSAGetLastError() == ERROR_IO_PENDING - || WSAGetLastError() == EWOULDBLOCK - || WSAGetLastError() == ERROR_IO_INCOMPLETE; - } + version (Posix) + { + return errno == EAGAIN || errno == EWOULDBLOCK; + } + else version (Windows) + { + return WSAGetLastError() == ERROR_IO_PENDING + || WSAGetLastError() == EWOULDBLOCK + || WSAGetLastError() == ERROR_IO_INCOMPLETE; + } } /** @@ -1400,12 +1406,12 @@ bool wouldHaveBlocked() nothrow @trusted @nogc */ private @property int lastError() nothrow @safe @nogc { - version (Windows) - { - return WSAGetLastError(); - } - else - { - return errno; - } + version (Windows) + { + return WSAGetLastError(); + } + else + { + return errno; + } } diff --git a/source/tanya/network/url.d b/source/tanya/network/url.d index d0377d4..0cbec34 100644 --- a/source/tanya/network/url.d +++ b/source/tanya/network/url.d @@ -3,10 +3,12 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** - * Copyright: Eugene Wissner 2016. + * URL parser. + * + * 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:belka@caraus.de, Eugene Wissner) + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) */ module tanya.network.url; @@ -18,8 +20,8 @@ import tanya.memory; version (unittest) private { - import std.typecons; - static Tuple!(string, string[string], ushort)[] URLTests; + import std.typecons; + static Tuple!(string, string[string], ushort)[] URLTests; } static this() @@ -632,445 +634,445 @@ static this() */ struct URL { - /** The URL scheme. */ - const(char)[] scheme; + /** The URL scheme. */ + const(char)[] scheme; - /** The username. */ - const(char)[] user; + /** The username. */ + const(char)[] user; - /** The password. */ - const(char)[] pass; + /** The password. */ + const(char)[] pass; - /** The hostname. */ - const(char)[] host; + /** The hostname. */ + const(char)[] host; - /** The port number. */ - ushort port; + /** The port number. */ + ushort port; - /** The path. */ - const(char)[] path; + /** The path. */ + const(char)[] path; - /** The query string. */ - const(char)[] query; + /** The query string. */ + const(char)[] query; - /** The anchor. */ - const(char)[] fragment; + /** The anchor. */ + const(char)[] fragment; - /** - * Attempts to parse an URL from a string. - * Output string data (scheme, user, etc.) are just slices of input string (e.g., no memory allocation and copying). - * - * Params: - * source = The string containing the URL. - * - * Throws: $(D_PSYMBOL URIException) if the URL is malformed. - */ - this(in char[] source) - { - auto value = source; - ptrdiff_t pos = -1, endPos = value.length, start; + /** + * Attempts to parse an URL from a string. + * Output string data (scheme, user, etc.) are just slices of input string (e.g., no memory allocation and copying). + * + * Params: + * source = The string containing the URL. + * + * Throws: $(D_PSYMBOL URIException) if the URL is malformed. + */ + this(in char[] source) + { + auto value = source; + ptrdiff_t pos = -1, endPos = value.length, start; - foreach (i, ref c; source) - { - if (pos == -1 && c == ':') - { - pos = i; - } - if (endPos == value.length && (c == '?' || c == '#')) - { - endPos = i; - } - } + foreach (i, ref c; source) + { + if (pos == -1 && c == ':') + { + pos = i; + } + if (endPos == value.length && (c == '?' || c == '#')) + { + endPos = i; + } + } - // Check if the colon is a part of the scheme or the port and parse - // the appropriate part - if (value.length > 1 && value[0] == '/' && value[1] == '/') - { - // Relative scheme - start = 2; - } - else if (pos > 0) - { - // Validate scheme - // [ toLower(alpha) | digit | "+" | "-" | "." ] - foreach (ref c; value[0..pos]) - { - if (!c.isAlphaNum && c != '+' && c != '-' && c != '.') - { - if (endPos > pos) - { - if (!parsePort(value[pos..$])) - { - throw defaultAllocator.make!URIException("Failed to parse port"); - } - } - goto ParsePath; - } - } + // Check if the colon is a part of the scheme or the port and parse + // the appropriate part + if (value.length > 1 && value[0] == '/' && value[1] == '/') + { + // Relative scheme + start = 2; + } + else if (pos > 0) + { + // Validate scheme + // [ toLower(alpha) | digit | "+" | "-" | "." ] + foreach (ref c; value[0..pos]) + { + if (!c.isAlphaNum && c != '+' && c != '-' && c != '.') + { + if (endPos > pos) + { + if (!parsePort(value[pos..$])) + { + throw defaultAllocator.make!URIException("Failed to parse port"); + } + } + goto ParsePath; + } + } - if (value.length == pos + 1) // only scheme is available - { - scheme = value[0 .. $ - 1]; - return; - } - else if (value.length > pos + 1 && value[pos + 1] == '/') - { - scheme = value[0..pos]; + if (value.length == pos + 1) // only scheme is available + { + scheme = value[0 .. $ - 1]; + return; + } + else if (value.length > pos + 1 && value[pos + 1] == '/') + { + scheme = value[0..pos]; - if (value.length > pos + 2 && value[pos + 2] == '/') - { - start = pos + 3; - if (scheme == "file" && value.length > start && value[start] == '/') - { - // Windows drive letters - if (value.length - start > 2 && value[start + 2] == ':') - { - ++start; - } - goto ParsePath; - } - } - else - { - start = pos + 1; - goto ParsePath; - } - } - else // certain schemas like mailto: and zlib: may not have any / after them - { - - if (!parsePort(value[pos..$])) - { - scheme = value[0..pos]; - start = pos + 1; - goto ParsePath; - } - } - } - else if (pos == 0 && parsePort(value[pos..$])) - { - // An URL shouldn't begin with a port number - throw defaultAllocator.make!URIException("URL begins with port"); - } - else - { - goto ParsePath; - } + if (value.length > pos + 2 && value[pos + 2] == '/') + { + start = pos + 3; + if (scheme == "file" && value.length > start && value[start] == '/') + { + // Windows drive letters + if (value.length - start > 2 && value[start + 2] == ':') + { + ++start; + } + goto ParsePath; + } + } + else + { + start = pos + 1; + goto ParsePath; + } + } + else // certain schemas like mailto: and zlib: may not have any / after them + { + + if (!parsePort(value[pos..$])) + { + scheme = value[0..pos]; + start = pos + 1; + goto ParsePath; + } + } + } + else if (pos == 0 && parsePort(value[pos..$])) + { + // An URL shouldn't begin with a port number + throw defaultAllocator.make!URIException("URL begins with port"); + } + else + { + goto ParsePath; + } - // Parse host - pos = -1; - for (ptrdiff_t i = start; i < value.length; ++i) - { - if (value[i] == '@') - { - pos = i; - } - else if (value[i] == '/') - { - endPos = i; - break; - } - } + // Parse host + pos = -1; + for (ptrdiff_t i = start; i < value.length; ++i) + { + if (value[i] == '@') + { + pos = i; + } + else if (value[i] == '/') + { + endPos = i; + break; + } + } - // Check for login and password - if (pos != -1) - { - // *( unreserved / pct-encoded / sub-delims / ":" ) - foreach (i, c; value[start..pos]) - { - if (c == ':') - { - if (user is null) - { - user = value[start .. start + i]; - pass = value[start + i + 1 .. pos]; - } - } - else if (!c.isAlpha && - !c.isNumber && - c != '!' && - c != ';' && - c != '=' && - c != '_' && - c != '~' && - !(c >= '$' && c <= '.')) - { - if (scheme !is null) - { - scheme = null; - } - if (user !is null) - { - user = null; - } - if (pass !is null) - { - pass = null; - } - throw make!URIException(defaultAllocator, - "Restricted characters in user information"); - } - } - if (user is null) - { - user = value[start..pos]; - } + // Check for login and password + if (pos != -1) + { + // *( unreserved / pct-encoded / sub-delims / ":" ) + foreach (i, c; value[start..pos]) + { + if (c == ':') + { + if (user is null) + { + user = value[start .. start + i]; + pass = value[start + i + 1 .. pos]; + } + } + else if (!c.isAlpha && + !c.isNumber && + c != '!' && + c != ';' && + c != '=' && + c != '_' && + c != '~' && + !(c >= '$' && c <= '.')) + { + if (scheme !is null) + { + scheme = null; + } + if (user !is null) + { + user = null; + } + if (pass !is null) + { + pass = null; + } + throw make!URIException(defaultAllocator, + "Restricted characters in user information"); + } + } + if (user is null) + { + user = value[start..pos]; + } - start = ++pos; - } + start = ++pos; + } - pos = endPos; - if (endPos <= 1 || value[start] != '[' || value[endPos - 1] != ']') - { - // Short circuit portscan - // IPv6 embedded address - for (ptrdiff_t i = endPos - 1; i >= start; --i) - { - if (value[i] == ':') - { - pos = i; - if (port == 0 && !parsePort(value[i..endPos])) - { - if (scheme !is null) - { - scheme = null; - } - if (user !is null) - { - user = null; - } - if (pass !is null) - { - pass = null; - } - throw defaultAllocator.make!URIException("Invalid port"); - } - break; - } - } - } + pos = endPos; + if (endPos <= 1 || value[start] != '[' || value[endPos - 1] != ']') + { + // Short circuit portscan + // IPv6 embedded address + for (ptrdiff_t i = endPos - 1; i >= start; --i) + { + if (value[i] == ':') + { + pos = i; + if (port == 0 && !parsePort(value[i..endPos])) + { + if (scheme !is null) + { + scheme = null; + } + if (user !is null) + { + user = null; + } + if (pass !is null) + { + pass = null; + } + throw defaultAllocator.make!URIException("Invalid port"); + } + break; + } + } + } - // Check if we have a valid host, if we don't reject the string as url - if (pos <= start) - { - if (scheme !is null) - { - scheme = null; - } - if (user !is null) - { - user = null; - } - if (pass !is null) - { - pass = null; - } - throw defaultAllocator.make!URIException("Invalid host"); - } + // Check if we have a valid host, if we don't reject the string as url + if (pos <= start) + { + if (scheme !is null) + { + scheme = null; + } + if (user !is null) + { + user = null; + } + if (pass !is null) + { + pass = null; + } + throw defaultAllocator.make!URIException("Invalid host"); + } - host = value[start..pos]; + host = value[start..pos]; - if (endPos == value.length) - { - return; - } + if (endPos == value.length) + { + return; + } - start = endPos; + start = endPos; - ParsePath: - endPos = value.length; - pos = -1; - foreach (i, ref c; value[start..$]) - { - if (c == '?' && pos == -1) - { - pos = start + i; - } - else if (c == '#') - { - endPos = start + i; - break; - } - } - if (pos == -1) - { - pos = endPos; - } + ParsePath: + endPos = value.length; + pos = -1; + foreach (i, ref c; value[start..$]) + { + if (c == '?' && pos == -1) + { + pos = start + i; + } + else if (c == '#') + { + endPos = start + i; + break; + } + } + if (pos == -1) + { + pos = endPos; + } - if (pos > start) - { - path = value[start..pos]; - } - if (endPos >= ++pos) - { - query = value[pos..endPos]; - } - if (++endPos <= value.length) - { - fragment = value[endPos..$]; - } - } + if (pos > start) + { + path = value[start..pos]; + } + if (endPos >= ++pos) + { + query = value[pos..endPos]; + } + if (++endPos <= value.length) + { + fragment = value[endPos..$]; + } + } -~this() -{ - if (scheme !is null) - { - scheme = null; - } - if (user !is null) - { - user = null; - } - if (pass !is null) - { - pass = null; - } - if (host !is null) - { - host = null; - } - if (path !is null) - { - path = null; - } - if (query !is null) - { - query = null; - } - if (fragment !is null) - { - fragment = null; - } -} + ~this() + { + if (scheme !is null) + { + scheme = null; + } + if (user !is null) + { + user = null; + } + if (pass !is null) + { + pass = null; + } + if (host !is null) + { + host = null; + } + if (path !is null) + { + path = null; + } + if (query !is null) + { + query = null; + } + if (fragment !is null) + { + fragment = null; + } + } - /** - * Attempts to parse and set the port. - * - * Params: - * port = String beginning with a colon followed by the port number and - * an optional path (query string and/or fragment), like: - * `:12345/some_path` or `:12345`. - * - * Returns: Whether the port could be found. - */ - private bool parsePort(in char[] port) pure nothrow @safe @nogc - { - ptrdiff_t i = 1; - float lPort = 0; + /** + * Attempts to parse and set the port. + * + * Params: + * port = String beginning with a colon followed by the port number and + * an optional path (query string and/or fragment), like: + * `:12345/some_path` or `:12345`. + * + * Returns: Whether the port could be found. + */ + private bool parsePort(in char[] port) pure nothrow @safe @nogc + { + ptrdiff_t i = 1; + float lPort = 0; - for (; i < port.length && port[i].isDigit() && i <= 6; ++i) - { - lPort += (port[i] - '0') / cast(float)(10 ^^ (i - 1)); - } - if (i == 1 && (i == port.length || port[i] == '/')) - { - return true; - } - else if (i == port.length || port[i] == '/') - { - lPort *= 10 ^^ (i - 2); - if (lPort > ushort.max) - { - return false; - } - this.port = cast(ushort)lPort; - return true; - } - return false; - } + for (; i < port.length && port[i].isDigit() && i <= 6; ++i) + { + lPort += (port[i] - '0') / cast(float)(10 ^^ (i - 1)); + } + if (i == 1 && (i == port.length || port[i] == '/')) + { + return true; + } + else if (i == port.length || port[i] == '/') + { + lPort *= 10 ^^ (i - 2); + if (lPort > ushort.max) + { + return false; + } + this.port = cast(ushort)lPort; + return true; + } + return false; + } } /// unittest { - auto u = URL("example.org"); - assert(u.path == "example.org"); + auto u = URL("example.org"); + assert(u.path == "example.org"); - u = URL("relative/path"); - assert(u.path == "relative/path"); + u = URL("relative/path"); + assert(u.path == "relative/path"); - // Host and scheme - u = URL("https://example.org"); - assert(u.scheme == "https"); - assert(u.host == "example.org"); - assert(u.path is null); - assert(u.port == 0); - assert(u.fragment is null); + // Host and scheme + u = URL("https://example.org"); + assert(u.scheme == "https"); + assert(u.host == "example.org"); + assert(u.path is null); + assert(u.port == 0); + assert(u.fragment is null); - // With user and port and path - u = URL("https://hilary:putnam@example.org:443/foo/bar"); - assert(u.scheme == "https"); - assert(u.host == "example.org"); - assert(u.path == "/foo/bar"); - assert(u.port == 443); - assert(u.user == "hilary"); - assert(u.pass == "putnam"); - assert(u.fragment is null); + // With user and port and path + u = URL("https://hilary:putnam@example.org:443/foo/bar"); + assert(u.scheme == "https"); + assert(u.host == "example.org"); + assert(u.path == "/foo/bar"); + assert(u.port == 443); + assert(u.user == "hilary"); + assert(u.pass == "putnam"); + assert(u.fragment is null); - // With query string - u = URL("https://example.org/?login=true"); - assert(u.scheme == "https"); - assert(u.host == "example.org"); - assert(u.path == "/"); - assert(u.query == "login=true"); - assert(u.fragment is null); + // With query string + u = URL("https://example.org/?login=true"); + assert(u.scheme == "https"); + assert(u.host == "example.org"); + assert(u.path == "/"); + assert(u.query == "login=true"); + assert(u.fragment is null); - // With query string and fragment - u = URL("https://example.org/?login=false#label"); - assert(u.scheme == "https"); - assert(u.host == "example.org"); - assert(u.path == "/"); - assert(u.query == "login=false"); - assert(u.fragment == "label"); + // With query string and fragment + u = URL("https://example.org/?login=false#label"); + assert(u.scheme == "https"); + assert(u.host == "example.org"); + assert(u.path == "/"); + assert(u.query == "login=false"); + assert(u.fragment == "label"); - u = URL("redis://root:password@localhost:2201/path?query=value#fragment"); - assert(u.scheme == "redis"); - assert(u.user == "root"); - assert(u.pass == "password"); - assert(u.host == "localhost"); - assert(u.port == 2201); - assert(u.path == "/path"); - assert(u.query == "query=value"); - assert(u.fragment == "fragment"); + u = URL("redis://root:password@localhost:2201/path?query=value#fragment"); + assert(u.scheme == "redis"); + assert(u.user == "root"); + assert(u.pass == "password"); + assert(u.host == "localhost"); + assert(u.port == 2201); + assert(u.path == "/path"); + assert(u.query == "query=value"); + assert(u.fragment == "fragment"); } private unittest { - foreach(t; URLTests) - { - if (t[1].length == 0 && t[2] == 0) - { - try - { - URL(t[0]); - assert(0); - } - catch (URIException e) - { - assert(1); - } - } - else - { - auto u = URL(t[0]); - assert("scheme" in t[1] ? u.scheme == t[1]["scheme"] : u.scheme is null, - t[0]); - assert("user" in t[1] ? u.user == t[1]["user"] : u.user is null, t[0]); - assert("pass" in t[1] ? u.pass == t[1]["pass"] : u.pass is null, t[0]); - assert("host" in t[1] ? u.host == t[1]["host"] : u.host is null, t[0]); - assert(u.port == t[2], t[0]); - assert("path" in t[1] ? u.path == t[1]["path"] : u.path is null, t[0]); - assert("query" in t[1] ? u.query == t[1]["query"] : u.query is null, t[0]); - if ("fragment" in t[1]) - { - assert(u.fragment == t[1]["fragment"], t[0]); - } - else - { - assert(u.fragment is null, t[0]); - } - } - } + foreach(t; URLTests) + { + if (t[1].length == 0 && t[2] == 0) + { + try + { + URL(t[0]); + assert(0); + } + catch (URIException e) + { + assert(1); + } + } + else + { + auto u = URL(t[0]); + assert("scheme" in t[1] ? u.scheme == t[1]["scheme"] : u.scheme is null, + t[0]); + assert("user" in t[1] ? u.user == t[1]["user"] : u.user is null, t[0]); + assert("pass" in t[1] ? u.pass == t[1]["pass"] : u.pass is null, t[0]); + assert("host" in t[1] ? u.host == t[1]["host"] : u.host is null, t[0]); + assert(u.port == t[2], t[0]); + assert("path" in t[1] ? u.path == t[1]["path"] : u.path is null, t[0]); + assert("query" in t[1] ? u.query == t[1]["query"] : u.query is null, t[0]); + if ("fragment" in t[1]) + { + assert(u.fragment == t[1]["fragment"], t[0]); + } + else + { + assert(u.fragment is null, t[0]); + } + } + } } /** @@ -1079,111 +1081,111 @@ private unittest */ enum Component : string { - scheme = "scheme", - host = "host", - port = "port", - user = "user", - pass = "pass", - path = "path", - query = "query", - fragment = "fragment", + scheme = "scheme", + host = "host", + port = "port", + user = "user", + pass = "pass", + path = "path", + query = "query", + fragment = "fragment", } /** * Attempts to parse an URL from a string. * * Params: - * T = $(D_SYMBOL Component) member or $(D_KEYWORD null) for a - * struct with all components. - * source = The string containing the URL. + * T = $(D_SYMBOL Component) member or $(D_KEYWORD null) for a + * struct with all components. + * source = The string containing the URL. * * Returns: Requested URL components. */ URL parseURL(typeof(null) T)(in char[] source) { - return URL(source); + return URL(source); } /// Ditto. const(char)[] parseURL(immutable(char)[] T)(in char[] source) - if (T == "scheme" - || T =="host" - || T == "user" - || T == "pass" - || T == "path" - || T == "query" - || T == "fragment") + if (T == "scheme" + || T == "host" + || T == "user" + || T == "pass" + || T == "path" + || T == "query" + || T == "fragment") { - auto ret = URL(source); - return mixin("ret." ~ T); + auto ret = URL(source); + return mixin("ret." ~ T); } /// Ditto. ushort parseURL(immutable(char)[] T)(in char[] source) - if (T == "port") + if (T == "port") { - auto ret = URL(source); - return ret.port; + auto ret = URL(source); + return ret.port; } unittest { - assert(parseURL!(Component.port)("http://example.org:5326") == 5326); + assert(parseURL!(Component.port)("http://example.org:5326") == 5326); } private unittest { - foreach(t; URLTests) - { - if (t[1].length == 0 && t[2] == 0) - { - try - { - parseURL!(Component.port)(t[0]); - parseURL!(Component.user)(t[0]); - parseURL!(Component.pass)(t[0]); - parseURL!(Component.host)(t[0]); - parseURL!(Component.path)(t[0]); - parseURL!(Component.query)(t[0]); - parseURL!(Component.fragment)(t[0]); - assert(0); - } - catch (URIException e) - { - assert(1); - } - } - else - { - ushort port = parseURL!(Component.port)(t[0]); - auto component = parseURL!(Component.scheme)(t[0]); - assert("scheme" in t[1] ? component == t[1]["scheme"] : component is null, - t[0]); - component = parseURL!(Component.user)(t[0]); - assert("user" in t[1] ? component == t[1]["user"] : component is null, - t[0]); - component = parseURL!(Component.pass)(t[0]); - assert("pass" in t[1] ? component == t[1]["pass"] : component is null, - t[0]); - component = parseURL!(Component.host)(t[0]); - assert("host" in t[1] ? component == t[1]["host"] : component is null, - t[0]); - assert(port == t[2], t[0]); - component = parseURL!(Component.path)(t[0]); - assert("path" in t[1] ? component == t[1]["path"] : component is null, - t[0]); - component = parseURL!(Component.query)(t[0]); - assert("query" in t[1] ? component == t[1]["query"] : component is null, - t[0]); - component = parseURL!(Component.fragment)(t[0]); - if ("fragment" in t[1]) - { - assert(component == t[1]["fragment"], t[0]); - } - else - { - assert(component is null, t[0]); - } - } - } + foreach(t; URLTests) + { + if (t[1].length == 0 && t[2] == 0) + { + try + { + parseURL!(Component.port)(t[0]); + parseURL!(Component.user)(t[0]); + parseURL!(Component.pass)(t[0]); + parseURL!(Component.host)(t[0]); + parseURL!(Component.path)(t[0]); + parseURL!(Component.query)(t[0]); + parseURL!(Component.fragment)(t[0]); + assert(0); + } + catch (URIException e) + { + assert(1); + } + } + else + { + ushort port = parseURL!(Component.port)(t[0]); + auto component = parseURL!(Component.scheme)(t[0]); + assert("scheme" in t[1] ? component == t[1]["scheme"] : component is null, + t[0]); + component = parseURL!(Component.user)(t[0]); + assert("user" in t[1] ? component == t[1]["user"] : component is null, + t[0]); + component = parseURL!(Component.pass)(t[0]); + assert("pass" in t[1] ? component == t[1]["pass"] : component is null, + t[0]); + component = parseURL!(Component.host)(t[0]); + assert("host" in t[1] ? component == t[1]["host"] : component is null, + t[0]); + assert(port == t[2], t[0]); + component = parseURL!(Component.path)(t[0]); + assert("path" in t[1] ? component == t[1]["path"] : component is null, + t[0]); + component = parseURL!(Component.query)(t[0]); + assert("query" in t[1] ? component == t[1]["query"] : component is null, + t[0]); + component = parseURL!(Component.fragment)(t[0]); + if ("fragment" in t[1]) + { + assert(component == t[1]["fragment"], t[0]); + } + else + { + assert(component is null, t[0]); + } + } + } } From 445b872e91e0256bce27fc9c7f271f728ed5bbee Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sun, 2 Apr 2017 09:29:54 +0200 Subject: [PATCH 19/26] Add tanya.network.inet.NetworkOrder NetworkOrder converts an integral type into a bidirectional range with big-endian byte order. --- source/tanya/network/inet.d | 292 +++++++++++++++++++++++++++++++++ source/tanya/network/package.d | 1 + 2 files changed, 293 insertions(+) create mode 100644 source/tanya/network/inet.d diff --git a/source/tanya/network/inet.d b/source/tanya/network/inet.d new file mode 100644 index 0000000..97da907 --- /dev/null +++ b/source/tanya/network/inet.d @@ -0,0 +1,292 @@ +/* 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/. */ + +/** + * Internet utilities. + * + * 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.network.inet; + +import std.algorithm.comparison; +import std.traits; + +version (assert) +{ + import std.math; +} + +version (unittest) +{ + import std.math; + import std.range.primitives; + + version (Windows) + { + import core.sys.windows.winsock2; + version = PlattformUnittest; + } + else version (Posix) + { + import core.sys.posix.arpa.inet; + version = PlattformUnittest; + } +} + +/** + * Represents an unsigned integer as an $(D_KEYWORD ubyte) range. + * + * The range is bidirectional. The byte order is always big-endian. + */ +struct NetworkOrder +{ + private uint value; + private size_t size; + + const pure nothrow @safe @nogc invariant + { + assert(this.size <= uint.sizeof); + } + + /** + * Constructs a new range. + * + * $(D_PARAM T) can be any unsigned type but $(D_PARAM value) shouldn't be + * larger than the maximum can be stored in $(D_PARAM length) bytes. + * Otherwise an assertion failure will be caused. + * + * If $(D_PARAM length) isn't specified, it is inferred from the + * $(D_INLINECODE T.sizeof). + * + * If $(D_PARAM T) is $(D_KEYWORD ulong), $(D_PARAM value) should be less + * than or equal to $(D_INLINECODE uint.max). + * + * Params: + * T = Value type. + * value = The value should be iterated over. + * length = $(D_PARAM value) size in bytes. + * + * Precondition: $(D_INLINECODE length < uint.sizeof + * && value <= 2 ^ (length * 8) - 1). + */ + this(T)(const T value, const size_t length) + if (isIntegral!T) + in + { + assert(length <= uint.sizeof); + assert(value >= 0); + assert(value <= pow(2, length * 8) - 1); + } + body + { + this.value = value & uint.max; + this.size = length; + } + + /// Ditto. + this(T)(const T value) + if (isIntegral!T) + { + this(value, min(T.sizeof, uint.sizeof)); + } + + /** + * Returns: LSB. + * + * Precondition: $(D_INLINECODE length > 0). + */ + @property ubyte back() const pure nothrow @safe @nogc + in + { + assert(this.length > 0); + } + body + { + return this.value & 0xff; + } + + /** + * Returns: MSB. + * + * Precondition: $(D_INLINECODE length > 0). + */ + @property ubyte front() const pure nothrow @safe @nogc + in + { + assert(this.length > 0); + } + body + { + return (this.value >> ((this.length - 1) * 8)) & 0xff; + } + + /** + * Eliminates the LSB. + * + * Precondition: $(D_INLINECODE length > 0). + */ + void popBack() pure nothrow @safe @nogc + in + { + assert(this.length > 0); + } + body + { + this.value >>= 8; + --this.size; + } + + /** + * Eliminates the MSB. + * + * Precondition: $(D_INLINECODE length > 0). + */ + void popFront() pure nothrow @safe @nogc + in + { + assert(this.length > 0); + } + body + { + this.value &= uint.max >> ((4 - this.length) * 8); + --this.size; + } + + /** + * Returns: Copy of this range. + */ + typeof(this) save() const pure nothrow @safe @nogc + { + return this; + } + + /** + * Returns: Whether the range is empty. + */ + @property bool empty() const pure nothrow @safe @nogc + { + return this.length == 0; + } + + /** + * Returns: Byte length. + */ + @property size_t length() const pure nothrow @safe @nogc + { + return this.size; + } +} + +/// +pure nothrow @safe @nogc unittest +{ + auto networkOrder = NetworkOrder(0xae34e2u, 3); + assert(!networkOrder.empty); + assert(networkOrder.front == 0xae); + + networkOrder.popFront(); + assert(networkOrder.length == 2); + assert(networkOrder.front == 0x34); + assert(networkOrder.back == 0xe2); + + networkOrder.popBack(); + assert(networkOrder.length == 1); + assert(networkOrder.front == 0x34); + assert(networkOrder.front == 0x34); + + networkOrder.popFront(); + assert(networkOrder.empty); +} + +// Static. +private unittest +{ + static assert(isBidirectionalRange!NetworkOrder); +} + +// Tests against the system's htonl, htons. +version (PlattformUnittest) +{ + private unittest + { + for (uint counter; counter <= 8 * uint.sizeof; ++counter) + { + const value = pow(2, counter) - 1; + const inNetworkOrder = htonl(value); + const p = cast(ubyte*) &inNetworkOrder; + auto networkOrder = NetworkOrder(value); + + assert(networkOrder.length == 4); + assert(!networkOrder.empty); + assert(networkOrder.front == *p); + assert(networkOrder.back == *(p + 3)); + + networkOrder.popBack(); + assert(networkOrder.length == 3); + assert(networkOrder.front == *p); + assert(networkOrder.back == *(p + 2)); + + networkOrder.popFront(); + assert(networkOrder.length == 2); + assert(networkOrder.front == *(p + 1)); + assert(networkOrder.back == *(p + 2)); + + networkOrder.popFront(); + assert(networkOrder.length == 1); + assert(networkOrder.front == *(p + 2)); + assert(networkOrder.back == *(p + 2)); + + networkOrder.popBack(); + assert(networkOrder.length == 0); + assert(networkOrder.empty); + } + + for (ushort counter; counter <= 8 * ushort.sizeof; ++counter) + { + const value = cast(ushort) (pow(2, counter) - 1); + const inNetworkOrder = htons(value); + const p = cast(ubyte*) &inNetworkOrder; + + auto networkOrder = NetworkOrder(value); + + assert(networkOrder.length == 2); + assert(!networkOrder.empty); + assert(networkOrder.front == *p); + assert(networkOrder.back == *(p + 1)); + + networkOrder.popBack(); + assert(networkOrder.length == 1); + assert(networkOrder.front == *p); + assert(networkOrder.back == *p); + + networkOrder.popBack(); + assert(networkOrder.length == 0); + assert(networkOrder.empty); + + networkOrder = NetworkOrder(value); + + networkOrder.popFront(); + assert(networkOrder.length == 1); + assert(networkOrder.front == *(p + 1)); + assert(networkOrder.back == *(p + 1)); + + networkOrder.popFront(); + assert(networkOrder.length == 0); + assert(networkOrder.empty); + } + + auto networkOrder = NetworkOrder(255u, 1); + assert(networkOrder.length == 1); + assert(!networkOrder.empty); + assert(networkOrder.front == 0xff); + assert(networkOrder.back == 0xff); + + networkOrder.popFront(); + assert(networkOrder.length == 0); + assert(networkOrder.empty); + } +} diff --git a/source/tanya/network/package.d b/source/tanya/network/package.d index 96b987c..14a6752 100644 --- a/source/tanya/network/package.d +++ b/source/tanya/network/package.d @@ -12,5 +12,6 @@ */ module tanya.network; +public import tanya.network.inet; public import tanya.network.socket; public import tanya.network.url; From b08d5e5d83e506a598e4f1bf8954562661fb5f84 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sun, 2 Apr 2017 11:16:08 +0200 Subject: [PATCH 20/26] Add tanya.network.inet.toHostOrder The function reverts NetworkOrder. --- source/tanya/network/inet.d | 75 +++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 4 deletions(-) diff --git a/source/tanya/network/inet.d b/source/tanya/network/inet.d index 97da907..75d610c 100644 --- a/source/tanya/network/inet.d +++ b/source/tanya/network/inet.d @@ -13,6 +13,7 @@ module tanya.network.inet; import std.algorithm.comparison; +import std.range.primitives; import std.traits; version (assert) @@ -23,7 +24,6 @@ version (assert) version (unittest) { import std.math; - import std.range.primitives; version (Windows) { @@ -74,11 +74,10 @@ struct NetworkOrder * && value <= 2 ^ (length * 8) - 1). */ this(T)(const T value, const size_t length) - if (isIntegral!T) + if (isIntegral!T && isUnsigned!T) in { assert(length <= uint.sizeof); - assert(value >= 0); assert(value <= pow(2, length * 8) - 1); } body @@ -89,7 +88,7 @@ struct NetworkOrder /// Ditto. this(T)(const T value) - if (isIntegral!T) + if (isIntegral!T && isUnsigned!T) { this(value, min(T.sizeof, uint.sizeof)); } @@ -290,3 +289,71 @@ version (PlattformUnittest) assert(networkOrder.empty); } } + +/** + * Converts the $(D_KEYWORD ubyte) input range $(D_PARAM r) to + * $(D_KEYWORD uint). + * + * The byte order of $(D_PARAM r) assumed to be big-endian. The length + * cannot be larger than $(D_INLINECODE uint.sizeof). Otherwise an assertion + * failure will be caused. + * + * Params: + * R = Range type. + * range = Input range. + * + * Returns: $(D_KEYWORD uint) representation of $(D_PARAM range) with host byte + * order. + */ +uint toHostOrder(R)(R range) + if (isInputRange!R && is(Unqual!(ElementType!R) == ubyte) && !isInfinite!R) +{ + uint ret; + ushort pos = 32; + + for (; !range.empty && range.front == 0; pos -= 8, range.popFront()) + { + } + for (; !range.empty; range.popFront()) + { + assert(pos != 0); + pos -= 8; + ret |= range.front << pos; + } + + return ret >> pos; +} + +/// +unittest +{ + const value = 0xae34e2u; + auto networkOrder = NetworkOrder(value); + assert(networkOrder.toHostOrder() == value); +} + +// Tests against the system's htonl, htons. +version (PlattformUnittest) +{ + private unittest + { + for (uint counter; counter <= 8 * uint.sizeof; ++counter) + { + const value = pow(2, counter) - 1; + const inNetworkOrder = htonl(value); + const p = cast(ubyte*) &inNetworkOrder; + auto networkOrder = NetworkOrder(value); + + assert(p[0 .. uint.sizeof].toHostOrder() == value); + } + for (ushort counter; counter <= 8 * ushort.sizeof; ++counter) + { + const value = cast(ushort) (pow(2, counter) - 1); + const inNetworkOrder = htons(value); + const p = cast(ubyte*) &inNetworkOrder; + auto networkOrder = NetworkOrder(value); + + assert(p[0 .. ushort.sizeof].toHostOrder() == value); + } + } +} From b458250ad7e33c17e18b0c67e17d6f011fa7639e Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sun, 2 Apr 2017 20:55:22 +0200 Subject: [PATCH 21/26] Make NetworkOrder work with 8-byte integers --- source/tanya/network/inet.d | 139 ++++++++++++++++++------------------ 1 file changed, 71 insertions(+), 68 deletions(-) diff --git a/source/tanya/network/inet.d b/source/tanya/network/inet.d index 75d610c..579bf7f 100644 --- a/source/tanya/network/inet.d +++ b/source/tanya/network/inet.d @@ -12,7 +12,6 @@ */ module tanya.network.inet; -import std.algorithm.comparison; import std.range.primitives; import std.traits; @@ -41,56 +40,63 @@ version (unittest) * Represents an unsigned integer as an $(D_KEYWORD ubyte) range. * * The range is bidirectional. The byte order is always big-endian. + * + * It can accept any unsigned integral type but the value should fit + * in $(D_PARAM L) bytes. + * + * Params: + * L = Desired range length. */ -struct NetworkOrder +struct NetworkOrder(uint L) + if (L > ubyte.sizeof && L <= ulong.sizeof) { - private uint value; - private size_t size; + static if (L > uint.sizeof) + { + private alias StorageType = ulong; + } + else static if (L > ushort.sizeof) + { + private alias StorageType = uint; + } + else static if (L > ubyte.sizeof) + { + private alias StorageType = ushort; + } + else + { + private alias StorageType = ubyte; + } + + private StorageType value; + private size_t size = L; const pure nothrow @safe @nogc invariant { - assert(this.size <= uint.sizeof); + assert(this.size <= L); } /** * Constructs a new range. * - * $(D_PARAM T) can be any unsigned type but $(D_PARAM value) shouldn't be - * larger than the maximum can be stored in $(D_PARAM length) bytes. - * Otherwise an assertion failure will be caused. - * - * If $(D_PARAM length) isn't specified, it is inferred from the - * $(D_INLINECODE T.sizeof). - * - * If $(D_PARAM T) is $(D_KEYWORD ulong), $(D_PARAM value) should be less - * than or equal to $(D_INLINECODE uint.max). + * $(D_PARAM T) can be any unsigned type but $(D_PARAM value) cannot be + * larger than the maximum can be stored in $(D_PARAM L) bytes. Otherwise + * an assertion failure will be caused. * * Params: * T = Value type. - * value = The value should be iterated over. - * length = $(D_PARAM value) size in bytes. + * value = The value should be represented by this range. * - * Precondition: $(D_INLINECODE length < uint.sizeof - * && value <= 2 ^ (length * 8) - 1). + * Precondition: $(D_INLINECODE value <= 2 ^^ (length * 8) - 1). */ - this(T)(const T value, const size_t length) - if (isIntegral!T && isUnsigned!T) + this(T)(const T value) + if (isUnsigned!T) in { - assert(length <= uint.sizeof); - assert(value <= pow(2, length * 8) - 1); + assert(value <= pow(2, L * 8) - 1); } body { - this.value = value & uint.max; - this.size = length; - } - - /// Ditto. - this(T)(const T value) - if (isIntegral!T && isUnsigned!T) - { - this(value, min(T.sizeof, uint.sizeof)); + this.value = value & StorageType.max; } /** @@ -98,7 +104,7 @@ struct NetworkOrder * * Precondition: $(D_INLINECODE length > 0). */ - @property ubyte back() const pure nothrow @safe @nogc + @property ubyte back() const in { assert(this.length > 0); @@ -113,7 +119,7 @@ struct NetworkOrder * * Precondition: $(D_INLINECODE length > 0). */ - @property ubyte front() const pure nothrow @safe @nogc + @property ubyte front() const in { assert(this.length > 0); @@ -128,7 +134,7 @@ struct NetworkOrder * * Precondition: $(D_INLINECODE length > 0). */ - void popBack() pure nothrow @safe @nogc + void popBack() in { assert(this.length > 0); @@ -144,21 +150,21 @@ struct NetworkOrder * * Precondition: $(D_INLINECODE length > 0). */ - void popFront() pure nothrow @safe @nogc + void popFront() in { assert(this.length > 0); } body { - this.value &= uint.max >> ((4 - this.length) * 8); + this.value &= StorageType.max >> ((StorageType.sizeof - this.length) * 8); --this.size; } /** * Returns: Copy of this range. */ - typeof(this) save() const pure nothrow @safe @nogc + typeof(this) save() const { return this; } @@ -166,7 +172,7 @@ struct NetworkOrder /** * Returns: Whether the range is empty. */ - @property bool empty() const pure nothrow @safe @nogc + @property bool empty() const { return this.length == 0; } @@ -174,7 +180,7 @@ struct NetworkOrder /** * Returns: Byte length. */ - @property size_t length() const pure nothrow @safe @nogc + @property size_t length() const { return this.size; } @@ -183,7 +189,7 @@ struct NetworkOrder /// pure nothrow @safe @nogc unittest { - auto networkOrder = NetworkOrder(0xae34e2u, 3); + auto networkOrder = NetworkOrder!3(0xae34e2u); assert(!networkOrder.empty); assert(networkOrder.front == 0xae); @@ -204,7 +210,10 @@ pure nothrow @safe @nogc unittest // Static. private unittest { - static assert(isBidirectionalRange!NetworkOrder); + static assert(isBidirectionalRange!(NetworkOrder!4)); + static assert(isBidirectionalRange!(NetworkOrder!8)); + static assert(!is(NetworkOrder!9)); + static assert(!is(NetworkOrder!1)); } // Tests against the system's htonl, htons. @@ -217,7 +226,7 @@ version (PlattformUnittest) const value = pow(2, counter) - 1; const inNetworkOrder = htonl(value); const p = cast(ubyte*) &inNetworkOrder; - auto networkOrder = NetworkOrder(value); + auto networkOrder = NetworkOrder!4(value); assert(networkOrder.length == 4); assert(!networkOrder.empty); @@ -250,7 +259,7 @@ version (PlattformUnittest) const inNetworkOrder = htons(value); const p = cast(ubyte*) &inNetworkOrder; - auto networkOrder = NetworkOrder(value); + auto networkOrder = NetworkOrder!2(value); assert(networkOrder.length == 2); assert(!networkOrder.empty); @@ -266,7 +275,7 @@ version (PlattformUnittest) assert(networkOrder.length == 0); assert(networkOrder.empty); - networkOrder = NetworkOrder(value); + networkOrder = NetworkOrder!2(value); networkOrder.popFront(); assert(networkOrder.length == 1); @@ -277,39 +286,33 @@ version (PlattformUnittest) assert(networkOrder.length == 0); assert(networkOrder.empty); } - - auto networkOrder = NetworkOrder(255u, 1); - assert(networkOrder.length == 1); - assert(!networkOrder.empty); - assert(networkOrder.front == 0xff); - assert(networkOrder.back == 0xff); - - networkOrder.popFront(); - assert(networkOrder.length == 0); - assert(networkOrder.empty); } } /** - * Converts the $(D_KEYWORD ubyte) input range $(D_PARAM r) to - * $(D_KEYWORD uint). + * Converts the $(D_KEYWORD ubyte) input range $(D_PARAM range) to + * $(D_PARAM T). * - * The byte order of $(D_PARAM r) assumed to be big-endian. The length - * cannot be larger than $(D_INLINECODE uint.sizeof). Otherwise an assertion + * The byte order of $(D_PARAM r) is assumed to be big-endian. The length + * cannot be larger than $(D_INLINECODE T.sizeof). Otherwise an assertion * failure will be caused. * * Params: * R = Range type. + * T = Desired return type. * range = Input range. * - * Returns: $(D_KEYWORD uint) representation of $(D_PARAM range) with host byte + * Returns: Integral representation of $(D_PARAM range) with the host byte * order. */ -uint toHostOrder(R)(R range) - if (isInputRange!R && is(Unqual!(ElementType!R) == ubyte) && !isInfinite!R) +T toHostOrder(R, T = size_t)(R range) + if (isInputRange!R + && !isInfinite!R + && is(Unqual!(ElementType!R) == ubyte) + && isUnsigned!T) { - uint ret; - ushort pos = 32; + T ret; + ushort pos = T.sizeof * 8; for (; !range.empty && range.front == 0; pos -= 8, range.popFront()) { @@ -318,17 +321,17 @@ uint toHostOrder(R)(R range) { assert(pos != 0); pos -= 8; - ret |= range.front << pos; + ret |= (cast(T) range.front) << pos; } return ret >> pos; } /// -unittest +pure nothrow @safe @nogc unittest { const value = 0xae34e2u; - auto networkOrder = NetworkOrder(value); + auto networkOrder = NetworkOrder!4(value); assert(networkOrder.toHostOrder() == value); } @@ -342,7 +345,7 @@ version (PlattformUnittest) const value = pow(2, counter) - 1; const inNetworkOrder = htonl(value); const p = cast(ubyte*) &inNetworkOrder; - auto networkOrder = NetworkOrder(value); + auto networkOrder = NetworkOrder!4(value); assert(p[0 .. uint.sizeof].toHostOrder() == value); } @@ -351,7 +354,7 @@ version (PlattformUnittest) const value = cast(ushort) (pow(2, counter) - 1); const inNetworkOrder = htons(value); const p = cast(ubyte*) &inNetworkOrder; - auto networkOrder = NetworkOrder(value); + auto networkOrder = NetworkOrder!2(value); assert(p[0 .. ushort.sizeof].toHostOrder() == value); } From bc2a6d2703314214eadec1c8b9714e19eaed434d Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Mon, 3 Apr 2017 15:32:15 +0200 Subject: [PATCH 22/26] Swap toHostOrder template parameters --- source/tanya/network/inet.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/tanya/network/inet.d b/source/tanya/network/inet.d index 579bf7f..d69517c 100644 --- a/source/tanya/network/inet.d +++ b/source/tanya/network/inet.d @@ -298,14 +298,14 @@ version (PlattformUnittest) * failure will be caused. * * Params: - * R = Range type. * T = Desired return type. + * R = Range type. * range = Input range. * * Returns: Integral representation of $(D_PARAM range) with the host byte * order. */ -T toHostOrder(R, T = size_t)(R range) +T toHostOrder(T = size_t, R)(R range) if (isInputRange!R && !isInfinite!R && is(Unqual!(ElementType!R) == ubyte) From 9b953198fafabffb2984df8ffff7d89366d909a7 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Tue, 4 Apr 2017 08:36:42 +0200 Subject: [PATCH 23/26] Fix network.inet release build --- source/tanya/network/inet.d | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/source/tanya/network/inet.d b/source/tanya/network/inet.d index d69517c..9b0f28a 100644 --- a/source/tanya/network/inet.d +++ b/source/tanya/network/inet.d @@ -12,18 +12,12 @@ */ module tanya.network.inet; +import std.math; import std.range.primitives; import std.traits; -version (assert) -{ - import std.math; -} - version (unittest) { - import std.math; - version (Windows) { import core.sys.windows.winsock2; From b1d2b9bd9e37c043ab382e93f8016231233de13c Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Tue, 4 Apr 2017 15:11:14 +0200 Subject: [PATCH 24/26] Fix Vector.insertAfter/Before an empty range --- source/tanya/container/vector.d | 250 ++++++++++++++++++-------------- 1 file changed, 144 insertions(+), 106 deletions(-) diff --git a/source/tanya/container/vector.d b/source/tanya/container/vector.d index 4a7b5c3..2bb0171 100644 --- a/source/tanya/container/vector.d +++ b/source/tanya/container/vector.d @@ -22,22 +22,42 @@ import std.meta; import std.traits; import tanya.memory; -// Defines the container's primary range. -private struct Range(E) +/** + * Random-access range for the $(D_PSYMBOL Vector). + * + * Params: + * E = Element type. + */ +struct Range(E) { private E* begin, end; + private alias ContainerType = CopyConstness!(E, Vector!(Unqual!E)); + private ContainerType* vector; invariant { - assert(begin <= end); + 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(E* begin, E* end) + private this(ref ContainerType vector, E* begin, E* 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 Range save() { return this; @@ -45,12 +65,12 @@ private struct Range(E) @property bool empty() const { - return begin == end; + return this.begin == this.end; } @property size_t length() const { - return end - begin; + return this.end - this.begin; } alias opDollar = length; @@ -62,7 +82,7 @@ private struct Range(E) } body { - return *begin; + return *this.begin; } @property ref inout(E) back() inout @trusted @@ -72,7 +92,7 @@ private struct Range(E) } body { - return *(end - 1); + return *(this.end - 1); } void popFront() @trusted @@ -82,7 +102,7 @@ private struct Range(E) } body { - ++begin; + ++this.begin; } void popBack() @trusted @@ -92,7 +112,7 @@ private struct Range(E) } body { - --end; + --this.end; } ref inout(E) opIndex(const size_t i) inout @trusted @@ -102,17 +122,17 @@ private struct Range(E) } body { - return *(begin + i); + return *(this.begin + i); } Range opIndex() { - return typeof(return)(begin, end); + return typeof(return)(*this.vector, this.begin, this.end); } Range!(const E) opIndex() const { - return typeof(return)(begin, end); + return typeof(return)(*this.vector, this.begin, this.end); } Range opSlice(const size_t i, const size_t j) @trusted @@ -123,7 +143,7 @@ private struct Range(E) } body { - return typeof(return)(begin + i, begin + j); + return typeof(return)(*this.vector, this.begin + i, this.begin + j); } Range!(const E) opSlice(const size_t i, const size_t j) const @trusted @@ -134,12 +154,12 @@ private struct Range(E) } body { - return typeof(return)(begin + i, begin + j); + return typeof(return)(*this.vector, this.begin + i, this.begin + j); } inout(E[]) get() inout @trusted { - return begin[0 .. length]; + return this.begin[0 .. length]; } } @@ -152,13 +172,13 @@ private struct Range(E) struct Vector(T) { private size_t length_; - private T* vector; + private T* data; private size_t capacity_; invariant { - assert(length_ <= capacity_); - assert(capacity_ == 0 || vector !is null); + assert(this.length_ <= this.capacity_); + assert(this.capacity_ == 0 || this.data !is null); } /** @@ -224,20 +244,20 @@ struct Vector(T) if (allocator is init.allocator) { // Just steal all references and the allocator. - vector = init.vector; - length_ = init.length_; - capacity_ = init.capacity_; + 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.vector = null; + init.data = null; } else { // Move each element. - reserve(init.length); - moveEmplaceAll(init.vector[0 .. init.length_], vector[0 .. init.length_]); - length_ = init.length; + 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. } } @@ -247,7 +267,6 @@ struct Vector(T) { 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])); @@ -260,7 +279,7 @@ struct Vector(T) { auto v1 = const Vector!int([1, 2, 3]); auto v2 = Vector!int(v1); - assert(v1.vector !is v2.vector); + assert(v1.data !is v2.data); assert(v1 == v2); auto v3 = const Vector!int(Vector!int([1, 2, 3])); @@ -281,7 +300,7 @@ struct Vector(T) { this(allocator); reserve(len); - uninitializedFill(vector[0 .. len], init); + uninitializedFill(this.data[0 .. len], init); length_ = len; } @@ -334,7 +353,7 @@ struct Vector(T) ~this() @trusted { clear(); - allocator.deallocate(vector[0 .. capacity_]); + allocator.deallocate(this.data[0 .. capacity]); } /** @@ -342,9 +361,9 @@ struct Vector(T) */ this(this) { - auto buf = opIndex(); - length_ = capacity_ = 0; - vector = null; + auto buf = this.data[0 .. this.length_]; + this.length_ = capacity_ = 0; + this.data = null; insertBack(buf); } @@ -409,14 +428,14 @@ struct Vector(T) else if (len > length) { reserve(len); - initializeAll(vector[length_ .. len]); + initializeAll(this.data[length_ .. len]); } else { static if (hasElaborateDestructor!T) { - const T* end = vector + length_ - 1; - for (T* e = vector + len; e != end; ++e) + const T* end = this.data + length_ - 1; + for (T* e = this.data + len; e != end; ++e) { destroy(*e); } @@ -467,7 +486,7 @@ struct Vector(T) immutable byteSize = mulu(size, T.sizeof, overflow); assert(!overflow); - void[] buf = vector[0 .. capacity_]; + void[] buf = this.data[0 .. this.capacity_]; if (!allocator.reallocateInPlace(buf, byteSize)) { buf = allocator.allocate(byteSize); @@ -479,8 +498,8 @@ struct Vector(T) { allocator.deallocate(buf); } - const T* end = vector + length_; - for (T* src = vector, dest = cast(T*) buf; src != end; ++src, ++dest) + 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) @@ -488,10 +507,10 @@ struct Vector(T) destroy(*src); } } - allocator.deallocate(vector[0 .. capacity_]); - vector = cast(T*) buf; + allocator.deallocate(this.data[0 .. this.capacity_]); + this.data = cast(T*) buf; } - capacity_ = size; + this.capacity_ = size; } /// @@ -517,15 +536,15 @@ struct Vector(T) */ void shrink(const size_t size) @trusted { - if (capacity_ <= size) + if (capacity <= size) { return; } immutable n = max(length, size); - void[] buf = vector[0 .. capacity_]; + void[] buf = this.data[0 .. this.capacity_]; if (allocator.reallocateInPlace(buf, n * T.sizeof)) { - capacity_ = n; + this.capacity_ = n; } } @@ -556,7 +575,7 @@ struct Vector(T) * * Returns: The number of elements removed * - * Precondition: $(D_INLINECODE !empty) + * Precondition: $(D_INLINECODE !empty). */ void removeBack() in @@ -611,22 +630,24 @@ struct Vector(T) * Params: * r = Range originally obtained from this vector. * - * Returns: Elements in $(D_PARAM r) after removing. + * 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). */ Range!T remove(Range!T r) @trusted in { - assert(r.begin >= vector); - assert(r.end <= vector + length_); + assert(r.vector is &this); + assert(r.begin >= this.data); + assert(r.end <= this.data + length); } body { - auto end = vector + length_; - moveAll(Range!T(r.end, end), Range!T(r.begin, end)); + auto end = this.data + this.length; + moveAll(Range!T(this, r.end, end), Range!T(this, r.begin, end)); length = length - r.length; - return r; + return Range!T(this, r.begin, this.data + length); } /// @@ -634,31 +655,28 @@ struct Vector(T) { auto v = Vector!int([5, 18, 17, 2, 4, 6, 1]); - assert(v.remove(v[1 .. 3]).length == 2); + 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 == 0); + 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 == 1); + 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 == 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_; + reserve(this.length + 1); + moveEmplace(el, *(this.data + this.length_)); + ++this.length_; } /** @@ -675,9 +693,9 @@ struct Vector(T) { static if (__traits(isRef, el)) { - reserve(length + 1); - emplace(vector + length_, el); - ++length_; + reserve(this.length_ + 1); + emplace(this.data + this.length_, el); + ++this.length_; } else { @@ -763,6 +781,8 @@ struct Vector(T) * 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)(Range!T r, R el) if (!isInfinite!R @@ -770,20 +790,28 @@ struct Vector(T) && isImplicitlyConvertible!(ElementType!R, T)) in { - assert(r.begin >= vector); - assert(r.end <= vector + length_); + assert(r.vector is &this); + assert(r.begin >= this.data); + assert(r.end <= this.data + length); } body { immutable oldLen = length; - immutable offset = r.end - vector; + immutable offset = r.end - this.data; immutable inserted = insertBack(el); - bringToFront(vector[offset .. oldLen], vector[oldLen .. length]); + bringToFront(this.data[offset .. oldLen], this.data[oldLen .. length]); return inserted; } /// Ditto. size_t insertAfter(size_t R)(Range!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[]); } @@ -793,13 +821,14 @@ struct Vector(T) if (isImplicitlyConvertible!(R, T)) in { - assert(r.begin >= vector); - assert(r.end <= vector + length_); + assert(r.vector is &this); + assert(r.begin >= this.data); + assert(r.end <= this.data + length); } body { immutable oldLen = length; - immutable offset = r.end - vector; + immutable offset = r.end - this.data; static if (__traits(isRef, el)) { @@ -809,7 +838,7 @@ struct Vector(T) { moveBack(el); } - bringToFront(vector[offset .. oldLen], vector[oldLen .. length_]); + bringToFront(this.data[offset .. oldLen], this.data[oldLen .. length]); return 1; } @@ -821,16 +850,24 @@ struct Vector(T) && isImplicitlyConvertible!(ElementType!R, T)) in { - assert(r.begin >= vector); - assert(r.end <= vector + length_); + assert(r.vector is &this); + assert(r.begin >= this.data); + assert(r.end <= this.data + length); } body { - return insertAfter(Range!T(vector, r.begin), el); + return insertAfter(Range!T(this, this.data, r.begin), el); } /// Ditto. size_t insertBefore(size_t R)(Range!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[]); } @@ -840,13 +877,14 @@ struct Vector(T) if (isImplicitlyConvertible!(R, T)) in { - assert(r.begin >= vector); - assert(r.end <= vector + length_); + assert(r.vector is &this); + assert(r.begin >= this.data); + assert(r.end <= this.data + length); } body { immutable oldLen = length; - immutable offset = r.begin - vector; + immutable offset = r.begin - this.data; static if (__traits(isRef, el)) { @@ -856,7 +894,7 @@ struct Vector(T) { moveBack(el); } - bringToFront(vector[offset .. oldLen], vector[oldLen .. length_]); + bringToFront(this.data[offset .. oldLen], this.data[oldLen .. length]); return 1; } @@ -942,7 +980,7 @@ struct Vector(T) * * Returns: Assigned value. * - * Precondition: $(D_INLINECODE length > pos) + * Precondition: $(D_INLINECODE length > pos). */ ref T opIndexAssign(ref T value, const size_t pos) { @@ -983,7 +1021,7 @@ struct Vector(T) * * Returns: Assigned value. * - * Precondition: $(D_INLINECODE length == value.length) + * Precondition: $(D_INLINECODE length == value.length). */ Range!T opIndexAssign(R)(R value) if (!isInfinite!R && isInputRange!R @@ -1020,7 +1058,7 @@ struct Vector(T) * * Returns: The value at a specified index. * - * Precondition: $(D_INLINECODE length > pos) + * Precondition: $(D_INLINECODE length > pos). */ ref inout(T) opIndex(const size_t pos) inout @trusted in @@ -1029,7 +1067,7 @@ struct Vector(T) } body { - return *(vector + pos); + return *(this.data + pos); } /** @@ -1038,13 +1076,13 @@ struct Vector(T) */ Range!T opIndex() @trusted { - return typeof(return)(vector, vector + length_); + return typeof(return)(this, this.data, this.data + length); } /// Ditto. Range!(const T) opIndex() const @trusted { - return typeof(return)(vector, vector + length_); + return typeof(return)(this, this.data, this.data + length); } /// @@ -1071,13 +1109,13 @@ struct Vector(T) */ bool opEquals()(auto ref typeof(this) that) @trusted { - return equal(vector[0 .. length_], that.vector[0 .. that.length_]); + return equal(this.data[0 .. length], that.data[0 .. that.length]); } /// Ditto. bool opEquals()(const auto ref typeof(this) that) const @trusted { - return equal(vector[0 .. length_], that.vector[0 .. that.length_]); + return equal(this.data[0 .. length], that.data[0 .. that.length]); } /// Ditto. @@ -1132,8 +1170,8 @@ struct Vector(T) */ int opApply(scope int delegate(ref T) @nogc dg) { - T* end = vector + length_ - 1; - for (T* begin = vector; begin != end; ++begin) + T* end = this.data + length - 1; + for (T* begin = this.data; begin != end; ++begin) { int result = dg(*begin); if (result != 0) @@ -1150,7 +1188,7 @@ struct Vector(T) for (size_t i = 0; i < length; ++i) { assert(i < length); - int result = dg(i, *(vector + i)); + int result = dg(i, *(this.data + i)); if (result != 0) { @@ -1163,7 +1201,7 @@ struct Vector(T) /// Ditto. int opApplyReverse(scope int delegate(ref T) dg) { - for (T* end = vector + length - 1; vector != end; --end) + for (T* end = this.data + length - 1; this.data != end; --end) { int result = dg(*end); if (result != 0) @@ -1184,7 +1222,7 @@ struct Vector(T) { --i; assert(i < length); - int result = dg(i, *(vector + i)); + int result = dg(i, *(this.data + i)); if (result != 0) { @@ -1239,7 +1277,7 @@ struct Vector(T) /** * Returns: The first element. * - * Precondition: $(D_INLINECODE !empty) + * Precondition: $(D_INLINECODE !empty). */ @property ref inout(T) front() inout in @@ -1248,7 +1286,7 @@ struct Vector(T) } body { - return *vector; + return *this.data; } /// @@ -1266,7 +1304,7 @@ struct Vector(T) /** * Returns: The last element. * - * Precondition: $(D_INLINECODE !empty) + * Precondition: $(D_INLINECODE !empty). */ @property ref inout(T) back() inout @trusted in @@ -1275,7 +1313,7 @@ struct Vector(T) } body { - return *(vector + length_ - 1); + return *(this.data + length - 1); } /// @@ -1298,7 +1336,7 @@ struct Vector(T) * 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) + * Precondition: $(D_INLINECODE i <= j && j <= length). */ Range!T opSlice(const size_t i, const size_t j) @trusted in @@ -1308,7 +1346,7 @@ struct Vector(T) } body { - return typeof(return)(vector + i, vector + j); + return typeof(return)(this, this.data + i, this.data + j); } /// Ditto. @@ -1320,7 +1358,7 @@ struct Vector(T) } body { - return typeof(return)(vector + i, vector + j); + return typeof(return)(this, this.data + i, this.data + j); } /// @@ -1394,7 +1432,7 @@ struct Vector(T) } body { - copy(value, vector[i .. j]); + copy(value, this.data[i .. j]); return opSlice(i, j); } @@ -1413,7 +1451,7 @@ struct Vector(T) } body { - fill(vector[i .. j], value); + fill(this.data[i .. j], value); return opSlice(i, j); } @@ -1454,7 +1492,7 @@ struct Vector(T) */ inout(T[]) get() inout @trusted { - return vector[0 .. length]; + return this.data[0 .. length]; } /// @@ -1498,7 +1536,7 @@ struct Vector(T) ref typeof(this) opAssign(R)(R that) @trusted if (is(R == Vector)) { - swap(this.vector, that.vector); + swap(this.data, that.data); swap(this.length_, that.length_); swap(this.capacity_, that.capacity_); swap(this.allocator_, that.allocator_); @@ -1519,7 +1557,7 @@ struct Vector(T) && isInputRange!R && isImplicitlyConvertible!(ElementType!R, T)) { - this.length = 0; + length = 0; insertBack(that); return this; } @@ -1596,7 +1634,7 @@ 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))); + static assert(is(PointerTarget!(typeof(v3.data)) == const(int))); } @nogc unittest From ba6bf554fb0813c3d358a5c78e74f24966346762 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Fri, 7 Apr 2017 15:17:14 +0200 Subject: [PATCH 25/26] Make SList range public --- source/tanya/container/list.d | 59 ++++++++++++++++++--------------- source/tanya/container/vector.d | 23 +++++++------ 2 files changed, 45 insertions(+), 37 deletions(-) diff --git a/source/tanya/container/list.d b/source/tanya/container/list.d index 2f7cdbb..7f3a616 100644 --- a/source/tanya/container/list.d +++ b/source/tanya/container/list.d @@ -20,25 +20,31 @@ import std.traits; import tanya.container.entry; import tanya.memory; -private struct Range(Entry) - if (__traits(isSame, TemplateOf!Entry, SEntry)) +/** + * Forward range for the $(D_PSYMBOL SList). + * + * Params: + * E = Element type. + */ +struct SRange(E) { - private alias T = typeof(E.content); - private alias E = CopyConstness!(Entry, Entry*); + private alias EntryPointer = CopyConstness!(E, SEntry!(Unqual!E)*); - private E* head; + private EntryPointer* head; invariant { assert(head !is null); } - private this(ref E head) @trusted + private this(ref EntryPointer head) @trusted { this.head = &head; } - @property Range save() + @disable this(); + + @property SRange save() { return this; } @@ -53,7 +59,7 @@ private struct Range(Entry) return *head is null; } - @property ref inout(T) front() inout + @property ref inout(E) front() inout in { assert(!empty); @@ -73,12 +79,12 @@ private struct Range(Entry) head = &(*head).next; } - Range opIndex() + SRange opIndex() { return typeof(return)(*head); } - Range!(const Entry) opIndex() const + SRange!(const E) opIndex() const { return typeof(return)(*head); } @@ -340,6 +346,14 @@ struct SList(T) return moveEntry(head, el); } + /// Ditto. + size_t insertFront(R)(ref R el) @trusted + if (isImplicitlyConvertible!(R, T)) + { + head = allocator.make!Entry(el, head); + return 1; + } + /// Ditto. size_t insertFront(R)(R el) @trusted if (!isInfinite!R @@ -376,13 +390,6 @@ struct SList(T) return insertFront!(T[])(el[]); } - /// Ditto. - size_t insertFront(ref T el) @trusted - { - head = allocator.make!Entry(el, head); - return 1; - } - /// Ditto. alias insert = insertFront; @@ -407,7 +414,7 @@ struct SList(T) version (assert) { - private bool checkRangeBelonging(ref Range!Entry r) const + private bool checkRangeBelonging(ref SRange!T r) const { const(Entry*)* pos; for (pos = &head; pos != r.head && *pos !is null; pos = &(*pos).next) @@ -429,7 +436,7 @@ struct SList(T) * * Precondition: $(D_PARAM r) is extracted from this list. */ - size_t insertBefore(R)(Range!Entry r, R el) + size_t insertBefore(R)(SRange!T r, R el) if (isImplicitlyConvertible!(R, T)) in { @@ -450,7 +457,7 @@ struct SList(T) } /// Ditto. - size_t insertBefore(R)(Range!Entry r, R el) + size_t insertBefore(R)(SRange!T r, R el) if (!isInfinite!R && isInputRange!R && isImplicitlyConvertible!(ElementType!R, T)) @@ -482,7 +489,7 @@ struct SList(T) } /// Ditto. - size_t insertBefore(Range!Entry r, ref T el) @trusted + size_t insertBefore(SRange!T r, ref T el) @trusted in { assert(checkRangeBelonging(r)); @@ -515,7 +522,7 @@ struct SList(T) * * Precondition: $(D_PARAM r) is extracted from this list. */ - size_t insertBefore(size_t R)(Range!Entry r, T[R] el) + size_t insertBefore(size_t R)(SRange!T r, T[R] el) { return insertFront!(T[])(el[]); } @@ -670,7 +677,7 @@ struct SList(T) * * Precondition: $(D_PARAM r) is extracted from this list. */ - Range!Entry remove(Range!Entry r) + SRange!T remove(SRange!T r) in { assert(checkRangeBelonging(r)); @@ -759,13 +766,13 @@ struct SList(T) * Returns: Range that iterates over all elements of the container, in * forward order. */ - Range!Entry opIndex() + SRange!T opIndex() { return typeof(return)(head); } /// Ditto. - Range!(const Entry) opIndex() const + SRange!(const T) opIndex() const { return typeof(return)(head); } @@ -827,7 +834,7 @@ struct SList(T) } next = &(*next).next; } - remove(Range!Entry(*next)); + remove(SRange!T(*next)); return this; } diff --git a/source/tanya/container/vector.d b/source/tanya/container/vector.d index 2bb0171..7a0caa4 100644 --- a/source/tanya/container/vector.d +++ b/source/tanya/container/vector.d @@ -688,19 +688,20 @@ struct Vector(T) * * Returns: The number of elements inserted. */ - size_t insertBack(R)(auto ref R el) @trusted + size_t insertBack(R)(R el) if (isImplicitlyConvertible!(R, T)) { - static if (__traits(isRef, el)) - { - reserve(this.length_ + 1); - emplace(this.data + this.length_, el); - ++this.length_; - } - else - { - moveBack(el); - } + 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; } From 6e2ce5d68618a74fbd9cf39abf2ccb8f9f954783 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Fri, 7 Apr 2017 16:00:50 +0200 Subject: [PATCH 26/26] Remove opApply from containers opApply requires additional overloads for the const containers (with a const delegate). If using a templated opApply foreach cannot infer the types for the variables. foreach with one argument still works (opIndex() is used), for more complex cases slicing should be used. --- source/tanya/container/list.d | 76 ++++------------- source/tanya/container/vector.d | 145 +++++++------------------------- 2 files changed, 48 insertions(+), 173 deletions(-) diff --git a/source/tanya/container/list.d b/source/tanya/container/list.d index 7f3a616..adbd4fd 100644 --- a/source/tanya/container/list.d +++ b/source/tanya/container/list.d @@ -704,64 +704,6 @@ struct SList(T) assert(l1 == l2); } - /** - * $(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; - - for (auto pos = head; pos; pos = pos.next, ++i) - { - result = dg(i, pos.content); - - if (result != 0) - { - return result; - } - } - return result; - } - - /// Ditto. - int opApply(scope int delegate(ref T) @nogc dg) - { - int result; - - for (auto pos = head; pos; pos = pos.next) - { - result = dg(pos.content); - - if (result != 0) - { - return result; - } - } - return result; - } - - /// - @nogc 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); - } - } - /** * Returns: Range that iterates over all elements of the container, in * forward order. @@ -901,3 +843,21 @@ struct SList(T) } static assert(is(SList!Stuff)); } + +// foreach called using opIndex(). +private @nogc @safe unittest +{ + 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; + } +} diff --git a/source/tanya/container/vector.d b/source/tanya/container/vector.d index 7a0caa4..7f56bf0 100644 --- a/source/tanya/container/vector.d +++ b/source/tanya/container/vector.d @@ -1161,120 +1161,6 @@ struct Vector(T) 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 = this.data + length - 1; - for (T* begin = this.data; 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, *(this.data + i)); - - if (result != 0) - { - return result; - } - } - return 0; - } - - /// Ditto. - int opApplyReverse(scope int delegate(ref T) dg) - { - for (T* end = this.data + length - 1; this.data != 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, *(this.data + 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. * @@ -1730,7 +1616,7 @@ unittest auto v = Vector!SWithDtor(); // Destructor can destroy empty vectors. } -unittest +private unittest { class A { @@ -1738,3 +1624,32 @@ unittest 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); + } +}