From 40857e69b770333de8c5d26ebe04e604860c1869 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sun, 18 Dec 2016 18:48:25 +0100 Subject: [PATCH] Add capacity capabilities to the vector --- source/tanya/async/event/epoll.d | 2 +- source/tanya/async/event/kqueue.d | 4 +- source/tanya/async/event/selector.d | 2 +- source/tanya/container/vector.d | 471 ++++++++++++++++++---------- source/tanya/math/mp.d | 186 +++++------ source/tanya/math/package.d | 2 +- source/tanya/memory/allocator.d | 33 +- source/tanya/memory/mmappool.d | 14 +- source/tanya/memory/package.d | 82 ++--- source/tanya/network/socket.d | 11 +- source/tanya/traits.d | 34 ++ 11 files changed, 500 insertions(+), 341 deletions(-) diff --git a/source/tanya/async/event/epoll.d b/source/tanya/async/event/epoll.d index 28af1c9..b48fc37 100644 --- a/source/tanya/async/event/epoll.d +++ b/source/tanya/async/event/epoll.d @@ -48,7 +48,7 @@ class EpollLoop : SelectorLoop throw MmapPool.instance.make!BadLoopException("epoll initialization failed"); } super(); - events = MmapPool.instance.makeArray!epoll_event(maxEvents); + MmapPool.instance.resizeArray(events, maxEvents); } /** diff --git a/source/tanya/async/event/kqueue.d b/source/tanya/async/event/kqueue.d index 85c9a2a..cf7f3c1 100644 --- a/source/tanya/async/event/kqueue.d +++ b/source/tanya/async/event/kqueue.d @@ -165,8 +165,8 @@ class KqueueLoop : SelectorLoop { throw MmapPool.instance.make!BadLoopException("epoll initialization failed"); } - events = MmapPool.instance.makeArray!kevent_t(64); - changes = MmapPool.instance.makeArray!kevent_t(64); + MmapPool.instance.resizeArray(events, 64); + MmapPool.instance.resizeArray(changes, 64); } /** diff --git a/source/tanya/async/event/selector.d b/source/tanya/async/event/selector.d index b023f50..85f8298 100644 --- a/source/tanya/async/event/selector.d +++ b/source/tanya/async/event/selector.d @@ -116,7 +116,7 @@ abstract class SelectorLoop : Loop this() @nogc { super(); - connections = MmapPool.instance.makeArray!ConnectionWatcher(maxEvents); + MmapPool.instance.resizeArray(connections, maxEvents); } ~this() @nogc diff --git a/source/tanya/container/vector.d b/source/tanya/container/vector.d index 695fae9..699aa89 100644 --- a/source/tanya/container/vector.d +++ b/source/tanya/container/vector.d @@ -10,11 +10,23 @@ */ module tanya.container.vector; +import core.exception; import std.algorithm.comparison; import std.range.primitives; import std.traits; import tanya.memory; +version (unittest) +{ + import tanya.traits; + struct TestA + { + ~this() @nogc + { + } + } +} + /** * One dimensional array. * @@ -26,11 +38,13 @@ template Vector(T) /** * Defines the container's primary range. */ - struct Range + struct Range(V) { - private Vector* data; + private alias E = typeof(data.vector[0]); - private @property ref inout(Vector) outer() inout return + private V* data; + + private @property ref inout(V) outer() inout return { return *data; } @@ -40,10 +54,15 @@ template Vector(T) invariant { assert(start <= end); - assert(start == 0 || end > 0); } - protected this(ref Vector data, in size_t a, in size_t b) + private this(ref inout V data, in size_t a, in size_t b) inout + in + { + assert(a <= b); + assert(b <= data.length); + } + body { this.data = &data; start = a; @@ -67,22 +86,12 @@ template Vector(T) alias opDollar = length; - @property ref inout(T) front() inout - in - { - assert(!empty); - } - body + @property ref inout(E) front() inout { return outer[start]; } - @property ref inout(T) back() inout - in - { - assert(!empty); - } - body + @property ref inout(E) back() inout { return outer[end - 1]; } @@ -107,12 +116,7 @@ template Vector(T) --end; } - ref inout(T) opIndex(in size_t i) inout - in - { - assert(start + i < end); - } - body + ref inout(E) opIndex(in size_t i) inout { return outer[start + i]; } @@ -133,60 +137,24 @@ template Vector(T) return typeof(return)(outer, start + i, start + j); } - Range opIndex() + static if (isMutable!V) { - return typeof(return)(outer, start, end); - } - - Range opSlice(in size_t i, in size_t j) - in - { - assert(i <= j); - assert(start + j <= end); - } - body - { - return typeof(return)(outer, start + i, start + j); - } - - static if (isMutable!Vector) - { - Range opIndexAssign(in T value) - in - { - assert(end <= outer.length); - } - body + Range opIndexAssign(T value) { return outer[start .. end] = value; } - Range opSliceAssign(in T value, in size_t i, in size_t j) - in - { - assert(start + j <= end); - } - body + Range opSliceAssign(T value, in size_t i, in size_t j) { return outer[start + i .. start + j] = value; } - Range opSliceAssign(in Range value, in size_t i, in size_t j) - in - { - assert(length == value.length); - } - body + Range opSliceAssign(Range value, in size_t i, in size_t j) { return outer[start + i .. start + j] = value; } - Range opSliceAssign(in T[] value, in size_t i, in size_t j) - in - { - assert(j - i == value.length); - } - body + Range opSliceAssign(T[] value, in size_t i, in size_t j) { return outer[start + i .. start + j] = value; } @@ -205,49 +173,92 @@ template Vector(T) /// Internal representation. private T[] vector; - /// The allocator. - private shared Allocator allocator; - - /** - * Creates an empty $(D_PSYMBOL Vector). - * - * Params: - * allocator = The allocator should be used for the element - * allocations. - */ - this(shared Allocator allocator) - { - this.allocator = allocator; - } - /** * Creates a new $(D_PSYMBOL Vector). * * Params: - * U = Variadic template for the constructor parameters. - * params = Values to initialize the array with. The last parameter can - * be an allocator, if not, $(D_PSYMBOL defaultAllocator) is used. + * U = Type of the static array with the initial elements. + * params = Values to initialize the array with. Use $(D_PSYMBOL IL) + * to generate a list. + * allocator = Allocator. */ - this(U...)(U params) + this(U)(U init, shared Allocator allocator = defaultAllocator) + if (isStaticArray!U) + in { - static if (isImplicitlyConvertible!(typeof(params[$ - 1]), Allocator)) + static assert(init.length > 0); + } + body + { + this(allocator); + vector = cast(T[]) allocator.allocate(init.length * T.sizeof); + if (vector is null) { - allocator = params[$ - 1]; - auto values = params[0 .. $ - 1]; - } - else - { - allocator = defaultAllocator; - alias values = params; + onOutOfMemoryError(); } + vector[0 .. $] = init[0 .. $]; + length_ = init.length; + } - resizeArray!T(allocator, vector, values.length); - length_ = values.length; - - foreach (i, v; values) + /// Ditto. + this(U)(U init, shared Allocator allocator = defaultAllocator) const + if (isStaticArray!U) + in + { + static assert(init.length > 0); + } + body + { + allocator_ = cast(const shared Allocator) allocator; + auto buf = cast(T[]) allocator.allocate(init.length * T.sizeof); + if (buf is null) { - vector[i] = v; + onOutOfMemoryError(); } + buf[0 .. $] = init[0 .. $]; + vector = cast(const (T[])) buf; + length_ = init.length; + } + + /// Ditto. + this(U)(U init, shared Allocator allocator = defaultAllocator) immutable + if (isStaticArray!U) + in + { + static assert(init.length > 0); + } + body + { + allocator_ = cast(immutable Allocator) allocator; + auto buf = cast(T[]) allocator.allocate(init.length * T.sizeof); + if (buf is null) + { + onOutOfMemoryError(); + } + buf[0 .. $] = init[0 .. $]; + vector = cast(immutable(T[])) buf; + length_ = init.length; + } + + /// Ditto. + this(shared Allocator allocator) + in + { + assert(allocator !is null); + } + body + { + allocator_ = allocator; + } + + /// + unittest + { + auto v = Vector!int(IL(3, 8, 2)); + + assert(v.capacity == 3); + assert(v.length == 3); + assert(v[0] == 3 && v[1] == 8 && v[2] == 2); } /** @@ -255,11 +266,7 @@ template Vector(T) */ ~this() { - if (allocator is null) - { - allocator = defaultAllocator; - } - dispose(allocator, vector); + allocator.dispose(vector); } /** @@ -267,15 +274,16 @@ template Vector(T) */ void clear() { - length_ = 0; + length = 0; } /// unittest { - auto v = defaultAllocator.make!(Vector!int)(18, 20, 15); + auto v = Vector!int(IL(18, 20, 15)); v.clear(); assert(v.length == 0); + assert(v.capacity == 3); } /** @@ -295,37 +303,7 @@ template Vector(T) } /// Ditto. - size_t opDollar() inout const - { - return length; - } - - /** - * Reserves space for $(D_PARAM n) elements. - */ - void reserve(in size_t n) - { - if (allocator is null) - { - allocator = defaultAllocator; - } - if (vector.length < n) - { - allocator.resizeArray!T(vector, n); - } - } - - /// - unittest - { - Vector!int v; - assert(v.capacity == 0); - assert(v.length == 0); - - v.reserve(3); - assert(v.capacity == 3); - assert(v.length == 0); - } + alias opDollar = length; /** * Expands/shrinks the vector. @@ -335,7 +313,25 @@ template Vector(T) */ @property void length(in size_t len) { - reserve(len); + if (len > length) + { + reserve(len); + vector[length .. len] = T.init; + } + else if (len < length) + { + static if (hasElaborateDestructor!T) + { + foreach (ref e; vector[len - 1 .. length_]) + { + destroy(e); + } + } + } + else + { + return; + } length_ = len; } @@ -352,11 +348,82 @@ template Vector(T) 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. + * + * Params: + * size = Desired size. + */ + void reserve(in size_t size) @trusted + { + if (vector.length < size) + { + void[] buf = vector; + allocator.reallocate(buf, size * T.sizeof); + vector = cast(T[]) buf; + } + } + + /// + 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 + { + auto n = max(length, size); + void[] buf = vector; + allocator.reallocate(buf, n * T.sizeof); + vector = cast(T[]) buf; + } + + /// + 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. */ @@ -381,14 +448,7 @@ template Vector(T) { immutable toRemove = min(howMany, length); - static if (hasElaborateDestructor!T) - { - foreach (ref e; vector[$ - toRemove ..$]) - { - allocator.dispose(e); - } - } - length_ -= toRemove; + length = length - toRemove; return toRemove; } @@ -399,7 +459,7 @@ template Vector(T) /// unittest { - auto v = Vector!int(5, 18, 17); + auto v = Vector!int(IL(5, 18, 17)); assert(v.removeBack(0) == 0); assert(v.removeBack(2) == 2); @@ -412,7 +472,7 @@ template Vector(T) * * Returns: The number of elements inserted. */ - size_t insertBack(in T el) + size_t insertBack(T el) { reserve(length + 1); vector[length] = el; @@ -421,12 +481,12 @@ template Vector(T) } /// Ditto. - size_t insertBack(in Range el) + size_t insertBack(Range!Vector el) { immutable newLength = length + el.length; reserve(newLength); - vector[length .. newLength] = el.data.vector[el.start .. el.end]; + vector[length .. newLength] = el.outer.vector[el.start .. el.end]; length_ = newLength; return el.length; @@ -486,7 +546,7 @@ template Vector(T) assert(v1.capacity == 4); assert(v1[0] == 5 && v1[1] == 6 && v1[2] == 4 && v1[3] == 2); - auto v2 = Vector!int(34, 234); + auto v2 = Vector!int(IL(34, 234)); assert(v1.insertBack(v2[]) == 2); assert(v1.length == 6); assert(v1.capacity == 6); @@ -504,7 +564,7 @@ template Vector(T) * * Precondition: $(D_INLINECODE length > pos) */ - T opIndexAssign(in T value, in size_t pos) + T opIndexAssign(T value, in size_t pos) in { assert(length > pos); @@ -515,7 +575,7 @@ template Vector(T) } /// Ditto. - Range opIndexAssign(in T value) + Range!Vector opIndexAssign(T value) { vector[0 .. $] = value; return opIndex(); @@ -524,7 +584,7 @@ template Vector(T) /// unittest { - auto v1 = Vector!int(12, 1, 7); + auto v1 = Vector!int(IL(12, 1, 7)); v1[] = 3; assert(v1[0] == 3); @@ -549,7 +609,19 @@ template Vector(T) } /// Ditto. - Range opIndex() + Range!Vector opIndex() + { + return typeof(return)(this, 0, length); + } + + /// Ditto. + Range!(const Vector) opIndex() const + { + return typeof(return)(this, 0, length); + } + + /// Ditto. + Range!(immutable Vector) opIndex() immutable { return typeof(return)(this, 0, length); } @@ -557,12 +629,18 @@ template Vector(T) /// unittest { - auto v = Vector!int(6, 123, 34, 5); + const v1 = Vector!int(IL(6, 123, 34, 5)); - assert(v[0] == 6); - assert(v[1] == 123); - assert(v[2] == 34); - assert(v[3] == 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[]))); + + auto v2 = immutable Vector!int(IL(6, 123, 34, 5)); + static assert(is(typeof(v2[0]) == immutable(int))); + static assert(is(typeof(v2[]))); } /** @@ -574,13 +652,13 @@ template Vector(T) * Returns: $(D_KEYWORD true) if the vectors are equal, $(D_KEYWORD false) * otherwise. */ - bool opEquals(typeof(this) v) + bool opEquals(typeof(this) v) const { return opEquals(v); } /// Ditto. - bool opEquals(ref typeof(this) v) + bool opEquals(ref typeof(this) v) const { return vector == v.vector; } @@ -673,7 +751,7 @@ template Vector(T) /// unittest { - auto v = Vector!int(5, 15, 8); + auto v = Vector!int(IL(5, 15, 8)); size_t i; foreach (j, ref e; v) @@ -693,7 +771,7 @@ template Vector(T) /// unittest { - auto v = Vector!int(5, 15, 8); + auto v = Vector!int(IL(5, 15, 8)); size_t i; foreach_reverse (j, ref e; v) @@ -728,7 +806,7 @@ template Vector(T) /// unittest { - auto v = Vector!int(5); + auto v = Vector!int(IL(5)); assert(v.front == 5); @@ -755,7 +833,7 @@ template Vector(T) /// unittest { - auto v = Vector!int(5); + auto v = Vector!int(IL(5)); assert(v.back == 5); @@ -774,7 +852,7 @@ template Vector(T) * * Precondition: $(D_INLINECODE i <= j && j <= length) */ - Range opSlice(in size_t i, in size_t j) + Range!Vector opSlice(in size_t i, in size_t j) in { assert(i <= j); @@ -798,7 +876,7 @@ template Vector(T) * Precondition: $(D_INLINECODE i <= j && j <= length); * The lenghts of the ranges and slices match. */ - Range opSliceAssign(in T value, in size_t i, in size_t j) + Range!Vector opSliceAssign(T value, in size_t i, in size_t j) in { assert(i <= j); @@ -811,7 +889,7 @@ template Vector(T) } /// Ditto. - Range opSliceAssign(in Range value, in size_t i, in size_t j) + Range!Vector opSliceAssign(Range!Vector value, in size_t i, in size_t j) in { assert(j - i == value.length); @@ -823,7 +901,7 @@ template Vector(T) } /// Ditto. - Range opSliceAssign(in T[] value, in size_t i, in size_t j) + Range!Vector opSliceAssign(T[] value, in size_t i, in size_t j) in { assert(j - i == value.length); @@ -837,8 +915,8 @@ template Vector(T) /// unittest { - auto v1 = Vector!int(3, 3, 3); - auto v2 = Vector!int(1, 2); + auto v1 = Vector!int(IL(3, 3, 3)); + auto v2 = Vector!int(IL(1, 2)); v1[0 .. 2] = 286; assert(v1[0] == 286); @@ -849,20 +927,69 @@ template Vector(T) assert(v2[0] == 286); assert(v2[1] == 3); } + + mixin DefaultAllocator; } } /// unittest { - auto v = Vector!int(5, 15, 8); + auto v = Vector!int(IL(5, 15, 8)); assert(v.front == 5); assert(v[1] == 15); assert(v.back == 8); } -private unittest +private @nogc unittest { -// const Vector!int v; + // Test the destructor can be called at the end of the scope. + auto a = Vector!A(); + + // Test that structs can be members of the vector. + static assert(is(typeof(Vector!TestA()))); + static assert(is(typeof(immutable Vector!TestA(IL(TestA()))))); + static assert(is(typeof(const Vector!TestA(IL(TestA()))))); +} + +private @nogc unittest +{ + const v1 = Vector!int(); + const Vector!int v2; + const v3 = Vector!int(IL(1, 5, 8)); + static assert(is(typeof(v3.vector) == const(int[]))); + static assert(is(typeof(v3.vector[0]) == const(int))); + + immutable v4 = immutable Vector!int(); + immutable v5 = immutable Vector!int(IL(2, 5, 8)); + static assert(is(typeof(v4.vector) == immutable(int[]))); + static assert(is(typeof(v4.vector[0]) == immutable(int))); +} + +private @nogc unittest +{ + // Test that immutable/const vectors return usable ranges. + auto v = immutable Vector!int(IL(1, 2, 4)); + auto r = v[]; + + assert(r.back == 4); + r.popBack(); + assert(r.back == 2); + r.popBack(); + assert(r.back == 1); + r.popBack(); +} + +private @nogc unittest +{ + Vector!int v1; + const Vector!int v2; + + auto r1 = v1[]; + auto r2 = v1[]; + + assert(r1.length == 0); + assert(r2.empty); + assert(r1 == r2); } diff --git a/source/tanya/math/mp.d b/source/tanya/math/mp.d index fafe077..1dd9075 100644 --- a/source/tanya/math/mp.d +++ b/source/tanya/math/mp.d @@ -26,12 +26,6 @@ struct Integer { private ubyte[] rep; private bool sign; - private shared Allocator allocator; - - pure nothrow @safe @nogc invariant - { - assert(rep.length || !sign, "0 should be positive."); - } /** * Creates a multiple precision integer. @@ -52,25 +46,29 @@ struct Integer { assignInt(value); } - else + else if (value.length > 0) { rep = () @trusted { - return cast(ubyte[]) allocator.allocate(value.length); + return cast(ubyte[]) allocator_.allocate(value.length); }(); + if (rep is null) + { + onOutOfMemoryError(); + } value.rep.copy(rep); sign = value.sign; } } /// Ditto. - this(shared Allocator allocator) nothrow @safe @nogc + this(shared Allocator allocator) pure nothrow @safe @nogc in { assert(allocator !is null); } body { - this.allocator = allocator; + allocator_ = allocator; } private @nogc unittest @@ -86,22 +84,12 @@ struct Integer assert(h2.sign); } + /** + * Destroys the internal representation. + */ ~this() nothrow @safe @nogc - in { - assert(allocator !is null || !rep.length); - } - body - { - if (allocator !is null) - { - allocator.dispose(rep); - } - } - - private @nogc unittest - { - Integer h; // allocator isn't set, but the destructor should work + allocator.dispose(rep); } /* @@ -113,7 +101,6 @@ struct Integer in { static assert(isIntegral!T); - assert(allocator !is null); } body { @@ -169,7 +156,6 @@ struct Integer ref Integer opAssign(T)(in auto ref T value) nothrow @safe @nogc if (isIntegral!T || is(T == Integer)) { - initialize(); static if (isIntegral!T) { assignInt(value); @@ -215,10 +201,10 @@ struct Integer * * Returns: Whether the two integers are equal. */ - bool opEquals(in Integer h) const nothrow @safe @nogc - { - return rep == h.rep; - } + bool opEquals(in Integer h) const nothrow @safe @nogc + { + return rep == h.rep; + } /// Ditto. bool opEquals(in ref Integer h) const nothrow @safe @nogc @@ -235,52 +221,52 @@ struct Integer 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; - } + * + * 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 - { + int opCmp(in Integer h) const nothrow @safe @nogc + { return opCmp(h); } /// - unittest - { + unittest + { auto h1 = Integer(1019); auto h2 = Integer(1019); assert(h1 == h2); @@ -290,18 +276,24 @@ struct Integer h2 = 688; assert(h1 > h2); - } + } - private void add(in ref ubyte[] h) nothrow @safe @nogc + private void add(in ref ubyte[] h) nothrow @trusted @nogc { uint sum; uint carry = 0; + ubyte[] tmp; if (h.length > length) { - auto tmp = allocator.makeArray!ubyte(h.length); + tmp = cast(ubyte[]) allocator.allocate(h.length); + if (tmp is null) + { + onOutOfMemoryError(); + } + tmp[0 .. h.length] = 0; tmp[h.length - length .. $] = rep[0 .. length]; - rep = tmp; + swap(rep, tmp); } auto i = length; @@ -327,15 +319,16 @@ struct Integer if (carry) { // Still overflowed; allocate more space - auto tmp = allocator.makeArray!ubyte(length + 1); - tmp[1 .. $] = rep[0..length]; + void[]* vtmp = cast(void[]*) &tmp; + allocator.reallocate(*vtmp, length + 1); + tmp[1 .. $] = rep[0 .. length]; tmp[0] = 0x01; - rep = tmp; + swap(rep, tmp); } - + allocator.deallocate(tmp); } - private void subtract(in ref ubyte[] h) nothrow @safe @nogc + private void subtract(in ref ubyte[] h) nothrow @trusted @nogc { auto i = rep.length; auto j = h.length; @@ -369,13 +362,14 @@ struct Integer immutable offset = rep.countUntil!((const ref a) => a != 0); if (offset > 0) { - ubyte[] tmp = allocator.makeArray!ubyte(rep.length - offset); + ubyte[] tmp = cast(ubyte[]) allocator.allocate(rep.length - offset); rep[offset .. $].copy(tmp); + allocator.deallocate(rep); rep = tmp; } else if (offset == -1) { - allocator.resizeArray(rep, 0); + allocator.dispose(rep); } } @@ -392,11 +386,11 @@ struct Integer if ((op == "+") || (op == "-")) out { + assert(rep.length || !sign, "0 should be positive."); assert(!rep.length || rep[0]); } body { - initialize(); static if (op == "+") { if (h.sign == sign) @@ -505,6 +499,7 @@ struct Integer if (op == "*") out { + assert(rep.length || !sign, "0 should be positive."); assert(!rep.length || rep[0]); } body @@ -550,8 +545,6 @@ struct Integer } body { - initialize(); - auto divisor = Integer(h, allocator); size_t bitSize; @@ -562,7 +555,9 @@ struct Integer } static if (op == "/") { - auto quotient = allocator.makeArray!ubyte(bitSize / 8 + 1); + auto quotient = (() @trusted => + cast(ubyte[]) allocator.allocate(bitSize / 8 + 1) + )(); } // "bitPosition" keeps track of which bit, of the quotient, @@ -592,8 +587,8 @@ struct Integer static if (op == "/") { - swap(rep, quotient); - allocator.dispose(quotient); + () @trusted { allocator.deallocate(rep); }(); + rep = quotient; sign = sign == h.sign ? false : true; } return this; @@ -628,6 +623,7 @@ struct Integer if (op == "^^") out { + assert(rep.length || !sign, "0 should be positive."); assert(!rep.length || rep[0]); } body @@ -683,7 +679,6 @@ struct Integer Integer opUnary(string op)() nothrow @safe @nogc if ((op == "+") || (op == "-") || (op == "~")) { - initialize(); auto h = Integer(this, allocator); static if (op == "-") { @@ -772,12 +767,11 @@ struct Integer if ((op == "++") || (op == "--")) out { + assert(rep.length || !sign, "0 should be positive."); assert(!rep.length || rep[0]); } body { - initialize(); - static if (op == "++") { if (sign) @@ -848,14 +842,6 @@ struct Integer assert(h.rep[0] == 0x01); } - private void initialize() nothrow @safe @nogc - { - if (allocator is null) - { - allocator = defaultAllocator; - } - } - /** * Casting. * @@ -917,13 +903,13 @@ struct Integer if (op == ">>") out { + assert(rep.length || !sign, "0 should be positive."); assert(!rep.length || rep[0]); } body { immutable step = n / 8; - initialize(); if (step >= rep.length) { allocator.resizeArray(rep, 0); @@ -994,6 +980,7 @@ struct Integer if (op == "<<") out { + assert(rep.length || !sign, "0 should be positive."); assert(!rep.length || rep[0]); } body @@ -1004,7 +991,6 @@ struct Integer immutable bit = n % 8; immutable delta = 8 - bit; - initialize(); if (cast(ubyte) (rep[0] >> delta)) { allocator.resizeArray(rep, i + n / 8 + 1); @@ -1043,7 +1029,6 @@ struct Integer if (op == "<<" || op == ">>" || op == "+" || op == "-" || op == "/" || op == "*" || op == "^^" || op == "%") { - initialize(); auto ret = Integer(this, allocator); mixin("ret " ~ op ~ "= n;"); return ret; @@ -1065,9 +1050,10 @@ struct Integer if (op == "+" || op == "-" || op == "/" || op == "*" || op == "^^" || op == "%") { - initialize(); auto ret = Integer(this, allocator); mixin("ret " ~ op ~ "= h;"); return ret; } + + mixin DefaultAllocator; } diff --git a/source/tanya/math/package.d b/source/tanya/math/package.d index 9eb5ed4..e6cc4f9 100644 --- a/source/tanya/math/package.d +++ b/source/tanya/math/package.d @@ -103,7 +103,7 @@ bool isPseudoprime(ulong x) nothrow pure @safe @nogc unittest { uint[30] known = [74623, 74653, 74687, 74699, 74707, 74713, 74717, 74719, - 74843, 74747, 74759, 74761, 74771, 74779, 74797, 74821, + 74843, 74747, 74759, 74761, 74771, 74779, 74797, 74821, 74827, 9973, 104729, 15485867, 49979693, 104395303, 593441861, 104729, 15485867, 49979693, 104395303, 593441861, 899809363, 982451653]; diff --git a/source/tanya/memory/allocator.d b/source/tanya/memory/allocator.d index fb86818..c9b7386 100644 --- a/source/tanya/memory/allocator.d +++ b/source/tanya/memory/allocator.d @@ -28,7 +28,7 @@ interface Allocator * * Returns: Pointer to the new allocated memory. */ - void[] allocate(size_t size) shared nothrow @safe @nogc; + void[] allocate(size_t size) shared nothrow @nogc; /** * Deallocates a memory block. @@ -38,7 +38,7 @@ interface Allocator * * Returns: Whether the deallocation was successful. */ - bool deallocate(void[] p) shared nothrow @safe @nogc; + bool deallocate(void[] p) shared nothrow @nogc; /** * Increases or decreases the size of a memory block. @@ -49,5 +49,32 @@ interface Allocator * * Returns: Pointer to the allocated memory. */ - bool reallocate(ref void[] p, size_t size) shared nothrow @safe @nogc; + bool reallocate(ref void[] p, size_t size) shared nothrow @nogc; +} + +/** + * The mixin generates common methods for classes and structs using + * allocators. It provides a protected member and a read-only property, + * that checks if an allocator was already set and sets it to the default + * one, if not (useful for structs which don't have a default constructor). + */ +mixin template DefaultAllocator() +{ + /// Allocator. + protected shared 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. + */ + @property shared(Allocator) allocator() nothrow @safe @nogc + { + if (allocator_ is null) + { + allocator_ = defaultAllocator; + } + return allocator_; + } } diff --git a/source/tanya/memory/mmappool.d b/source/tanya/memory/mmappool.d index e915659..788407e 100644 --- a/source/tanya/memory/mmappool.d +++ b/source/tanya/memory/mmappool.d @@ -73,7 +73,7 @@ final class MmapPool : Allocator * * Returns: Pointer to the new allocated memory. */ - void[] allocate(size_t size) shared nothrow @trusted + void[] allocate(size_t size) shared nothrow { if (!size) { @@ -91,7 +91,7 @@ final class MmapPool : Allocator } /// - @safe nothrow unittest + nothrow unittest { auto p = MmapPool.instance.allocate(20); @@ -167,7 +167,7 @@ final class MmapPool : Allocator * * Returns: Whether the deallocation was successful. */ - bool deallocate(void[] p) shared nothrow @trusted + bool deallocate(void[] p) shared nothrow { if (p is null) { @@ -207,7 +207,7 @@ final class MmapPool : Allocator } /// - @safe nothrow unittest + nothrow unittest { auto p = MmapPool.instance.allocate(20); @@ -223,7 +223,7 @@ final class MmapPool : Allocator * * Returns: Whether the reallocation was successful. */ - bool reallocate(ref void[] p, size_t size) shared nothrow @trusted + bool reallocate(ref void[] p, size_t size) shared nothrow { void[] reallocP; @@ -291,7 +291,7 @@ final class MmapPool : Allocator * * Returns: Global $(D_PSYMBOL MmapPool) instance. */ - static @property ref shared(MmapPool) instance() nothrow @trusted + static @property ref shared(MmapPool) instance() nothrow { if (instance_ is null) { @@ -310,7 +310,7 @@ final class MmapPool : Allocator } /// - @safe nothrow unittest + nothrow unittest { assert(instance is instance); } diff --git a/source/tanya/memory/package.d b/source/tanya/memory/package.d index a19796e..e7564c4 100644 --- a/source/tanya/memory/package.d +++ b/source/tanya/memory/package.d @@ -20,7 +20,7 @@ private extern (C) void _d_monitordelete(Object h, bool det) nothrow @nogc; shared Allocator allocator; -shared static this() nothrow @safe @nogc +shared static this() nothrow @trusted @nogc { import tanya.memory.mmappool; allocator = MmapPool.instance; @@ -75,17 +75,20 @@ bool resizeArray(T)(shared Allocator allocator, void[] buf = array; immutable oldLength = array.length; - if (!allocator.reallocate(buf, length * T.sizeof)) - { - return false; - } - // Casting from void[] is unsafe, but we know we cast to the original type - array = () @trusted { return cast(T[]) buf; }(); - if (oldLength < length) + auto result = () @trusted { + if (!allocator.reallocate(buf, length * T.sizeof)) + { + return false; + } + // Casting from void[] is unsafe, but we know we cast to the original type. + array = cast(T[]) buf; + return true; + }(); + if (result && oldLength < length) { array[oldLength .. $] = init; } - return true; + return result; } /// @@ -106,26 +109,6 @@ unittest assert(p is null); } -private void deStruct(T)(ref T s) - if (is(T == struct)) -{ - static if (__traits(hasMember, T, "__xdtor") - && __traits(isSame, T, __traits(parent, s.__xdtor))) - { - s.__xdtor(); - } - auto buf = (cast(ubyte*) &s)[0 .. T.sizeof]; - auto init = cast(ubyte[])typeid(T).initializer(); - if (init.ptr is null) // null ptr means initialize to 0s - { - buf[] = 0; - } - else - { - buf[] = init[]; - } -} - /** * Destroys and deallocates $(D_PARAM p) of type $(D_PARAM T). * It is assumed the respective entities had been allocated with the same @@ -136,17 +119,18 @@ private void deStruct(T)(ref T s) * allocator = Allocator the $(D_PARAM p) was allocated with. * p = Object or array to be destroyed. */ -void dispose(T)(shared Allocator allocator, T* p) +void dispose(T)(shared Allocator allocator, auto ref T* p) { - static if (hasElaborateDestructor!T) - { - deStruct(*p); - } - allocator.deallocate((cast(void*) p)[0 .. T.sizeof]); + static if (hasElaborateDestructor!T) + { + destroy(*p); + } + () @trusted { allocator.deallocate((cast(void*) p)[0 .. T.sizeof]); }(); + p = null; } /// Ditto. -void dispose(T)(shared Allocator allocator, T p) +void dispose(T)(shared Allocator allocator, auto ref T p) if (is(T == class) || is(T == interface)) { if (p is null) @@ -172,7 +156,8 @@ void dispose(T)(shared Allocator allocator, T p) auto support = ptr[0 .. typeid(ob).initializer.length]; scope (success) { - allocator.deallocate(support); + () @trusted { allocator.deallocate(support); }(); + p = null; } auto ppv = cast(void**) ptr; @@ -193,7 +178,7 @@ void dispose(T)(shared Allocator allocator, T p) // shouldn't throw and if it does, it is an error anyway. if (c.destructor) { - (cast(void function (Object) nothrow @nogc) c.destructor)(ob); + (cast(void function (Object) nothrow @safe @nogc) c.destructor)(ob); } } while ((c = c.base) !is null); @@ -202,19 +187,18 @@ void dispose(T)(shared Allocator allocator, T p) { _d_monitordelete(cast(Object) ptr, true); } - auto w = (*pc).initializer; - ptr[0 .. w.length] = w[]; } /// Ditto. -void dispose(T)(shared Allocator allocator, T[] array) +void dispose(T)(shared Allocator allocator, auto ref T[] array) { - static if (hasElaborateDestructor!(typeof(array[0]))) - { - foreach (ref e; array) - { - deStruct(e); - } - } - allocator.deallocate(array); + static if (hasElaborateDestructor!(typeof(array[0]))) + { + foreach (ref e; array) + { + destroy(e); + } + } + () @trusted { allocator.deallocate(array); }(); + array = null; } diff --git a/source/tanya/network/socket.d b/source/tanya/network/socket.d index a63b231..281a4d2 100644 --- a/source/tanya/network/socket.d +++ b/source/tanya/network/socket.d @@ -425,9 +425,10 @@ else version (Windows) DWORD dwBytes; overlapped.handle = cast(HANDLE) socket; overlapped.event = OverlappedSocketEvent.accept; - overlapped.buffer.len = (sockaddr_in.sizeof + 16) * 2; - overlapped.buffer.buf = makeArray!char(defaultAllocator, - overlapped.buffer.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_, @@ -1267,12 +1268,12 @@ class InternetAddress : Address port_ = port; // Make C-string from host. - char[] node = defaultAllocator.makeArray!char(host.length + 1); + auto node = cast(char[]) allocator.allocate(host.length + 1); node[0.. $ - 1] = host; node[$ - 1] = '\0'; scope (exit) { - defaultAllocator.dispose(node); + allocator.deallocate(node); } // Convert port to a C-string. diff --git a/source/tanya/traits.d b/source/tanya/traits.d index 74e18be..3f7dfcf 100644 --- a/source/tanya/traits.d +++ b/source/tanya/traits.d @@ -11,6 +11,7 @@ module tanya.traits; import std.traits; +import std.meta; /** * Params: @@ -21,3 +22,36 @@ import std.traits; */ enum bool isReference(T) = isDynamicArray!T || isPointer!T || is(T == class) || is(T == interface); + +/** + * Initializer list. + * + * Generates a static array with elements from $(D_PARAM args). All elements + * should have the same type. It can be used in constructors which accept a + * list of the elements of the same type in the situations where variadic + * functions and templates can't be used. + * + * Params: + * Args = Argument type. + * args = Arguments. + */ +static enum IL(Args...)(Args args) + if (Args.length > 0) +{ + alias BaseType = typeof(args[0]); + + BaseType[args.length] result; + + foreach (i, a; args) + { + static assert(isImplicitlyConvertible!(typeof(a), BaseType)); + result[i] = a; + } + return result; +} + +/// +unittest +{ + static assert(IL(1, 5, 8).length == 3); +}