Add capacity capabilities to the vector

This commit is contained in:
Eugen Wissner 2016-12-18 18:48:25 +01:00
parent c1fb89af99
commit 40857e69b7
11 changed files with 500 additions and 341 deletions

View File

@ -48,7 +48,7 @@ class EpollLoop : SelectorLoop
throw MmapPool.instance.make!BadLoopException("epoll initialization failed"); throw MmapPool.instance.make!BadLoopException("epoll initialization failed");
} }
super(); super();
events = MmapPool.instance.makeArray!epoll_event(maxEvents); MmapPool.instance.resizeArray(events, maxEvents);
} }
/** /**

View File

@ -165,8 +165,8 @@ class KqueueLoop : SelectorLoop
{ {
throw MmapPool.instance.make!BadLoopException("epoll initialization failed"); throw MmapPool.instance.make!BadLoopException("epoll initialization failed");
} }
events = MmapPool.instance.makeArray!kevent_t(64); MmapPool.instance.resizeArray(events, 64);
changes = MmapPool.instance.makeArray!kevent_t(64); MmapPool.instance.resizeArray(changes, 64);
} }
/** /**

View File

@ -116,7 +116,7 @@ abstract class SelectorLoop : Loop
this() @nogc this() @nogc
{ {
super(); super();
connections = MmapPool.instance.makeArray!ConnectionWatcher(maxEvents); MmapPool.instance.resizeArray(connections, maxEvents);
} }
~this() @nogc ~this() @nogc

View File

@ -10,11 +10,23 @@
*/ */
module tanya.container.vector; module tanya.container.vector;
import core.exception;
import std.algorithm.comparison; import std.algorithm.comparison;
import std.range.primitives; import std.range.primitives;
import std.traits; import std.traits;
import tanya.memory; import tanya.memory;
version (unittest)
{
import tanya.traits;
struct TestA
{
~this() @nogc
{
}
}
}
/** /**
* One dimensional array. * One dimensional array.
* *
@ -26,11 +38,13 @@ template Vector(T)
/** /**
* Defines the container's primary range. * 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; return *data;
} }
@ -40,10 +54,15 @@ template Vector(T)
invariant invariant
{ {
assert(start <= end); 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; this.data = &data;
start = a; start = a;
@ -67,22 +86,12 @@ template Vector(T)
alias opDollar = length; alias opDollar = length;
@property ref inout(T) front() inout @property ref inout(E) front() inout
in
{
assert(!empty);
}
body
{ {
return outer[start]; return outer[start];
} }
@property ref inout(T) back() inout @property ref inout(E) back() inout
in
{
assert(!empty);
}
body
{ {
return outer[end - 1]; return outer[end - 1];
} }
@ -107,12 +116,7 @@ template Vector(T)
--end; --end;
} }
ref inout(T) opIndex(in size_t i) inout ref inout(E) opIndex(in size_t i) inout
in
{
assert(start + i < end);
}
body
{ {
return outer[start + i]; return outer[start + i];
} }
@ -133,60 +137,24 @@ template Vector(T)
return typeof(return)(outer, start + i, start + j); return typeof(return)(outer, start + i, start + j);
} }
Range opIndex() static if (isMutable!V)
{ {
return typeof(return)(outer, start, end); Range opIndexAssign(T value)
}
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
{ {
return outer[start .. end] = value; return outer[start .. end] = value;
} }
Range opSliceAssign(in T value, in size_t i, in size_t j) Range opSliceAssign(T value, in size_t i, in size_t j)
in
{
assert(start + j <= end);
}
body
{ {
return outer[start + i .. start + j] = value; return outer[start + i .. start + j] = value;
} }
Range opSliceAssign(in Range value, in size_t i, in size_t j) Range opSliceAssign(Range value, in size_t i, in size_t j)
in
{
assert(length == value.length);
}
body
{ {
return outer[start + i .. start + j] = value; return outer[start + i .. start + j] = value;
} }
Range opSliceAssign(in T[] value, in size_t i, in size_t j) Range opSliceAssign(T[] value, in size_t i, in size_t j)
in
{
assert(j - i == value.length);
}
body
{ {
return outer[start + i .. start + j] = value; return outer[start + i .. start + j] = value;
} }
@ -205,49 +173,92 @@ template Vector(T)
/// Internal representation. /// Internal representation.
private T[] vector; 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). * Creates a new $(D_PSYMBOL Vector).
* *
* Params: * Params:
* U = Variadic template for the constructor parameters. * U = Type of the static array with the initial elements.
* params = Values to initialize the array with. The last parameter can * params = Values to initialize the array with. Use $(D_PSYMBOL IL)
* be an allocator, if not, $(D_PSYMBOL defaultAllocator) is used. * 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]; onOutOfMemoryError();
auto values = params[0 .. $ - 1];
}
else
{
allocator = defaultAllocator;
alias values = params;
} }
vector[0 .. $] = init[0 .. $];
length_ = init.length;
}
resizeArray!T(allocator, vector, values.length); /// Ditto.
length_ = values.length; this(U)(U init, shared Allocator allocator = defaultAllocator) const
if (isStaticArray!U)
foreach (i, v; values) 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() ~this()
{ {
if (allocator is null) allocator.dispose(vector);
{
allocator = defaultAllocator;
}
dispose(allocator, vector);
} }
/** /**
@ -267,15 +274,16 @@ template Vector(T)
*/ */
void clear() void clear()
{ {
length_ = 0; length = 0;
} }
/// ///
unittest unittest
{ {
auto v = defaultAllocator.make!(Vector!int)(18, 20, 15); auto v = Vector!int(IL(18, 20, 15));
v.clear(); v.clear();
assert(v.length == 0); assert(v.length == 0);
assert(v.capacity == 3);
} }
/** /**
@ -295,37 +303,7 @@ template Vector(T)
} }
/// Ditto. /// Ditto.
size_t opDollar() inout const alias opDollar = length;
{
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);
}
/** /**
* Expands/shrinks the vector. * Expands/shrinks the vector.
@ -335,7 +313,25 @@ template Vector(T)
*/ */
@property void length(in size_t len) @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; length_ = len;
} }
@ -352,11 +348,82 @@ template Vector(T)
assert(v.length == 7); assert(v.length == 7);
assert(v.capacity == 7); assert(v.capacity == 7);
assert(v[$ - 1] == 0);
v[$ - 1] = 3;
assert(v[$ - 1] == 3);
v.length = 0; v.length = 0;
assert(v.length == 0); assert(v.length == 0);
assert(v.capacity == 7); 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. * Returns: $(D_KEYWORD true) if the vector is empty.
*/ */
@ -381,14 +448,7 @@ template Vector(T)
{ {
immutable toRemove = min(howMany, length); immutable toRemove = min(howMany, length);
static if (hasElaborateDestructor!T) length = length - toRemove;
{
foreach (ref e; vector[$ - toRemove ..$])
{
allocator.dispose(e);
}
}
length_ -= toRemove;
return toRemove; return toRemove;
} }
@ -399,7 +459,7 @@ template Vector(T)
/// ///
unittest unittest
{ {
auto v = Vector!int(5, 18, 17); auto v = Vector!int(IL(5, 18, 17));
assert(v.removeBack(0) == 0); assert(v.removeBack(0) == 0);
assert(v.removeBack(2) == 2); assert(v.removeBack(2) == 2);
@ -412,7 +472,7 @@ template Vector(T)
* *
* Returns: The number of elements inserted. * Returns: The number of elements inserted.
*/ */
size_t insertBack(in T el) size_t insertBack(T el)
{ {
reserve(length + 1); reserve(length + 1);
vector[length] = el; vector[length] = el;
@ -421,12 +481,12 @@ template Vector(T)
} }
/// Ditto. /// Ditto.
size_t insertBack(in Range el) size_t insertBack(Range!Vector el)
{ {
immutable newLength = length + el.length; immutable newLength = length + el.length;
reserve(newLength); reserve(newLength);
vector[length .. newLength] = el.data.vector[el.start .. el.end]; vector[length .. newLength] = el.outer.vector[el.start .. el.end];
length_ = newLength; length_ = newLength;
return el.length; return el.length;
@ -486,7 +546,7 @@ template Vector(T)
assert(v1.capacity == 4); assert(v1.capacity == 4);
assert(v1[0] == 5 && v1[1] == 6 && v1[2] == 4 && v1[3] == 2); 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.insertBack(v2[]) == 2);
assert(v1.length == 6); assert(v1.length == 6);
assert(v1.capacity == 6); assert(v1.capacity == 6);
@ -504,7 +564,7 @@ template Vector(T)
* *
* Precondition: $(D_INLINECODE length > pos) * Precondition: $(D_INLINECODE length > pos)
*/ */
T opIndexAssign(in T value, in size_t pos) T opIndexAssign(T value, in size_t pos)
in in
{ {
assert(length > pos); assert(length > pos);
@ -515,7 +575,7 @@ template Vector(T)
} }
/// Ditto. /// Ditto.
Range opIndexAssign(in T value) Range!Vector opIndexAssign(T value)
{ {
vector[0 .. $] = value; vector[0 .. $] = value;
return opIndex(); return opIndex();
@ -524,7 +584,7 @@ template Vector(T)
/// ///
unittest unittest
{ {
auto v1 = Vector!int(12, 1, 7); auto v1 = Vector!int(IL(12, 1, 7));
v1[] = 3; v1[] = 3;
assert(v1[0] == 3); assert(v1[0] == 3);
@ -549,7 +609,19 @@ template Vector(T)
} }
/// Ditto. /// 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); return typeof(return)(this, 0, length);
} }
@ -557,12 +629,18 @@ template Vector(T)
/// ///
unittest unittest
{ {
auto v = Vector!int(6, 123, 34, 5); const v1 = Vector!int(IL(6, 123, 34, 5));
assert(v[0] == 6); assert(v1[0] == 6);
assert(v[1] == 123); assert(v1[1] == 123);
assert(v[2] == 34); assert(v1[2] == 34);
assert(v[3] == 5); 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) * Returns: $(D_KEYWORD true) if the vectors are equal, $(D_KEYWORD false)
* otherwise. * otherwise.
*/ */
bool opEquals(typeof(this) v) bool opEquals(typeof(this) v) const
{ {
return opEquals(v); return opEquals(v);
} }
/// Ditto. /// Ditto.
bool opEquals(ref typeof(this) v) bool opEquals(ref typeof(this) v) const
{ {
return vector == v.vector; return vector == v.vector;
} }
@ -673,7 +751,7 @@ template Vector(T)
/// ///
unittest unittest
{ {
auto v = Vector!int(5, 15, 8); auto v = Vector!int(IL(5, 15, 8));
size_t i; size_t i;
foreach (j, ref e; v) foreach (j, ref e; v)
@ -693,7 +771,7 @@ template Vector(T)
/// ///
unittest unittest
{ {
auto v = Vector!int(5, 15, 8); auto v = Vector!int(IL(5, 15, 8));
size_t i; size_t i;
foreach_reverse (j, ref e; v) foreach_reverse (j, ref e; v)
@ -728,7 +806,7 @@ template Vector(T)
/// ///
unittest unittest
{ {
auto v = Vector!int(5); auto v = Vector!int(IL(5));
assert(v.front == 5); assert(v.front == 5);
@ -755,7 +833,7 @@ template Vector(T)
/// ///
unittest unittest
{ {
auto v = Vector!int(5); auto v = Vector!int(IL(5));
assert(v.back == 5); assert(v.back == 5);
@ -774,7 +852,7 @@ template Vector(T)
* *
* Precondition: $(D_INLINECODE i <= j && j <= length) * 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 in
{ {
assert(i <= j); assert(i <= j);
@ -798,7 +876,7 @@ template Vector(T)
* Precondition: $(D_INLINECODE i <= j && j <= length); * Precondition: $(D_INLINECODE i <= j && j <= length);
* The lenghts of the ranges and slices match. * 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 in
{ {
assert(i <= j); assert(i <= j);
@ -811,7 +889,7 @@ template Vector(T)
} }
/// Ditto. /// 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 in
{ {
assert(j - i == value.length); assert(j - i == value.length);
@ -823,7 +901,7 @@ template Vector(T)
} }
/// Ditto. /// 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 in
{ {
assert(j - i == value.length); assert(j - i == value.length);
@ -837,8 +915,8 @@ template Vector(T)
/// ///
unittest unittest
{ {
auto v1 = Vector!int(3, 3, 3); auto v1 = Vector!int(IL(3, 3, 3));
auto v2 = Vector!int(1, 2); auto v2 = Vector!int(IL(1, 2));
v1[0 .. 2] = 286; v1[0 .. 2] = 286;
assert(v1[0] == 286); assert(v1[0] == 286);
@ -849,20 +927,69 @@ template Vector(T)
assert(v2[0] == 286); assert(v2[0] == 286);
assert(v2[1] == 3); assert(v2[1] == 3);
} }
mixin DefaultAllocator;
} }
} }
/// ///
unittest unittest
{ {
auto v = Vector!int(5, 15, 8); auto v = Vector!int(IL(5, 15, 8));
assert(v.front == 5); assert(v.front == 5);
assert(v[1] == 15); assert(v[1] == 15);
assert(v.back == 8); 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);
} }

View File

@ -26,12 +26,6 @@ struct Integer
{ {
private ubyte[] rep; private ubyte[] rep;
private bool sign; 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. * Creates a multiple precision integer.
@ -52,25 +46,29 @@ struct Integer
{ {
assignInt(value); assignInt(value);
} }
else else if (value.length > 0)
{ {
rep = () @trusted { 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); value.rep.copy(rep);
sign = value.sign; sign = value.sign;
} }
} }
/// Ditto. /// Ditto.
this(shared Allocator allocator) nothrow @safe @nogc this(shared Allocator allocator) pure nothrow @safe @nogc
in in
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body body
{ {
this.allocator = allocator; allocator_ = allocator;
} }
private @nogc unittest private @nogc unittest
@ -86,22 +84,12 @@ struct Integer
assert(h2.sign); assert(h2.sign);
} }
/**
* Destroys the internal representation.
*/
~this() nothrow @safe @nogc ~this() nothrow @safe @nogc
in
{ {
assert(allocator !is null || !rep.length); allocator.dispose(rep);
}
body
{
if (allocator !is null)
{
allocator.dispose(rep);
}
}
private @nogc unittest
{
Integer h; // allocator isn't set, but the destructor should work
} }
/* /*
@ -113,7 +101,6 @@ struct Integer
in in
{ {
static assert(isIntegral!T); static assert(isIntegral!T);
assert(allocator !is null);
} }
body body
{ {
@ -169,7 +156,6 @@ struct Integer
ref Integer opAssign(T)(in auto ref T value) nothrow @safe @nogc ref Integer opAssign(T)(in auto ref T value) nothrow @safe @nogc
if (isIntegral!T || is(T == Integer)) if (isIntegral!T || is(T == Integer))
{ {
initialize();
static if (isIntegral!T) static if (isIntegral!T)
{ {
assignInt(value); assignInt(value);
@ -215,10 +201,10 @@ struct Integer
* *
* Returns: Whether the two integers are equal. * Returns: Whether the two integers are equal.
*/ */
bool opEquals(in Integer h) const nothrow @safe @nogc bool opEquals(in Integer h) const nothrow @safe @nogc
{ {
return rep == h.rep; return rep == h.rep;
} }
/// Ditto. /// Ditto.
bool opEquals(in ref Integer h) const nothrow @safe @nogc bool opEquals(in ref Integer h) const nothrow @safe @nogc
@ -235,52 +221,52 @@ struct Integer
assert(h1 != Integer(109)); assert(h1 != Integer(109));
} }
/** /**
* Params: * Params:
* h = The second integer. * h = The second integer.
* *
* Returns: A positive number if $(D_INLINECODE this > h), a negative * Returns: A positive number if $(D_INLINECODE this > h), a negative
* number if $(D_INLINECODE this > h), `0` otherwise. * number if $(D_INLINECODE this > h), `0` otherwise.
*/ */
int opCmp(in ref Integer h) const nothrow @safe @nogc int opCmp(in ref Integer h) const nothrow @safe @nogc
{ {
if (length > h.length) if (length > h.length)
{ {
return 1; return 1;
} }
if (length < h.length) if (length < h.length)
{ {
return -1; return -1;
} }
// Otherwise, keep searching through the representational integers // Otherwise, keep searching through the representational integers
// until one is bigger than another - once we've found one, it's // 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 // safe to stop, since the lower order bytes can't affect the
// comparison // comparison
for (size_t i, j; i < length && j < h.length; ++i, ++j) for (size_t i, j; i < length && j < h.length; ++i, ++j)
{ {
if (rep[i] < h.rep[j]) if (rep[i] < h.rep[j])
{ {
return -1; return -1;
} }
else if (rep[i] > h.rep[j]) else if (rep[i] > h.rep[j])
{ {
return 1; return 1;
} }
} }
// if we got all the way to the end without a comparison, the // if we got all the way to the end without a comparison, the
// two are equal // two are equal
return 0; return 0;
} }
/// Ditto. /// Ditto.
int opCmp(in Integer h) const nothrow @safe @nogc int opCmp(in Integer h) const nothrow @safe @nogc
{ {
return opCmp(h); return opCmp(h);
} }
/// ///
unittest unittest
{ {
auto h1 = Integer(1019); auto h1 = Integer(1019);
auto h2 = Integer(1019); auto h2 = Integer(1019);
assert(h1 == h2); assert(h1 == h2);
@ -290,18 +276,24 @@ struct Integer
h2 = 688; h2 = 688;
assert(h1 > h2); 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 sum;
uint carry = 0; uint carry = 0;
ubyte[] tmp;
if (h.length > length) 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]; tmp[h.length - length .. $] = rep[0 .. length];
rep = tmp; swap(rep, tmp);
} }
auto i = length; auto i = length;
@ -327,15 +319,16 @@ struct Integer
if (carry) if (carry)
{ {
// Still overflowed; allocate more space // Still overflowed; allocate more space
auto tmp = allocator.makeArray!ubyte(length + 1); void[]* vtmp = cast(void[]*) &tmp;
tmp[1 .. $] = rep[0..length]; allocator.reallocate(*vtmp, length + 1);
tmp[1 .. $] = rep[0 .. length];
tmp[0] = 0x01; 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 i = rep.length;
auto j = h.length; auto j = h.length;
@ -369,13 +362,14 @@ struct Integer
immutable offset = rep.countUntil!((const ref a) => a != 0); immutable offset = rep.countUntil!((const ref a) => a != 0);
if (offset > 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); rep[offset .. $].copy(tmp);
allocator.deallocate(rep);
rep = tmp; rep = tmp;
} }
else if (offset == -1) else if (offset == -1)
{ {
allocator.resizeArray(rep, 0); allocator.dispose(rep);
} }
} }
@ -392,11 +386,11 @@ struct Integer
if ((op == "+") || (op == "-")) if ((op == "+") || (op == "-"))
out out
{ {
assert(rep.length || !sign, "0 should be positive.");
assert(!rep.length || rep[0]); assert(!rep.length || rep[0]);
} }
body body
{ {
initialize();
static if (op == "+") static if (op == "+")
{ {
if (h.sign == sign) if (h.sign == sign)
@ -505,6 +499,7 @@ struct Integer
if (op == "*") if (op == "*")
out out
{ {
assert(rep.length || !sign, "0 should be positive.");
assert(!rep.length || rep[0]); assert(!rep.length || rep[0]);
} }
body body
@ -550,8 +545,6 @@ struct Integer
} }
body body
{ {
initialize();
auto divisor = Integer(h, allocator); auto divisor = Integer(h, allocator);
size_t bitSize; size_t bitSize;
@ -562,7 +555,9 @@ struct Integer
} }
static if (op == "/") 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, // "bitPosition" keeps track of which bit, of the quotient,
@ -592,8 +587,8 @@ struct Integer
static if (op == "/") static if (op == "/")
{ {
swap(rep, quotient); () @trusted { allocator.deallocate(rep); }();
allocator.dispose(quotient); rep = quotient;
sign = sign == h.sign ? false : true; sign = sign == h.sign ? false : true;
} }
return this; return this;
@ -628,6 +623,7 @@ struct Integer
if (op == "^^") if (op == "^^")
out out
{ {
assert(rep.length || !sign, "0 should be positive.");
assert(!rep.length || rep[0]); assert(!rep.length || rep[0]);
} }
body body
@ -683,7 +679,6 @@ struct Integer
Integer opUnary(string op)() nothrow @safe @nogc Integer opUnary(string op)() nothrow @safe @nogc
if ((op == "+") || (op == "-") || (op == "~")) if ((op == "+") || (op == "-") || (op == "~"))
{ {
initialize();
auto h = Integer(this, allocator); auto h = Integer(this, allocator);
static if (op == "-") static if (op == "-")
{ {
@ -772,12 +767,11 @@ struct Integer
if ((op == "++") || (op == "--")) if ((op == "++") || (op == "--"))
out out
{ {
assert(rep.length || !sign, "0 should be positive.");
assert(!rep.length || rep[0]); assert(!rep.length || rep[0]);
} }
body body
{ {
initialize();
static if (op == "++") static if (op == "++")
{ {
if (sign) if (sign)
@ -848,14 +842,6 @@ struct Integer
assert(h.rep[0] == 0x01); assert(h.rep[0] == 0x01);
} }
private void initialize() nothrow @safe @nogc
{
if (allocator is null)
{
allocator = defaultAllocator;
}
}
/** /**
* Casting. * Casting.
* *
@ -917,13 +903,13 @@ struct Integer
if (op == ">>") if (op == ">>")
out out
{ {
assert(rep.length || !sign, "0 should be positive.");
assert(!rep.length || rep[0]); assert(!rep.length || rep[0]);
} }
body body
{ {
immutable step = n / 8; immutable step = n / 8;
initialize();
if (step >= rep.length) if (step >= rep.length)
{ {
allocator.resizeArray(rep, 0); allocator.resizeArray(rep, 0);
@ -994,6 +980,7 @@ struct Integer
if (op == "<<") if (op == "<<")
out out
{ {
assert(rep.length || !sign, "0 should be positive.");
assert(!rep.length || rep[0]); assert(!rep.length || rep[0]);
} }
body body
@ -1004,7 +991,6 @@ struct Integer
immutable bit = n % 8; immutable bit = n % 8;
immutable delta = 8 - bit; immutable delta = 8 - bit;
initialize();
if (cast(ubyte) (rep[0] >> delta)) if (cast(ubyte) (rep[0] >> delta))
{ {
allocator.resizeArray(rep, i + n / 8 + 1); allocator.resizeArray(rep, i + n / 8 + 1);
@ -1043,7 +1029,6 @@ struct Integer
if (op == "<<" || op == ">>" || op == "+" || op == "-" || op == "/" if (op == "<<" || op == ">>" || op == "+" || op == "-" || op == "/"
|| op == "*" || op == "^^" || op == "%") || op == "*" || op == "^^" || op == "%")
{ {
initialize();
auto ret = Integer(this, allocator); auto ret = Integer(this, allocator);
mixin("ret " ~ op ~ "= n;"); mixin("ret " ~ op ~ "= n;");
return ret; return ret;
@ -1065,9 +1050,10 @@ struct Integer
if (op == "+" || op == "-" || op == "/" if (op == "+" || op == "-" || op == "/"
|| op == "*" || op == "^^" || op == "%") || op == "*" || op == "^^" || op == "%")
{ {
initialize();
auto ret = Integer(this, allocator); auto ret = Integer(this, allocator);
mixin("ret " ~ op ~ "= h;"); mixin("ret " ~ op ~ "= h;");
return ret; return ret;
} }
mixin DefaultAllocator;
} }

View File

@ -103,7 +103,7 @@ bool isPseudoprime(ulong x) nothrow pure @safe @nogc
unittest unittest
{ {
uint[30] known = [74623, 74653, 74687, 74699, 74707, 74713, 74717, 74719, 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, 74827, 9973, 104729, 15485867, 49979693, 104395303,
593441861, 104729, 15485867, 49979693, 104395303, 593441861, 104729, 15485867, 49979693, 104395303,
593441861, 899809363, 982451653]; 593441861, 899809363, 982451653];

View File

@ -28,7 +28,7 @@ interface Allocator
* *
* Returns: Pointer to the new allocated memory. * 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. * Deallocates a memory block.
@ -38,7 +38,7 @@ interface Allocator
* *
* Returns: Whether the deallocation was successful. * 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. * Increases or decreases the size of a memory block.
@ -49,5 +49,32 @@ interface Allocator
* *
* Returns: Pointer to the allocated memory. * 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_;
}
} }

View File

@ -73,7 +73,7 @@ final class MmapPool : Allocator
* *
* Returns: Pointer to the new allocated memory. * Returns: Pointer to the new allocated memory.
*/ */
void[] allocate(size_t size) shared nothrow @trusted void[] allocate(size_t size) shared nothrow
{ {
if (!size) if (!size)
{ {
@ -91,7 +91,7 @@ final class MmapPool : Allocator
} }
/// ///
@safe nothrow unittest nothrow unittest
{ {
auto p = MmapPool.instance.allocate(20); auto p = MmapPool.instance.allocate(20);
@ -167,7 +167,7 @@ final class MmapPool : Allocator
* *
* Returns: Whether the deallocation was successful. * Returns: Whether the deallocation was successful.
*/ */
bool deallocate(void[] p) shared nothrow @trusted bool deallocate(void[] p) shared nothrow
{ {
if (p is null) if (p is null)
{ {
@ -207,7 +207,7 @@ final class MmapPool : Allocator
} }
/// ///
@safe nothrow unittest nothrow unittest
{ {
auto p = MmapPool.instance.allocate(20); auto p = MmapPool.instance.allocate(20);
@ -223,7 +223,7 @@ final class MmapPool : Allocator
* *
* Returns: Whether the reallocation was successful. * 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; void[] reallocP;
@ -291,7 +291,7 @@ final class MmapPool : Allocator
* *
* Returns: Global $(D_PSYMBOL MmapPool) instance. * 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) if (instance_ is null)
{ {
@ -310,7 +310,7 @@ final class MmapPool : Allocator
} }
/// ///
@safe nothrow unittest nothrow unittest
{ {
assert(instance is instance); assert(instance is instance);
} }

View File

@ -20,7 +20,7 @@ private extern (C) void _d_monitordelete(Object h, bool det) nothrow @nogc;
shared Allocator allocator; shared Allocator allocator;
shared static this() nothrow @safe @nogc shared static this() nothrow @trusted @nogc
{ {
import tanya.memory.mmappool; import tanya.memory.mmappool;
allocator = MmapPool.instance; allocator = MmapPool.instance;
@ -75,17 +75,20 @@ bool resizeArray(T)(shared Allocator allocator,
void[] buf = array; void[] buf = array;
immutable oldLength = array.length; immutable oldLength = array.length;
if (!allocator.reallocate(buf, length * T.sizeof)) auto result = () @trusted {
{ if (!allocator.reallocate(buf, length * T.sizeof))
return false; {
} return false;
// Casting from void[] is unsafe, but we know we cast to the original type }
array = () @trusted { return cast(T[]) buf; }(); // Casting from void[] is unsafe, but we know we cast to the original type.
if (oldLength < length) array = cast(T[]) buf;
return true;
}();
if (result && oldLength < length)
{ {
array[oldLength .. $] = init; array[oldLength .. $] = init;
} }
return true; return result;
} }
/// ///
@ -106,26 +109,6 @@ unittest
assert(p is null); 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). * Destroys and deallocates $(D_PARAM p) of type $(D_PARAM T).
* It is assumed the respective entities had been allocated with the same * 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. * allocator = Allocator the $(D_PARAM p) was allocated with.
* p = Object or array to be destroyed. * 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) static if (hasElaborateDestructor!T)
{ {
deStruct(*p); destroy(*p);
} }
allocator.deallocate((cast(void*) p)[0 .. T.sizeof]); () @trusted { allocator.deallocate((cast(void*) p)[0 .. T.sizeof]); }();
p = null;
} }
/// Ditto. /// 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 (is(T == class) || is(T == interface))
{ {
if (p is null) if (p is null)
@ -172,7 +156,8 @@ void dispose(T)(shared Allocator allocator, T p)
auto support = ptr[0 .. typeid(ob).initializer.length]; auto support = ptr[0 .. typeid(ob).initializer.length];
scope (success) scope (success)
{ {
allocator.deallocate(support); () @trusted { allocator.deallocate(support); }();
p = null;
} }
auto ppv = cast(void**) ptr; 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. // shouldn't throw and if it does, it is an error anyway.
if (c.destructor) 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); while ((c = c.base) !is null);
@ -202,19 +187,18 @@ void dispose(T)(shared Allocator allocator, T p)
{ {
_d_monitordelete(cast(Object) ptr, true); _d_monitordelete(cast(Object) ptr, true);
} }
auto w = (*pc).initializer;
ptr[0 .. w.length] = w[];
} }
/// Ditto. /// 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]))) static if (hasElaborateDestructor!(typeof(array[0])))
{ {
foreach (ref e; array) foreach (ref e; array)
{ {
deStruct(e); destroy(e);
} }
} }
allocator.deallocate(array); () @trusted { allocator.deallocate(array); }();
array = null;
} }

View File

@ -425,9 +425,10 @@ else version (Windows)
DWORD dwBytes; DWORD dwBytes;
overlapped.handle = cast(HANDLE) socket; overlapped.handle = cast(HANDLE) socket;
overlapped.event = OverlappedSocketEvent.accept; overlapped.event = OverlappedSocketEvent.accept;
overlapped.buffer.len = (sockaddr_in.sizeof + 16) * 2;
overlapped.buffer.buf = makeArray!char(defaultAllocator, immutable len = (sockaddr_in.sizeof + 16) * 2;
overlapped.buffer.len).ptr; 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 // We don't want to get any data now, but only start to accept the connections
BOOL result = acceptExtension(handle_, BOOL result = acceptExtension(handle_,
@ -1267,12 +1268,12 @@ class InternetAddress : Address
port_ = port; port_ = port;
// Make C-string from host. // 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[0.. $ - 1] = host;
node[$ - 1] = '\0'; node[$ - 1] = '\0';
scope (exit) scope (exit)
{ {
defaultAllocator.dispose(node); allocator.deallocate(node);
} }
// Convert port to a C-string. // Convert port to a C-string.

View File

@ -11,6 +11,7 @@
module tanya.traits; module tanya.traits;
import std.traits; import std.traits;
import std.meta;
/** /**
* Params: * Params:
@ -21,3 +22,36 @@ import std.traits;
*/ */
enum bool isReference(T) = isDynamicArray!T || isPointer!T enum bool isReference(T) = isDynamicArray!T || isPointer!T
|| is(T == class) || is(T == interface); || 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);
}