Make allocator shared and fix some RefCounted bugs

This commit is contained in:
Eugen Wissner 2016-12-06 21:29:08 +01:00
parent b3fdd6fd4a
commit fa607141e4
19 changed files with 2682 additions and 2717 deletions

View File

@ -107,7 +107,7 @@ class EpollLoop : SelectorLoop
{ {
if (errno != EINTR) if (errno != EINTR)
{ {
throw theAllocator.make!BadLoopException(); throw defaultAllocator.make!BadLoopException();
} }
return; return;
} }

View File

@ -45,7 +45,7 @@ class IOCPStreamTransport : StreamTransport
body body
{ {
socket_ = socket; socket_ = socket;
input = MmapPool.instance.make!WriteBuffer(); input = MmapPool.instance.make!WriteBuffer(8192, MmapPool.instance);
} }
~this() ~this()
@ -101,7 +101,8 @@ class IOCPStreamTransport : StreamTransport
completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (!completionPort) if (!completionPort)
{ {
throw theAllocator.make!BadLoopException("Creating completion port failed"); throw make!BadLoopException(defaultAllocator,
"Creating completion port failed");
} }
} }
@ -141,7 +142,7 @@ class IOCPStreamTransport : StreamTransport
catch (SocketException e) catch (SocketException e)
{ {
MmapPool.instance.dispose(overlapped); MmapPool.instance.dispose(overlapped);
theAllocator.dispose(e); defaultAllocator.dispose(e);
return false; return false;
} }
} }
@ -173,7 +174,7 @@ class IOCPStreamTransport : StreamTransport
catch (SocketException e) catch (SocketException e)
{ {
MmapPool.instance.dispose(overlapped); MmapPool.instance.dispose(overlapped);
theAllocator.dispose(e); defaultAllocator.dispose(e);
return false; return false;
} }
} }

View File

@ -250,7 +250,7 @@ class KqueueLoop : SelectorLoop
{ {
if (errno != EINTR) if (errno != EINTR)
{ {
throw theAllocator.make!BadLoopException(); throw defaultAllocatorAllocator.make!BadLoopException();
} }
return; return;
} }

View File

@ -46,7 +46,7 @@ class SelectorStreamTransport : StreamTransport
{ {
socket_ = socket; socket_ = socket;
this.loop = loop; this.loop = loop;
input = MmapPool.instance.make!WriteBuffer(); input = MmapPool.instance.make!WriteBuffer(8192, MmapPool.instance);
} }
/** /**
@ -224,7 +224,7 @@ abstract class SelectorLoop : Loop
} }
catch (SocketException e) catch (SocketException e)
{ {
theAllocator.dispose(e); defaultAllocator.dispose(e);
break; break;
} }
if (client is null) if (client is null)

View File

@ -34,31 +34,31 @@
* *
* void main() * void main()
* { * {
* auto address = theAllocator.make!InternetAddress("127.0.0.1", cast(ushort) 8192); * auto address = defaultAllocator.make!InternetAddress("127.0.0.1", cast(ushort) 8192);
* *
* version (Windows) * version (Windows)
* { * {
* auto sock = theAllocator.make!OverlappedStreamSocket(AddressFamily.INET); * auto sock = defaultAllocator.make!OverlappedStreamSocket(AddressFamily.INET);
* } * }
* else * else
* { * {
* auto sock = theAllocator.make!StreamSocket(AddressFamily.INET); * auto sock = defaultAllocator.make!StreamSocket(AddressFamily.INET);
* sock.blocking = false; * sock.blocking = false;
* } * }
* *
* sock.bind(address); * sock.bind(address);
* sock.listen(5); * sock.listen(5);
* *
* auto io = theAllocator.make!ConnectionWatcher(sock); * auto io = defaultAllocator.make!ConnectionWatcher(sock);
* io.setProtocol!EchoProtocol; * io.setProtocol!EchoProtocol;
* *
* defaultLoop.start(io); * defaultLoop.start(io);
* defaultLoop.run(); * defaultLoop.run();
* *
* sock.shutdown(); * sock.shutdown();
* theAllocator.dispose(io); * defaultAllocator.dispose(io);
* theAllocator.dispose(sock); * defaultAllocator.dispose(sock);
* theAllocator.dispose(address); * defaultAllocator.dispose(address);
* } * }
* --- * ---
*/ */
@ -278,7 +278,7 @@ abstract class Loop
protected void kill(IOWatcher watcher, SocketException exception) protected void kill(IOWatcher watcher, SocketException exception)
{ {
watcher.socket.shutdown(); watcher.socket.shutdown();
theAllocator.dispose(watcher.socket); defaultAllocator.dispose(watcher.socket);
MmapPool.instance.dispose(watcher.transport); MmapPool.instance.dispose(watcher.transport);
watcher.exception = exception; watcher.exception = exception;
swapPendings.insertBack(watcher); swapPendings.insertBack(watcher);

View File

@ -157,7 +157,7 @@ class IOWatcher : ConnectionWatcher
super(); super();
transport_ = transport; transport_ = transport;
protocol_ = protocol; protocol_ = protocol;
output = MmapPool.instance.make!ReadBuffer(); output = MmapPool.instance.make!ReadBuffer(8192, 1024, MmapPool.instance);
active = true; active = true;
} }

View File

@ -108,30 +108,36 @@ class ReadBuffer : Buffer
/// Size by which the buffer will grow. /// Size by which the buffer will grow.
protected immutable size_t blockSize; protected immutable size_t blockSize;
/// Allocator.
protected shared Allocator allocator;
invariant invariant
{ {
assert(length_ <= buffer_.length); assert(length_ <= buffer_.length);
assert(blockSize > 0); assert(blockSize > 0);
assert(minAvailable > 0); assert(minAvailable > 0);
assert(allocator !is null);
} }
/** /**
* Creates a new read buffer. * Creates a new read buffer.
* *
* Params: * Params:
* size = Initial buffer size and the size by which the buffer * size = Initial buffer size and the size by which the buffer
* will grow. * will grow.
* minAvailable = minimal size should be always available to fill. * minAvailable = minimal size should be always available to fill.
* So it will reallocate if $(D_INLINECODE * So it will reallocate if $(D_INLINECODE
* $(D_PSYMBOL free) < $(D_PARAM minAvailable) * $(D_PSYMBOL free) < $(D_PARAM minAvailable)).
* ). * allocator = Allocator.
*/ */
this(size_t size = 8192, this(size_t size = 8192,
size_t minAvailable = 1024) size_t minAvailable = 1024,
shared Allocator allocator = defaultAllocator)
{ {
this.minAvailable = minAvailable; this.minAvailable = minAvailable;
this.blockSize = size; this.blockSize = size;
theAllocator.resizeArray!ubyte(buffer_, size); this.allocator = allocator;
allocator.resizeArray!ubyte(buffer_, size);
} }
/** /**
@ -139,17 +145,17 @@ class ReadBuffer : Buffer
*/ */
~this() ~this()
{ {
theAllocator.dispose(buffer_); allocator.dispose(buffer_);
} }
/// ///
unittest unittest
{ {
auto b = theAllocator.make!ReadBuffer; auto b = defaultAllocator.make!ReadBuffer;
assert(b.capacity == 8192); assert(b.capacity == 8192);
assert(b.length == 0); assert(b.length == 0);
theAllocator.dispose(b); defaultAllocator.dispose(b);
} }
/** /**
@ -190,7 +196,7 @@ class ReadBuffer : Buffer
/// ///
unittest unittest
{ {
auto b = theAllocator.make!ReadBuffer; auto b = defaultAllocator.make!ReadBuffer;
size_t numberRead; size_t numberRead;
// Fills the buffer with values 0..10 // Fills the buffer with values 0..10
@ -202,7 +208,7 @@ class ReadBuffer : Buffer
b.clear(); b.clear();
assert(b.free == b.blockSize); assert(b.free == b.blockSize);
theAllocator.dispose(b); defaultAllocator.dispose(b);
} }
/** /**
@ -224,7 +230,7 @@ class ReadBuffer : Buffer
/// ///
unittest unittest
{ {
auto b = theAllocator.make!ReadBuffer; auto b = defaultAllocator.make!ReadBuffer;
size_t numberRead; size_t numberRead;
ubyte[] result; ubyte[] result;
@ -252,7 +258,7 @@ class ReadBuffer : Buffer
assert(result[10] == 20); assert(result[10] == 20);
assert(result[14] == 24); assert(result[14] == 24);
theAllocator.dispose(b); defaultAllocator.dispose(b);
} }
/** /**
@ -294,7 +300,7 @@ class ReadBuffer : Buffer
{ {
if (capacity - length < minAvailable) if (capacity - length < minAvailable)
{ {
theAllocator.resizeArray!ubyte(buffer_, capacity + blockSize); allocator.resizeArray!ubyte(buffer_, capacity + blockSize);
} }
ring = length_; ring = length_;
return buffer_[length_..$]; return buffer_[length_..$];
@ -304,7 +310,7 @@ class ReadBuffer : Buffer
/// ///
unittest unittest
{ {
auto b = theAllocator.make!ReadBuffer; auto b = defaultAllocator.make!ReadBuffer;
size_t numberRead; size_t numberRead;
ubyte[] result; ubyte[] result;
@ -319,7 +325,7 @@ class ReadBuffer : Buffer
b.clear(); b.clear();
assert(b.length == 0); assert(b.length == 0);
theAllocator.dispose(b); defaultAllocator.dispose(b);
} }
} }
@ -349,23 +355,29 @@ class WriteBuffer : Buffer
/// The position of the free area in the buffer. /// The position of the free area in the buffer.
protected size_t position; protected size_t position;
/// Allocator.
protected shared Allocator allocator;
invariant invariant
{ {
assert(blockSize > 0); assert(blockSize > 0);
// position can refer to an element outside the buffer if the buffer is full. // position can refer to an element outside the buffer if the buffer is full.
assert(position <= buffer_.length); assert(position <= buffer_.length);
assert(allocator !is null);
} }
/** /**
* Params: * Params:
* size = Initial buffer size and the size by which the buffer * size = Initial buffer size and the size by which the buffer will
* will grow. * grow.
* allocator = Allocator.
*/ */
this(size_t size = 8192) this(size_t size = 8192, shared Allocator allocator = defaultAllocator)
{ {
this.allocator = allocator;
blockSize = size; blockSize = size;
ring = size - 1; ring = size - 1;
theAllocator.resizeArray!ubyte(buffer_, size); allocator.resizeArray!ubyte(buffer_, size);
} }
/** /**
@ -373,7 +385,7 @@ class WriteBuffer : Buffer
*/ */
~this() ~this()
{ {
theAllocator.dispose(buffer_); allocator.dispose(buffer_);
} }
/** /**
@ -415,7 +427,7 @@ class WriteBuffer : Buffer
/// ///
unittest unittest
{ {
auto b = theAllocator.make!WriteBuffer(4); auto b = defaultAllocator.make!WriteBuffer(4);
ubyte[3] buf = [48, 23, 255]; ubyte[3] buf = [48, 23, 255];
b ~= buf; b ~= buf;
@ -433,7 +445,7 @@ class WriteBuffer : Buffer
b += b.length; b += b.length;
assert(b.length == 0); assert(b.length == 0);
theAllocator.dispose(b); defaultAllocator.dispose(b);
} }
/** /**
@ -498,7 +510,7 @@ class WriteBuffer : Buffer
{ {
auto newSize = end / blockSize * blockSize + blockSize; auto newSize = end / blockSize * blockSize + blockSize;
theAllocator.resizeArray!ubyte(buffer_, newSize); allocator.resizeArray!ubyte(buffer_, newSize);
} }
buffer_[position..end] = buffer[start..$]; buffer_[position..end] = buffer[start..$];
position = end; position = end;
@ -514,7 +526,7 @@ class WriteBuffer : Buffer
/// ///
unittest unittest
{ {
auto b = theAllocator.make!WriteBuffer(4); auto b = defaultAllocator.make!WriteBuffer(4);
ubyte[3] buf = [48, 23, 255]; ubyte[3] buf = [48, 23, 255];
b ~= buf; b ~= buf;
@ -533,9 +545,9 @@ class WriteBuffer : Buffer
assert(b.buffer_[0] == 23 && b.buffer_[1] == 255 assert(b.buffer_[0] == 23 && b.buffer_[1] == 255
&& b.buffer_[2] == 48 && b.buffer_[3] == 23 && b.buffer_[4] == 255); && b.buffer_[2] == 48 && b.buffer_[3] == 23 && b.buffer_[4] == 255);
theAllocator.dispose(b); defaultAllocator.dispose(b);
b = make!WriteBuffer(theAllocator, 2); b = make!WriteBuffer(defaultAllocator, 2);
b ~= buf; b ~= buf;
assert(b.start == 0); assert(b.start == 0);
@ -543,7 +555,7 @@ class WriteBuffer : Buffer
assert(b.ring == 3); assert(b.ring == 3);
assert(b.position == 3); assert(b.position == 3);
theAllocator.dispose(b); defaultAllocator.dispose(b);
} }
/** /**
@ -620,7 +632,7 @@ class WriteBuffer : Buffer
/// ///
unittest unittest
{ {
auto b = theAllocator.make!WriteBuffer; auto b = defaultAllocator.make!WriteBuffer;
ubyte[6] buf = [23, 23, 255, 128, 127, 9]; ubyte[6] buf = [23, 23, 255, 128, 127, 9];
b ~= buf; b ~= buf;
@ -630,7 +642,7 @@ class WriteBuffer : Buffer
b += 4; b += 4;
assert(b.length == 0); assert(b.length == 0);
theAllocator.dispose(b); defaultAllocator.dispose(b);
} }
/** /**
@ -663,7 +675,7 @@ class WriteBuffer : Buffer
/// ///
unittest unittest
{ {
auto b = theAllocator.make!WriteBuffer(6); auto b = defaultAllocator.make!WriteBuffer(6);
ubyte[6] buf = [23, 23, 255, 128, 127, 9]; ubyte[6] buf = [23, 23, 255, 128, 127, 9];
b ~= buf; b ~= buf;
@ -679,7 +691,7 @@ class WriteBuffer : Buffer
assert(b[0..$] == buf[0..6]); assert(b[0..$] == buf[0..6]);
b += b.length; b += b.length;
theAllocator.dispose(b); defaultAllocator.dispose(b);
} }
/** /**

View File

@ -27,7 +27,7 @@ class SList(T)
* allocator = The allocator should be used for the element * allocator = The allocator should be used for the element
* allocations. * allocations.
*/ */
this(IAllocator allocator = theAllocator) this(shared Allocator allocator = defaultAllocator)
{ {
this.allocator = allocator; this.allocator = allocator;
} }
@ -54,14 +54,14 @@ class SList(T)
/// ///
unittest unittest
{ {
auto l = make!(SList!int)(theAllocator); auto l = make!(SList!int)(defaultAllocator);
l.insertFront(8); l.insertFront(8);
l.insertFront(5); l.insertFront(5);
l.clear(); l.clear();
assert(l.empty); assert(l.empty);
dispose(theAllocator, l); dispose(defaultAllocator, l);
} }
/** /**
@ -98,14 +98,14 @@ class SList(T)
/// ///
unittest unittest
{ {
auto l = make!(SList!int)(theAllocator); auto l = make!(SList!int)(defaultAllocator);
l.insertFront(8); l.insertFront(8);
assert(l.front == 8); assert(l.front == 8);
l.insertFront(9); l.insertFront(9);
assert(l.front == 9); assert(l.front == 9);
dispose(theAllocator, l); dispose(defaultAllocator, l);
} }
/** /**
@ -140,7 +140,7 @@ class SList(T)
/// ///
unittest unittest
{ {
auto l = make!(SList!int)(theAllocator); auto l = make!(SList!int)(defaultAllocator);
l.insertFront(8); l.insertFront(8);
l.insertFront(9); l.insertFront(9);
@ -148,7 +148,7 @@ class SList(T)
l.popFront(); l.popFront();
assert(l.front == 8); assert(l.front == 8);
dispose(theAllocator, l); dispose(defaultAllocator, l);
} }
/** /**
@ -179,7 +179,7 @@ class SList(T)
/// ///
unittest unittest
{ {
auto l = make!(SList!int)(theAllocator); auto l = make!(SList!int)(defaultAllocator);
l.insertFront(8); l.insertFront(8);
l.insertFront(5); l.insertFront(5);
@ -189,7 +189,7 @@ class SList(T)
assert(l.removeFront(3) == 1); assert(l.removeFront(3) == 1);
assert(l.removeFront(3) == 0); assert(l.removeFront(3) == 0);
dispose(theAllocator, l); dispose(defaultAllocator, l);
} }
/** /**
@ -235,7 +235,7 @@ class SList(T)
/// ///
unittest unittest
{ {
auto l = make!(SList!int)(theAllocator); auto l = make!(SList!int)(defaultAllocator);
l.insertFront(5); l.insertFront(5);
l.insertFront(4); l.insertFront(4);
@ -246,7 +246,7 @@ class SList(T)
assert(i != 1 || e == 4); assert(i != 1 || e == 4);
assert(i != 2 || e == 5); assert(i != 2 || e == 5);
} }
dispose(theAllocator, l); dispose(defaultAllocator, l);
} }
/** /**
@ -265,13 +265,13 @@ class SList(T)
protected Entry first; protected Entry first;
/// Allocator. /// Allocator.
protected IAllocator allocator; protected shared Allocator allocator;
} }
/// ///
unittest unittest
{ {
auto l = make!(SList!int)(theAllocator); auto l = make!(SList!int)(defaultAllocator);
size_t i; size_t i;
l.insertFront(5); l.insertFront(5);
@ -286,7 +286,7 @@ unittest
} }
assert(i == 3); assert(i == 3);
dispose(theAllocator, l); dispose(defaultAllocator, l);
} }
private unittest private unittest
@ -295,7 +295,7 @@ private unittest
{ {
} }
auto l = make!(SList!Stuff)(theAllocator); auto l = make!(SList!Stuff)(defaultAllocator);
dispose(theAllocator, l); dispose(defaultAllocator, l);
} }

View File

@ -27,7 +27,7 @@ class Queue(T)
* allocator = The allocator should be used for the element * allocator = The allocator should be used for the element
* allocations. * allocations.
*/ */
this(IAllocator allocator = theAllocator) this(shared Allocator allocator = defaultAllocator)
{ {
this.allocator = allocator; this.allocator = allocator;
} }
@ -54,7 +54,7 @@ class Queue(T)
/// ///
unittest unittest
{ {
auto q = theAllocator.make!(Queue!int); auto q = defaultAllocator.make!(Queue!int);
assert(q.empty); assert(q.empty);
q.insertBack(8); q.insertBack(8);
@ -62,7 +62,7 @@ class Queue(T)
q.clear(); q.clear();
assert(q.empty); assert(q.empty);
theAllocator.dispose(q); defaultAllocator.dispose(q);
} }
/** /**
@ -107,7 +107,7 @@ class Queue(T)
/// ///
unittest unittest
{ {
auto q = make!(Queue!int)(theAllocator); auto q = make!(Queue!int)(defaultAllocator);
assert(q.empty); assert(q.empty);
q.insertBack(8); q.insertBack(8);
@ -115,7 +115,7 @@ class Queue(T)
q.insertBack(9); q.insertBack(9);
assert(q.front == 8); assert(q.front == 8);
dispose(theAllocator, q); dispose(defaultAllocator, q);
} }
/** /**
@ -129,14 +129,14 @@ class Queue(T)
/// ///
unittest unittest
{ {
auto q = make!(Queue!int)(theAllocator); auto q = make!(Queue!int)(defaultAllocator);
int value = 7; int value = 7;
assert(q.empty); assert(q.empty);
q.insertBack(value); q.insertBack(value);
assert(!q.empty); assert(!q.empty);
dispose(theAllocator, q); dispose(defaultAllocator, q);
} }
/** /**
@ -158,7 +158,7 @@ class Queue(T)
/// ///
unittest unittest
{ {
auto q = make!(Queue!int)(theAllocator); auto q = make!(Queue!int)(defaultAllocator);
q.insertBack(8); q.insertBack(8);
q.insertBack(9); q.insertBack(9);
@ -166,7 +166,7 @@ class Queue(T)
q.popFront(); q.popFront();
assert(q.front == 9); assert(q.front == 9);
dispose(theAllocator, q); dispose(defaultAllocator, q);
} }
/** /**
@ -210,7 +210,7 @@ class Queue(T)
/// ///
unittest unittest
{ {
auto q = theAllocator.make!(Queue!int); auto q = defaultAllocator.make!(Queue!int);
size_t j; size_t j;
q.insertBack(5); q.insertBack(5);
@ -240,7 +240,7 @@ class Queue(T)
assert(j == 3); assert(j == 3);
assert(q.empty); assert(q.empty);
dispose(theAllocator, q); dispose(defaultAllocator, q);
} }
/** /**
@ -262,13 +262,13 @@ class Queue(T)
protected Entry* rear; protected Entry* rear;
/// The allocator. /// The allocator.
protected IAllocator allocator; protected shared Allocator allocator;
} }
/// ///
unittest unittest
{ {
auto q = theAllocator.make!(Queue!int); auto q = defaultAllocator.make!(Queue!int);
q.insertBack(5); q.insertBack(5);
assert(!q.empty); assert(!q.empty);
@ -289,5 +289,5 @@ unittest
} }
assert(q.empty); assert(q.empty);
theAllocator.dispose(q); defaultAllocator.dispose(q);
} }

View File

@ -200,7 +200,7 @@ class Vector(T)
* allocator = The allocator should be used for the element * allocator = The allocator should be used for the element
* allocations. * allocations.
*/ */
this(IAllocator allocator = theAllocator) this(shared Allocator allocator = defaultAllocator)
{ {
this.allocator = allocator; this.allocator = allocator;
} }
@ -211,18 +211,18 @@ class Vector(T)
* Params: * Params:
* U = Variadic template for the constructor parameters. * U = Variadic template for the constructor parameters.
* params = Values to initialize the array with. The last parameter can * params = Values to initialize the array with. The last parameter can
* be an allocator, if not, $(D_PSYMBOL theAllocator) is used. * be an allocator, if not, $(D_PSYMBOL defaultAllocator) is used.
*/ */
this(U...)(U params) this(U...)(U params)
{ {
static if (isImplicitlyConvertible!(typeof(params[$ - 1]), IAllocator)) static if (isImplicitlyConvertible!(typeof(params[$ - 1]), Allocator))
{ {
allocator = params[$ - 1]; allocator = params[$ - 1];
auto values = params[0 .. $ - 1]; auto values = params[0 .. $ - 1];
} }
else else
{ {
allocator = theAllocator; allocator = defaultAllocator;
alias values = params; alias values = params;
} }
@ -252,7 +252,7 @@ class Vector(T)
/// ///
unittest unittest
{ {
auto v = theAllocator.make!(Vector!int)(18, 20, 15); auto v = defaultAllocator.make!(Vector!int)(18, 20, 15);
v.clear(); v.clear();
assert(v.length == 0); assert(v.length == 0);
@ -286,7 +286,7 @@ class Vector(T)
/// ///
unittest unittest
{ {
auto v = theAllocator.make!(Vector!int); auto v = defaultAllocator.make!(Vector!int);
v.length = 5; v.length = 5;
assert(v.length == 5); assert(v.length == 5);
@ -337,14 +337,14 @@ class Vector(T)
/// ///
unittest unittest
{ {
auto v = theAllocator.make!(Vector!int)(5, 18, 17); auto v = defaultAllocator.make!(Vector!int)(5, 18, 17);
assert(v.removeBack(0) == 0); assert(v.removeBack(0) == 0);
assert(v.removeBack(2) == 2); assert(v.removeBack(2) == 2);
assert(v.removeBack(3) == 1); assert(v.removeBack(3) == 1);
assert(v.removeBack(3) == 0); assert(v.removeBack(3) == 0);
theAllocator.dispose(v); defaultAllocator.dispose(v);
} }
/** /**
@ -377,14 +377,14 @@ class Vector(T)
/// ///
unittest unittest
{ {
auto v1 = theAllocator.make!(Vector!int)(12, 1, 7); auto v1 = defaultAllocator.make!(Vector!int)(12, 1, 7);
v1[] = 3; v1[] = 3;
assert(v1[0] == 3); assert(v1[0] == 3);
assert(v1[1] == 3); assert(v1[1] == 3);
assert(v1[2] == 3); assert(v1[2] == 3);
theAllocator.dispose(v1); defaultAllocator.dispose(v1);
} }
@ -406,14 +406,14 @@ class Vector(T)
/// ///
unittest unittest
{ {
auto v = theAllocator.make!(Vector!int)(6, 123, 34, 5); auto v = defaultAllocator.make!(Vector!int)(6, 123, 34, 5);
assert(v[0] == 6); assert(v[0] == 6);
assert(v[1] == 123); assert(v[1] == 123);
assert(v[2] == 34); assert(v[2] == 34);
assert(v[3] == 5); assert(v[3] == 5);
theAllocator.dispose(v); defaultAllocator.dispose(v);
} }
/** /**
@ -435,8 +435,8 @@ class Vector(T)
/// ///
unittest unittest
{ {
auto v1 = theAllocator.make!(Vector!int); auto v1 = defaultAllocator.make!(Vector!int);
auto v2 = theAllocator.make!(Vector!int); auto v2 = defaultAllocator.make!(Vector!int);
assert(v1 == v2); assert(v1 == v2);
@ -453,8 +453,8 @@ class Vector(T)
v2[1] = 3; v2[1] = 3;
assert(v1 == v2); assert(v1 == v2);
theAllocator.dispose(v1); defaultAllocator.dispose(v1);
theAllocator.dispose(v2); defaultAllocator.dispose(v2);
} }
/** /**
@ -495,7 +495,7 @@ class Vector(T)
/// ///
unittest unittest
{ {
auto v = theAllocator.make!(Vector!int)(5, 15, 8); auto v = defaultAllocator.make!(Vector!int)(5, 15, 8);
size_t i; size_t i;
foreach (j, ref e; v) foreach (j, ref e; v)
@ -510,7 +510,7 @@ class Vector(T)
assert(j != 1 || e == 15); assert(j != 1 || e == 15);
assert(j != 2 || e == 8); assert(j != 2 || e == 8);
} }
theAllocator.dispose(v); defaultAllocator.dispose(v);
} }
/** /**
@ -551,7 +551,7 @@ class Vector(T)
/// ///
unittest unittest
{ {
auto v = theAllocator.make!(Vector!int)(5, 15, 8); auto v = defaultAllocator.make!(Vector!int)(5, 15, 8);
size_t i; size_t i;
foreach_reverse (j, ref e; v) foreach_reverse (j, ref e; v)
@ -566,7 +566,7 @@ class Vector(T)
assert(j != 1 || e == 15); assert(j != 1 || e == 15);
assert(j != 0 || e == 5); assert(j != 0 || e == 5);
} }
theAllocator.dispose(v); defaultAllocator.dispose(v);
} }
/** /**
@ -587,7 +587,7 @@ class Vector(T)
/// ///
unittest unittest
{ {
auto v = theAllocator.make!(Vector!int)(5); auto v = defaultAllocator.make!(Vector!int)(5);
assert(v.front == 5); assert(v.front == 5);
@ -595,7 +595,7 @@ class Vector(T)
v[1] = 15; v[1] = 15;
assert(v.front == 5); assert(v.front == 5);
theAllocator.dispose(v); defaultAllocator.dispose(v);
} }
/** /**
@ -616,7 +616,7 @@ class Vector(T)
/// ///
unittest unittest
{ {
auto v = theAllocator.make!(Vector!int)(5); auto v = defaultAllocator.make!(Vector!int)(5);
assert(v.back == 5); assert(v.back == 5);
@ -624,7 +624,7 @@ class Vector(T)
v[1] = 15; v[1] = 15;
assert(v.back == 15); assert(v.back == 15);
theAllocator.dispose(v); defaultAllocator.dispose(v);
} }
/** /**
@ -745,8 +745,8 @@ class Vector(T)
/// ///
unittest unittest
{ {
auto v1 = theAllocator.make!(Vector!int)(3, 3, 3); auto v1 = defaultAllocator.make!(Vector!int)(3, 3, 3);
auto v2 = theAllocator.make!(Vector!int)(1, 2); auto v2 = defaultAllocator.make!(Vector!int)(1, 2);
v1[0..2] = 286; v1[0..2] = 286;
assert(v1[0] == 286); assert(v1[0] == 286);
@ -757,25 +757,25 @@ class Vector(T)
assert(v2[0] == 286); assert(v2[0] == 286);
assert(v2[1] == 3); assert(v2[1] == 3);
theAllocator.dispose(v2); defaultAllocator.dispose(v2);
theAllocator.dispose(v1); defaultAllocator.dispose(v1);
} }
/// Internal representation. /// Internal representation.
protected T[] vector; protected T[] vector;
/// The allocator. /// The allocator.
protected IAllocator allocator; protected shared Allocator allocator;
} }
/// ///
unittest unittest
{ {
auto v = theAllocator.make!(Vector!int)(5, 15, 8); auto v = defaultAllocator.make!(Vector!int)(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);
theAllocator.dispose(v); defaultAllocator.dispose(v);
} }

View File

@ -20,260 +20,260 @@ import std.typecons;
* Supported padding mode. * Supported padding mode.
* *
* See_Also: * See_Also:
* $(D_PSYMBOL pad) * $(D_PSYMBOL pad)
*/ */
enum PaddingMode enum PaddingMode
{ {
zero, zero,
pkcs7, pkcs7,
ansiX923, ansiX923,
} }
/** /**
* Params: * Params:
* input = Sequence that should be padded. * input = Sequence that should be padded.
* mode = Padding mode. * mode = Padding mode.
* blockSize = Block size. * blockSize = Block size.
* allocator = Allocator was used to allocate $(D_PARAM input). * allocator = Allocator was used to allocate $(D_PARAM input).
* *
* Returns: The function modifies the initial array and returns it. * Returns: The function modifies the initial array and returns it.
* *
* See_Also: * See_Also:
* $(D_PSYMBOL PaddingMode) * $(D_PSYMBOL PaddingMode)
*/ */
ubyte[] pad(ref ubyte[] input, ubyte[] pad(ref ubyte[] input,
in PaddingMode mode, in PaddingMode mode,
in ushort blockSize, in ushort blockSize,
IAllocator allocator = theAllocator) shared Allocator allocator = defaultAllocator)
in in
{ {
assert(blockSize > 0 && blockSize <= 256); assert(blockSize > 0 && blockSize <= 256);
assert(blockSize % 64 == 0); assert(blockSize % 64 == 0);
assert(input.length > 0); assert(input.length > 0);
} }
body body
{ {
immutable rest = cast(ubyte) input.length % blockSize; immutable rest = cast(ubyte) input.length % blockSize;
immutable size_t lastBlock = input.length - (rest > 0 ? rest : blockSize); immutable size_t lastBlock = input.length - (rest > 0 ? rest : blockSize);
immutable needed = cast(ubyte) (rest > 0 ? blockSize - rest : 0); immutable needed = cast(ubyte) (rest > 0 ? blockSize - rest : 0);
final switch (mode) with (PaddingMode) final switch (mode) with (PaddingMode)
{ {
case zero: case zero:
allocator.expandArray(input, needed); allocator.expandArray(input, needed);
break; break;
case pkcs7: case pkcs7:
if (needed) if (needed)
{ {
allocator.expandArray(input, needed); allocator.expandArray(input, needed);
input[input.length - needed ..$].each!((ref e) => e = needed); input[input.length - needed ..$].each!((ref e) => e = needed);
} }
else else
{ {
allocator.expandArray(input, blockSize); allocator.expandArray(input, blockSize);
} }
break; break;
case ansiX923: case ansiX923:
allocator.expandArray(input, needed ? needed : blockSize); allocator.expandArray(input, needed ? needed : blockSize);
input[$ - 1] = needed; input[$ - 1] = needed;
break; break;
} }
return input; return input;
} }
/// ///
unittest unittest
{ {
{ // Zeros { // Zeros
auto input = theAllocator.makeArray!ubyte(50); auto input = defaultAllocator.makeArray!ubyte(50);
pad(input, PaddingMode.zero, 64); pad(input, PaddingMode.zero, 64);
assert(input.length == 64); assert(input.length == 64);
pad(input, PaddingMode.zero, 64); pad(input, PaddingMode.zero, 64);
assert(input.length == 64); assert(input.length == 64);
assert(input[63] == 0); assert(input[63] == 0);
theAllocator.dispose(input); defaultAllocator.dispose(input);
} }
{ // PKCS#7 { // PKCS#7
auto input = theAllocator.makeArray!ubyte(50); auto input = defaultAllocator.makeArray!ubyte(50);
for (ubyte i; i < 40; ++i) for (ubyte i; i < 40; ++i)
{ {
input[i] = i; input[i] = i;
} }
pad(input, PaddingMode.pkcs7, 64); pad(input, PaddingMode.pkcs7, 64);
assert(input.length == 64); assert(input.length == 64);
for (ubyte i; i < 64; ++i) for (ubyte i; i < 64; ++i)
{ {
if (i >= 40 && i < 50) if (i >= 40 && i < 50)
{ {
assert(input[i] == 0); assert(input[i] == 0);
} }
else if (i >= 50) else if (i >= 50)
{ {
assert(input[i] == 14); assert(input[i] == 14);
} }
else else
{ {
assert(input[i] == i); assert(input[i] == i);
} }
} }
pad(input, PaddingMode.pkcs7, 64); pad(input, PaddingMode.pkcs7, 64);
assert(input.length == 128); assert(input.length == 128);
for (ubyte i; i < 128; ++i) for (ubyte i; i < 128; ++i)
{ {
if (i >= 64 || (i >= 40 && i < 50)) if (i >= 64 || (i >= 40 && i < 50))
{ {
assert(input[i] == 0); assert(input[i] == 0);
} }
else if (i >= 50 && i < 64) else if (i >= 50 && i < 64)
{ {
assert(input[i] == 14); assert(input[i] == 14);
} }
else else
{ {
assert(input[i] == i); assert(input[i] == i);
} }
} }
theAllocator.dispose(input); defaultAllocator.dispose(input);
} }
{ // ANSI X.923 { // ANSI X.923
auto input = theAllocator.makeArray!ubyte(50); auto input = defaultAllocator.makeArray!ubyte(50);
for (ubyte i; i < 40; ++i) for (ubyte i; i < 40; ++i)
{ {
input[i] = i; input[i] = i;
} }
pad(input, PaddingMode.ansiX923, 64); pad(input, PaddingMode.ansiX923, 64);
assert(input.length == 64); assert(input.length == 64);
for (ubyte i; i < 64; ++i) for (ubyte i; i < 64; ++i)
{ {
if (i < 40) if (i < 40)
{ {
assert(input[i] == i); assert(input[i] == i);
} }
else if (i == 63) else if (i == 63)
{ {
assert(input[i] == 14); assert(input[i] == 14);
} }
else else
{ {
assert(input[i] == 0); assert(input[i] == 0);
} }
} }
pad(input, PaddingMode.pkcs7, 64); pad(input, PaddingMode.pkcs7, 64);
assert(input.length == 128); assert(input.length == 128);
for (ubyte i = 0; i < 128; ++i) for (ubyte i = 0; i < 128; ++i)
{ {
if (i < 40) if (i < 40)
{ {
assert(input[i] == i); assert(input[i] == i);
} }
else if (i == 63) else if (i == 63)
{ {
assert(input[i] == 14); assert(input[i] == 14);
} }
else else
{ {
assert(input[i] == 0); assert(input[i] == 0);
} }
} }
theAllocator.dispose(input); defaultAllocator.dispose(input);
} }
} }
/** /**
* Params: * Params:
* input = Sequence that should be padded. * input = Sequence that should be padded.
* mode = Padding mode. * mode = Padding mode.
* blockSize = Block size. * blockSize = Block size.
* allocator = Allocator was used to allocate $(D_PARAM input). * allocator = Allocator was used to allocate $(D_PARAM input).
* *
* Returns: The function modifies the initial array and returns it. * Returns: The function modifies the initial array and returns it.
* *
* See_Also: * See_Also:
* $(D_PSYMBOL pad) * $(D_PSYMBOL pad)
*/ */
ref ubyte[] unpad(ref ubyte[] input, ref ubyte[] unpad(ref ubyte[] input,
in PaddingMode mode, in PaddingMode mode,
in ushort blockSize, in ushort blockSize,
IAllocator allocator = theAllocator) shared Allocator allocator = defaultAllocator)
in in
{ {
assert(input.length != 0); assert(input.length != 0);
assert(input.length % 64 == 0); assert(input.length % 64 == 0);
} }
body body
{ {
final switch (mode) with (PaddingMode) final switch (mode) with (PaddingMode)
{ {
case zero: case zero:
break; break;
case pkcs7: case pkcs7:
case ansiX923: case ansiX923:
immutable last = input[$ - 1]; immutable last = input[$ - 1];
allocator.shrinkArray(input, last ? last : blockSize); allocator.shrinkArray(input, last ? last : blockSize);
break; break;
} }
return input; return input;
} }
/// ///
unittest unittest
{ {
{ // Zeros { // Zeros
auto input = theAllocator.makeArray!ubyte(50); auto input = defaultAllocator.makeArray!ubyte(50);
auto inputDup = theAllocator.makeArray!ubyte(50); auto inputDup = defaultAllocator.makeArray!ubyte(50);
pad(input, PaddingMode.zero, 64); pad(input, PaddingMode.zero, 64);
pad(inputDup, PaddingMode.zero, 64); pad(inputDup, PaddingMode.zero, 64);
unpad(input, PaddingMode.zero, 64); unpad(input, PaddingMode.zero, 64);
assert(input == inputDup); assert(input == inputDup);
theAllocator.dispose(input); defaultAllocator.dispose(input);
theAllocator.dispose(inputDup); defaultAllocator.dispose(inputDup);
} }
{ // PKCS#7 { // PKCS#7
auto input = theAllocator.makeArray!ubyte(50); auto input = defaultAllocator.makeArray!ubyte(50);
auto inputDup = theAllocator.makeArray!ubyte(50); auto inputDup = defaultAllocator.makeArray!ubyte(50);
for (ubyte i; i < 40; ++i) for (ubyte i; i < 40; ++i)
{ {
input[i] = i; input[i] = i;
inputDup[i] = i; inputDup[i] = i;
} }
pad(input, PaddingMode.pkcs7, 64); pad(input, PaddingMode.pkcs7, 64);
unpad(input, PaddingMode.pkcs7, 64); unpad(input, PaddingMode.pkcs7, 64);
assert(input == inputDup); assert(input == inputDup);
theAllocator.dispose(input); defaultAllocator.dispose(input);
theAllocator.dispose(inputDup); defaultAllocator.dispose(inputDup);
} }
{ // ANSI X.923 { // ANSI X.923
auto input = theAllocator.makeArray!ubyte(50); auto input = defaultAllocator.makeArray!ubyte(50);
auto inputDup = theAllocator.makeArray!ubyte(50); auto inputDup = defaultAllocator.makeArray!ubyte(50);
for (ubyte i; i < 40; ++i) for (ubyte i; i < 40; ++i)
{ {
input[i] = i; input[i] = i;
inputDup[i] = i; inputDup[i] = i;
} }
pad(input, PaddingMode.pkcs7, 64); pad(input, PaddingMode.pkcs7, 64);
unpad(input, PaddingMode.pkcs7, 64); unpad(input, PaddingMode.pkcs7, 64);
assert(input == inputDup); assert(input == inputDup);
theAllocator.dispose(input); defaultAllocator.dispose(input);
theAllocator.dispose(inputDup); defaultAllocator.dispose(inputDup);
} }
} }

View File

@ -13,17 +13,16 @@ module tanya.math.mp;
import std.algorithm.iteration; import std.algorithm.iteration;
import std.algorithm.searching; import std.algorithm.searching;
import std.algorithm.mutation; import std.algorithm.mutation;
import std.experimental.allocator;
import std.math; import std.math;
import std.range; import std.range;
import std.traits; import std.traits;
import tanya.memory.allocator; import tanya.memory;
import tanya.memory.types;
struct Integer struct Integer
{ {
private RefCounted!(ubyte[]) rep; private RefCounted!(ubyte[]) rep;
private bool sign; private bool sign;
private shared Allocator allocator;
invariant invariant
{ {
@ -34,10 +33,11 @@ struct Integer
* Creates a multiple precision integer. * Creates a multiple precision integer.
* *
* Params: * Params:
* T = Value type.
* value = Initial value. * value = Initial value.
* allocator = Allocator. * allocator = Allocator.
*/ */
this(T)(in T value, IAllocator allocator = theAllocator) this(T)(in T value, shared Allocator allocator = defaultAllocator)
if (isIntegral!T) if (isIntegral!T)
in in
{ {
@ -47,21 +47,30 @@ struct Integer
{ {
this(allocator); this(allocator);
immutable size = calculateSizeFromInt(value); T absolute = value;
immutable size = calculateSizeFromInt(absolute);
allocator.resizeArray(rep, size); allocator.resizeArray(rep, size);
assignInt(value); assignInt(absolute);
} }
/// ///
unittest unittest
{ {
auto h = Integer(79); {
assert(h.length == 1); auto h = Integer(79);
assert(h.rep[0] == 79); assert(h.length == 1);
assert(h.rep[0] == 79);
}
{
auto h = Integer(-2);
assert(h.length == 1);
assert(h.rep[0] == 2);
assert(h.sign);
}
} }
/// Ditto. /// Ditto.
this(in Integer value, IAllocator allocator = theAllocator) this(in Integer value, shared Allocator allocator = defaultAllocator)
in in
{ {
assert(allocator !is null); assert(allocator !is null);
@ -76,21 +85,35 @@ struct Integer
} }
/// Ditto. /// Ditto.
this(IAllocator allocator) this(shared Allocator allocator)
{ {
this.allocator = allocator; this.allocator = allocator;
rep = RefCounted!(ubyte[])(allocator); rep = RefCounted!(ubyte[])(allocator);
} }
/* /*
* Figure out the minimum amount of space this value will take * Figures out the minimum amount of space this value will take
* up in bytes. * up in bytes. Set the sign.
*/ */
pragma(inline, true) private ubyte calculateSizeFromInt(T)(ref T value)
private ubyte calculateSizeFromInt(in ulong value) pure nothrow @safe @nogc
const pure nothrow @safe @nogc in
{
static assert(isIntegral!T);
}
body
{ {
ubyte size = ulong.sizeof; ubyte size = ulong.sizeof;
static if (isSigned!T)
{
sign = value < 0 ? true : false;
value = abs(value);
}
else
{
sign = false;
}
for (ulong mask = 0xff00000000000000; mask >= 0xff; mask >>= 8) for (ulong mask = 0xff00000000000000; mask >= 0xff; mask >>= 8)
{ {
if (value & mask) if (value & mask)
@ -107,40 +130,42 @@ struct Integer
* (up to the first 0 byte) and copy it into the internal * (up to the first 0 byte) and copy it into the internal
* representation in big-endian format. * representation in big-endian format.
*/ */
pragma(inline, true) private void assignInt(in ulong value)
private void assignInt(T)(ref in T value)
pure nothrow @safe @nogc pure nothrow @safe @nogc
in
{
static assert(isIntegral!T);
}
body
{ {
uint mask = 0xff, shift; uint mask = 0xff, shift;
immutable absolute = abs(value);
sign = value < 0 ? true : false;
for (auto i = length; i; --i) for (auto i = length; i; --i)
{ {
rep[i - 1] = cast(ubyte) ((absolute & mask) >> shift); rep[i - 1] = cast(ubyte) ((value & mask) >> shift);
mask <<= 8; mask <<= 8;
shift += 8; shift += 8;
} }
} }
/**
* Assigns a new value.
*
* Params:
* T = Value type.
* value = Value.
*
* Returns: $(D_KEYWORD this).
*/
ref Integer opAssign(T)(in T value) ref Integer opAssign(T)(in T value)
if (isIntegral!T) if (isIntegral!T)
{ {
immutable size = calculateSizeFromInt(value); T absolute = value;
immutable size = calculateSizeFromInt(absolute);
checkAllocator(); checkAllocator();
allocator.resizeArray(rep.get, size); allocator.resizeArray(rep.get, size);
assignInt(value); assignInt(absolute);
return this; return this;
} }
/// Ditto.
ref Integer opAssign(in Integer value) ref Integer opAssign(in Integer value)
{ {
checkAllocator(); checkAllocator();
@ -251,23 +276,11 @@ struct Integer
assert(h1 > h2); assert(h1 > h2);
} }
/** private void add(in ref RefCounted!(ubyte[]) h)
* Assignment operators with another $(D_PSYMBOL Integer).
*
* Params:
* op = Operation.
* h = The second integer.
*
* Returns: $(D_KEYWORD this).
*/
ref Integer opOpAssign(string op)(in Integer h)
if (op == "+")
{ {
uint sum; uint sum;
uint carry = 0; uint carry = 0;
checkAllocator();
// Adding h2 to h1. If h2 is > h1 to begin with, resize h1 // Adding h2 to h1. If h2 is > h1 to begin with, resize h1
if (h.length > length) if (h.length > length)
@ -286,7 +299,7 @@ struct Integer
if (j) if (j)
{ {
--j; --j;
sum = rep[i] + h.rep[j] + carry; sum = rep[i] + h[j] + carry;
} }
else else
{ {
@ -305,36 +318,15 @@ struct Integer
tmp[0] = 0x01; tmp[0] = 0x01;
rep = tmp; rep = tmp;
} }
return this;
} }
/// private void subtract(in ref RefCounted!(ubyte[]) h)
unittest
{
auto h1 = Integer(1019);
auto h2 = Integer(3337);
h1 += h2;
assert(h1.rep == [0x11, 0x04]);
h2 = 2_147_483_647;
h1 += h2;
assert(h1.rep == [0x80, 0x00, 0x11, 0x03]);
h1 += h2;
assert(h1.rep == [0x01, 0x00, 0x00, 0x11, 0x02]);
}
/// Ditto.
ref Integer opOpAssign(string op)(in Integer h)
if (op == "-")
{ {
auto i = rep.length; auto i = rep.length;
auto j = h.rep.length; auto j = h.length;
uint borrow = 0; uint borrow = 0;
checkAllocator();
do do
{ {
int difference; int difference;
@ -343,7 +335,7 @@ struct Integer
if (j) if (j)
{ {
--j; --j;
difference = rep[i] - h.rep[j] - borrow; difference = rep[i] - h[j] - borrow;
} }
else else
{ {
@ -371,11 +363,49 @@ struct Integer
{ {
allocator.resizeArray(rep, 0); allocator.resizeArray(rep, 0);
} }
}
/**
* Assignment operators with another $(D_PSYMBOL Integer).
*
* Params:
* op = Operation.
* h = The second integer.
*
* Returns: $(D_KEYWORD this).
*/
ref Integer opOpAssign(string op)(in Integer h)
if ((op == "+") || (op == "-"))
{
checkAllocator();
static if (op == "+")
{
add(h.rep);
}
else
{
subtract(h.rep);
}
return this; return this;
} }
/// private unittest
unittest {
auto h1 = Integer(1019);
auto h2 = Integer(3337);
h1 += h2;
assert(h1.rep == [0x11, 0x04]);
h2 = 2_147_483_647;
h1 += h2;
assert(h1.rep == [0x80, 0x00, 0x11, 0x03]);
h1 += h2;
assert(h1.rep == [0x01, 0x00, 0x00, 0x11, 0x02]);
}
private unittest
{ {
auto h1 = Integer(4294967295); auto h1 = Integer(4294967295);
auto h2 = Integer(4294967295); auto h2 = Integer(4294967295);
@ -417,8 +447,7 @@ struct Integer
} }
do do
{ {
--i; --i, --j;
--j;
immutable oldCarry = carry; immutable oldCarry = carry;
carry = rep[i] >> delta; carry = rep[i] >> delta;
rep[j] = cast(ubyte) ((rep[i] << bit) | oldCarry); rep[j] = cast(ubyte) ((rep[i] << bit) | oldCarry);
@ -546,6 +575,11 @@ struct Integer
/// Ditto. /// Ditto.
ref Integer opOpAssign(string op)(in Integer h) ref Integer opOpAssign(string op)(in Integer h)
if ((op == "/") || (op == "%")) if ((op == "/") || (op == "%"))
in
{
assert(h.length > 0, "Division by zero.");
}
body
{ {
checkAllocator(); checkAllocator();
@ -715,6 +749,40 @@ struct Integer
assert(h2.rep[0] == ~cast(ubyte) 79); assert(h2.rep[0] == ~cast(ubyte) 79);
} }
private void decrement()
{
immutable size = rep.get.retro.countUntil!((const ref a) => a != 0);
if (rep[0] == 1)
{
allocator.resizeArray(rep, rep.length - 1);
rep[0 .. $] = typeof(rep[0]).max;
}
else
{
--rep[$ - size - 1];
rep[$ - size .. $] = typeof(rep[0]).max;
}
}
private void increment()
{
auto size = rep
.get
.retro
.countUntil!((const ref a) => a != typeof(rep[0]).max);
if (size == -1)
{
size = length;
allocator.resizeArray(rep.get, rep.length + 1);
rep[0] = 1;
}
else
{
++rep[$ - size - 1];
}
rep[$ - size .. $] = 0;
}
/** /**
* Increment/decrement. * Increment/decrement.
* *
@ -728,45 +796,28 @@ struct Integer
{ {
checkAllocator(); checkAllocator();
if (op == "++" || sign || length == 0) static if (op == "++")
{ {
static if (op == "--") if (sign)
{ {
sign = true; decrement();
} if (length == 0)
auto size = rep {
.get sign = false;
.retro }
.countUntil!((const ref a) => a != typeof(rep[0]).max);
if (size == -1)
{
size = length;
allocator.resizeArray(rep.get, rep.length + 1);
rep[0] = 1;
} }
else else
{ {
++rep[$ - size - 1]; increment();
} }
rep[$ - size .. $] = 0; }
else if (sign)
{
increment();
} }
else else
{ {
immutable size = rep.get.retro.countUntil!((const ref a) => a != 0); decrement();
if (rep[0] == 1)
{
allocator.resizeArray(rep, rep.length - 1);
rep[0 .. $] = typeof(rep[0]).max;
}
else
{
--rep[$ - size - 1];
rep[$ - size .. $] = typeof(rep[0]).max;
}
if (rep.length == 0)
{
sign = false;
}
} }
return this; return this;
} }
@ -803,7 +854,17 @@ struct Integer
--h; --h;
assert(h.rep == [0xff, 0xff]); assert(h.rep == [0xff, 0xff]);
h = -2;
++h;
assert(h.rep == [0x01]);
} }
mixin StructAllocator; private void checkAllocator() nothrow @safe @nogc
{
if (allocator is null)
{
allocator = defaultAllocator;
}
}
} }

View File

@ -10,214 +10,42 @@
*/ */
module tanya.memory.allocator; module tanya.memory.allocator;
import std.algorithm.mutation;
import std.experimental.allocator;
import std.typecons;
/** /**
* Abstract class implementing a basic allocator. * Abstract class implementing a basic allocator.
*/ */
abstract class Allocator : IAllocator interface Allocator
{ {
/** @nogc:
* Not supported. @property uint alignment() const shared pure nothrow @safe;
*
* Returns: $(D_KEYWORD false).
*/
bool deallocateAll() const @nogc @safe pure nothrow
{
return false;
}
/** /**
* Not supported. * Allocates $(D_PARAM size) bytes of memory.
* *
* Returns $(D_PSYMBOL Ternary.unknown). * Params:
*/ * size = Amount of memory to allocate.
Ternary empty() const @nogc @safe pure nothrow *
{ * Returns: The pointer to the new allocated memory.
return Ternary.unknown; */
} void[] allocate(size_t size, TypeInfo ti = null) shared nothrow @safe;
/** /**
* Not supported. * Deallocates a memory block.
* *
* Params: * Params:
* b = Memory block. * p = A pointer to the memory block to be freed.
* *
* Returns: $(D_PSYMBOL Ternary.unknown). * Returns: Whether the deallocation was successful.
*/ */
Ternary owns(void[] b) const @nogc @safe pure nothrow bool deallocate(void[] p) shared nothrow @safe;
{
return Ternary.unknown;
}
/** /**
* Not supported. * Increases or decreases the size of a memory block.
* *
* Params: * Params:
* p = Pointer to a memory block. * p = A pointer to the memory block.
* result = Full block allocated. * size = Size of the reallocated block.
* *
* Returns: $(D_PSYMBOL Ternary.unknown). * Returns: Whether the reallocation was successful.
*/ */
Ternary resolveInternalPointer(void* p, ref void[] result) bool reallocate(ref void[] p, size_t size) shared nothrow @safe;
const @nogc @safe pure nothrow
{
return Ternary.unknown;
}
/**
* Params:
* size = Amount of memory to allocate.
*
* Returns: The good allocation size that guarantees zero internal
* fragmentation.
*/
size_t goodAllocSize(size_t s)
{
auto rem = s % alignment;
return rem ? s + alignment - rem : s;
}
/**
* Not supported.
*
* Returns: $(D_KEYWORD null).
*
*/
void[] allocateAll() const @nogc @safe pure nothrow
{
return null;
}
/**
* Not supported.
*
* Params:
* b = Block to be expanded.
* s = New size.
*
* Returns: $(D_KEYWORD false).
*/
bool expand(ref void[] b, size_t s) const @nogc @safe pure nothrow
{
return false;
}
/**
* Not supported.
*
* Params:
* n = Amount of memory to allocate.
* a = Alignment.
*
* Returns: $(D_KEYWORD null).
*/
void[] alignedAllocate(size_t n, uint a) const @nogc @safe pure nothrow
{
return null;
}
/**
* Not supported.
*
* Params:
* n = Amount of memory to allocate.
* a = Alignment.
*
* Returns: $(D_KEYWORD false).
*/
bool alignedReallocate(ref void[] b, size_t size, uint alignment)
const @nogc @safe pure nothrow
{
return false;
}
}
/**
* Params:
* T = Element type of the array being created.
* allocator = The allocator used for getting memory.
* array = A reference to the array being changed.
* length = New array length.
* init = The value to fill the new part of the array with if it becomes
* larger.
*
* Returns: $(D_KEYWORD true) upon success, $(D_KEYWORD false) if memory could
* not be reallocated. In the latter
*/
bool resizeArray(T)(IAllocator allocator,
ref T[] array,
in size_t length,
T init = T.init)
{
void[] buf = array;
immutable oldLength = array.length;
if (!allocator.reallocate(buf, length * T.sizeof))
{
return false;
}
array = cast(T[]) buf;
if (oldLength < length)
{
array[oldLength .. $].uninitializedFill(init);
}
return true;
}
///
unittest
{
int[] p;
theAllocator.resizeArray(p, 20);
assert(p.length == 20);
theAllocator.resizeArray(p, 30);
assert(p.length == 30);
theAllocator.resizeArray(p, 10);
assert(p.length == 10);
theAllocator.resizeArray(p, 0);
assert(p is null);
}
/**
* Mixin to get around the impossibility to define a default constructor for
* structs. It can be used for the structs that don't disable the default
* constructor and don't wan't to force passing the allocator each time to
* the constructor.
*
* It defines the private property `allocator`, a constructor that accepts only
* an allocator instance and the method `checkAllocator` that checks if an
* allocator is set and sets it to ` $(D_PSYMBOL theAllocator) if not.
*
* `checkAllocator` should be used at beginning of functions that
* allocate/free memory.
*/
mixin template StructAllocator()
{
private IAllocator allocator;
this(IAllocator allocator) pure nothrow @safe @nogc
in
{
assert(allocator !is null);
}
body
{
this.allocator = allocator;
}
pragma(inline, true)
private void checkAllocator() nothrow @safe @nogc
{
if (allocator is null)
{
allocator = theAllocator;
}
}
} }

View File

@ -16,14 +16,14 @@ import core.exception;
version (Posix) version (Posix)
{ {
import core.stdc.errno; import core.stdc.errno;
import core.sys.posix.sys.mman; import core.sys.posix.sys.mman;
import core.sys.posix.unistd; import core.sys.posix.unistd;
} }
else version (Windows) else version (Windows)
{ {
import core.sys.windows.winbase; import core.sys.windows.winbase;
import core.sys.windows.windows; import core.sys.windows.windows;
} }
/** /**
@ -49,437 +49,436 @@ else version (Windows)
* --------------------------------------------------- ------------------------ * --------------------------------------------------- ------------------------
* *
* TODO: * TODO:
* $(UL * $(UL
* $(LI Thread safety (core.atomic.cas)) * $(LI Thread safety (core.atomic.cas))
* $(LI If two neighbour blocks are free, they can be merged) * $(LI If two neighbour blocks are free, they can be merged)
* $(LI Reallocation shoud check if there is enough free space in the * $(LI Reallocation shoud check if there is enough free space in the
* next block instead of always moving the memory) * next block instead of always moving the memory)
* $(LI Make 64 KB regions mininmal region size on Linux) * $(LI Make 64 KB regions mininmal region size on Linux)
* ) * )
*/ */
class MmapPool : Allocator class MmapPool : Allocator
{ {
@disable this(); @nogc:
shared static this()
{
version (Posix)
{
pageSize = sysconf(_SC_PAGE_SIZE);
}
else version (Windows)
{
SYSTEM_INFO si;
GetSystemInfo(&si);
pageSize = si.dwPageSize;
}
}
shared static this() /**
{ * Allocates $(D_PARAM size) bytes of memory.
version (Posix) *
{ * Params:
pageSize = sysconf(_SC_PAGE_SIZE); * size = Amount of memory to allocate.
} *
else version (Windows) * Returns: The pointer to the new allocated memory.
{ */
SYSTEM_INFO si; void[] allocate(size_t size, TypeInfo ti = null) shared nothrow @trusted
GetSystemInfo(&si); {
pageSize = si.dwPageSize; if (!size)
} {
} return null;
}
immutable dataSize = addAlignment(size);
/** void* data = findBlock(dataSize);
* Allocates $(D_PARAM size) bytes of memory. if (data is null)
* {
* Params: data = initializeRegion(dataSize);
* size = Amount of memory to allocate. }
*
* Returns: The pointer to the new allocated memory.
*/
void[] allocate(size_t size, TypeInfo ti = null) @nogc @trusted nothrow
{
if (!size)
{
return null;
}
immutable dataSize = addAlignment(size);
void* data = findBlock(dataSize); return data is null ? null : data[0..size];
if (data is null) }
{
data = initializeRegion(dataSize);
}
return data is null ? null : data[0..size]; ///
} @safe nothrow unittest
{
auto p = MmapPool.instance.allocate(20);
/// assert(p);
@nogc @safe nothrow unittest
{
auto p = MmapPool.instance.allocate(20);
assert(p); MmapPool.instance.deallocate(p);
}
MmapPool.instance.deallocate(p); /**
} * Search for a block large enough to keep $(D_PARAM size) and split it
* into two blocks if the block is too large.
*
* Params:
* size = Minimum size the block should have.
*
* Returns: Data the block points to or $(D_KEYWORD null).
*/
private void* findBlock(size_t size) shared nothrow
{
Block block1;
RegionLoop: for (auto r = head; r !is null; r = r.next)
{
block1 = cast(Block) (cast(void*) r + regionEntrySize);
do
{
if (block1.free && block1.size >= size)
{
break RegionLoop;
}
}
while ((block1 = block1.next) !is null);
}
if (block1 is null)
{
return null;
}
else if (block1.size >= size + alignment + blockEntrySize)
{ // Split the block if needed
Block block2 = cast(Block) (cast(void*) block1 + blockEntrySize + size);
block2.prev = block1;
if (block1.next is null)
{
block2.next = null;
}
else
{
block2.next = block1.next.next;
}
block1.next = block2;
/** block1.free = false;
* Search for a block large enough to keep $(D_PARAM size) and split it block2.free = true;
* into two blocks if the block is too large.
*
* Params:
* size = Minimum size the block should have.
*
* Returns: Data the block points to or $(D_KEYWORD null).
*/
private void* findBlock(size_t size) @nogc nothrow
{
Block block1;
RegionLoop: for (auto r = head; r !is null; r = r.next)
{
block1 = cast(Block) (cast(void*) r + regionEntrySize);
do
{
if (block1.free && block1.size >= size)
{
break RegionLoop;
}
}
while ((block1 = block1.next) !is null);
}
if (block1 is null)
{
return null;
}
else if (block1.size >= size + alignment + blockEntrySize)
{ // Split the block if needed
Block block2 = cast(Block) (cast(void*) block1 + blockEntrySize + size);
block2.prev = block1;
if (block1.next is null)
{
block2.next = null;
}
else
{
block2.next = block1.next.next;
}
block1.next = block2;
block1.free = false; block2.size = block1.size - blockEntrySize - size;
block2.free = true; block1.size = size;
block2.size = block1.size - blockEntrySize - size; block2.region = block1.region;
block1.size = size; atomicOp!"+="(block1.region.blocks, 1);
}
else
{
block1.free = false;
atomicOp!"+="(block1.region.blocks, 1);
}
return cast(void*) block1 + blockEntrySize;
}
block2.region = block1.region; /**
atomicOp!"+="(block1.region.blocks, 1); * Deallocates a memory block.
} *
else * Params:
{ * p = A pointer to the memory block to be freed.
block1.free = false; *
atomicOp!"+="(block1.region.blocks, 1); * Returns: Whether the deallocation was successful.
} */
return cast(void*) block1 + blockEntrySize; bool deallocate(void[] p) shared nothrow @trusted
} {
if (p is null)
{
return true;
}
/** Block block = cast(Block) (p.ptr - blockEntrySize);
* Deallocates a memory block. if (block.region.blocks <= 1)
* {
* Params: if (block.region.prev !is null)
* p = A pointer to the memory block to be freed. {
* block.region.prev.next = block.region.next;
* Returns: Whether the deallocation was successful. }
*/ else // Replace the list head. It is being deallocated
bool deallocate(void[] p) @nogc @trusted nothrow {
{ head = block.region.next;
if (p is null) }
{ if (block.region.next !is null)
return true; {
} block.region.next.prev = block.region.prev;
}
version (Posix)
{
return munmap(cast(void*) block.region, block.region.size) == 0;
}
version (Windows)
{
return VirtualFree(cast(void*) block.region, 0, MEM_RELEASE) == 0;
}
}
else
{
block.free = true;
atomicOp!"-="(block.region.blocks, 1);
return true;
}
}
Block block = cast(Block) (p.ptr - blockEntrySize); ///
if (block.region.blocks <= 1) @safe nothrow unittest
{ {
if (block.region.prev !is null) auto p = MmapPool.instance.allocate(20);
{
block.region.prev.next = block.region.next;
}
else // Replace the list head. It is being deallocated
{
head = block.region.next;
}
if (block.region.next !is null)
{
block.region.next.prev = block.region.prev;
}
version (Posix)
{
return munmap(cast(void*) block.region, block.region.size) == 0;
}
version (Windows)
{
return VirtualFree(cast(void*) block.region, 0, MEM_RELEASE) == 0;
}
}
else
{
block.free = true;
atomicOp!"-="(block.region.blocks, 1);
return true;
}
}
/// assert(MmapPool.instance.deallocate(p));
@nogc @safe nothrow unittest }
{
auto p = MmapPool.instance.allocate(20);
assert(MmapPool.instance.deallocate(p)); /**
} * 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, size_t size) shared nothrow @trusted
{
void[] reallocP;
/** if (size == p.length)
* Increases or decreases the size of a memory block. {
* return true;
* Params: }
* p = A pointer to the memory block. else if (size > 0)
* size = Size of the reallocated block. {
* reallocP = allocate(size);
* Returns: Whether the reallocation was successful. if (reallocP is null)
*/ {
bool reallocate(ref void[] p, size_t size) @nogc @trusted nothrow return false;
{ }
void[] reallocP; }
if (size == p.length) if (p !is null)
{ {
return true; if (size > p.length)
} {
else if (size > 0) reallocP[0..p.length] = p[0..$];
{ }
reallocP = allocate(size); else if (size > 0)
if (reallocP is null) {
{ reallocP[0..size] = p[0..size];
return false; }
} deallocate(p);
} }
p = reallocP;
if (p !is null) return true;
{ }
if (size > p.length)
{
reallocP[0..p.length] = p[0..$];
}
else if (size > 0)
{
reallocP[0..size] = p[0..size];
}
deallocate(p);
}
p = reallocP;
return true; ///
} nothrow unittest
{
void[] p;
MmapPool.instance.reallocate(p, 10 * int.sizeof);
(cast(int[]) p)[7] = 123;
/// assert(p.length == 40);
@nogc nothrow unittest
{
void[] p;
MmapPool.instance.reallocate(p, 10 * int.sizeof);
(cast(int[]) p)[7] = 123;
assert(p.length == 40); MmapPool.instance.reallocate(p, 8 * int.sizeof);
MmapPool.instance.reallocate(p, 8 * int.sizeof); assert(p.length == 32);
assert((cast(int[]) p)[7] == 123);
assert(p.length == 32); MmapPool.instance.reallocate(p, 20 * int.sizeof);
assert((cast(int[]) p)[7] == 123); (cast(int[]) p)[15] = 8;
MmapPool.instance.reallocate(p, 20 * int.sizeof); assert(p.length == 80);
(cast(int[]) p)[15] = 8; assert((cast(int[]) p)[15] == 8);
assert((cast(int[]) p)[7] == 123);
assert(p.length == 80); MmapPool.instance.reallocate(p, 8 * int.sizeof);
assert((cast(int[]) p)[15] == 8);
assert((cast(int[]) p)[7] == 123);
MmapPool.instance.reallocate(p, 8 * int.sizeof); assert(p.length == 32);
assert((cast(int[]) p)[7] == 123);
assert(p.length == 32); MmapPool.instance.deallocate(p);
assert((cast(int[]) p)[7] == 123); }
MmapPool.instance.deallocate(p); /**
} * Static allocator instance and initializer.
*
* Returns: Global $(D_PSYMBOL MmapPool) instance.
*/
static @property ref shared(MmapPool) instance() nothrow @trusted
{
if (instance_ is null)
{
immutable instanceSize = addAlignment(__traits(classInstanceSize, MmapPool));
/** Region head; // Will become soon our region list head
* Static allocator instance and initializer. void* data = initializeRegion(instanceSize, head);
* if (data !is null)
* Returns: Global $(D_PSYMBOL MmapPool) instance. {
*/ data[0..instanceSize] = typeid(MmapPool).initializer[];
static @property ref MmapPool instance() @nogc @trusted nothrow instance_ = cast(shared MmapPool) data;
{ instance_.head = head;
if (instance_ is null) }
{ }
immutable instanceSize = addAlignment(__traits(classInstanceSize, MmapPool)); return instance_;
}
Region head; // Will become soon our region list head ///
void* data = initializeRegion(instanceSize, head); @safe nothrow unittest
if (data !is null) {
{ assert(instance is instance);
data[0..instanceSize] = typeid(MmapPool).initializer[]; }
instance_ = cast(MmapPool) data;
instance_.head = head;
}
}
return instance_;
}
/// /**
@nogc @safe nothrow unittest * Initializes a region for one element.
{ *
assert(instance is instance); * Params:
} * size = Aligned size of the first data block in the region.
* head = Region list head.
*
* Returns: A pointer to the data.
*/
private static void* initializeRegion(size_t size,
ref Region head) nothrow
{
immutable regionSize = calculateRegionSize(size);
version (Posix)
{
void* p = mmap(null,
regionSize,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1,
0);
if (p is MAP_FAILED)
{
if (errno == ENOMEM)
{
onOutOfMemoryError();
}
return null;
}
}
else version (Windows)
{
void* p = VirtualAlloc(null,
regionSize,
MEM_COMMIT,
PAGE_READWRITE);
if (p is null)
{
if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY)
{
onOutOfMemoryError();
}
return null;
}
}
/** Region region = cast(Region) p;
* Initializes a region for one element. region.blocks = 1;
* region.size = regionSize;
* Params:
* size = Aligned size of the first data block in the region.
* head = Region list head.
*
* Returns: A pointer to the data.
*/
private static void* initializeRegion(size_t size,
ref Region head) @nogc nothrow
{
immutable regionSize = calculateRegionSize(size);
version (Posix)
{
void* p = mmap(null,
regionSize,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1,
0);
if (p is MAP_FAILED)
{
if (errno == ENOMEM)
{
onOutOfMemoryError();
}
return null;
}
}
else version (Windows)
{
void* p = VirtualAlloc(null,
regionSize,
MEM_COMMIT,
PAGE_READWRITE);
if (p is null)
{
if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY)
{
onOutOfMemoryError();
}
return null;
}
}
Region region = cast(Region) p; // Set the pointer to the head of the region list
region.blocks = 1; if (head !is null)
region.size = regionSize; {
head.prev = region;
}
region.next = head;
region.prev = null;
head = region;
// Set the pointer to the head of the region list // Initialize the data block
if (head !is null) void* memoryPointer = p + regionEntrySize;
{ Block block1 = cast(Block) memoryPointer;
head.prev = region; block1.size = size;
} block1.free = false;
region.next = head;
region.prev = null;
head = region;
// Initialize the data block // It is what we want to return
void* memoryPointer = p + regionEntrySize; void* data = memoryPointer + blockEntrySize;
Block block1 = cast(Block) memoryPointer;
block1.size = size;
block1.free = false;
// It is what we want to return // Free block after data
void* data = memoryPointer + blockEntrySize; memoryPointer = data + size;
Block block2 = cast(Block) memoryPointer;
block1.prev = block2.next = null;
block1.next = block2;
block2.prev = block1;
block2.size = regionSize - size - regionEntrySize - blockEntrySize * 2;
block2.free = true;
block1.region = block2.region = region;
// Free block after data return data;
memoryPointer = data + size; }
Block block2 = cast(Block) memoryPointer;
block1.prev = block2.next = null;
block1.next = block2;
block2.prev = block1;
block2.size = regionSize - size - regionEntrySize - blockEntrySize * 2;
block2.free = true;
block1.region = block2.region = region;
return data; /// Ditto.
} private void* initializeRegion(size_t size) shared nothrow
{
return initializeRegion(size, head);
}
/// Ditto. /**
private void* initializeRegion(size_t size) @nogc nothrow * Params:
{ * x = Space to be aligned.
return initializeRegion(size, head); *
} * Returns: Aligned size of $(D_PARAM x).
*/
pragma(inline)
private static immutable(size_t) addAlignment(size_t x)
@safe pure nothrow
out (result)
{
assert(result > 0);
}
body
{
return (x - 1) / alignment_ * alignment_ + alignment_;
}
/** /**
* Params: * Params:
* x = Space to be aligned. * x = Required space.
* *
* Returns: Aligned size of $(D_PARAM x). * Returns: Minimum region size (a multiple of $(D_PSYMBOL pageSize)).
*/ */
pragma(inline) pragma(inline)
private static immutable(size_t) addAlignment(size_t x) private static immutable(size_t) calculateRegionSize(size_t x)
@nogc @safe pure nothrow @safe pure nothrow
out (result) out (result)
{ {
assert(result > 0); assert(result > 0);
} }
body body
{ {
return (x - 1) / alignment_ * alignment_ + alignment_; x += regionEntrySize + blockEntrySize * 2;
} return x / pageSize * pageSize + pageSize;
}
/** @property uint alignment() shared const pure nothrow @safe
* Params: {
* x = Required space. return alignment_;
* }
* Returns: Minimum region size (a multiple of $(D_PSYMBOL pageSize)). private enum alignment_ = 8;
*/
pragma(inline)
private static immutable(size_t) calculateRegionSize(size_t x)
@nogc @safe pure nothrow
out (result)
{
assert(result > 0);
}
body
{
x += regionEntrySize + blockEntrySize * 2;
return x / pageSize * pageSize + pageSize;
}
@property uint alignment() const @nogc @safe pure nothrow private static shared MmapPool instance_;
{
return alignment_;
}
private enum alignment_ = 8;
private static MmapPool instance_; private shared static immutable size_t pageSize;
private shared static immutable size_t pageSize; private shared struct RegionEntry
{
Region prev;
Region next;
uint blocks;
size_t size;
}
private alias Region = shared RegionEntry*;
private enum regionEntrySize = 32;
private shared struct RegionEntry private shared Region head;
{
Region prev;
Region next;
uint blocks;
size_t size;
}
private alias Region = shared RegionEntry*;
private enum regionEntrySize = 32;
private shared Region head; private shared struct BlockEntry
{
private shared struct BlockEntry Block prev;
{ Block next;
Block prev; bool free;
Block next; size_t size;
bool free; Region region;
size_t size; }
Region region; private alias Block = shared BlockEntry*;
} private enum blockEntrySize = 40;
private alias Block = shared BlockEntry*;
private enum blockEntrySize = 40;
} }

View File

@ -10,6 +10,75 @@
*/ */
module tanya.memory; module tanya.memory;
import std.algorithm.mutation;
public import std.experimental.allocator; public import std.experimental.allocator;
public import tanya.memory.allocator; public import tanya.memory.allocator;
public import tanya.memory.types; public import tanya.memory.types;
shared Allocator allocator;
shared static this() nothrow @safe @nogc
{
import tanya.memory.mmappool;
allocator = MmapPool.instance;
}
@property ref shared(Allocator) defaultAllocator() nothrow @safe @nogc
{
return allocator;
}
@property void defaultAllocator(shared(Allocator) allocator) nothrow @safe @nogc
{
.allocator = allocator;
}
/**
* Params:
* T = Element type of the array being created.
* allocator = The allocator used for getting memory.
* array = A reference to the array being changed.
* length = New array length.
* init = The value to fill the new part of the array with if it becomes
* larger.
*
* Returns: $(D_KEYWORD true) upon success, $(D_KEYWORD false) if memory could
* not be reallocated. In the latter
*/
bool resizeArray(T)(shared Allocator allocator,
ref T[] array,
in size_t length,
T init = T.init)
{
void[] buf = array;
immutable oldLength = array.length;
if (!allocator.reallocate(buf, length * T.sizeof))
{
return false;
}
array = cast(T[]) buf;
if (oldLength < length)
{
array[oldLength .. $].uninitializedFill(init);
}
return true;
}
///
unittest
{
int[] p;
defaultAllocator.resizeArray(p, 20);
assert(p.length == 20);
defaultAllocator.resizeArray(p, 30);
assert(p.length == 30);
defaultAllocator.resizeArray(p, 10);
assert(p.length == 10);
defaultAllocator.resizeArray(p, 0);
assert(p is null);
}

View File

@ -37,13 +37,15 @@ struct RefCounted(T)
private T* payload; private T* payload;
} }
private uint *counter; private uint counter;
invariant invariant
{ {
assert(counter is null || allocator !is null); assert(counter == 0 || allocator !is null);
} }
private shared Allocator allocator;
/** /**
* Takes ownership over $(D_PARAM value), setting the counter to 1. * Takes ownership over $(D_PARAM value), setting the counter to 1.
* *
@ -54,20 +56,29 @@ struct RefCounted(T)
* Precondition: $(D_INLINECODE allocator !is null) * Precondition: $(D_INLINECODE allocator !is null)
*/ */
this(T value, IAllocator allocator = theAllocator) this(T value, shared Allocator allocator = defaultAllocator)
in in
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body body
{ {
this.allocator = allocator; this(allocator);
initialize(); static if (!isReference!T)
move(value, get); {
payload = cast(T*) allocator.allocate(stateSize!T).ptr;
move(value, *payload);
counter = 1;
}
else if (value !is null)
{
move(value, payload);
counter = 1;
}
} }
/// Ditto. /// Ditto.
this(IAllocator allocator) this(shared Allocator allocator) pure nothrow @safe @nogc
in in
{ {
assert(allocator !is null); assert(allocator !is null);
@ -77,28 +88,6 @@ struct RefCounted(T)
this.allocator = allocator; this.allocator = allocator;
} }
/**
* Allocates the internal storage.
*/
private void initialize()
{
static if (isReference!T)
{
counter = allocator.make!uint(1);
}
else
{
// Allocate for the counter and the payload together.
auto p = allocator.allocate(uint.sizeof + T.sizeof);
if (p is null)
{
onOutOfMemoryError();
}
counter = emplace(cast(uint*) p.ptr, 1);
payload = cast(T*) p[uint.sizeof .. $].ptr;
}
}
/** /**
* Increases the reference counter by one. * Increases the reference counter by one.
*/ */
@ -106,7 +95,7 @@ struct RefCounted(T)
{ {
if (isInitialized) if (isInitialized)
{ {
++(*counter); ++counter;
} }
} }
@ -117,15 +106,14 @@ struct RefCounted(T)
*/ */
~this() ~this()
{ {
if (!isInitialized || (--(*counter))) if (isInitialized && !--counter)
{ {
return; static if (isReference!T)
{
allocator.dispose(payload);
payload = null;
}
} }
allocator.dispose(payload);
payload = null;
allocator.dispose(counter);
counter = null;
} }
/** /**
@ -135,7 +123,7 @@ struct RefCounted(T)
* If it is the last reference of the previously owned object, * If it is the last reference of the previously owned object,
* it will be destroyed. * it will be destroyed.
* *
* If the allocator wasn't set before, $(D_PSYMBOL theAllocator) will * If the allocator wasn't set before, $(D_PSYMBOL defaultAllocator) will
* be used. If you need a different allocator, create a new * be used. If you need a different allocator, create a new
* $(D_PSYMBOL RefCounted). * $(D_PSYMBOL RefCounted).
* *
@ -144,21 +132,18 @@ struct RefCounted(T)
*/ */
ref T opAssign(T rhs) ref T opAssign(T rhs)
{ {
checkAllocator(); if (allocator is null)
if (isInitialized)
{ {
static if (isReference!T) allocator = defaultAllocator;
{
if (!--(*counter))
{
allocator.dispose(payload);
*counter = 1;
}
}
} }
else static if (isReference!T)
{ {
initialize(); counter == 1 ? allocator.dispose(payload) : --counter;
}
else if (!isInitialized)
{
payload = cast(T*) allocator.allocate(stateSize!T).ptr;
counter = 1;
} }
move(rhs, get); move(rhs, get);
return get; return get;
@ -212,7 +197,7 @@ struct RefCounted(T)
*/ */
@property uint count() const pure nothrow @safe @nogc @property uint count() const pure nothrow @safe @nogc
{ {
return counter is null ? 0 : *counter; return counter;
} }
/** /**
@ -220,11 +205,9 @@ struct RefCounted(T)
*/ */
@property bool isInitialized() const pure nothrow @safe @nogc @property bool isInitialized() const pure nothrow @safe @nogc
{ {
return counter !is null; return counter != 0;
} }
mixin StructAllocator;
alias get this; alias get this;
} }
@ -247,9 +230,11 @@ version (unittest)
struct B struct B
{ {
int prop;
@disable this(); @disable this();
this(int param1) this(int param1)
{ {
prop = param1;
} }
} }
} }
@ -269,7 +254,7 @@ unittest
} }
} }
auto arr = theAllocator.makeArray!ubyte(2); auto arr = defaultAllocator.makeArray!ubyte(2);
{ {
auto a = S(arr); auto a = S(arr);
assert(a.member.count == 1); assert(a.member.count == 1);
@ -288,7 +273,7 @@ unittest
private unittest private unittest
{ {
uint destroyed; uint destroyed;
auto a = theAllocator.make!A(destroyed); auto a = defaultAllocator.make!A(destroyed);
assert(destroyed == 0); assert(destroyed == 0);
{ {
@ -323,6 +308,7 @@ private unittest
static assert(!is(typeof(cast(int) (RefCounted!A())))); static assert(!is(typeof(cast(int) (RefCounted!A()))));
static assert(is(RefCounted!B)); static assert(is(RefCounted!B));
static assert(is(RefCounted!A));
} }
/** /**
@ -341,40 +327,26 @@ private unittest
* *
* Returns: Newly created $(D_PSYMBOL RefCounted!T). * Returns: Newly created $(D_PSYMBOL RefCounted!T).
*/ */
RefCounted!T refCounted(T, A...)(IAllocator allocator, auto ref A args) RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args)
if (!is(T == interface) && !isAbstractClass!T) if (!is(T == interface) && !isAbstractClass!T)
{ {
auto rc = typeof(return)(allocator); static if (isReference!T)
immutable toAllocate = max(stateSize!T, 1) + uint.sizeof;
auto p = allocator.allocate(toAllocate);
if (p is null)
{ {
onOutOfMemoryError(); return typeof(return)(allocator.make!T(args), allocator);
}
scope (failure)
{
allocator.deallocate(p);
}
rc.counter = emplace(cast(uint*) p.ptr, 1);
static if (is(T == class))
{
rc.payload = emplace!T(p[uint.sizeof .. $], args);
} }
else else
{ {
rc.payload = emplace(cast(T*) p[uint.sizeof .. $].ptr, args); auto rc = typeof(return)(allocator);
rc.counter = 1;
rc.payload = allocator.make!T(args);
return rc;
} }
return rc;
} }
/// ///
unittest unittest
{ {
auto rc = theAllocator.refCounted!int(5); auto rc = defaultAllocator.refCounted!int(5);
assert(rc.count == 1); assert(rc.count == 1);
void func(RefCounted!int param) void func(RefCounted!int param)
@ -395,7 +367,21 @@ unittest
private unittest private unittest
{ {
static assert(!is(theAllocator.refCounted!A)); struct E
static assert(!is(typeof(theAllocator.refCounted!B()))); {
static assert(is(typeof(theAllocator.refCounted!B(5)))); }
static assert(is(typeof(defaultAllocator.refCounted!bool(false))));
static assert(is(typeof(defaultAllocator.refCounted!B(5))));
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.isInitialized);
}
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -10,9 +10,10 @@
*/ */
module tanya.random; module tanya.random;
import tanya.memory; import std.experimental.allocator;
import std.digest.sha; import std.digest.sha;
import std.typecons; import std.typecons;
import tanya.memory;
/// Block size of entropy accumulator (SHA-512). /// Block size of entropy accumulator (SHA-512).
enum blockSize = 64; enum blockSize = 64;
@ -148,13 +149,13 @@ version (linux)
/** /**
* Pseudorandom number generator. * Pseudorandom number generator.
* --- * ---
* auto entropy = theAllocator.make!Entropy; * auto entropy = defaultAllocator.make!Entropy();
* *
* ubyte[blockSize] output; * ubyte[blockSize] output;
* *
* output = entropy.random; * output = entropy.random;
* *
* theAllocator.finalize(entropy); * defaultAllocator.finalize(entropy);
* --- * ---
*/ */
class Entropy class Entropy
@ -175,7 +176,7 @@ class Entropy
* allocator = Allocator to allocate entropy sources available on the * allocator = Allocator to allocate entropy sources available on the
* system. * system.
*/ */
this(size_t maxSources = 20, IAllocator allocator = theAllocator) this(size_t maxSources = 20, shared Allocator allocator = defaultAllocator)
in in
{ {
assert(maxSources > 0 && maxSources <= ubyte.max); assert(maxSources > 0 && maxSources <= ubyte.max);