From fa607141e4139bd8d72875bc7329409733e3fa85 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Tue, 6 Dec 2016 21:29:08 +0100 Subject: [PATCH] Make allocator shared and fix some RefCounted bugs --- source/tanya/async/event/epoll.d | 2 +- source/tanya/async/event/iocp.d | 9 +- source/tanya/async/event/kqueue.d | 2 +- source/tanya/async/event/selector.d | 4 +- source/tanya/async/loop.d | 16 +- source/tanya/async/watcher.d | 2 +- source/tanya/container/buffer.d | 80 +- source/tanya/container/list.d | 32 +- source/tanya/container/queue.d | 28 +- source/tanya/container/vector.d | 62 +- source/tanya/crypto/mode.d | 376 ++--- source/tanya/math/mp.d | 267 +-- source/tanya/memory/allocator.d | 234 +-- source/tanya/memory/mmappool.d | 777 +++++---- source/tanya/memory/package.d | 69 + source/tanya/memory/types.d | 148 +- source/tanya/network/socket.d | 2316 ++++++++++++++------------- source/tanya/network/url.d | 966 +++++------ source/tanya/random.d | 9 +- 19 files changed, 2682 insertions(+), 2717 deletions(-) diff --git a/source/tanya/async/event/epoll.d b/source/tanya/async/event/epoll.d index 7717b52..751ab82 100644 --- a/source/tanya/async/event/epoll.d +++ b/source/tanya/async/event/epoll.d @@ -107,7 +107,7 @@ class EpollLoop : SelectorLoop { if (errno != EINTR) { - throw theAllocator.make!BadLoopException(); + throw defaultAllocator.make!BadLoopException(); } return; } diff --git a/source/tanya/async/event/iocp.d b/source/tanya/async/event/iocp.d index 935dd5e..91fe71c 100644 --- a/source/tanya/async/event/iocp.d +++ b/source/tanya/async/event/iocp.d @@ -45,7 +45,7 @@ class IOCPStreamTransport : StreamTransport body { socket_ = socket; - input = MmapPool.instance.make!WriteBuffer(); + input = MmapPool.instance.make!WriteBuffer(8192, MmapPool.instance); } ~this() @@ -101,7 +101,8 @@ class IOCPStreamTransport : StreamTransport completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); 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) { MmapPool.instance.dispose(overlapped); - theAllocator.dispose(e); + defaultAllocator.dispose(e); return false; } } @@ -173,7 +174,7 @@ class IOCPStreamTransport : StreamTransport catch (SocketException e) { MmapPool.instance.dispose(overlapped); - theAllocator.dispose(e); + defaultAllocator.dispose(e); return false; } } diff --git a/source/tanya/async/event/kqueue.d b/source/tanya/async/event/kqueue.d index fa9261f..d7792ef 100644 --- a/source/tanya/async/event/kqueue.d +++ b/source/tanya/async/event/kqueue.d @@ -250,7 +250,7 @@ class KqueueLoop : SelectorLoop { if (errno != EINTR) { - throw theAllocator.make!BadLoopException(); + throw defaultAllocatorAllocator.make!BadLoopException(); } return; } diff --git a/source/tanya/async/event/selector.d b/source/tanya/async/event/selector.d index c35a782..003e1cd 100644 --- a/source/tanya/async/event/selector.d +++ b/source/tanya/async/event/selector.d @@ -46,7 +46,7 @@ class SelectorStreamTransport : StreamTransport { socket_ = socket; 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) { - theAllocator.dispose(e); + defaultAllocator.dispose(e); break; } if (client is null) diff --git a/source/tanya/async/loop.d b/source/tanya/async/loop.d index 15dfafd..e0f924b 100644 --- a/source/tanya/async/loop.d +++ b/source/tanya/async/loop.d @@ -34,31 +34,31 @@ * * 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) * { - * auto sock = theAllocator.make!OverlappedStreamSocket(AddressFamily.INET); + * auto sock = defaultAllocator.make!OverlappedStreamSocket(AddressFamily.INET); * } * else * { - * auto sock = theAllocator.make!StreamSocket(AddressFamily.INET); + * auto sock = defaultAllocator.make!StreamSocket(AddressFamily.INET); * sock.blocking = false; * } * * sock.bind(address); * sock.listen(5); * - * auto io = theAllocator.make!ConnectionWatcher(sock); + * auto io = defaultAllocator.make!ConnectionWatcher(sock); * io.setProtocol!EchoProtocol; * * defaultLoop.start(io); * defaultLoop.run(); * * sock.shutdown(); - * theAllocator.dispose(io); - * theAllocator.dispose(sock); - * theAllocator.dispose(address); + * defaultAllocator.dispose(io); + * defaultAllocator.dispose(sock); + * defaultAllocator.dispose(address); * } * --- */ @@ -278,7 +278,7 @@ abstract class Loop protected void kill(IOWatcher watcher, SocketException exception) { watcher.socket.shutdown(); - theAllocator.dispose(watcher.socket); + defaultAllocator.dispose(watcher.socket); MmapPool.instance.dispose(watcher.transport); watcher.exception = exception; swapPendings.insertBack(watcher); diff --git a/source/tanya/async/watcher.d b/source/tanya/async/watcher.d index 9a9e441..d376ae8 100644 --- a/source/tanya/async/watcher.d +++ b/source/tanya/async/watcher.d @@ -157,7 +157,7 @@ class IOWatcher : ConnectionWatcher super(); transport_ = transport; protocol_ = protocol; - output = MmapPool.instance.make!ReadBuffer(); + output = MmapPool.instance.make!ReadBuffer(8192, 1024, MmapPool.instance); active = true; } diff --git a/source/tanya/container/buffer.d b/source/tanya/container/buffer.d index ce7c476..3f60a06 100644 --- a/source/tanya/container/buffer.d +++ b/source/tanya/container/buffer.d @@ -108,30 +108,36 @@ class ReadBuffer : Buffer /// Size by which the buffer will grow. protected immutable size_t blockSize; + /// Allocator. + protected shared Allocator allocator; + invariant { assert(length_ <= buffer_.length); assert(blockSize > 0); assert(minAvailable > 0); + assert(allocator !is null); } /** * Creates a new read buffer. * * Params: - * size = Initial buffer size and the size by which the buffer - * will grow. - * minAvailable = minimal size should be always available to fill. - * So it will reallocate if $(D_INLINECODE - * $(D_PSYMBOL free) < $(D_PARAM minAvailable) - * ). + * size = Initial buffer size and the size by which the buffer + * will grow. + * minAvailable = minimal size should be always available to fill. + * So it will reallocate if $(D_INLINECODE + * $(D_PSYMBOL free) < $(D_PARAM minAvailable)). + * allocator = Allocator. */ this(size_t size = 8192, - size_t minAvailable = 1024) + size_t minAvailable = 1024, + shared Allocator allocator = defaultAllocator) { this.minAvailable = minAvailable; this.blockSize = size; - theAllocator.resizeArray!ubyte(buffer_, size); + this.allocator = allocator; + allocator.resizeArray!ubyte(buffer_, size); } /** @@ -139,17 +145,17 @@ class ReadBuffer : Buffer */ ~this() { - theAllocator.dispose(buffer_); + allocator.dispose(buffer_); } /// unittest { - auto b = theAllocator.make!ReadBuffer; + auto b = defaultAllocator.make!ReadBuffer; assert(b.capacity == 8192); assert(b.length == 0); - theAllocator.dispose(b); + defaultAllocator.dispose(b); } /** @@ -190,7 +196,7 @@ class ReadBuffer : Buffer /// unittest { - auto b = theAllocator.make!ReadBuffer; + auto b = defaultAllocator.make!ReadBuffer; size_t numberRead; // Fills the buffer with values 0..10 @@ -202,7 +208,7 @@ class ReadBuffer : Buffer b.clear(); assert(b.free == b.blockSize); - theAllocator.dispose(b); + defaultAllocator.dispose(b); } /** @@ -224,7 +230,7 @@ class ReadBuffer : Buffer /// unittest { - auto b = theAllocator.make!ReadBuffer; + auto b = defaultAllocator.make!ReadBuffer; size_t numberRead; ubyte[] result; @@ -252,7 +258,7 @@ class ReadBuffer : Buffer assert(result[10] == 20); assert(result[14] == 24); - theAllocator.dispose(b); + defaultAllocator.dispose(b); } /** @@ -294,7 +300,7 @@ class ReadBuffer : Buffer { if (capacity - length < minAvailable) { - theAllocator.resizeArray!ubyte(buffer_, capacity + blockSize); + allocator.resizeArray!ubyte(buffer_, capacity + blockSize); } ring = length_; return buffer_[length_..$]; @@ -304,7 +310,7 @@ class ReadBuffer : Buffer /// unittest { - auto b = theAllocator.make!ReadBuffer; + auto b = defaultAllocator.make!ReadBuffer; size_t numberRead; ubyte[] result; @@ -319,7 +325,7 @@ class ReadBuffer : Buffer b.clear(); 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. protected size_t position; + /// Allocator. + protected shared Allocator allocator; + invariant { assert(blockSize > 0); // position can refer to an element outside the buffer if the buffer is full. assert(position <= buffer_.length); + assert(allocator !is null); } /** * Params: - * size = Initial buffer size and the size by which the buffer - * will grow. + * size = Initial buffer size and the size by which the buffer will + * grow. + * allocator = Allocator. */ - this(size_t size = 8192) + this(size_t size = 8192, shared Allocator allocator = defaultAllocator) { + this.allocator = allocator; blockSize = size; ring = size - 1; - theAllocator.resizeArray!ubyte(buffer_, size); + allocator.resizeArray!ubyte(buffer_, size); } /** @@ -373,7 +385,7 @@ class WriteBuffer : Buffer */ ~this() { - theAllocator.dispose(buffer_); + allocator.dispose(buffer_); } /** @@ -415,7 +427,7 @@ class WriteBuffer : Buffer /// unittest { - auto b = theAllocator.make!WriteBuffer(4); + auto b = defaultAllocator.make!WriteBuffer(4); ubyte[3] buf = [48, 23, 255]; b ~= buf; @@ -433,7 +445,7 @@ class WriteBuffer : Buffer b += b.length; assert(b.length == 0); - theAllocator.dispose(b); + defaultAllocator.dispose(b); } /** @@ -498,7 +510,7 @@ class WriteBuffer : Buffer { auto newSize = end / blockSize * blockSize + blockSize; - theAllocator.resizeArray!ubyte(buffer_, newSize); + allocator.resizeArray!ubyte(buffer_, newSize); } buffer_[position..end] = buffer[start..$]; position = end; @@ -514,7 +526,7 @@ class WriteBuffer : Buffer /// unittest { - auto b = theAllocator.make!WriteBuffer(4); + auto b = defaultAllocator.make!WriteBuffer(4); ubyte[3] buf = [48, 23, 255]; b ~= buf; @@ -533,9 +545,9 @@ class WriteBuffer : Buffer assert(b.buffer_[0] == 23 && b.buffer_[1] == 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; assert(b.start == 0); @@ -543,7 +555,7 @@ class WriteBuffer : Buffer assert(b.ring == 3); assert(b.position == 3); - theAllocator.dispose(b); + defaultAllocator.dispose(b); } /** @@ -620,7 +632,7 @@ class WriteBuffer : Buffer /// unittest { - auto b = theAllocator.make!WriteBuffer; + auto b = defaultAllocator.make!WriteBuffer; ubyte[6] buf = [23, 23, 255, 128, 127, 9]; b ~= buf; @@ -630,7 +642,7 @@ class WriteBuffer : Buffer b += 4; assert(b.length == 0); - theAllocator.dispose(b); + defaultAllocator.dispose(b); } /** @@ -663,7 +675,7 @@ class WriteBuffer : Buffer /// unittest { - auto b = theAllocator.make!WriteBuffer(6); + auto b = defaultAllocator.make!WriteBuffer(6); ubyte[6] buf = [23, 23, 255, 128, 127, 9]; b ~= buf; @@ -679,7 +691,7 @@ class WriteBuffer : Buffer assert(b[0..$] == buf[0..6]); b += b.length; - theAllocator.dispose(b); + defaultAllocator.dispose(b); } /** diff --git a/source/tanya/container/list.d b/source/tanya/container/list.d index 3e83ee4..0ce7e4f 100644 --- a/source/tanya/container/list.d +++ b/source/tanya/container/list.d @@ -27,7 +27,7 @@ class SList(T) * allocator = The allocator should be used for the element * allocations. */ - this(IAllocator allocator = theAllocator) + this(shared Allocator allocator = defaultAllocator) { this.allocator = allocator; } @@ -54,14 +54,14 @@ class SList(T) /// unittest { - auto l = make!(SList!int)(theAllocator); + auto l = make!(SList!int)(defaultAllocator); l.insertFront(8); l.insertFront(5); l.clear(); assert(l.empty); - dispose(theAllocator, l); + dispose(defaultAllocator, l); } /** @@ -98,14 +98,14 @@ class SList(T) /// unittest { - auto l = make!(SList!int)(theAllocator); + auto l = make!(SList!int)(defaultAllocator); l.insertFront(8); assert(l.front == 8); l.insertFront(9); assert(l.front == 9); - dispose(theAllocator, l); + dispose(defaultAllocator, l); } /** @@ -140,7 +140,7 @@ class SList(T) /// unittest { - auto l = make!(SList!int)(theAllocator); + auto l = make!(SList!int)(defaultAllocator); l.insertFront(8); l.insertFront(9); @@ -148,7 +148,7 @@ class SList(T) l.popFront(); assert(l.front == 8); - dispose(theAllocator, l); + dispose(defaultAllocator, l); } /** @@ -179,7 +179,7 @@ class SList(T) /// unittest { - auto l = make!(SList!int)(theAllocator); + auto l = make!(SList!int)(defaultAllocator); l.insertFront(8); l.insertFront(5); @@ -189,7 +189,7 @@ class SList(T) assert(l.removeFront(3) == 1); assert(l.removeFront(3) == 0); - dispose(theAllocator, l); + dispose(defaultAllocator, l); } /** @@ -235,7 +235,7 @@ class SList(T) /// unittest { - auto l = make!(SList!int)(theAllocator); + auto l = make!(SList!int)(defaultAllocator); l.insertFront(5); l.insertFront(4); @@ -246,7 +246,7 @@ class SList(T) assert(i != 1 || e == 4); assert(i != 2 || e == 5); } - dispose(theAllocator, l); + dispose(defaultAllocator, l); } /** @@ -265,13 +265,13 @@ class SList(T) protected Entry first; /// Allocator. - protected IAllocator allocator; + protected shared Allocator allocator; } /// unittest { - auto l = make!(SList!int)(theAllocator); + auto l = make!(SList!int)(defaultAllocator); size_t i; l.insertFront(5); @@ -286,7 +286,7 @@ unittest } assert(i == 3); - dispose(theAllocator, l); + dispose(defaultAllocator, l); } 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); } diff --git a/source/tanya/container/queue.d b/source/tanya/container/queue.d index cee163d..d525949 100644 --- a/source/tanya/container/queue.d +++ b/source/tanya/container/queue.d @@ -27,7 +27,7 @@ class Queue(T) * allocator = The allocator should be used for the element * allocations. */ - this(IAllocator allocator = theAllocator) + this(shared Allocator allocator = defaultAllocator) { this.allocator = allocator; } @@ -54,7 +54,7 @@ class Queue(T) /// unittest { - auto q = theAllocator.make!(Queue!int); + auto q = defaultAllocator.make!(Queue!int); assert(q.empty); q.insertBack(8); @@ -62,7 +62,7 @@ class Queue(T) q.clear(); assert(q.empty); - theAllocator.dispose(q); + defaultAllocator.dispose(q); } /** @@ -107,7 +107,7 @@ class Queue(T) /// unittest { - auto q = make!(Queue!int)(theAllocator); + auto q = make!(Queue!int)(defaultAllocator); assert(q.empty); q.insertBack(8); @@ -115,7 +115,7 @@ class Queue(T) q.insertBack(9); assert(q.front == 8); - dispose(theAllocator, q); + dispose(defaultAllocator, q); } /** @@ -129,14 +129,14 @@ class Queue(T) /// unittest { - auto q = make!(Queue!int)(theAllocator); + auto q = make!(Queue!int)(defaultAllocator); int value = 7; assert(q.empty); q.insertBack(value); assert(!q.empty); - dispose(theAllocator, q); + dispose(defaultAllocator, q); } /** @@ -158,7 +158,7 @@ class Queue(T) /// unittest { - auto q = make!(Queue!int)(theAllocator); + auto q = make!(Queue!int)(defaultAllocator); q.insertBack(8); q.insertBack(9); @@ -166,7 +166,7 @@ class Queue(T) q.popFront(); assert(q.front == 9); - dispose(theAllocator, q); + dispose(defaultAllocator, q); } /** @@ -210,7 +210,7 @@ class Queue(T) /// unittest { - auto q = theAllocator.make!(Queue!int); + auto q = defaultAllocator.make!(Queue!int); size_t j; q.insertBack(5); @@ -240,7 +240,7 @@ class Queue(T) assert(j == 3); assert(q.empty); - dispose(theAllocator, q); + dispose(defaultAllocator, q); } /** @@ -262,13 +262,13 @@ class Queue(T) protected Entry* rear; /// The allocator. - protected IAllocator allocator; + protected shared Allocator allocator; } /// unittest { - auto q = theAllocator.make!(Queue!int); + auto q = defaultAllocator.make!(Queue!int); q.insertBack(5); assert(!q.empty); @@ -289,5 +289,5 @@ unittest } assert(q.empty); - theAllocator.dispose(q); + defaultAllocator.dispose(q); } diff --git a/source/tanya/container/vector.d b/source/tanya/container/vector.d index 90793ad..daa867c 100644 --- a/source/tanya/container/vector.d +++ b/source/tanya/container/vector.d @@ -200,7 +200,7 @@ class Vector(T) * allocator = The allocator should be used for the element * allocations. */ - this(IAllocator allocator = theAllocator) + this(shared Allocator allocator = defaultAllocator) { this.allocator = allocator; } @@ -211,18 +211,18 @@ class Vector(T) * Params: * U = Variadic template for the constructor parameters. * params = Values to initialize the array with. The last parameter can - * be an allocator, if not, $(D_PSYMBOL theAllocator) is used. + * be an allocator, if not, $(D_PSYMBOL defaultAllocator) is used. */ this(U...)(U params) { - static if (isImplicitlyConvertible!(typeof(params[$ - 1]), IAllocator)) + static if (isImplicitlyConvertible!(typeof(params[$ - 1]), Allocator)) { allocator = params[$ - 1]; auto values = params[0 .. $ - 1]; } else { - allocator = theAllocator; + allocator = defaultAllocator; alias values = params; } @@ -252,7 +252,7 @@ class Vector(T) /// unittest { - auto v = theAllocator.make!(Vector!int)(18, 20, 15); + auto v = defaultAllocator.make!(Vector!int)(18, 20, 15); v.clear(); assert(v.length == 0); @@ -286,7 +286,7 @@ class Vector(T) /// unittest { - auto v = theAllocator.make!(Vector!int); + auto v = defaultAllocator.make!(Vector!int); v.length = 5; assert(v.length == 5); @@ -337,14 +337,14 @@ class Vector(T) /// 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(2) == 2); assert(v.removeBack(3) == 1); assert(v.removeBack(3) == 0); - theAllocator.dispose(v); + defaultAllocator.dispose(v); } /** @@ -377,14 +377,14 @@ class Vector(T) /// unittest { - auto v1 = theAllocator.make!(Vector!int)(12, 1, 7); + auto v1 = defaultAllocator.make!(Vector!int)(12, 1, 7); v1[] = 3; assert(v1[0] == 3); assert(v1[1] == 3); assert(v1[2] == 3); - theAllocator.dispose(v1); + defaultAllocator.dispose(v1); } @@ -406,14 +406,14 @@ class Vector(T) /// 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[1] == 123); assert(v[2] == 34); assert(v[3] == 5); - theAllocator.dispose(v); + defaultAllocator.dispose(v); } /** @@ -435,8 +435,8 @@ class Vector(T) /// unittest { - auto v1 = theAllocator.make!(Vector!int); - auto v2 = theAllocator.make!(Vector!int); + auto v1 = defaultAllocator.make!(Vector!int); + auto v2 = defaultAllocator.make!(Vector!int); assert(v1 == v2); @@ -453,8 +453,8 @@ class Vector(T) v2[1] = 3; assert(v1 == v2); - theAllocator.dispose(v1); - theAllocator.dispose(v2); + defaultAllocator.dispose(v1); + defaultAllocator.dispose(v2); } /** @@ -495,7 +495,7 @@ class Vector(T) /// unittest { - auto v = theAllocator.make!(Vector!int)(5, 15, 8); + auto v = defaultAllocator.make!(Vector!int)(5, 15, 8); size_t i; foreach (j, ref e; v) @@ -510,7 +510,7 @@ class Vector(T) assert(j != 1 || e == 15); assert(j != 2 || e == 8); } - theAllocator.dispose(v); + defaultAllocator.dispose(v); } /** @@ -551,7 +551,7 @@ class Vector(T) /// unittest { - auto v = theAllocator.make!(Vector!int)(5, 15, 8); + auto v = defaultAllocator.make!(Vector!int)(5, 15, 8); size_t i; foreach_reverse (j, ref e; v) @@ -566,7 +566,7 @@ class Vector(T) assert(j != 1 || e == 15); assert(j != 0 || e == 5); } - theAllocator.dispose(v); + defaultAllocator.dispose(v); } /** @@ -587,7 +587,7 @@ class Vector(T) /// unittest { - auto v = theAllocator.make!(Vector!int)(5); + auto v = defaultAllocator.make!(Vector!int)(5); assert(v.front == 5); @@ -595,7 +595,7 @@ class Vector(T) v[1] = 15; assert(v.front == 5); - theAllocator.dispose(v); + defaultAllocator.dispose(v); } /** @@ -616,7 +616,7 @@ class Vector(T) /// unittest { - auto v = theAllocator.make!(Vector!int)(5); + auto v = defaultAllocator.make!(Vector!int)(5); assert(v.back == 5); @@ -624,7 +624,7 @@ class Vector(T) v[1] = 15; assert(v.back == 15); - theAllocator.dispose(v); + defaultAllocator.dispose(v); } /** @@ -745,8 +745,8 @@ class Vector(T) /// unittest { - auto v1 = theAllocator.make!(Vector!int)(3, 3, 3); - auto v2 = theAllocator.make!(Vector!int)(1, 2); + auto v1 = defaultAllocator.make!(Vector!int)(3, 3, 3); + auto v2 = defaultAllocator.make!(Vector!int)(1, 2); v1[0..2] = 286; assert(v1[0] == 286); @@ -757,25 +757,25 @@ class Vector(T) assert(v2[0] == 286); assert(v2[1] == 3); - theAllocator.dispose(v2); - theAllocator.dispose(v1); + defaultAllocator.dispose(v2); + defaultAllocator.dispose(v1); } /// Internal representation. protected T[] vector; /// The allocator. - protected IAllocator allocator; + protected shared Allocator allocator; } /// 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[1] == 15); assert(v.back == 8); - theAllocator.dispose(v); + defaultAllocator.dispose(v); } diff --git a/source/tanya/crypto/mode.d b/source/tanya/crypto/mode.d index cab543a..0179f89 100644 --- a/source/tanya/crypto/mode.d +++ b/source/tanya/crypto/mode.d @@ -20,260 +20,260 @@ import std.typecons; * Supported padding mode. * * See_Also: - * $(D_PSYMBOL pad) + * $(D_PSYMBOL pad) */ enum PaddingMode { - zero, - pkcs7, - ansiX923, + zero, + pkcs7, + ansiX923, } /** * Params: - * input = Sequence that should be padded. - * mode = Padding mode. - * blockSize = Block size. - * allocator = Allocator was used to allocate $(D_PARAM input). + * input = Sequence that should be padded. + * mode = Padding mode. + * blockSize = Block size. + * allocator = Allocator was used to allocate $(D_PARAM input). * * Returns: The function modifies the initial array and returns it. * * See_Also: - * $(D_PSYMBOL PaddingMode) + * $(D_PSYMBOL PaddingMode) */ ubyte[] pad(ref ubyte[] input, in PaddingMode mode, in ushort blockSize, - IAllocator allocator = theAllocator) + shared Allocator allocator = defaultAllocator) in { - assert(blockSize > 0 && blockSize <= 256); - assert(blockSize % 64 == 0); - assert(input.length > 0); + assert(blockSize > 0 && blockSize <= 256); + assert(blockSize % 64 == 0); + assert(input.length > 0); } body { - immutable rest = cast(ubyte) input.length % blockSize; - immutable size_t lastBlock = input.length - (rest > 0 ? rest : blockSize); - immutable needed = cast(ubyte) (rest > 0 ? blockSize - rest : 0); + immutable rest = cast(ubyte) input.length % blockSize; + immutable size_t lastBlock = input.length - (rest > 0 ? rest : blockSize); + immutable needed = cast(ubyte) (rest > 0 ? blockSize - rest : 0); - final switch (mode) with (PaddingMode) - { - case zero: - allocator.expandArray(input, needed); - break; - case pkcs7: - if (needed) - { - allocator.expandArray(input, needed); - input[input.length - needed ..$].each!((ref e) => e = needed); - } - else - { - allocator.expandArray(input, blockSize); - } - break; - case ansiX923: - allocator.expandArray(input, needed ? needed : blockSize); - input[$ - 1] = needed; - break; - } + final switch (mode) with (PaddingMode) + { + case zero: + allocator.expandArray(input, needed); + break; + case pkcs7: + if (needed) + { + allocator.expandArray(input, needed); + input[input.length - needed ..$].each!((ref e) => e = needed); + } + else + { + allocator.expandArray(input, blockSize); + } + break; + case ansiX923: + allocator.expandArray(input, needed ? needed : blockSize); + input[$ - 1] = needed; + break; + } - return input; + return input; } /// unittest { - { // Zeros - auto input = theAllocator.makeArray!ubyte(50); + { // Zeros + auto input = defaultAllocator.makeArray!ubyte(50); - pad(input, PaddingMode.zero, 64); - assert(input.length == 64); + pad(input, PaddingMode.zero, 64); + assert(input.length == 64); - pad(input, PaddingMode.zero, 64); - assert(input.length == 64); - assert(input[63] == 0); + pad(input, PaddingMode.zero, 64); + assert(input.length == 64); + assert(input[63] == 0); - theAllocator.dispose(input); - } - { // PKCS#7 - auto input = theAllocator.makeArray!ubyte(50); - for (ubyte i; i < 40; ++i) - { - input[i] = i; - } + defaultAllocator.dispose(input); + } + { // PKCS#7 + auto input = defaultAllocator.makeArray!ubyte(50); + for (ubyte i; i < 40; ++i) + { + input[i] = i; + } - pad(input, PaddingMode.pkcs7, 64); - assert(input.length == 64); - for (ubyte i; i < 64; ++i) - { - if (i >= 40 && i < 50) - { - assert(input[i] == 0); - } - else if (i >= 50) - { - assert(input[i] == 14); - } - else - { - assert(input[i] == i); - } - } + pad(input, PaddingMode.pkcs7, 64); + assert(input.length == 64); + for (ubyte i; i < 64; ++i) + { + if (i >= 40 && i < 50) + { + assert(input[i] == 0); + } + else if (i >= 50) + { + assert(input[i] == 14); + } + else + { + assert(input[i] == i); + } + } - pad(input, PaddingMode.pkcs7, 64); - assert(input.length == 128); - for (ubyte i; i < 128; ++i) - { - if (i >= 64 || (i >= 40 && i < 50)) - { - assert(input[i] == 0); - } - else if (i >= 50 && i < 64) - { - assert(input[i] == 14); - } - else - { - assert(input[i] == i); - } - } + pad(input, PaddingMode.pkcs7, 64); + assert(input.length == 128); + for (ubyte i; i < 128; ++i) + { + if (i >= 64 || (i >= 40 && i < 50)) + { + assert(input[i] == 0); + } + else if (i >= 50 && i < 64) + { + assert(input[i] == 14); + } + else + { + assert(input[i] == i); + } + } - theAllocator.dispose(input); - } - { // ANSI X.923 - auto input = theAllocator.makeArray!ubyte(50); - for (ubyte i; i < 40; ++i) - { - input[i] = i; - } + defaultAllocator.dispose(input); + } + { // ANSI X.923 + auto input = defaultAllocator.makeArray!ubyte(50); + for (ubyte i; i < 40; ++i) + { + input[i] = i; + } - pad(input, PaddingMode.ansiX923, 64); - assert(input.length == 64); - for (ubyte i; i < 64; ++i) - { - if (i < 40) - { - assert(input[i] == i); - } - else if (i == 63) - { - assert(input[i] == 14); - } - else - { - assert(input[i] == 0); - } - } + pad(input, PaddingMode.ansiX923, 64); + assert(input.length == 64); + for (ubyte i; i < 64; ++i) + { + if (i < 40) + { + assert(input[i] == i); + } + else if (i == 63) + { + assert(input[i] == 14); + } + else + { + assert(input[i] == 0); + } + } - pad(input, PaddingMode.pkcs7, 64); - assert(input.length == 128); - for (ubyte i = 0; i < 128; ++i) - { - if (i < 40) - { - assert(input[i] == i); - } - else if (i == 63) - { - assert(input[i] == 14); - } - else - { - assert(input[i] == 0); - } - } + pad(input, PaddingMode.pkcs7, 64); + assert(input.length == 128); + for (ubyte i = 0; i < 128; ++i) + { + if (i < 40) + { + assert(input[i] == i); + } + else if (i == 63) + { + assert(input[i] == 14); + } + else + { + assert(input[i] == 0); + } + } - theAllocator.dispose(input); - } + defaultAllocator.dispose(input); + } } /** * Params: - * input = Sequence that should be padded. - * mode = Padding mode. - * blockSize = Block size. - * allocator = Allocator was used to allocate $(D_PARAM input). + * input = Sequence that should be padded. + * mode = Padding mode. + * blockSize = Block size. + * allocator = Allocator was used to allocate $(D_PARAM input). * * Returns: The function modifies the initial array and returns it. * * See_Also: - * $(D_PSYMBOL pad) + * $(D_PSYMBOL pad) */ ref ubyte[] unpad(ref ubyte[] input, in PaddingMode mode, in ushort blockSize, - IAllocator allocator = theAllocator) + shared Allocator allocator = defaultAllocator) in { - assert(input.length != 0); - assert(input.length % 64 == 0); + assert(input.length != 0); + assert(input.length % 64 == 0); } body { - final switch (mode) with (PaddingMode) - { - case zero: - break; - case pkcs7: - case ansiX923: - immutable last = input[$ - 1]; + final switch (mode) with (PaddingMode) + { + case zero: + break; + case pkcs7: + case ansiX923: + immutable last = input[$ - 1]; - allocator.shrinkArray(input, last ? last : blockSize); - break; - } + allocator.shrinkArray(input, last ? last : blockSize); + break; + } - return input; + return input; } /// unittest { - { // Zeros - auto input = theAllocator.makeArray!ubyte(50); - auto inputDup = theAllocator.makeArray!ubyte(50); + { // Zeros + auto input = defaultAllocator.makeArray!ubyte(50); + auto inputDup = defaultAllocator.makeArray!ubyte(50); - pad(input, PaddingMode.zero, 64); - pad(inputDup, PaddingMode.zero, 64); + pad(input, PaddingMode.zero, 64); + pad(inputDup, PaddingMode.zero, 64); - unpad(input, PaddingMode.zero, 64); - assert(input == inputDup); + unpad(input, PaddingMode.zero, 64); + assert(input == inputDup); - theAllocator.dispose(input); - theAllocator.dispose(inputDup); + defaultAllocator.dispose(input); + defaultAllocator.dispose(inputDup); - } - { // PKCS#7 - auto input = theAllocator.makeArray!ubyte(50); - auto inputDup = theAllocator.makeArray!ubyte(50); - for (ubyte i; i < 40; ++i) - { - input[i] = i; - inputDup[i] = i; - } + } + { // PKCS#7 + auto input = defaultAllocator.makeArray!ubyte(50); + auto inputDup = defaultAllocator.makeArray!ubyte(50); + for (ubyte i; i < 40; ++i) + { + input[i] = i; + inputDup[i] = i; + } - pad(input, PaddingMode.pkcs7, 64); - unpad(input, PaddingMode.pkcs7, 64); - assert(input == inputDup); + pad(input, PaddingMode.pkcs7, 64); + unpad(input, PaddingMode.pkcs7, 64); + assert(input == inputDup); - theAllocator.dispose(input); - theAllocator.dispose(inputDup); - } - { // ANSI X.923 - auto input = theAllocator.makeArray!ubyte(50); - auto inputDup = theAllocator.makeArray!ubyte(50); - for (ubyte i; i < 40; ++i) - { - input[i] = i; - inputDup[i] = i; - } + defaultAllocator.dispose(input); + defaultAllocator.dispose(inputDup); + } + { // ANSI X.923 + auto input = defaultAllocator.makeArray!ubyte(50); + auto inputDup = defaultAllocator.makeArray!ubyte(50); + for (ubyte i; i < 40; ++i) + { + input[i] = i; + inputDup[i] = i; + } - pad(input, PaddingMode.pkcs7, 64); - unpad(input, PaddingMode.pkcs7, 64); - assert(input == inputDup); + pad(input, PaddingMode.pkcs7, 64); + unpad(input, PaddingMode.pkcs7, 64); + assert(input == inputDup); - theAllocator.dispose(input); - theAllocator.dispose(inputDup); - } + defaultAllocator.dispose(input); + defaultAllocator.dispose(inputDup); + } } diff --git a/source/tanya/math/mp.d b/source/tanya/math/mp.d index 9cbbd0f..b371669 100644 --- a/source/tanya/math/mp.d +++ b/source/tanya/math/mp.d @@ -13,17 +13,16 @@ module tanya.math.mp; import std.algorithm.iteration; import std.algorithm.searching; import std.algorithm.mutation; -import std.experimental.allocator; import std.math; import std.range; import std.traits; -import tanya.memory.allocator; -import tanya.memory.types; +import tanya.memory; struct Integer { private RefCounted!(ubyte[]) rep; private bool sign; + private shared Allocator allocator; invariant { @@ -34,10 +33,11 @@ struct Integer * Creates a multiple precision integer. * * Params: + * T = Value type. * value = Initial value. * allocator = Allocator. */ - this(T)(in T value, IAllocator allocator = theAllocator) + this(T)(in T value, shared Allocator allocator = defaultAllocator) if (isIntegral!T) in { @@ -47,21 +47,30 @@ struct Integer { this(allocator); - immutable size = calculateSizeFromInt(value); + T absolute = value; + immutable size = calculateSizeFromInt(absolute); allocator.resizeArray(rep, size); - assignInt(value); + assignInt(absolute); } /// unittest { - auto h = Integer(79); - assert(h.length == 1); - assert(h.rep[0] == 79); + { + auto h = Integer(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. - this(in Integer value, IAllocator allocator = theAllocator) + this(in Integer value, shared Allocator allocator = defaultAllocator) in { assert(allocator !is null); @@ -76,21 +85,35 @@ struct Integer } /// Ditto. - this(IAllocator allocator) + this(shared Allocator allocator) { this.allocator = allocator; rep = RefCounted!(ubyte[])(allocator); } /* - * Figure out the minimum amount of space this value will take - * up in bytes. + * Figures out the minimum amount of space this value will take + * up in bytes. Set the sign. */ - pragma(inline, true) - private ubyte calculateSizeFromInt(in ulong value) - const pure nothrow @safe @nogc + private ubyte calculateSizeFromInt(T)(ref T value) + pure nothrow @safe @nogc + in + { + static assert(isIntegral!T); + } + body { 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) { if (value & mask) @@ -107,40 +130,42 @@ struct Integer * (up to the first 0 byte) and copy it into the internal * representation in big-endian format. */ - pragma(inline, true) - private void assignInt(T)(ref in T value) + private void assignInt(in ulong value) pure nothrow @safe @nogc - in - { - static assert(isIntegral!T); - } - body { uint mask = 0xff, shift; - immutable absolute = abs(value); - sign = value < 0 ? true : false; for (auto i = length; i; --i) { - rep[i - 1] = cast(ubyte) ((absolute & mask) >> shift); + rep[i - 1] = cast(ubyte) ((value & mask) >> shift); mask <<= 8; shift += 8; } - } + /** + * Assigns a new value. + * + * Params: + * T = Value type. + * value = Value. + * + * Returns: $(D_KEYWORD this). + */ ref Integer opAssign(T)(in T value) if (isIntegral!T) { - immutable size = calculateSizeFromInt(value); + T absolute = value; + immutable size = calculateSizeFromInt(absolute); checkAllocator(); allocator.resizeArray(rep.get, size); - assignInt(value); + assignInt(absolute); return this; } + /// Ditto. ref Integer opAssign(in Integer value) { checkAllocator(); @@ -251,23 +276,11 @@ struct Integer assert(h1 > h2); } - /** - * 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 == "+") + private void add(in ref RefCounted!(ubyte[]) h) { uint sum; uint carry = 0; - checkAllocator(); - // Adding h2 to h1. If h2 is > h1 to begin with, resize h1 if (h.length > length) @@ -286,7 +299,7 @@ struct Integer if (j) { --j; - sum = rep[i] + h.rep[j] + carry; + sum = rep[i] + h[j] + carry; } else { @@ -305,36 +318,15 @@ struct Integer tmp[0] = 0x01; rep = tmp; } - return this; + } - /// - 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 == "-") + private void subtract(in ref RefCounted!(ubyte[]) h) { auto i = rep.length; - auto j = h.rep.length; + auto j = h.length; uint borrow = 0; - checkAllocator(); - do { int difference; @@ -343,7 +335,7 @@ struct Integer if (j) { --j; - difference = rep[i] - h.rep[j] - borrow; + difference = rep[i] - h[j] - borrow; } else { @@ -371,11 +363,49 @@ struct Integer { 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; } - /// - unittest + private 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 h2 = Integer(4294967295); @@ -417,8 +447,7 @@ struct Integer } do { - --i; - --j; + --i, --j; immutable oldCarry = carry; carry = rep[i] >> delta; rep[j] = cast(ubyte) ((rep[i] << bit) | oldCarry); @@ -546,6 +575,11 @@ struct Integer /// Ditto. ref Integer opOpAssign(string op)(in Integer h) if ((op == "/") || (op == "%")) + in + { + assert(h.length > 0, "Division by zero."); + } + body { checkAllocator(); @@ -715,6 +749,40 @@ struct Integer 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. * @@ -728,45 +796,28 @@ struct Integer { checkAllocator(); - if (op == "++" || sign || length == 0) + static if (op == "++") { - static if (op == "--") + if (sign) { - sign = true; - } - 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; + decrement(); + if (length == 0) + { + sign = false; + } } else { - ++rep[$ - size - 1]; + increment(); } - rep[$ - size .. $] = 0; + } + else if (sign) + { + increment(); } else { - 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; - } - if (rep.length == 0) - { - sign = false; - } + decrement(); } return this; } @@ -803,7 +854,17 @@ struct Integer --h; 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; + } + } } diff --git a/source/tanya/memory/allocator.d b/source/tanya/memory/allocator.d index cf86730..c8d0b42 100644 --- a/source/tanya/memory/allocator.d +++ b/source/tanya/memory/allocator.d @@ -10,214 +10,42 @@ */ module tanya.memory.allocator; -import std.algorithm.mutation; -import std.experimental.allocator; -import std.typecons; - /** * Abstract class implementing a basic allocator. */ -abstract class Allocator : IAllocator +interface Allocator { - /** - * Not supported. - * - * Returns: $(D_KEYWORD false). - */ - bool deallocateAll() const @nogc @safe pure nothrow - { - return false; - } +@nogc: + @property uint alignment() const shared pure nothrow @safe; - /** - * Not supported. - * - * Returns $(D_PSYMBOL Ternary.unknown). - */ - Ternary empty() const @nogc @safe pure nothrow - { - return Ternary.unknown; - } + /** + * Allocates $(D_PARAM size) bytes of memory. + * + * Params: + * size = Amount of memory to allocate. + * + * Returns: The pointer to the new allocated memory. + */ + void[] allocate(size_t size, TypeInfo ti = null) shared nothrow @safe; - /** - * Not supported. - * - * Params: - * b = Memory block. - * - * Returns: $(D_PSYMBOL Ternary.unknown). - */ - Ternary owns(void[] b) const @nogc @safe pure nothrow - { - return Ternary.unknown; - } + /** + * Deallocates a memory block. + * + * Params: + * p = A pointer to the memory block to be freed. + * + * Returns: Whether the deallocation was successful. + */ + bool deallocate(void[] p) shared nothrow @safe; - /** - * Not supported. - * - * Params: - * p = Pointer to a memory block. - * result = Full block allocated. - * - * Returns: $(D_PSYMBOL Ternary.unknown). - */ - Ternary resolveInternalPointer(void* p, ref void[] result) - 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; - } - } + /** + * 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 @safe; } diff --git a/source/tanya/memory/mmappool.d b/source/tanya/memory/mmappool.d index 0e82af2..9c7da4d 100644 --- a/source/tanya/memory/mmappool.d +++ b/source/tanya/memory/mmappool.d @@ -16,14 +16,14 @@ import core.exception; version (Posix) { - import core.stdc.errno; - import core.sys.posix.sys.mman; - import core.sys.posix.unistd; + import core.stdc.errno; + import core.sys.posix.sys.mman; + import core.sys.posix.unistd; } else version (Windows) { - import core.sys.windows.winbase; - import core.sys.windows.windows; + import core.sys.windows.winbase; + import core.sys.windows.windows; } /** @@ -49,437 +49,436 @@ else version (Windows) * --------------------------------------------------- ------------------------ * * TODO: - * $(UL - * $(LI Thread safety (core.atomic.cas)) - * $(LI If two neighbour blocks are free, they can be merged) - * $(LI Reallocation shoud check if there is enough free space in the - * next block instead of always moving the memory) - * $(LI Make 64 KB regions mininmal region size on Linux) - * ) + * $(UL + * $(LI Thread safety (core.atomic.cas)) + * $(LI If two neighbour blocks are free, they can be merged) + * $(LI Reallocation shoud check if there is enough free space in the + * next block instead of always moving the memory) + * $(LI Make 64 KB regions mininmal region size on Linux) + * ) */ 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() - { - version (Posix) - { - pageSize = sysconf(_SC_PAGE_SIZE); - } - else version (Windows) - { - SYSTEM_INFO si; - GetSystemInfo(&si); - pageSize = si.dwPageSize; - } - } + /** + * Allocates $(D_PARAM size) bytes of memory. + * + * Params: + * size = Amount of memory to allocate. + * + * Returns: The pointer to the new allocated memory. + */ + void[] allocate(size_t size, TypeInfo ti = null) shared nothrow @trusted + { + if (!size) + { + return null; + } + immutable dataSize = addAlignment(size); - /** - * Allocates $(D_PARAM size) bytes of memory. - * - * Params: - * 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); + if (data is null) + { + data = initializeRegion(dataSize); + } - void* data = findBlock(dataSize); - if (data is null) - { - data = initializeRegion(dataSize); - } + return data is null ? null : data[0..size]; + } - return data is null ? null : data[0..size]; - } + /// + @safe nothrow unittest + { + auto p = MmapPool.instance.allocate(20); - /// - @nogc @safe nothrow unittest - { - auto p = MmapPool.instance.allocate(20); + assert(p); - 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; - /** - * 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) @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.free = true; - block1.free = false; - block2.free = true; + block2.size = block1.size - blockEntrySize - size; + block1.size = size; - block2.size = block1.size - blockEntrySize - size; - block1.size = size; + block2.region = block1.region; + 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); - } - else - { - block1.free = false; - atomicOp!"+="(block1.region.blocks, 1); - } - return cast(void*) block1 + blockEntrySize; - } + /** + * Deallocates a memory block. + * + * Params: + * p = A pointer to the memory block to be freed. + * + * Returns: Whether the deallocation was successful. + */ + bool deallocate(void[] p) shared nothrow @trusted + { + if (p is null) + { + return true; + } - /** - * Deallocates a memory block. - * - * Params: - * p = A pointer to the memory block to be freed. - * - * Returns: Whether the deallocation was successful. - */ - bool deallocate(void[] p) @nogc @trusted nothrow - { - if (p is null) - { - return true; - } + Block block = cast(Block) (p.ptr - blockEntrySize); + if (block.region.blocks <= 1) + { + if (block.region.prev !is null) + { + 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; + } + } - Block block = cast(Block) (p.ptr - blockEntrySize); - if (block.region.blocks <= 1) - { - if (block.region.prev !is null) - { - 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; - } - } + /// + @safe nothrow unittest + { + auto p = MmapPool.instance.allocate(20); - /// - @nogc @safe nothrow unittest - { - auto p = MmapPool.instance.allocate(20); + assert(MmapPool.instance.deallocate(p)); + } - 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; - /** - * 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) @nogc @trusted nothrow - { - void[] reallocP; + if (size == p.length) + { + return true; + } + else if (size > 0) + { + reallocP = allocate(size); + if (reallocP is null) + { + return false; + } + } - if (size == p.length) - { - return true; - } - else if (size > 0) - { - reallocP = allocate(size); - if (reallocP is null) - { - return false; - } - } + if (p !is null) + { + if (size > p.length) + { + reallocP[0..p.length] = p[0..$]; + } + else if (size > 0) + { + reallocP[0..size] = p[0..size]; + } + deallocate(p); + } + p = reallocP; - if (p !is null) - { - 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; + } - return true; - } + /// + nothrow unittest + { + void[] p; + MmapPool.instance.reallocate(p, 10 * int.sizeof); + (cast(int[]) p)[7] = 123; - /// - @nogc nothrow unittest - { - void[] p; - MmapPool.instance.reallocate(p, 10 * int.sizeof); - (cast(int[]) p)[7] = 123; + assert(p.length == 40); - 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); - assert((cast(int[]) p)[7] == 123); + MmapPool.instance.reallocate(p, 20 * int.sizeof); + (cast(int[]) p)[15] = 8; - MmapPool.instance.reallocate(p, 20 * int.sizeof); - (cast(int[]) p)[15] = 8; + assert(p.length == 80); + assert((cast(int[]) p)[15] == 8); + assert((cast(int[]) p)[7] == 123); - assert(p.length == 80); - assert((cast(int[]) p)[15] == 8); - assert((cast(int[]) p)[7] == 123); + 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); - assert((cast(int[]) p)[7] == 123); + MmapPool.instance.deallocate(p); + } - 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)); - /** - * Static allocator instance and initializer. - * - * Returns: Global $(D_PSYMBOL MmapPool) instance. - */ - static @property ref MmapPool instance() @nogc @trusted nothrow - { - if (instance_ is null) - { - immutable instanceSize = addAlignment(__traits(classInstanceSize, MmapPool)); + Region head; // Will become soon our region list head + void* data = initializeRegion(instanceSize, head); + if (data !is null) + { + data[0..instanceSize] = typeid(MmapPool).initializer[]; + instance_ = cast(shared MmapPool) data; + instance_.head = head; + } + } + return instance_; + } - Region head; // Will become soon our region list head - void* data = initializeRegion(instanceSize, head); - if (data !is null) - { - data[0..instanceSize] = typeid(MmapPool).initializer[]; - instance_ = cast(MmapPool) data; - instance_.head = head; - } - } - return instance_; - } + /// + @safe nothrow unittest + { + assert(instance is instance); + } - /// - @nogc @safe nothrow unittest - { - assert(instance is instance); - } + /** + * Initializes a region for one element. + * + * 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; + } + } - /** - * Initializes a region for one element. - * - * 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; + region.blocks = 1; + region.size = regionSize; - Region region = cast(Region) p; - region.blocks = 1; - region.size = regionSize; + // Set the pointer to the head of the region list + if (head !is null) + { + head.prev = region; + } + region.next = head; + region.prev = null; + head = region; - // Set the pointer to the head of the region list - if (head !is null) - { - head.prev = region; - } - region.next = head; - region.prev = null; - head = region; + // Initialize the data block + void* memoryPointer = p + regionEntrySize; + Block block1 = cast(Block) memoryPointer; + block1.size = size; + block1.free = false; - // Initialize the data block - void* memoryPointer = p + regionEntrySize; - Block block1 = cast(Block) memoryPointer; - block1.size = size; - block1.free = false; + // It is what we want to return + void* data = memoryPointer + blockEntrySize; - // It is what we want to return - void* data = memoryPointer + blockEntrySize; + // Free block after 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; - // Free block after 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; + } - return data; - } + /// Ditto. + private void* initializeRegion(size_t size) shared nothrow + { + return initializeRegion(size, head); + } - /// Ditto. - private void* initializeRegion(size_t size) @nogc nothrow - { - return initializeRegion(size, head); - } + /** + * Params: + * x = Space to be aligned. + * + * 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: - * x = Space to be aligned. - * - * Returns: Aligned size of $(D_PARAM x). - */ - pragma(inline) - private static immutable(size_t) addAlignment(size_t x) - @nogc @safe pure nothrow - out (result) - { - assert(result > 0); - } - body - { - return (x - 1) / alignment_ * alignment_ + alignment_; - } + /** + * Params: + * x = Required space. + * + * Returns: Minimum region size (a multiple of $(D_PSYMBOL pageSize)). + */ + pragma(inline) + private static immutable(size_t) calculateRegionSize(size_t x) + @safe pure nothrow + out (result) + { + assert(result > 0); + } + body + { + x += regionEntrySize + blockEntrySize * 2; + return x / pageSize * pageSize + pageSize; + } - /** - * Params: - * x = Required space. - * - * Returns: Minimum region size (a multiple of $(D_PSYMBOL pageSize)). - */ - 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() shared const pure nothrow @safe + { + return alignment_; + } + private enum alignment_ = 8; - @property uint alignment() const @nogc @safe pure nothrow - { - return alignment_; - } - private enum alignment_ = 8; + private static shared MmapPool instance_; - 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 - { - Region prev; - Region next; - uint blocks; - size_t size; - } - private alias Region = shared RegionEntry*; - private enum regionEntrySize = 32; + private shared Region head; - private shared Region head; - - private shared struct BlockEntry - { - Block prev; - Block next; - bool free; - size_t size; - Region region; - } - private alias Block = shared BlockEntry*; - private enum blockEntrySize = 40; + private shared struct BlockEntry + { + Block prev; + Block next; + bool free; + size_t size; + Region region; + } + private alias Block = shared BlockEntry*; + private enum blockEntrySize = 40; } diff --git a/source/tanya/memory/package.d b/source/tanya/memory/package.d index a524504..7c288da 100644 --- a/source/tanya/memory/package.d +++ b/source/tanya/memory/package.d @@ -10,6 +10,75 @@ */ module tanya.memory; +import std.algorithm.mutation; public import std.experimental.allocator; public import tanya.memory.allocator; 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); +} diff --git a/source/tanya/memory/types.d b/source/tanya/memory/types.d index 655740d..19c1e4b 100644 --- a/source/tanya/memory/types.d +++ b/source/tanya/memory/types.d @@ -37,13 +37,15 @@ struct RefCounted(T) private T* payload; } - private uint *counter; + private uint counter; 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. * @@ -54,20 +56,29 @@ struct RefCounted(T) * Precondition: $(D_INLINECODE allocator !is null) */ - this(T value, IAllocator allocator = theAllocator) + this(T value, shared Allocator allocator = defaultAllocator) in { assert(allocator !is null); } body { - this.allocator = allocator; - initialize(); - move(value, get); + this(allocator); + static if (!isReference!T) + { + payload = cast(T*) allocator.allocate(stateSize!T).ptr; + move(value, *payload); + counter = 1; + } + else if (value !is null) + { + move(value, payload); + counter = 1; + } } /// Ditto. - this(IAllocator allocator) + this(shared Allocator allocator) pure nothrow @safe @nogc in { assert(allocator !is null); @@ -77,28 +88,6 @@ struct RefCounted(T) 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. */ @@ -106,7 +95,7 @@ struct RefCounted(T) { if (isInitialized) { - ++(*counter); + ++counter; } } @@ -117,15 +106,14 @@ struct RefCounted(T) */ ~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, * 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 * $(D_PSYMBOL RefCounted). * @@ -144,21 +132,18 @@ struct RefCounted(T) */ ref T opAssign(T rhs) { - checkAllocator(); - if (isInitialized) + if (allocator is null) { - static if (isReference!T) - { - if (!--(*counter)) - { - allocator.dispose(payload); - *counter = 1; - } - } + allocator = defaultAllocator; } - 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); return get; @@ -212,7 +197,7 @@ struct RefCounted(T) */ @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 { - return counter !is null; + return counter != 0; } - mixin StructAllocator; - alias get this; } @@ -247,9 +230,11 @@ version (unittest) struct B { + int prop; @disable this(); 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); assert(a.member.count == 1); @@ -288,7 +273,7 @@ unittest private unittest { uint destroyed; - auto a = theAllocator.make!A(destroyed); + auto a = defaultAllocator.make!A(destroyed); assert(destroyed == 0); { @@ -323,6 +308,7 @@ private unittest static assert(!is(typeof(cast(int) (RefCounted!A())))); static assert(is(RefCounted!B)); + static assert(is(RefCounted!A)); } /** @@ -341,40 +327,26 @@ private unittest * * 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) { - auto rc = typeof(return)(allocator); - - immutable toAllocate = max(stateSize!T, 1) + uint.sizeof; - auto p = allocator.allocate(toAllocate); - if (p is null) + static if (isReference!T) { - onOutOfMemoryError(); - } - 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); + return typeof(return)(allocator.make!T(args), allocator); } 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 { - auto rc = theAllocator.refCounted!int(5); + auto rc = defaultAllocator.refCounted!int(5); assert(rc.count == 1); void func(RefCounted!int param) @@ -395,7 +367,21 @@ unittest private unittest { - static assert(!is(theAllocator.refCounted!A)); - static assert(!is(typeof(theAllocator.refCounted!B()))); - static assert(is(typeof(theAllocator.refCounted!B(5)))); + struct E + { + } + 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); + } } diff --git a/source/tanya/network/socket.d b/source/tanya/network/socket.d index c585bf1..b64986d 100644 --- a/source/tanya/network/socket.d +++ b/source/tanya/network/socket.d @@ -23,511 +23,514 @@ import std.typecons; version (Posix) { - import core.stdc.errno; - import core.sys.posix.fcntl; - import core.sys.posix.netdb; - import core.sys.posix.netinet.in_; - import core.sys.posix.sys.socket; - import core.sys.posix.sys.time; - import core.sys.posix.unistd; + import core.stdc.errno; + import core.sys.posix.fcntl; + import core.sys.posix.netdb; + import core.sys.posix.netinet.in_; + import core.sys.posix.sys.socket; + import core.sys.posix.sys.time; + import core.sys.posix.unistd; - private enum SOCKET_ERROR = -1; + private enum SOCKET_ERROR = -1; } else version (Windows) { - import tanya.async.iocp; - import core.sys.windows.basetyps; - import core.sys.windows.mswsock; - import core.sys.windows.winbase; - import core.sys.windows.windef; - import core.sys.windows.winsock2; + import tanya.async.iocp; + import core.sys.windows.basetyps; + import core.sys.windows.mswsock; + import core.sys.windows.winbase; + import core.sys.windows.windef; + import core.sys.windows.winsock2; - enum : uint - { - IOC_UNIX = 0x00000000, - IOC_WS2 = 0x08000000, - IOC_PROTOCOL = 0x10000000, - IOC_VOID = 0x20000000, /// No parameters. - IOC_OUT = 0x40000000, /// Copy parameters back. - IOC_IN = 0x80000000, /// Copy parameters into. - IOC_VENDOR = 0x18000000, - IOC_INOUT = (IOC_IN | IOC_OUT), /// Copy parameter into and get back. - } + enum : uint + { + IOC_UNIX = 0x00000000, + IOC_WS2 = 0x08000000, + IOC_PROTOCOL = 0x10000000, + IOC_VOID = 0x20000000, /// No parameters. + IOC_OUT = 0x40000000, /// Copy parameters back. + IOC_IN = 0x80000000, /// Copy parameters into. + IOC_VENDOR = 0x18000000, + IOC_INOUT = (IOC_IN | IOC_OUT), /// Copy parameter into and get back. + } - template _WSAIO(int x, int y) - { - enum _WSAIO = IOC_VOID | x | y; - } - template _WSAIOR(int x, int y) - { - enum _WSAIOR = IOC_OUT | x | y; - } - template _WSAIOW(int x, int y) - { - enum _WSAIOW = IOC_IN | x | y; - } - template _WSAIORW(int x, int y) - { - enum _WSAIORW = IOC_INOUT | x | y; - } + template _WSAIO(int x, int y) + { + enum _WSAIO = IOC_VOID | x | y; + } + template _WSAIOR(int x, int y) + { + enum _WSAIOR = IOC_OUT | x | y; + } + template _WSAIOW(int x, int y) + { + enum _WSAIOW = IOC_IN | x | y; + } + template _WSAIORW(int x, int y) + { + enum _WSAIORW = IOC_INOUT | x | y; + } - alias SIO_ASSOCIATE_HANDLE = _WSAIOW!(IOC_WS2, 1); - alias SIO_ENABLE_CIRCULAR_QUEUEING = _WSAIO!(IOC_WS2, 2); - alias SIO_FIND_ROUTE = _WSAIOR!(IOC_WS2, 3); - alias SIO_FLUSH = _WSAIO!(IOC_WS2, 4); - alias SIO_GET_BROADCAST_ADDRESS = _WSAIOR!(IOC_WS2, 5); - alias SIO_GET_EXTENSION_FUNCTION_POINTER = _WSAIORW!(IOC_WS2, 6); - alias SIO_GET_QOS = _WSAIORW!(IOC_WS2, 7); - alias SIO_GET_GROUP_QOS = _WSAIORW!(IOC_WS2, 8); - alias SIO_MULTIPOINT_LOOPBACK = _WSAIOW!(IOC_WS2, 9); - alias SIO_MULTICAST_SCOPE = _WSAIOW!(IOC_WS2, 10); - alias SIO_SET_QOS = _WSAIOW!(IOC_WS2, 11); - alias SIO_SET_GROUP_QOS = _WSAIOW!(IOC_WS2, 12); - alias SIO_TRANSLATE_HANDLE = _WSAIORW!(IOC_WS2, 13); - alias SIO_ROUTING_INTERFACE_QUERY = _WSAIORW!(IOC_WS2, 20); - alias SIO_ROUTING_INTERFACE_CHANGE = _WSAIOW!(IOC_WS2, 21); - alias SIO_ADDRESS_LIST_QUERY = _WSAIOR!(IOC_WS2, 22); - alias SIO_ADDRESS_LIST_CHANGE = _WSAIO!(IOC_WS2, 23); - alias SIO_QUERY_TARGET_PNP_HANDLE = _WSAIOR!(IOC_WS2, 24); - alias SIO_NSP_NOTIFY_CHANGE = _WSAIOW!(IOC_WS2, 25); + alias SIO_ASSOCIATE_HANDLE = _WSAIOW!(IOC_WS2, 1); + alias SIO_ENABLE_CIRCULAR_QUEUEING = _WSAIO!(IOC_WS2, 2); + alias SIO_FIND_ROUTE = _WSAIOR!(IOC_WS2, 3); + alias SIO_FLUSH = _WSAIO!(IOC_WS2, 4); + alias SIO_GET_BROADCAST_ADDRESS = _WSAIOR!(IOC_WS2, 5); + alias SIO_GET_EXTENSION_FUNCTION_POINTER = _WSAIORW!(IOC_WS2, 6); + alias SIO_GET_QOS = _WSAIORW!(IOC_WS2, 7); + alias SIO_GET_GROUP_QOS = _WSAIORW!(IOC_WS2, 8); + alias SIO_MULTIPOINT_LOOPBACK = _WSAIOW!(IOC_WS2, 9); + alias SIO_MULTICAST_SCOPE = _WSAIOW!(IOC_WS2, 10); + alias SIO_SET_QOS = _WSAIOW!(IOC_WS2, 11); + alias SIO_SET_GROUP_QOS = _WSAIOW!(IOC_WS2, 12); + alias SIO_TRANSLATE_HANDLE = _WSAIORW!(IOC_WS2, 13); + alias SIO_ROUTING_INTERFACE_QUERY = _WSAIORW!(IOC_WS2, 20); + alias SIO_ROUTING_INTERFACE_CHANGE = _WSAIOW!(IOC_WS2, 21); + alias SIO_ADDRESS_LIST_QUERY = _WSAIOR!(IOC_WS2, 22); + alias SIO_ADDRESS_LIST_CHANGE = _WSAIO!(IOC_WS2, 23); + alias SIO_QUERY_TARGET_PNP_HANDLE = _WSAIOR!(IOC_WS2, 24); + alias SIO_NSP_NOTIFY_CHANGE = _WSAIOW!(IOC_WS2, 25); - private alias GROUP = uint; + private alias GROUP = uint; - enum - { - WSA_FLAG_OVERLAPPED = 0x01, - MAX_PROTOCOL_CHAIN = 7, - WSAPROTOCOL_LEN = 255, - } + enum + { + WSA_FLAG_OVERLAPPED = 0x01, + MAX_PROTOCOL_CHAIN = 7, + WSAPROTOCOL_LEN = 255, + } - struct WSAPROTOCOLCHAIN - { - int ChainLen; - DWORD[MAX_PROTOCOL_CHAIN] ChainEntries; - } - alias LPWSAPROTOCOLCHAIN = WSAPROTOCOLCHAIN*; + struct WSAPROTOCOLCHAIN + { + int ChainLen; + DWORD[MAX_PROTOCOL_CHAIN] ChainEntries; + } + alias LPWSAPROTOCOLCHAIN = WSAPROTOCOLCHAIN*; - struct WSAPROTOCOL_INFO - { - DWORD dwServiceFlags1; - DWORD dwServiceFlags2; - DWORD dwServiceFlags3; - DWORD dwServiceFlags4; - DWORD dwProviderFlags; - GUID ProviderId; - DWORD dwCatalogEntryId; - WSAPROTOCOLCHAIN ProtocolChain; - int iVersion; - int iAddressFamily; - int iMaxSockAddr; - int iMinSockAddr; - int iSocketType; - int iProtocol; - int iProtocolMaxOffset; - int iNetworkByteOrder; - int iSecurityScheme; - DWORD dwMessageSize; - DWORD dwProviderReserved; - TCHAR[WSAPROTOCOL_LEN + 1] szProtocol; - } - alias LPWSAPROTOCOL_INFO = WSAPROTOCOL_INFO*; + struct WSAPROTOCOL_INFO + { + DWORD dwServiceFlags1; + DWORD dwServiceFlags2; + DWORD dwServiceFlags3; + DWORD dwServiceFlags4; + DWORD dwProviderFlags; + GUID ProviderId; + DWORD dwCatalogEntryId; + WSAPROTOCOLCHAIN ProtocolChain; + int iVersion; + int iAddressFamily; + int iMaxSockAddr; + int iMinSockAddr; + int iSocketType; + int iProtocol; + int iProtocolMaxOffset; + int iNetworkByteOrder; + int iSecurityScheme; + DWORD dwMessageSize; + DWORD dwProviderReserved; + TCHAR[WSAPROTOCOL_LEN + 1] szProtocol; + } + alias LPWSAPROTOCOL_INFO = WSAPROTOCOL_INFO*; - extern (Windows) @nogc nothrow - { - private SOCKET WSASocketW(int af, - int type, - int protocol, - LPWSAPROTOCOL_INFO lpProtocolInfo, - GROUP g, - DWORD dwFlags); - int WSARecv(SOCKET s, - LPWSABUF lpBuffers, - DWORD dwBufferCount, - LPDWORD lpNumberOfBytesRecvd, - LPDWORD lpFlags, - LPOVERLAPPED lpOverlapped, - LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); - int WSASend(SOCKET s, - LPWSABUF lpBuffers, - DWORD dwBufferCount, - LPDWORD lpNumberOfBytesRecvd, - DWORD lpFlags, - LPOVERLAPPED lpOverlapped, - LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); - } - alias WSASocket = WSASocketW; + extern (Windows) @nogc nothrow + { + private SOCKET WSASocketW(int af, + int type, + int protocol, + LPWSAPROTOCOL_INFO lpProtocolInfo, + GROUP g, + DWORD dwFlags); + int WSARecv(SOCKET s, + LPWSABUF lpBuffers, + DWORD dwBufferCount, + LPDWORD lpNumberOfBytesRecvd, + LPDWORD lpFlags, + LPOVERLAPPED lpOverlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); + int WSASend(SOCKET s, + LPWSABUF lpBuffers, + DWORD dwBufferCount, + LPDWORD lpNumberOfBytesRecvd, + DWORD lpFlags, + LPOVERLAPPED lpOverlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); + } + alias WSASocket = WSASocketW; - alias LPFN_GETACCEPTEXSOCKADDRS = VOID function(PVOID, - DWORD, - DWORD, - DWORD, - SOCKADDR**, - LPINT, - SOCKADDR**, - LPINT); - const GUID WSAID_GETACCEPTEXSOCKADDRS = {0xb5367df2,0xcbac,0x11cf,[0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92]}; + alias LPFN_GETACCEPTEXSOCKADDRS = VOID function(PVOID, + DWORD, + DWORD, + DWORD, + SOCKADDR**, + LPINT, + SOCKADDR**, + LPINT); + const GUID WSAID_GETACCEPTEXSOCKADDRS = {0xb5367df2,0xcbac,0x11cf,[0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92]}; - struct WSABUF { - ULONG len; - CHAR* buf; - } - alias WSABUF* LPWSABUF; + struct WSABUF { + ULONG len; + CHAR* buf; + } + alias WSABUF* LPWSABUF; - struct WSAOVERLAPPED { - ULONG_PTR Internal; - ULONG_PTR InternalHigh; - union { - struct { - DWORD Offset; - DWORD OffsetHigh; - } - PVOID Pointer; - } - HANDLE hEvent; - } - alias LPWSAOVERLAPPED = WSAOVERLAPPED*; + struct WSAOVERLAPPED { + ULONG_PTR Internal; + ULONG_PTR InternalHigh; + union { + struct { + DWORD Offset; + DWORD OffsetHigh; + } + PVOID Pointer; + } + HANDLE hEvent; + } + alias LPWSAOVERLAPPED = WSAOVERLAPPED*; - enum SO_UPDATE_ACCEPT_CONTEXT = 0x700B; + enum SO_UPDATE_ACCEPT_CONTEXT = 0x700B; - enum OverlappedSocketEvent - { - accept = 1, - read = 2, - write = 3, - } + enum OverlappedSocketEvent + { + accept = 1, + read = 2, + write = 3, + } - class SocketState : State - { - private WSABUF buffer; - } + class SocketState : State + { + private WSABUF buffer; + } - /** - * Socket returned if a connection has been established. - */ - class OverlappedConnectedSocket : ConnectedSocket - { - /** - * Create a socket. - * - * Params: - * handle = Socket handle. - * af = Address family. - */ - this(socket_t handle, AddressFamily af) - { - super(handle, af); - } + /** + * Socket returned if a connection has been established. + */ + class OverlappedConnectedSocket : ConnectedSocket + { + /** + * Create a socket. + * + * Params: + * handle = Socket handle. + * af = Address family. + */ + this(socket_t handle, AddressFamily af) + { + super(handle, af); + } - /** - * Begins to asynchronously receive data from a connected socket. - * - * Params: - * buffer = Storage location for the received data. - * flags = Flags. - * overlapped = Unique operation identifier. - * - * Returns: $(D_KEYWORD true) if the operation could be finished synchronously. - * $(D_KEYWORD false) otherwise. - * - * Throws: $(D_PSYMBOL SocketException) if unable to receive. - */ - bool beginReceive(ubyte[] buffer, - SocketState overlapped, - Flags flags = Flags(Flag.none)) @trusted - { - auto receiveFlags = cast(DWORD) flags; + /** + * Begins to asynchronously receive data from a connected socket. + * + * Params: + * buffer = Storage location for the received data. + * flags = Flags. + * overlapped = Unique operation identifier. + * + * Returns: $(D_KEYWORD true) if the operation could be finished synchronously. + * $(D_KEYWORD false) otherwise. + * + * Throws: $(D_PSYMBOL SocketException) if unable to receive. + */ + bool beginReceive(ubyte[] buffer, + SocketState overlapped, + Flags flags = Flags(Flag.none)) @trusted + { + auto receiveFlags = cast(DWORD) flags; - overlapped.handle = cast(HANDLE) handle_; - overlapped.event = OverlappedSocketEvent.read; - overlapped.buffer.len = buffer.length; - overlapped.buffer.buf = cast(char*) buffer.ptr; + overlapped.handle = cast(HANDLE) handle_; + overlapped.event = OverlappedSocketEvent.read; + overlapped.buffer.len = buffer.length; + overlapped.buffer.buf = cast(char*) buffer.ptr; - auto result = WSARecv(handle_, - &overlapped.buffer, - 1u, - NULL, - &receiveFlags, - &overlapped.overlapped, - NULL); + auto result = WSARecv(handle_, + &overlapped.buffer, + 1u, + NULL, + &receiveFlags, + &overlapped.overlapped, + NULL); - if (result == SOCKET_ERROR && !wouldHaveBlocked) - { - throw theAllocator.make!SocketException("Unable to receive"); - } - return result == 0; - } + if (result == SOCKET_ERROR && !wouldHaveBlocked) + { + throw defaultAllocator.make!SocketException("Unable to receive"); + } + return result == 0; + } - /** - * Ends a pending asynchronous read. - * - * Params - * overlapped = Unique operation identifier. - * - * Returns: Number of bytes received. - * - * Throws: $(D_PSYMBOL SocketException) if unable to receive. - */ - int endReceive(SocketState overlapped) @trusted - out (count) - { - assert(count >= 0); - } - body - { - DWORD lpNumber; - BOOL result = GetOverlappedResult(overlapped.handle, - &overlapped.overlapped, - &lpNumber, - FALSE); - if (result == FALSE && !wouldHaveBlocked) - { - disconnected_ = true; - throw theAllocator.make!SocketException("Unable to receive"); - } - if (lpNumber == 0) - { - disconnected_ = true; - } - return lpNumber; - } + /** + * Ends a pending asynchronous read. + * + * Params + * overlapped = Unique operation identifier. + * + * Returns: Number of bytes received. + * + * Throws: $(D_PSYMBOL SocketException) if unable to receive. + */ + int endReceive(SocketState overlapped) @trusted + out (count) + { + assert(count >= 0); + } + body + { + DWORD lpNumber; + BOOL result = GetOverlappedResult(overlapped.handle, + &overlapped.overlapped, + &lpNumber, + FALSE); + if (result == FALSE && !wouldHaveBlocked) + { + disconnected_ = true; + throw defaultAllocator.make!SocketException("Unable to receive"); + } + if (lpNumber == 0) + { + disconnected_ = true; + } + return lpNumber; + } - /** - * Sends data asynchronously to a connected socket. - * - * Params: - * buffer = Data to be sent. - * flags = Flags. - * overlapped = Unique operation identifier. - * - * Returns: $(D_KEYWORD true) if the operation could be finished synchronously. - * $(D_KEYWORD false) otherwise. - * - * Throws: $(D_PSYMBOL SocketException) if unable to send. - */ - bool beginSend(ubyte[] buffer, - SocketState overlapped, - Flags flags = Flags(Flag.none)) @trusted - { - overlapped.handle = cast(HANDLE) handle_; - overlapped.event = OverlappedSocketEvent.write; - overlapped.buffer.len = buffer.length; - overlapped.buffer.buf = cast(char*) buffer.ptr; + /** + * Sends data asynchronously to a connected socket. + * + * Params: + * buffer = Data to be sent. + * flags = Flags. + * overlapped = Unique operation identifier. + * + * Returns: $(D_KEYWORD true) if the operation could be finished synchronously. + * $(D_KEYWORD false) otherwise. + * + * Throws: $(D_PSYMBOL SocketException) if unable to send. + */ + bool beginSend(ubyte[] buffer, + SocketState overlapped, + Flags flags = Flags(Flag.none)) @trusted + { + overlapped.handle = cast(HANDLE) handle_; + overlapped.event = OverlappedSocketEvent.write; + overlapped.buffer.len = buffer.length; + overlapped.buffer.buf = cast(char*) buffer.ptr; - auto result = WSASend(handle_, - &overlapped.buffer, - 1u, - NULL, - cast(DWORD) flags, - &overlapped.overlapped, - NULL); + auto result = WSASend(handle_, + &overlapped.buffer, + 1u, + NULL, + cast(DWORD) flags, + &overlapped.overlapped, + NULL); - if (result == SOCKET_ERROR && !wouldHaveBlocked) - { - disconnected_ = true; - throw theAllocator.make!SocketException("Unable to send"); - } - return result == 0; - } + if (result == SOCKET_ERROR && !wouldHaveBlocked) + { + disconnected_ = true; + throw defaultAllocator.make!SocketException("Unable to send"); + } + return result == 0; + } - /** - * Ends a pending asynchronous send. - * - * Params - * overlapped = Unique operation identifier. - * - * Returns: Number of bytes sent. - * - * Throws: $(D_PSYMBOL SocketException) if unable to receive. - */ - int endSend(SocketState overlapped) @trusted - out (count) - { - assert(count >= 0); - } - body - { - DWORD lpNumber; - BOOL result = GetOverlappedResult(overlapped.handle, - &overlapped.overlapped, - &lpNumber, - FALSE); - if (result == FALSE && !wouldHaveBlocked) - { - disconnected_ = true; - throw theAllocator.make!SocketException("Unable to receive"); - } - return lpNumber; - } - } + /** + * Ends a pending asynchronous send. + * + * Params + * overlapped = Unique operation identifier. + * + * Returns: Number of bytes sent. + * + * Throws: $(D_PSYMBOL SocketException) if unable to receive. + */ + int endSend(SocketState overlapped) @trusted + out (count) + { + assert(count >= 0); + } + body + { + DWORD lpNumber; + BOOL result = GetOverlappedResult(overlapped.handle, + &overlapped.overlapped, + &lpNumber, + FALSE); + if (result == FALSE && !wouldHaveBlocked) + { + disconnected_ = true; + throw defaultAllocator.make!SocketException("Unable to receive"); + } + return lpNumber; + } + } - class OverlappedStreamSocket : StreamSocket - { - /// Accept extension function pointer. - package LPFN_ACCEPTEX acceptExtension; + class OverlappedStreamSocket : StreamSocket + { + /// Accept extension function pointer. + package LPFN_ACCEPTEX acceptExtension; - /** - * Create a socket. - * - * Params: - * af = Address family. - * - * Throws: $(D_PSYMBOL SocketException) on errors. - */ - this(AddressFamily af) @trusted - { - super(af); - scope (failure) - { - this.close(); - } - blocking = false; + /** + * Create a socket. + * + * Params: + * af = Address family. + * + * Throws: $(D_PSYMBOL SocketException) on errors. + */ + this(AddressFamily af) @trusted + { + super(af); + scope (failure) + { + this.close(); + } + blocking = false; - GUID guidAcceptEx = WSAID_ACCEPTEX; - DWORD dwBytes; + GUID guidAcceptEx = WSAID_ACCEPTEX; + DWORD dwBytes; - auto result = WSAIoctl(handle_, - SIO_GET_EXTENSION_FUNCTION_POINTER, - &guidAcceptEx, - guidAcceptEx.sizeof, - &acceptExtension, - acceptExtension.sizeof, - &dwBytes, - NULL, - NULL); - if (!result == SOCKET_ERROR) - { - throw theAllocator.make!SocketException("Unable to retrieve an accept extension function pointer"); - } - } + auto result = WSAIoctl(handle_, + SIO_GET_EXTENSION_FUNCTION_POINTER, + &guidAcceptEx, + guidAcceptEx.sizeof, + &acceptExtension, + acceptExtension.sizeof, + &dwBytes, + NULL, + NULL); + if (!result == SOCKET_ERROR) + { + throw make!SocketException(defaultAllocator, + "Unable to retrieve an accept extension function pointer"); + } + } - /** - * Begins an asynchronous operation to accept an incoming connection attempt. - * - * Params: - * overlapped = Unique operation identifier. - * - * Returns: $(D_KEYWORD true) if the operation could be finished synchronously. - * $(D_KEYWORD false) otherwise. - * - * Throws: $(D_PSYMBOL SocketException) on accept errors. - */ - bool beginAccept(SocketState overlapped) @trusted - { - auto socket = cast(socket_t) socket(addressFamily, SOCK_STREAM, 0); - if (socket == socket_t.init) - { - throw theAllocator.make!SocketException("Unable to create socket"); - } - scope (failure) - { - closesocket(socket); - } - DWORD dwBytes; - overlapped.handle = cast(HANDLE) socket; - overlapped.event = OverlappedSocketEvent.accept; - overlapped.buffer.len = (sockaddr_in.sizeof + 16) * 2; - overlapped.buffer.buf = theAllocator.makeArray!char(overlapped.buffer.len).ptr; + /** + * Begins an asynchronous operation to accept an incoming connection attempt. + * + * Params: + * overlapped = Unique operation identifier. + * + * Returns: $(D_KEYWORD true) if the operation could be finished synchronously. + * $(D_KEYWORD false) otherwise. + * + * Throws: $(D_PSYMBOL SocketException) on accept errors. + */ + bool beginAccept(SocketState overlapped) @trusted + { + auto socket = cast(socket_t) socket(addressFamily, SOCK_STREAM, 0); + if (socket == socket_t.init) + { + throw defaultAllocator.make!SocketException("Unable to create socket"); + } + scope (failure) + { + closesocket(socket); + } + DWORD dwBytes; + overlapped.handle = cast(HANDLE) socket; + overlapped.event = OverlappedSocketEvent.accept; + overlapped.buffer.len = (sockaddr_in.sizeof + 16) * 2; + overlapped.buffer.buf = makeArray!char(defaultAllocator, + overlapped.buffer.len).ptr; - // We don't want to get any data now, but only start to accept the connections - BOOL result = acceptExtension(handle_, - socket, - overlapped.buffer.buf, - 0u, - sockaddr_in.sizeof + 16, - sockaddr_in.sizeof + 16, - &dwBytes, - &overlapped.overlapped); - if (result == FALSE && !wouldHaveBlocked) - { - throw theAllocator.make!SocketException("Unable to accept socket connection"); - } - return result == TRUE; - } + // We don't want to get any data now, but only start to accept the connections + BOOL result = acceptExtension(handle_, + socket, + overlapped.buffer.buf, + 0u, + sockaddr_in.sizeof + 16, + sockaddr_in.sizeof + 16, + &dwBytes, + &overlapped.overlapped); + if (result == FALSE && !wouldHaveBlocked) + { + throw defaultAllocator.make!SocketException("Unable to accept socket connection"); + } + return result == TRUE; + } - /** - * Asynchronously accepts an incoming connection attempt and creates a - * new socket to handle remote host communication. - * - * Params: - * overlapped = Unique operation identifier. - * - * Returns: Connected socket. - * - * Throws: $(D_PSYMBOL SocketException) if unable to accept. - */ - OverlappedConnectedSocket endAccept(SocketState overlapped) @trusted - { - scope (exit) - { - theAllocator.dispose(overlapped.buffer.buf[0..overlapped.buffer.len]); - } - auto socket = theAllocator.make!OverlappedConnectedSocket(cast(socket_t) overlapped.handle, - addressFamily); - scope (failure) - { - theAllocator.dispose(socket); - } - socket.setOption(SocketOptionLevel.SOCKET, - cast(SocketOption) SO_UPDATE_ACCEPT_CONTEXT, - cast(size_t) handle); - return socket; - } - } + /** + * Asynchronously accepts an incoming connection attempt and creates a + * new socket to handle remote host communication. + * + * Params: + * overlapped = Unique operation identifier. + * + * Returns: Connected socket. + * + * Throws: $(D_PSYMBOL SocketException) if unable to accept. + */ + OverlappedConnectedSocket endAccept(SocketState overlapped) @trusted + { + scope (exit) + { + defaultAllocator.dispose(overlapped.buffer.buf[0..overlapped.buffer.len]); + } + auto socket = make!OverlappedConnectedSocket(defaultAllocator, + cast(socket_t) overlapped.handle, + addressFamily); + scope (failure) + { + defaultAllocator.dispose(socket); + } + socket.setOption(SocketOptionLevel.SOCKET, + cast(SocketOption) SO_UPDATE_ACCEPT_CONTEXT, + cast(size_t) handle); + return socket; + } + } } version (linux) { - enum SOCK_NONBLOCK = O_NONBLOCK; - extern(C) int accept4(int, sockaddr*, socklen_t*, int flags) @nogc nothrow; + enum SOCK_NONBLOCK = O_NONBLOCK; + extern(C) int accept4(int, sockaddr*, socklen_t*, int flags) @nogc nothrow; } else version (OSX) { - version = MacBSD; + version = MacBSD; } else version (iOS) { - version = MacBSD; + version = MacBSD; } else version (FreeBSD) { - version = MacBSD; + version = MacBSD; } else version (OpenBSD) { - version = MacBSD; + version = MacBSD; } else version (DragonFlyBSD) { - version = MacBSD; + version = MacBSD; } version (MacBSD) { - enum ESOCKTNOSUPPORT = 44; /// Socket type not suppoted. + enum ESOCKTNOSUPPORT = 44; /// Socket type not suppoted. } private immutable { - typeof(&getaddrinfo) getaddrinfoPointer; - typeof(&freeaddrinfo) freeaddrinfoPointer; + typeof(&getaddrinfo) getaddrinfoPointer; + typeof(&freeaddrinfo) freeaddrinfoPointer; } shared static this() { - version (Windows) - { - auto ws2Lib = GetModuleHandle("ws2_32.dll"); + version (Windows) + { + auto ws2Lib = GetModuleHandle("ws2_32.dll"); - getaddrinfoPointer = cast(typeof(getaddrinfoPointer)) - GetProcAddress(ws2Lib, "getaddrinfo"); - freeaddrinfoPointer = cast(typeof(freeaddrinfoPointer)) - GetProcAddress(ws2Lib, "freeaddrinfo"); - } - else version (Posix) - { - getaddrinfoPointer = &getaddrinfo; - freeaddrinfoPointer = &freeaddrinfo; - } + getaddrinfoPointer = cast(typeof(getaddrinfoPointer)) + GetProcAddress(ws2Lib, "getaddrinfo"); + freeaddrinfoPointer = cast(typeof(freeaddrinfoPointer)) + GetProcAddress(ws2Lib, "freeaddrinfo"); + } + else version (Posix) + { + getaddrinfoPointer = &getaddrinfo; + freeaddrinfoPointer = &freeaddrinfo; + } } /** @@ -535,32 +538,32 @@ shared static this() */ enum SocketError : int { - /// Unknown error - unknown = 0, - /// Firewall rules forbid connection. - accessDenied = EPERM, - /// A socket operation was attempted on a non-socket. - notSocket = EBADF, - /// The network is not available. - networkDown = ECONNABORTED, - /// An invalid pointer address was detected by the underlying socket provider. - fault = EFAULT, - /// An invalid argument was supplied to a $(D_PSYMBOL Socket) member. - invalidArgument = EINVAL, - /// The limit on the number of open sockets has been reached. - tooManyOpenSockets = ENFILE, - /// No free buffer space is available for a Socket operation. - noBufferSpaceAvailable = ENOBUFS, - /// The address family is not supported by the protocol family. - operationNotSupported = EOPNOTSUPP, - /// The protocol is not implemented or has not been configured. - protocolNotSupported = EPROTONOSUPPORT, - /// Protocol error. - protocolError = EPROTOTYPE, - /// The connection attempt timed out, or the connected host has failed to respond. - timedOut = ETIMEDOUT, - /// The support for the specified socket type does not exist in this address family. - socketNotSupported = ESOCKTNOSUPPORT, + /// Unknown error + unknown = 0, + /// Firewall rules forbid connection. + accessDenied = EPERM, + /// A socket operation was attempted on a non-socket. + notSocket = EBADF, + /// The network is not available. + networkDown = ECONNABORTED, + /// An invalid pointer address was detected by the underlying socket provider. + fault = EFAULT, + /// An invalid argument was supplied to a $(D_PSYMBOL Socket) member. + invalidArgument = EINVAL, + /// The limit on the number of open sockets has been reached. + tooManyOpenSockets = ENFILE, + /// No free buffer space is available for a Socket operation. + noBufferSpaceAvailable = ENOBUFS, + /// The address family is not supported by the protocol family. + operationNotSupported = EOPNOTSUPP, + /// The protocol is not implemented or has not been configured. + protocolNotSupported = EPROTONOSUPPORT, + /// Protocol error. + protocolError = EPROTOTYPE, + /// The connection attempt timed out, or the connected host has failed to respond. + timedOut = ETIMEDOUT, + /// The support for the specified socket type does not exist in this address family. + socketNotSupported = ESOCKTNOSUPPORT, } /** @@ -570,53 +573,53 @@ enum SocketError : int */ class SocketException : Exception { - immutable SocketError error = SocketError.unknown; + immutable SocketError error = SocketError.unknown; - /** - * Params: - * msg = The message for the exception. - * file = The file where the exception occurred. - * line = The line number where the exception occurred. - * next = The previous exception in the chain of exceptions, if any. - */ - this(string msg, - string file = __FILE__, - size_t line = __LINE__, - Throwable next = null) @nogc @safe nothrow - { - super(msg, file, line, next); + /** + * Params: + * msg = The message for the exception. + * file = The file where the exception occurred. + * line = The line number where the exception occurred. + * next = The previous exception in the chain of exceptions, if any. + */ + this(string msg, + string file = __FILE__, + size_t line = __LINE__, + Throwable next = null) @nogc @safe nothrow + { + super(msg, file, line, next); - foreach (member; EnumMembers!SocketError) - { - if (member == lastError) - { - error = member; - return; - } - } - if (lastError == ENOMEM) - { - error = SocketError.noBufferSpaceAvailable; - } - else if (lastError == EMFILE) - { - error = SocketError.tooManyOpenSockets; - } - else version (linux) - { - if (lastError == ENOSR) - { - error = SocketError.networkDown; - } - } - else version (Posix) - { - if (lastError == EPROTO) - { - error = SocketError.networkDown; - } - } - } + foreach (member; EnumMembers!SocketError) + { + if (member == lastError) + { + error = member; + return; + } + } + if (lastError == ENOMEM) + { + error = SocketError.noBufferSpaceAvailable; + } + else if (lastError == EMFILE) + { + error = SocketError.tooManyOpenSockets; + } + else version (linux) + { + if (lastError == ENOSR) + { + error = SocketError.networkDown; + } + } + else version (Posix) + { + if (lastError == EPROTO) + { + error = SocketError.networkDown; + } + } + } } /** @@ -625,339 +628,341 @@ class SocketException : Exception */ abstract class Socket { - version (Posix) - { - /** - * How a socket is shutdown. - */ - enum Shutdown : int - { - receive = SHUT_RD, /// Socket receives are disallowed - send = SHUT_WR, /// Socket sends are disallowed - both = SHUT_RDWR, /// Both receive and send - } - } - else version (Windows) - { - /// Property to get or set whether the socket is blocking or nonblocking. - private bool blocking_ = true; + version (Posix) + { + /** + * How a socket is shutdown. + */ + enum Shutdown : int + { + receive = SHUT_RD, /// Socket receives are disallowed + send = SHUT_WR, /// Socket sends are disallowed + both = SHUT_RDWR, /// Both receive and send + } + } + else version (Windows) + { + /// Property to get or set whether the socket is blocking or nonblocking. + private bool blocking_ = true; - /** - * How a socket is shutdown. - */ - enum Shutdown : int - { - receive = SD_RECEIVE, /// Socket receives are disallowed. - send = SD_SEND, /// Socket sends are disallowed. - both = SD_BOTH, /// Both receive and send. - } + /** + * How a socket is shutdown. + */ + enum Shutdown : int + { + receive = SD_RECEIVE, /// Socket receives are disallowed. + send = SD_SEND, /// Socket sends are disallowed. + both = SD_BOTH, /// Both receive and send. + } - // The WinSock timeouts seem to be effectively skewed by a constant - // offset of about half a second (in milliseconds). - private enum WINSOCK_TIMEOUT_SKEW = 500; - } + // The WinSock timeouts seem to be effectively skewed by a constant + // offset of about half a second (in milliseconds). + private enum WINSOCK_TIMEOUT_SKEW = 500; + } - /// Socket handle. - protected socket_t handle_; + /// Socket handle. + protected socket_t handle_; - /// Address family. - protected AddressFamily family; + /// Address family. + protected AddressFamily family; - private @property void handle(socket_t handle) - in - { - assert(handle != socket_t.init); - assert(handle_ == socket_t.init, "Socket handle cannot be changed"); - } - body - { - handle_ = handle; + private @property void handle(socket_t handle) + in + { + assert(handle != socket_t.init); + assert(handle_ == socket_t.init, "Socket handle cannot be changed"); + } + body + { + handle_ = handle; - // Set the option to disable SIGPIPE on send() if the platform - // has it (e.g. on OS X). - static if (is(typeof(SO_NOSIGPIPE))) - { - setOption(SocketOptionLevel.SOCKET, cast(SocketOption)SO_NOSIGPIPE, true); - } - } + // Set the option to disable SIGPIPE on send() if the platform + // has it (e.g. on OS X). + static if (is(typeof(SO_NOSIGPIPE))) + { + setOption(SocketOptionLevel.SOCKET, cast(SocketOption)SO_NOSIGPIPE, true); + } + } - @property inout(socket_t) handle() inout const pure nothrow @safe @nogc - { - return handle_; - } + @property inout(socket_t) handle() inout const pure nothrow @safe @nogc + { + return handle_; + } - /** - * Create a socket. - * - * Params: - * handle = Socket. - * af = Address family. - */ - this(socket_t handle, AddressFamily af) - in - { - assert(handle != socket_t.init); - } - body - { - scope (failure) - { - this.close(); - } - this.handle = handle; - family = af; - } + /** + * Create a socket. + * + * Params: + * handle = Socket. + * af = Address family. + */ + this(socket_t handle, AddressFamily af) + in + { + assert(handle != socket_t.init); + } + body + { + scope (failure) + { + this.close(); + } + this.handle = handle; + family = af; + } - /** - * Closes the socket and calls the destructor on itself. - */ - ~this() nothrow @trusted @nogc - { - this.close(); - } + /** + * Closes the socket and calls the destructor on itself. + */ + ~this() nothrow @trusted @nogc + { + this.close(); + } - /** - * Get a socket option. - * - * Params: - * level = Protocol level at that the option exists. - * option = Option. - * result = Buffer to save the result. - * - * Returns: The number of bytes written to $(D_PARAM result). - * - * Throws: $(D_PSYMBOL SocketException) on error. - */ - protected int getOption(SocketOptionLevel level, SocketOption option, void[] result) const @trusted - { - auto length = cast(socklen_t) result.length; - if (getsockopt(handle_, - cast(int) level, - cast(int) option, - result.ptr, - &length) == SOCKET_ERROR) - { - throw theAllocator.make!SocketException("Unable to get socket option"); - } - return length; - } + /** + * Get a socket option. + * + * Params: + * level = Protocol level at that the option exists. + * option = Option. + * result = Buffer to save the result. + * + * Returns: The number of bytes written to $(D_PARAM result). + * + * Throws: $(D_PSYMBOL SocketException) on error. + */ + protected int getOption(SocketOptionLevel level, SocketOption option, void[] result) const @trusted + { + auto length = cast(socklen_t) result.length; + if (getsockopt(handle_, + cast(int) level, + cast(int) option, + result.ptr, + &length) == SOCKET_ERROR) + { + throw defaultAllocator.make!SocketException("Unable to get socket option"); + } + return length; + } - /// Ditto. - int getOption(SocketOptionLevel level, SocketOption option, out size_t result) const @trusted - { - return getOption(level, option, (&result)[0..1]); - } + /// Ditto. + int getOption(SocketOptionLevel level, SocketOption option, out size_t result) const @trusted + { + return getOption(level, option, (&result)[0..1]); + } - /// Ditto. - int getOption(SocketOptionLevel level, SocketOption option, out Linger result) const @trusted - { - return getOption(level, option, (&result.clinger)[0..1]); - } + /// Ditto. + int getOption(SocketOptionLevel level, SocketOption option, out Linger result) const @trusted + { + return getOption(level, option, (&result.clinger)[0..1]); + } - /// Ditto. - int getOption(SocketOptionLevel level, SocketOption option, out Duration result) const @trusted - { - // WinSock returns the timeout values as a milliseconds DWORD, - // while Linux and BSD return a timeval struct. - version (Posix) - { - timeval tv; - auto ret = getOption(level, option, (&tv)[0..1]); - result = dur!"seconds"(tv.tv_sec) + dur!"usecs"(tv.tv_usec); - } - else version (Windows) - { - int msecs; - auto ret = getOption(level, option, (&msecs)[0 .. 1]); - if (option == SocketOption.RCVTIMEO) - { - msecs += WINSOCK_TIMEOUT_SKEW; - } - result = dur!"msecs"(msecs); - } - return ret; - } + /// Ditto. + int getOption(SocketOptionLevel level, SocketOption option, out Duration result) const @trusted + { + // WinSock returns the timeout values as a milliseconds DWORD, + // while Linux and BSD return a timeval struct. + version (Posix) + { + timeval tv; + auto ret = getOption(level, option, (&tv)[0..1]); + result = dur!"seconds"(tv.tv_sec) + dur!"usecs"(tv.tv_usec); + } + else version (Windows) + { + int msecs; + auto ret = getOption(level, option, (&msecs)[0 .. 1]); + if (option == SocketOption.RCVTIMEO) + { + msecs += WINSOCK_TIMEOUT_SKEW; + } + result = dur!"msecs"(msecs); + } + return ret; + } - /** - * Set a socket option. - * - * Params: - * level = Protocol level at that the option exists. - * option = Option. - * result = Option value. - * - * Throws: $(D_PSYMBOL SocketException) on error. - */ - protected void setOption(SocketOptionLevel level, SocketOption option, void[] value) - const @trusted - { - if (setsockopt(handle_, - cast(int)level, - cast(int)option, - value.ptr, - cast(uint) value.length) == SOCKET_ERROR) - { - throw theAllocator.make!SocketException("Unable to set socket option"); - } - } + /** + * Set a socket option. + * + * Params: + * level = Protocol level at that the option exists. + * option = Option. + * result = Option value. + * + * Throws: $(D_PSYMBOL SocketException) on error. + */ + protected void setOption(SocketOptionLevel level, SocketOption option, void[] value) + const @trusted + { + if (setsockopt(handle_, + cast(int)level, + cast(int)option, + value.ptr, + cast(uint) value.length) == SOCKET_ERROR) + { + throw defaultAllocator.make!SocketException("Unable to set socket option"); + } + } - /// Ditto. - void setOption(SocketOptionLevel level, SocketOption option, size_t value) const @trusted - { - setOption(level, option, (&value)[0..1]); - } + /// Ditto. + void setOption(SocketOptionLevel level, SocketOption option, size_t value) const @trusted + { + setOption(level, option, (&value)[0..1]); + } - /// Ditto. - void setOption(SocketOptionLevel level, SocketOption option, Linger value) const @trusted - { - setOption(level, option, (&value.clinger)[0..1]); - } + /// Ditto. + void setOption(SocketOptionLevel level, SocketOption option, Linger value) const @trusted + { + setOption(level, option, (&value.clinger)[0..1]); + } - /// Ditto. - void setOption(SocketOptionLevel level, SocketOption option, Duration value) const @trusted - { - version (Posix) - { - timeval tv; - value.split!("seconds", "usecs")(tv.tv_sec, tv.tv_usec); - setOption(level, option, (&tv)[0..1]); - } - else version (Windows) - { - auto msecs = cast(int) value.total!"msecs"; - if (msecs > 0 && option == SocketOption.RCVTIMEO) - { - msecs = max(1, msecs - WINSOCK_TIMEOUT_SKEW); - } - setOption(level, option, msecs); - } - } + /// Ditto. + void setOption(SocketOptionLevel level, SocketOption option, Duration value) const @trusted + { + version (Posix) + { + timeval tv; + value.split!("seconds", "usecs")(tv.tv_sec, tv.tv_usec); + setOption(level, option, (&tv)[0..1]); + } + else version (Windows) + { + auto msecs = cast(int) value.total!"msecs"; + if (msecs > 0 && option == SocketOption.RCVTIMEO) + { + msecs = max(1, msecs - WINSOCK_TIMEOUT_SKEW); + } + setOption(level, option, msecs); + } + } - /** - * Returns: Socket's blocking flag. - */ - @property inout(bool) blocking() inout const nothrow @nogc - { - version (Posix) - { - return !(fcntl(handle_, F_GETFL, 0) & O_NONBLOCK); - } - else version (Windows) - { - return blocking_; - } - } + /** + * Returns: Socket's blocking flag. + */ + @property inout(bool) blocking() inout const nothrow @nogc + { + version (Posix) + { + return !(fcntl(handle_, F_GETFL, 0) & O_NONBLOCK); + } + else version (Windows) + { + return blocking_; + } + } - /** - * Params: - * yes = Socket's blocking flag. - */ - @property void blocking(bool yes) - { - version (Posix) - { - int fl = fcntl(handle_, F_GETFL, 0); + /** + * Params: + * yes = Socket's blocking flag. + */ + @property void blocking(bool yes) + { + version (Posix) + { + int fl = fcntl(handle_, F_GETFL, 0); - if (fl != SOCKET_ERROR) - { - fl = yes ? fl & ~O_NONBLOCK : fl | O_NONBLOCK; - fl = fcntl(handle_, F_SETFL, fl); - } - if (fl == SOCKET_ERROR) - { - throw theAllocator.make!SocketException("Unable to set socket blocking"); - } - } - else version (Windows) - { - uint num = !yes; - if (ioctlsocket(handle_, FIONBIO, &num) == SOCKET_ERROR) - { - throw theAllocator.make!SocketException("Unable to set socket blocking"); - } - blocking_ = yes; - } - } + if (fl != SOCKET_ERROR) + { + fl = yes ? fl & ~O_NONBLOCK : fl | O_NONBLOCK; + fl = fcntl(handle_, F_SETFL, fl); + } + if (fl == SOCKET_ERROR) + { + throw make!SocketException(defaultAllocator, + "Unable to set socket blocking"); + } + } + else version (Windows) + { + uint num = !yes; + if (ioctlsocket(handle_, FIONBIO, &num) == SOCKET_ERROR) + { + throw make!SocketException(defaultAllocator, + "Unable to set socket blocking"); + } + blocking_ = yes; + } + } - /** - * Returns: The socket's address family. - */ - @property AddressFamily addressFamily() const @nogc @safe pure nothrow - { - return family; - } + /** + * Returns: The socket's address family. + */ + @property AddressFamily addressFamily() const @nogc @safe pure nothrow + { + return family; + } - /** - * Returns: $(D_KEYWORD true) if this is a valid, alive socket. - */ - @property bool isAlive() @trusted const nothrow @nogc - { - int type; - socklen_t typesize = cast(socklen_t) type.sizeof; - return !getsockopt(handle_, SOL_SOCKET, SO_TYPE, cast(char*)&type, &typesize); - } + /** + * Returns: $(D_KEYWORD true) if this is a valid, alive socket. + */ + @property bool isAlive() @trusted const nothrow @nogc + { + int type; + socklen_t typesize = cast(socklen_t) type.sizeof; + return !getsockopt(handle_, SOL_SOCKET, SO_TYPE, cast(char*)&type, &typesize); + } - /** - * Disables sends and/or receives. - * - * Params: - * how = What to disable. - * - * See_Also: - * $(D_PSYMBOL Shutdown) - */ - void shutdown(Shutdown how = Shutdown.both) @nogc @trusted const nothrow - { - .shutdown(handle_, cast(int)how); - } + /** + * Disables sends and/or receives. + * + * Params: + * how = What to disable. + * + * See_Also: + * $(D_PSYMBOL Shutdown) + */ + void shutdown(Shutdown how = Shutdown.both) @nogc @trusted const nothrow + { + .shutdown(handle_, cast(int)how); + } - /** - * Immediately drop any connections and release socket resources. - * Calling $(D_PSYMBOL shutdown) before $(D_PSYMBOL close) is recommended - * for connection-oriented sockets. The $(D_PSYMBOL Socket) object is no - * longer usable after $(D_PSYMBOL close). - */ - void close() nothrow @trusted @nogc - { - version(Windows) - { - .closesocket(handle_); - } - else version(Posix) - { - .close(handle_); - } - handle_ = socket_t.init; - } + /** + * Immediately drop any connections and release socket resources. + * Calling $(D_PSYMBOL shutdown) before $(D_PSYMBOL close) is recommended + * for connection-oriented sockets. The $(D_PSYMBOL Socket) object is no + * longer usable after $(D_PSYMBOL close). + */ + void close() nothrow @trusted @nogc + { + version(Windows) + { + .closesocket(handle_); + } + else version(Posix) + { + .close(handle_); + } + handle_ = socket_t.init; + } - /** - * Listen for an incoming connection. $(D_PSYMBOL bind) must be called before you - * can $(D_PSYMBOL listen). - * - * Params: - * backlog = Request of how many pending incoming connections are - * queued until $(D_PSYMBOL accept)ed. - */ - void listen(int backlog) const @trusted - { - if (.listen(handle_, backlog) == SOCKET_ERROR) - { - throw theAllocator.make!SocketException("Unable to listen on socket"); - } - } + /** + * Listen for an incoming connection. $(D_PSYMBOL bind) must be called before you + * can $(D_PSYMBOL listen). + * + * Params: + * backlog = Request of how many pending incoming connections are + * queued until $(D_PSYMBOL accept)ed. + */ + void listen(int backlog) const @trusted + { + if (.listen(handle_, backlog) == SOCKET_ERROR) + { + throw defaultAllocator.make!SocketException("Unable to listen on socket"); + } + } - /** - * Compare handles. - * - * Params: - * that = Another handle. - * - * Returns: Comparision result. - */ - int opCmp(size_t that) const pure nothrow @safe @nogc - { - return handle_ < that ? -1 : handle_ > that ? 1 : 0; - } + /** + * Compare handles. + * + * Params: + * that = Another handle. + * + * Returns: Comparision result. + */ + int opCmp(size_t that) const pure nothrow @safe @nogc + { + return handle_ < that ? -1 : handle_ > that ? 1 : 0; + } } /** @@ -965,118 +970,119 @@ abstract class Socket */ interface ConnectionOrientedSocket { - /** - * Flags may be OR'ed together. - */ - enum Flag : int - { - none = 0, /// No flags specified - outOfBand = MSG_OOB, /// Out-of-band stream data - peek = MSG_PEEK, /// Peek at incoming data without removing it from the queue, only for receiving - dontRoute = MSG_DONTROUTE, /// Data should not be subject to routing; this flag may be ignored. Only for sending - } + /** + * Flags may be OR'ed together. + */ + enum Flag : int + { + none = 0, /// No flags specified + outOfBand = MSG_OOB, /// Out-of-band stream data + peek = MSG_PEEK, /// Peek at incoming data without removing it from the queue, only for receiving + dontRoute = MSG_DONTROUTE, /// Data should not be subject to routing; this flag may be ignored. Only for sending + } - alias Flags = BitFlags!Flag; + alias Flags = BitFlags!Flag; } class StreamSocket : Socket, ConnectionOrientedSocket { - /** - * Create a socket. - * - * Params: - * af = Address family. - */ - this(AddressFamily af) @trusted - { - auto handle = cast(socket_t) socket(af, SOCK_STREAM, 0); - if (handle == socket_t.init) - { - throw theAllocator.make!SocketException("Unable to create socket"); - } - super(handle, af); - } + /** + * Create a socket. + * + * Params: + * af = Address family. + */ + this(AddressFamily af) @trusted + { + auto handle = cast(socket_t) socket(af, SOCK_STREAM, 0); + if (handle == socket_t.init) + { + throw defaultAllocator.make!SocketException("Unable to create socket"); + } + super(handle, af); + } - /** - * Associate a local address with this socket. - * - * Params: - * address = Local address. - * - * Throws: $(D_PSYMBOL SocketException) if unable to bind. - */ - void bind(Address address) @trusted const - { - if (.bind(handle_, address.name, address.length) == SOCKET_ERROR) - { - throw theAllocator.make!SocketException("Unable to bind socket"); - } - } + /** + * Associate a local address with this socket. + * + * Params: + * address = Local address. + * + * Throws: $(D_PSYMBOL SocketException) if unable to bind. + */ + void bind(Address address) @trusted const + { + if (.bind(handle_, address.name, address.length) == SOCKET_ERROR) + { + throw defaultAllocator.make!SocketException("Unable to bind socket"); + } + } - /** - * Accept an incoming connection. - * - * The blocking mode is always inherited. - * - * Returns: $(D_PSYMBOL Socket) for the accepted connection or - * $(D_KEYWORD null) if the call would block on a - * non-blocking socket. - * - * Throws: $(D_PSYMBOL SocketException) if unable to accept. - */ - ConnectedSocket accept() @trusted - { - socket_t sock; + /** + * Accept an incoming connection. + * + * The blocking mode is always inherited. + * + * Returns: $(D_PSYMBOL Socket) for the accepted connection or + * $(D_KEYWORD null) if the call would block on a + * non-blocking socket. + * + * Throws: $(D_PSYMBOL SocketException) if unable to accept. + */ + ConnectedSocket accept() @trusted + { + socket_t sock; - version (linux) - { - int flags; - if (!blocking) - { - flags |= SOCK_NONBLOCK; - } - sock = cast(socket_t).accept4(handle_, null, null, flags); - } - else - { - sock = cast(socket_t).accept(handle_, null, null); - } + version (linux) + { + int flags; + if (!blocking) + { + flags |= SOCK_NONBLOCK; + } + sock = cast(socket_t).accept4(handle_, null, null, flags); + } + else + { + sock = cast(socket_t).accept(handle_, null, null); + } - if (sock == socket_t.init) - { - if (wouldHaveBlocked()) - { - return null; - } - throw theAllocator.make!SocketException("Unable to accept socket connection"); - } + if (sock == socket_t.init) + { + if (wouldHaveBlocked()) + { + return null; + } + throw make!SocketException(defaultAllocator, + "Unable to accept socket connection"); + } - auto newSocket = theAllocator.make!ConnectedSocket(sock, addressFamily); + auto newSocket = defaultAllocator.make!ConnectedSocket(sock, addressFamily); - version (linux) - { // Blocking mode already set - } - else version (Posix) - { - if (!blocking) - { - try - { - newSocket.blocking = blocking; - } - catch (SocketException e) - { - theAllocator.dispose(newSocket); - throw e; - } - } - } - else version (Windows) - { // Inherits blocking mode - newSocket.blocking_ = blocking; - } - return newSocket; - } + version (linux) + { // Blocking mode already set + } + else version (Posix) + { + if (!blocking) + { + try + { + newSocket.blocking = blocking; + } + catch (SocketException e) + { + defaultAllocator.dispose(newSocket); + throw e; + } + } + } + else version (Windows) + { // Inherits blocking mode + newSocket.blocking_ = blocking; + } + return newSocket; + } } /** @@ -1084,123 +1090,123 @@ class StreamSocket : Socket, ConnectionOrientedSocket */ class ConnectedSocket : Socket, ConnectionOrientedSocket { - /** - * $(D_KEYWORD true) if the stream socket peer has performed an orderly - * shutdown. - */ - protected bool disconnected_; + /** + * $(D_KEYWORD true) if the stream socket peer has performed an orderly + * shutdown. + */ + protected bool disconnected_; - /** - * Returns: $(D_KEYWORD true) if the stream socket peer has performed an orderly - * shutdown. - */ - @property inout(bool) disconnected() inout const pure nothrow @safe @nogc - { - return disconnected_; - } + /** + * Returns: $(D_KEYWORD true) if the stream socket peer has performed an orderly + * shutdown. + */ + @property inout(bool) disconnected() inout const pure nothrow @safe @nogc + { + return disconnected_; + } - /** - * Create a socket. - * - * Params: - * handle = Socket. - * af = Address family. - */ - this(socket_t handle, AddressFamily af) - { - super(handle, af); - } + /** + * Create a socket. + * + * Params: + * handle = Socket. + * af = Address family. + */ + this(socket_t handle, AddressFamily af) + { + super(handle, af); + } - version (Windows) - { - private static int capToMaxBuffer(size_t size) pure nothrow @safe @nogc - { - // Windows uses int instead of size_t for length arguments. - // Luckily, the send/recv functions make no guarantee that - // all the data is sent, so we use that to send at most - // int.max bytes. - return size > size_t (int.max) ? int.max : cast(int) size; - } - } - else - { - private static size_t capToMaxBuffer(size_t size) pure nothrow @safe @nogc - { - return size; - } - } + version (Windows) + { + private static int capToMaxBuffer(size_t size) pure nothrow @safe @nogc + { + // Windows uses int instead of size_t for length arguments. + // Luckily, the send/recv functions make no guarantee that + // all the data is sent, so we use that to send at most + // int.max bytes. + return size > size_t (int.max) ? int.max : cast(int) size; + } + } + else + { + private static size_t capToMaxBuffer(size_t size) pure nothrow @safe @nogc + { + return size; + } + } - /** - * Receive data on the connection. - * - * Params: - * buf = Buffer to save received data. - * flags = Flags. - * - * Returns: The number of bytes received or 0 if nothing received - * because the call would block. - * - * Throws: $(D_PSYMBOL SocketException) if unable to receive. - */ - ptrdiff_t receive(ubyte[] buf, Flags flags = Flag.none) @trusted - { - ptrdiff_t ret; - if (!buf.length) - { - return 0; - } + /** + * Receive data on the connection. + * + * Params: + * buf = Buffer to save received data. + * flags = Flags. + * + * Returns: The number of bytes received or 0 if nothing received + * because the call would block. + * + * Throws: $(D_PSYMBOL SocketException) if unable to receive. + */ + ptrdiff_t receive(ubyte[] buf, Flags flags = Flag.none) @trusted + { + ptrdiff_t ret; + if (!buf.length) + { + return 0; + } - ret = recv(handle_, buf.ptr, capToMaxBuffer(buf.length), cast(int) flags); - if (ret == 0) - { - disconnected_ = true; - } - else if (ret == SOCKET_ERROR) - { - if (wouldHaveBlocked()) - { - return 0; - } - disconnected_ = true; - throw theAllocator.make!SocketException("Unable to receive"); - } - return ret; - } + ret = recv(handle_, buf.ptr, capToMaxBuffer(buf.length), cast(int) flags); + if (ret == 0) + { + disconnected_ = true; + } + else if (ret == SOCKET_ERROR) + { + if (wouldHaveBlocked()) + { + return 0; + } + disconnected_ = true; + throw defaultAllocator.make!SocketException("Unable to receive"); + } + return ret; + } - /** - * Send data on the connection. If the socket is blocking and there is no - * buffer space left, $(D_PSYMBOL send) waits, non-blocking socket returns - * 0 in this case. - * - * Params: - * buf = Data to be sent. - * flags = Flags. - * - * Returns: The number of bytes actually sent. - * - * Throws: $(D_PSYMBOL SocketException) if unable to send. - */ - ptrdiff_t send(const(ubyte)[] buf, Flags flags = Flag.none) const @trusted - { - int sendFlags = cast(int) flags; - ptrdiff_t sent; + /** + * Send data on the connection. If the socket is blocking and there is no + * buffer space left, $(D_PSYMBOL send) waits, non-blocking socket returns + * 0 in this case. + * + * Params: + * buf = Data to be sent. + * flags = Flags. + * + * Returns: The number of bytes actually sent. + * + * Throws: $(D_PSYMBOL SocketException) if unable to send. + */ + ptrdiff_t send(const(ubyte)[] buf, Flags flags = Flag.none) const @trusted + { + int sendFlags = cast(int) flags; + ptrdiff_t sent; - static if (is(typeof(MSG_NOSIGNAL))) - { - sendFlags |= MSG_NOSIGNAL; - } + static if (is(typeof(MSG_NOSIGNAL))) + { + sendFlags |= MSG_NOSIGNAL; + } - sent = .send(handle_, buf.ptr, capToMaxBuffer(buf.length), sendFlags); - if (sent != SOCKET_ERROR) - { - return sent; - } - else if (wouldHaveBlocked()) - { - return 0; - } - throw theAllocator.make!SocketException("Unable to send"); - } + sent = .send(handle_, buf.ptr, capToMaxBuffer(buf.length), sendFlags); + if (sent != SOCKET_ERROR) + { + return sent; + } + else if (wouldHaveBlocked()) + { + return 0; + } + throw defaultAllocator.make!SocketException("Unable to send"); + } } /** @@ -1208,131 +1214,132 @@ class ConnectedSocket : Socket, ConnectionOrientedSocket */ abstract class Address { - /** - * Returns: Pointer to underlying $(D_PSYMBOL sockaddr) structure. - */ - abstract @property inout(sockaddr)* name() inout pure nothrow @nogc; + /** + * Returns: Pointer to underlying $(D_PSYMBOL sockaddr) structure. + */ + abstract @property inout(sockaddr)* name() inout pure nothrow @nogc; - /** - * Returns: Actual size of underlying $(D_PSYMBOL sockaddr) structure. - */ - abstract @property inout(socklen_t) length() inout const pure nothrow @nogc; + /** + * Returns: Actual size of underlying $(D_PSYMBOL sockaddr) structure. + */ + abstract @property inout(socklen_t) length() inout const pure nothrow @nogc; } class InternetAddress : Address { - version (Windows) - { - /// Internal internet address representation. - protected SOCKADDR_STORAGE storage; - } - else version (Posix) - { - /// Internal internet address representation. - protected sockaddr_storage storage; - } - immutable ushort port_; + version (Windows) + { + /// Internal internet address representation. + protected SOCKADDR_STORAGE storage; + } + else version (Posix) + { + /// Internal internet address representation. + protected sockaddr_storage storage; + } + immutable ushort port_; - enum - { - anyPort = 0, - } + enum + { + anyPort = 0, + } - this(in string host, ushort port = anyPort) - { - if (getaddrinfoPointer is null || freeaddrinfoPointer is null) - { - throw theAllocator.make!AddressException("Address info lookup is not available on this system"); - } - addrinfo* ai_res; - port_ = port; + this(in string host, ushort port = anyPort) + { + if (getaddrinfoPointer is null || freeaddrinfoPointer is null) + { + throw make!AddressException(defaultAllocator, + "Address info lookup is not available on this system"); + } + addrinfo* ai_res; + port_ = port; - // Make C-string from host. - char[] node = theAllocator.makeArray!char(host.length + 1); - node[0.. $ - 1] = host; - node[$ - 1] = '\0'; - scope (exit) - { - theAllocator.dispose(node); - } + // Make C-string from host. + char[] node = defaultAllocator.makeArray!char(host.length + 1); + node[0.. $ - 1] = host; + node[$ - 1] = '\0'; + scope (exit) + { + defaultAllocator.dispose(node); + } - // Convert port to a C-string. - char[6] service = [0, 0, 0, 0, 0, 0]; - const(char)* servicePointer; - if (port) - { - ushort start; - for (ushort j = 10, i = 4; i > 0; j *= 10, --i) - { - ushort rest = port % 10; - if (rest != 0) - { - service[i] = cast(char) (rest + '0'); - start = i; - } - port /= 10; - } - servicePointer = service[start..$].ptr; - } + // Convert port to a C-string. + char[6] service = [0, 0, 0, 0, 0, 0]; + const(char)* servicePointer; + if (port) + { + ushort start; + for (ushort j = 10, i = 4; i > 0; j *= 10, --i) + { + ushort rest = port % 10; + if (rest != 0) + { + service[i] = cast(char) (rest + '0'); + start = i; + } + port /= 10; + } + servicePointer = service[start..$].ptr; + } - auto ret = getaddrinfoPointer(node.ptr, servicePointer, null, &ai_res); - if (ret) - { - throw theAllocator.make!AddressException("Address info lookup failed"); - } - scope (exit) - { - freeaddrinfoPointer(ai_res); - } - - ubyte* dp = cast(ubyte*) &storage, sp = cast(ubyte*) ai_res.ai_addr; - for (auto i = ai_res.ai_addrlen; i > 0; --i, *dp++, *sp++) - { - *dp = *sp; - } - if (ai_res.ai_family != AddressFamily.INET && ai_res.ai_family != AddressFamily.INET6) - { - throw theAllocator.make!AddressException("Wrong address family"); - } - } + auto ret = getaddrinfoPointer(node.ptr, servicePointer, null, &ai_res); + if (ret) + { + throw defaultAllocator.make!AddressException("Address info lookup failed"); + } + scope (exit) + { + freeaddrinfoPointer(ai_res); + } + + ubyte* dp = cast(ubyte*) &storage, sp = cast(ubyte*) ai_res.ai_addr; + for (auto i = ai_res.ai_addrlen; i > 0; --i, *dp++, *sp++) + { + *dp = *sp; + } + if (ai_res.ai_family != AddressFamily.INET && ai_res.ai_family != AddressFamily.INET6) + { + throw defaultAllocator.make!AddressException("Wrong address family"); + } + } - /** - * Returns: Pointer to underlying $(D_PSYMBOL sockaddr) structure. - */ - override @property inout(sockaddr)* name() inout pure nothrow @nogc - { - return cast(sockaddr*) &storage; - } + /** + * Returns: Pointer to underlying $(D_PSYMBOL sockaddr) structure. + */ + override @property inout(sockaddr)* name() inout pure nothrow @nogc + { + return cast(sockaddr*) &storage; + } - /** - * Returns: Actual size of underlying $(D_PSYMBOL sockaddr) structure. - */ - override @property inout(socklen_t) length() inout const pure nothrow @nogc - { - // FreeBSD wants to know the exact length of the address on bind. - switch (family) - { - case AddressFamily.INET: - return sockaddr_in.sizeof; - case AddressFamily.INET6: - return sockaddr_in6.sizeof; - default: - assert(false); - } - } + /** + * Returns: Actual size of underlying $(D_PSYMBOL sockaddr) structure. + */ + override @property inout(socklen_t) length() inout const pure nothrow @nogc + { + // FreeBSD wants to know the exact length of the address on bind. + switch (family) + { + case AddressFamily.INET: + return sockaddr_in.sizeof; + case AddressFamily.INET6: + return sockaddr_in6.sizeof; + default: + assert(false); + } + } - /** - * Returns: Family of this address. - */ - @property inout(AddressFamily) family() inout const pure nothrow @nogc - { - return cast(AddressFamily) storage.ss_family; - } + /** + * Returns: Family of this address. + */ + @property inout(AddressFamily) family() inout const pure nothrow @nogc + { + return cast(AddressFamily) storage.ss_family; + } - @property inout(ushort) port() inout const pure nothrow @nogc - { - return port_; - } + @property inout(ushort) port() inout const pure nothrow @nogc + { + return port_; + } } /** @@ -1346,15 +1353,16 @@ class InternetAddress : Address */ bool wouldHaveBlocked() nothrow @trusted @nogc { - version (Posix) - { - return errno == EAGAIN || errno == EWOULDBLOCK; - } - else version (Windows) - { - return WSAGetLastError() == ERROR_IO_PENDING || WSAGetLastError() == EWOULDBLOCK - || WSAGetLastError() == ERROR_IO_INCOMPLETE; - } + version (Posix) + { + return errno == EAGAIN || errno == EWOULDBLOCK; + } + else version (Windows) + { + return WSAGetLastError() == ERROR_IO_PENDING + || WSAGetLastError() == EWOULDBLOCK + || WSAGetLastError() == ERROR_IO_INCOMPLETE; + } } /** @@ -1362,12 +1370,12 @@ bool wouldHaveBlocked() nothrow @trusted @nogc */ private @property int lastError() nothrow @safe @nogc { - version (Windows) - { - return WSAGetLastError(); - } - else - { - return errno; - } + version (Windows) + { + return WSAGetLastError(); + } + else + { + return errno; + } } diff --git a/source/tanya/network/url.d b/source/tanya/network/url.d index ee58e7e..6f0e964 100644 --- a/source/tanya/network/url.d +++ b/source/tanya/network/url.d @@ -18,8 +18,8 @@ import tanya.memory; version (unittest) private { - import std.typecons; - static Tuple!(string, string[string], ushort)[] URLTests; + import std.typecons; + static Tuple!(string, string[string], ushort)[] URLTests; } static this() @@ -631,447 +631,447 @@ static this() * A Unique Resource Locator. */ struct URL(U = string) - if (isSomeString!U) + if (isSomeString!U) { - /** The URL scheme. */ - U scheme; + /** The URL scheme. */ + U scheme; - /** The username. */ - U user; + /** The username. */ + U user; - /** The password. */ - U pass; + /** The password. */ + U pass; - /** The hostname. */ - U host; + /** The hostname. */ + U host; - /** The port number. */ - ushort port; + /** The port number. */ + ushort port; - /** The path. */ - U path; + /** The path. */ + U path; - /** The query string. */ - U query; + /** The query string. */ + U query; - /** The anchor. */ - U fragment; + /** The anchor. */ + U fragment; - /** - * Attempts to parse an URL from a string. - * Output string data (scheme, user, etc.) are just slices of input string (e.g., no memory allocation and copying). - * - * Params: - * source = The string containing the URL. - * - * Throws: $(D_PSYMBOL URIException) if the URL is malformed. - */ - this(U source) - { - auto value = source; - ptrdiff_t pos = -1, endPos = value.length, start; + /** + * Attempts to parse an URL from a string. + * Output string data (scheme, user, etc.) are just slices of input string (e.g., no memory allocation and copying). + * + * Params: + * source = The string containing the URL. + * + * Throws: $(D_PSYMBOL URIException) if the URL is malformed. + */ + this(U source) + { + auto value = source; + ptrdiff_t pos = -1, endPos = value.length, start; - foreach (i, ref c; source) - { - if (pos == -1 && c == ':') - { - pos = i; - } - if (endPos == value.length && (c == '?' || c == '#')) - { - endPos = i; - } - } + foreach (i, ref c; source) + { + if (pos == -1 && c == ':') + { + pos = i; + } + if (endPos == value.length && (c == '?' || c == '#')) + { + endPos = i; + } + } - // Check if the colon is a part of the scheme or the port and parse - // the appropriate part - if (value.length > 1 && value[0] == '/' && value[1] == '/') - { - // Relative scheme - start = 2; - } - else if (pos > 0) - { - // Validate scheme - // [ toLower(alpha) | digit | "+" | "-" | "." ] - foreach (ref c; value[0..pos]) - { - if (!c.isAlphaNum && c != '+' && c != '-' && c != '.') - { - if (endPos > pos) - { - if (!parsePort(value[pos..$])) - { - throw theAllocator.make!URIException("Failed to parse port"); - } - } - goto ParsePath; - } - } + // Check if the colon is a part of the scheme or the port and parse + // the appropriate part + if (value.length > 1 && value[0] == '/' && value[1] == '/') + { + // Relative scheme + start = 2; + } + else if (pos > 0) + { + // Validate scheme + // [ toLower(alpha) | digit | "+" | "-" | "." ] + foreach (ref c; value[0..pos]) + { + if (!c.isAlphaNum && c != '+' && c != '-' && c != '.') + { + if (endPos > pos) + { + if (!parsePort(value[pos..$])) + { + throw defaultAllocator.make!URIException("Failed to parse port"); + } + } + goto ParsePath; + } + } - if (value.length == pos + 1) // only scheme is available - { - scheme = value[0 .. $ - 1]; - return; - } - else if (value.length > pos + 1 && value[pos + 1] == '/') - { - scheme = value[0..pos]; + if (value.length == pos + 1) // only scheme is available + { + scheme = value[0 .. $ - 1]; + return; + } + else if (value.length > pos + 1 && value[pos + 1] == '/') + { + scheme = value[0..pos]; - if (value.length > pos + 2 && value[pos + 2] == '/') - { - start = pos + 3; - if (scheme == "file" && value.length > start && value[start] == '/') - { - // Windows drive letters - if (value.length - start > 2 && value[start + 2] == ':') - { - ++start; - } - goto ParsePath; - } - } - else - { - start = pos + 1; - goto ParsePath; - } - } - else // certain schemas like mailto: and zlib: may not have any / after them - { - - if (!parsePort(value[pos..$])) - { - scheme = value[0..pos]; - start = pos + 1; - goto ParsePath; - } - } - } - else if (pos == 0 && parsePort(value[pos..$])) - { - // An URL shouldn't begin with a port number - throw theAllocator.make!URIException("URL begins with port"); - } - else - { - goto ParsePath; - } + if (value.length > pos + 2 && value[pos + 2] == '/') + { + start = pos + 3; + if (scheme == "file" && value.length > start && value[start] == '/') + { + // Windows drive letters + if (value.length - start > 2 && value[start + 2] == ':') + { + ++start; + } + goto ParsePath; + } + } + else + { + start = pos + 1; + goto ParsePath; + } + } + else // certain schemas like mailto: and zlib: may not have any / after them + { + + if (!parsePort(value[pos..$])) + { + scheme = value[0..pos]; + start = pos + 1; + goto ParsePath; + } + } + } + else if (pos == 0 && parsePort(value[pos..$])) + { + // An URL shouldn't begin with a port number + throw defaultAllocator.make!URIException("URL begins with port"); + } + else + { + goto ParsePath; + } - // Parse host - pos = -1; - for (ptrdiff_t i = start; i < value.length; ++i) - { - if (value[i] == '@') - { - pos = i; - } - else if (value[i] == '/') - { - endPos = i; - break; - } - } + // Parse host + pos = -1; + for (ptrdiff_t i = start; i < value.length; ++i) + { + if (value[i] == '@') + { + pos = i; + } + else if (value[i] == '/') + { + endPos = i; + break; + } + } - // Check for login and password - if (pos != -1) - { - // *( unreserved / pct-encoded / sub-delims / ":" ) - foreach (i, c; value[start..pos]) - { - if (c == ':') - { - if (user is null) - { - user = value[start .. start + i]; - pass = value[start + i + 1 .. pos]; - } - } - else if (!c.isAlpha && - !c.isNumber && - c != '!' && - c != ';' && - c != '=' && - c != '_' && - c != '~' && - !(c >= '$' && c <= '.')) - { - if (scheme !is null) - { - scheme = null; - } - if (user !is null) - { - user = null; - } - if (pass !is null) - { - pass = null; - } - throw make!URIException(theAllocator, - "Restricted characters in user information"); - } - } - if (user is null) - { - user = value[start..pos]; - } + // Check for login and password + if (pos != -1) + { + // *( unreserved / pct-encoded / sub-delims / ":" ) + foreach (i, c; value[start..pos]) + { + if (c == ':') + { + if (user is null) + { + user = value[start .. start + i]; + pass = value[start + i + 1 .. pos]; + } + } + else if (!c.isAlpha && + !c.isNumber && + c != '!' && + c != ';' && + c != '=' && + c != '_' && + c != '~' && + !(c >= '$' && c <= '.')) + { + if (scheme !is null) + { + scheme = null; + } + if (user !is null) + { + user = null; + } + if (pass !is null) + { + pass = null; + } + throw make!URIException(defaultAllocator, + "Restricted characters in user information"); + } + } + if (user is null) + { + user = value[start..pos]; + } - start = ++pos; - } + start = ++pos; + } - pos = endPos; - if (endPos <= 1 || value[start] != '[' || value[endPos - 1] != ']') - { - // Short circuit portscan - // IPv6 embedded address - for (ptrdiff_t i = endPos - 1; i >= start; --i) - { - if (value[i] == ':') - { - pos = i; - if (port == 0 && !parsePort(value[i..endPos])) - { - if (scheme !is null) - { - scheme = null; - } - if (user !is null) - { - user = null; - } - if (pass !is null) - { - pass = null; - } - throw theAllocator.make!URIException("Invalid port"); - } - break; - } - } - } + pos = endPos; + if (endPos <= 1 || value[start] != '[' || value[endPos - 1] != ']') + { + // Short circuit portscan + // IPv6 embedded address + for (ptrdiff_t i = endPos - 1; i >= start; --i) + { + if (value[i] == ':') + { + pos = i; + if (port == 0 && !parsePort(value[i..endPos])) + { + if (scheme !is null) + { + scheme = null; + } + if (user !is null) + { + user = null; + } + if (pass !is null) + { + pass = null; + } + throw defaultAllocator.make!URIException("Invalid port"); + } + break; + } + } + } - // Check if we have a valid host, if we don't reject the string as url - if (pos <= start) - { - if (scheme !is null) - { - scheme = null; - } - if (user !is null) - { - user = null; - } - if (pass !is null) - { - pass = null; - } - throw theAllocator.make!URIException("Invalid host"); - } + // Check if we have a valid host, if we don't reject the string as url + if (pos <= start) + { + if (scheme !is null) + { + scheme = null; + } + if (user !is null) + { + user = null; + } + if (pass !is null) + { + pass = null; + } + throw defaultAllocator.make!URIException("Invalid host"); + } - host = value[start..pos]; + host = value[start..pos]; - if (endPos == value.length) - { - return; - } + if (endPos == value.length) + { + return; + } - start = endPos; + start = endPos; - ParsePath: - endPos = value.length; - pos = -1; - foreach (i, ref c; value[start..$]) - { - if (c == '?' && pos == -1) - { - pos = start + i; - } - else if (c == '#') - { - endPos = start + i; - break; - } - } - if (pos == -1) - { - pos = endPos; - } + ParsePath: + endPos = value.length; + pos = -1; + foreach (i, ref c; value[start..$]) + { + if (c == '?' && pos == -1) + { + pos = start + i; + } + else if (c == '#') + { + endPos = start + i; + break; + } + } + if (pos == -1) + { + pos = endPos; + } - if (pos > start) - { - path = value[start..pos]; - } - if (endPos >= ++pos) - { - query = value[pos..endPos]; - } - if (++endPos <= value.length) - { - fragment = value[endPos..$]; - } - } + if (pos > start) + { + path = value[start..pos]; + } + if (endPos >= ++pos) + { + query = value[pos..endPos]; + } + if (++endPos <= value.length) + { + fragment = value[endPos..$]; + } + } - ~this() - { - if (scheme !is null) - { - scheme = null; - } - if (user !is null) - { - user = null; - } - if (pass !is null) - { - pass = null; - } - if (host !is null) - { - host = null; - } - if (path !is null) - { - path = null; - } - if (query !is null) - { - query = null; - } - if (fragment !is null) - { - fragment = null; - } - } +~this() +{ + if (scheme !is null) + { + scheme = null; + } + if (user !is null) + { + user = null; + } + if (pass !is null) + { + pass = null; + } + if (host !is null) + { + host = null; + } + if (path !is null) + { + path = null; + } + if (query !is null) + { + query = null; + } + if (fragment !is null) + { + fragment = null; + } +} - /** - * Attempts to parse and set the port. - * - * Params: - * port = String beginning with a colon followed by the port number and - * an optional path (query string and/or fragment), like: - * `:12345/some_path` or `:12345`. - * - * Returns: Whether the port could be found. - */ - private bool parsePort(U port) pure nothrow @safe @nogc - { - ptrdiff_t i = 1; - float lPort = 0; + /** + * Attempts to parse and set the port. + * + * Params: + * port = String beginning with a colon followed by the port number and + * an optional path (query string and/or fragment), like: + * `:12345/some_path` or `:12345`. + * + * Returns: Whether the port could be found. + */ + private bool parsePort(U port) pure nothrow @safe @nogc + { + ptrdiff_t i = 1; + float lPort = 0; - for (; i < port.length && port[i].isDigit() && i <= 6; ++i) - { - lPort += (port[i] - '0') / cast(float)(10 ^^ (i - 1)); - } - if (i == 1 && (i == port.length || port[i] == '/')) - { - return true; - } - else if (i == port.length || port[i] == '/') - { - lPort *= 10 ^^ (i - 2); - if (lPort > ushort.max) - { - return false; - } - this.port = cast(ushort)lPort; - return true; - } - return false; - } + for (; i < port.length && port[i].isDigit() && i <= 6; ++i) + { + lPort += (port[i] - '0') / cast(float)(10 ^^ (i - 1)); + } + if (i == 1 && (i == port.length || port[i] == '/')) + { + return true; + } + else if (i == port.length || port[i] == '/') + { + lPort *= 10 ^^ (i - 2); + if (lPort > ushort.max) + { + return false; + } + this.port = cast(ushort)lPort; + return true; + } + return false; + } } /// unittest { - auto u = URL!()("example.org"); - assert(u.path == "example.org"); + auto u = URL!()("example.org"); + assert(u.path == "example.org"); - u = URL!()("relative/path"); - assert(u.path == "relative/path"); + u = URL!()("relative/path"); + assert(u.path == "relative/path"); - // Host and scheme - u = URL!()("https://example.org"); - assert(u.scheme == "https"); - assert(u.host == "example.org"); - assert(u.path is null); - assert(u.port == 0); - assert(u.fragment is null); + // Host and scheme + u = URL!()("https://example.org"); + assert(u.scheme == "https"); + assert(u.host == "example.org"); + assert(u.path is null); + assert(u.port == 0); + assert(u.fragment is null); - // With user and port and path - u = URL!()("https://hilary:putnam@example.org:443/foo/bar"); - assert(u.scheme == "https"); - assert(u.host == "example.org"); - assert(u.path == "/foo/bar"); - assert(u.port == 443); - assert(u.user == "hilary"); - assert(u.pass == "putnam"); - assert(u.fragment is null); + // With user and port and path + u = URL!()("https://hilary:putnam@example.org:443/foo/bar"); + assert(u.scheme == "https"); + assert(u.host == "example.org"); + assert(u.path == "/foo/bar"); + assert(u.port == 443); + assert(u.user == "hilary"); + assert(u.pass == "putnam"); + assert(u.fragment is null); - // With query string - u = URL!()("https://example.org/?login=true"); - assert(u.scheme == "https"); - assert(u.host == "example.org"); - assert(u.path == "/"); - assert(u.query == "login=true"); - assert(u.fragment is null); + // With query string + u = URL!()("https://example.org/?login=true"); + assert(u.scheme == "https"); + assert(u.host == "example.org"); + assert(u.path == "/"); + assert(u.query == "login=true"); + assert(u.fragment is null); - // With query string and fragment - u = URL!()("https://example.org/?login=false#label"); - assert(u.scheme == "https"); - assert(u.host == "example.org"); - assert(u.path == "/"); - assert(u.query == "login=false"); - assert(u.fragment == "label"); + // With query string and fragment + u = URL!()("https://example.org/?login=false#label"); + assert(u.scheme == "https"); + assert(u.host == "example.org"); + assert(u.path == "/"); + assert(u.query == "login=false"); + assert(u.fragment == "label"); - u = URL!()("redis://root:password@localhost:2201/path?query=value#fragment"); - assert(u.scheme == "redis"); - assert(u.user == "root"); - assert(u.pass == "password"); - assert(u.host == "localhost"); - assert(u.port == 2201); - assert(u.path == "/path"); - assert(u.query == "query=value"); - assert(u.fragment == "fragment"); + u = URL!()("redis://root:password@localhost:2201/path?query=value#fragment"); + assert(u.scheme == "redis"); + assert(u.user == "root"); + assert(u.pass == "password"); + assert(u.host == "localhost"); + assert(u.port == 2201); + assert(u.path == "/path"); + assert(u.query == "query=value"); + assert(u.fragment == "fragment"); } private unittest { - foreach(t; URLTests) - { - if (t[1].length == 0 && t[2] == 0) - { - try - { - URL!()(t[0]); - assert(0); - } - catch (URIException e) - { - assert(1); - } - } - else - { - auto u = URL!()(t[0]); - assert("scheme" in t[1] ? u.scheme == t[1]["scheme"] : u.scheme is null, - t[0]); - assert("user" in t[1] ? u.user == t[1]["user"] : u.user is null, t[0]); - assert("pass" in t[1] ? u.pass == t[1]["pass"] : u.pass is null, t[0]); - assert("host" in t[1] ? u.host == t[1]["host"] : u.host is null, t[0]); - assert(u.port == t[2], t[0]); - assert("path" in t[1] ? u.path == t[1]["path"] : u.path is null, t[0]); - assert("query" in t[1] ? u.query == t[1]["query"] : u.query is null, t[0]); - if ("fragment" in t[1]) - { - assert(u.fragment == t[1]["fragment"], t[0]); - } - else - { - assert(u.fragment is null, t[0]); - } - } - } + foreach(t; URLTests) + { + if (t[1].length == 0 && t[2] == 0) + { + try + { + URL!()(t[0]); + assert(0); + } + catch (URIException e) + { + assert(1); + } + } + else + { + auto u = URL!()(t[0]); + assert("scheme" in t[1] ? u.scheme == t[1]["scheme"] : u.scheme is null, + t[0]); + assert("user" in t[1] ? u.user == t[1]["user"] : u.user is null, t[0]); + assert("pass" in t[1] ? u.pass == t[1]["pass"] : u.pass is null, t[0]); + assert("host" in t[1] ? u.host == t[1]["host"] : u.host is null, t[0]); + assert(u.port == t[2], t[0]); + assert("path" in t[1] ? u.path == t[1]["path"] : u.path is null, t[0]); + assert("query" in t[1] ? u.query == t[1]["query"] : u.query is null, t[0]); + if ("fragment" in t[1]) + { + assert(u.fragment == t[1]["fragment"], t[0]); + } + else + { + assert(u.fragment is null, t[0]); + } + } + } } /** @@ -1080,112 +1080,112 @@ private unittest */ enum Component : string { - scheme = "scheme", - host = "host", - port = "port", - user = "user", - pass = "pass", - path = "path", - query = "query", - fragment = "fragment", + scheme = "scheme", + host = "host", + port = "port", + user = "user", + pass = "pass", + path = "path", + query = "query", + fragment = "fragment", } /** * Attempts to parse an URL from a string. * * Params: - * T = $(D_SYMBOL Component) member or $(D_KEYWORD null) for a - * struct with all components. - * source = The string containing the URL. + * T = $(D_SYMBOL Component) member or $(D_KEYWORD null) for a + * struct with all components. + * source = The string containing the URL. * * Returns: Requested URL components. */ URL parseURL(U)(in U source) - if (isSomeString!U) + if (isSomeString!U) { - return URL!U(source); + return URL!U(source); } -/** ditto */ +/// Ditto. string parseURL(string T, U)(in U source) - if ((T == "scheme" - || T =="host" - || T == "user" - || T == "pass" - || T == "path" - || T == "query" - || T == "fragment") && isSomeString!U) + if ((T == "scheme" + || T =="host" + || T == "user" + || T == "pass" + || T == "path" + || T == "query" + || T == "fragment") && isSomeString!U) { - auto ret = URL!U(source); - return mixin("ret." ~ T); + auto ret = URL!U(source); + return mixin("ret." ~ T); } -/** ditto */ +/// Ditto. ushort parseURL(string T, U)(in U source) - if (T == "port" && isSomeString!U) + if (T == "port" && isSomeString!U) { - auto ret = URL!U(source); - return ret.port; + auto ret = URL!U(source); + return ret.port; } unittest { - assert(parseURL!(Component.port)("http://example.org:5326") == 5326); + assert(parseURL!(Component.port)("http://example.org:5326") == 5326); } private unittest { - foreach(t; URLTests) - { - if (t[1].length == 0 && t[2] == 0) - { - try - { - parseURL!(Component.port)(t[0]); - parseURL!(Component.user)(t[0]); - parseURL!(Component.pass)(t[0]); - parseURL!(Component.host)(t[0]); - parseURL!(Component.path)(t[0]); - parseURL!(Component.query)(t[0]); - parseURL!(Component.fragment)(t[0]); - assert(0); - } - catch (URIException e) - { - assert(1); - } - } - else - { - ushort port = parseURL!(Component.port)(t[0]); - string component = parseURL!(Component.scheme)(t[0]); - assert("scheme" in t[1] ? component == t[1]["scheme"] : component is null, - t[0]); - component = parseURL!(Component.user)(t[0]); - assert("user" in t[1] ? component == t[1]["user"] : component is null, - t[0]); - component = parseURL!(Component.pass)(t[0]); - assert("pass" in t[1] ? component == t[1]["pass"] : component is null, - t[0]); - component = parseURL!(Component.host)(t[0]); - assert("host" in t[1] ? component == t[1]["host"] : component is null, - t[0]); - assert(port == t[2], t[0]); - component = parseURL!(Component.path)(t[0]); - assert("path" in t[1] ? component == t[1]["path"] : component is null, - t[0]); - component = parseURL!(Component.query)(t[0]); - assert("query" in t[1] ? component == t[1]["query"] : component is null, - t[0]); - component = parseURL!(Component.fragment)(t[0]); - if ("fragment" in t[1]) - { - assert(component == t[1]["fragment"], t[0]); - } - else - { - assert(component is null, t[0]); - } - } - } + foreach(t; URLTests) + { + if (t[1].length == 0 && t[2] == 0) + { + try + { + parseURL!(Component.port)(t[0]); + parseURL!(Component.user)(t[0]); + parseURL!(Component.pass)(t[0]); + parseURL!(Component.host)(t[0]); + parseURL!(Component.path)(t[0]); + parseURL!(Component.query)(t[0]); + parseURL!(Component.fragment)(t[0]); + assert(0); + } + catch (URIException e) + { + assert(1); + } + } + else + { + ushort port = parseURL!(Component.port)(t[0]); + string component = parseURL!(Component.scheme)(t[0]); + assert("scheme" in t[1] ? component == t[1]["scheme"] : component is null, + t[0]); + component = parseURL!(Component.user)(t[0]); + assert("user" in t[1] ? component == t[1]["user"] : component is null, + t[0]); + component = parseURL!(Component.pass)(t[0]); + assert("pass" in t[1] ? component == t[1]["pass"] : component is null, + t[0]); + component = parseURL!(Component.host)(t[0]); + assert("host" in t[1] ? component == t[1]["host"] : component is null, + t[0]); + assert(port == t[2], t[0]); + component = parseURL!(Component.path)(t[0]); + assert("path" in t[1] ? component == t[1]["path"] : component is null, + t[0]); + component = parseURL!(Component.query)(t[0]); + assert("query" in t[1] ? component == t[1]["query"] : component is null, + t[0]); + component = parseURL!(Component.fragment)(t[0]); + if ("fragment" in t[1]) + { + assert(component == t[1]["fragment"], t[0]); + } + else + { + assert(component is null, t[0]); + } + } + } } diff --git a/source/tanya/random.d b/source/tanya/random.d index ad00d28..8e69c32 100644 --- a/source/tanya/random.d +++ b/source/tanya/random.d @@ -10,9 +10,10 @@ */ module tanya.random; -import tanya.memory; +import std.experimental.allocator; import std.digest.sha; import std.typecons; +import tanya.memory; /// Block size of entropy accumulator (SHA-512). enum blockSize = 64; @@ -148,13 +149,13 @@ version (linux) /** * Pseudorandom number generator. * --- - * auto entropy = theAllocator.make!Entropy; + * auto entropy = defaultAllocator.make!Entropy(); * * ubyte[blockSize] output; * * output = entropy.random; * - * theAllocator.finalize(entropy); + * defaultAllocator.finalize(entropy); * --- */ class Entropy @@ -175,7 +176,7 @@ class Entropy * allocator = Allocator to allocate entropy sources available on the * system. */ - this(size_t maxSources = 20, IAllocator allocator = theAllocator) + this(size_t maxSources = 20, shared Allocator allocator = defaultAllocator) in { assert(maxSources > 0 && maxSources <= ubyte.max);