summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/tanya/async/event/epoll.d2
-rw-r--r--source/tanya/async/event/iocp.d9
-rw-r--r--source/tanya/async/event/kqueue.d2
-rw-r--r--source/tanya/async/event/selector.d4
-rw-r--r--source/tanya/async/loop.d16
-rw-r--r--source/tanya/async/watcher.d2
-rw-r--r--source/tanya/container/buffer.d80
-rw-r--r--source/tanya/container/list.d32
-rw-r--r--source/tanya/container/queue.d28
-rw-r--r--source/tanya/container/vector.d62
-rw-r--r--source/tanya/crypto/mode.d376
-rw-r--r--source/tanya/math/mp.d265
-rw-r--r--source/tanya/memory/allocator.d240
-rw-r--r--source/tanya/memory/mmappool.d869
-rw-r--r--source/tanya/memory/package.d69
-rw-r--r--source/tanya/memory/types.d148
-rw-r--r--source/tanya/network/socket.d2488
-rw-r--r--source/tanya/network/url.d1026
-rw-r--r--source/tanya/random.d9
19 files changed, 2846 insertions, 2881 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 == "--")
- {
- sign = true;
- }
- auto size = rep
- .get
- .retro
- .countUntil!((const ref a) => a != typeof(rep[0]).max);
- if (size == -1)
+ if (sign)
{
- 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;
- }
-
- /**
- * Not supported.
- *
- * Returns $(D_PSYMBOL Ternary.unknown).
- */
- Ternary empty() const @nogc @safe pure nothrow
- {
- return Ternary.unknown;
- }
-
- /**
- * Not supported.
- *
- * Params:
- * b = Memory block.
- *
- * Returns: $(D_PSYMBOL Ternary.unknown).
- */
- Ternary owns(void[] b) const @nogc @safe pure nothrow
- {
- return Ternary.unknown;
- }
-
- /**
- * 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;
- }
- }
+@nogc:
+ @property uint alignment() const shared pure nothrow @safe;
+
+ /**
+ * 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;
+
+ /**
+ * 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;
+
+ /**
+ * 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();
-
- 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) @nogc @trusted nothrow
- {
- if (!size)
- {
- return null;
- }
- immutable dataSize = addAlignment(size);
-
- void* data = findBlock(dataSize);
- if (data is null)
- {
- data = initializeRegion(dataSize);
- }
-
- return data is null ? null : data[0..size];
- }
-
- ///
- @nogc @safe nothrow unittest
- {
- auto p = MmapPool.instance.allocate(20);
-
- assert(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) @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;
-
- 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;
- }
-
- /**
- * 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;
- }
- }
-
- ///
- @nogc @safe nothrow unittest
- {
- auto p = MmapPool.instance.allocate(20);
-
- assert(MmapPool.instance.deallocate(p));
- }
-
- /**
- * Increases or decreases the size of a memory block.
- *
- * Params:
- * p = A pointer to the memory block.
- * size = Size of the reallocated block.
- *
- * Returns: Whether the reallocation was successful.
- */
- bool reallocate(ref void[] p, size_t size) @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 (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;
- }
-
- ///
- @nogc nothrow unittest
- {
- void[] p;
- MmapPool.instance.reallocate(p, 10 * int.sizeof);
- (cast(int[]) p)[7] = 123;
-
- assert(p.length == 40);
-
- MmapPool.instance.reallocate(p, 8 * int.sizeof);
-
- assert(p.length == 32);
- assert((cast(int[]) p)[7] == 123);
-
- 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);
-
- MmapPool.instance.reallocate(p, 8 * int.sizeof);
-
- assert(p.length == 32);
- assert((cast(int[]) p)[7] == 123);
-
- MmapPool.instance.deallocate(p);
- }
-
- /**
- * 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(MmapPool) data;
- instance_.head = head;
- }
- }
- return 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) @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;
-
- // 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;
-
- // 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;
-
- return data;
- }
-
- /// 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)
- @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)
- @nogc @safe pure nothrow
- out (result)
- {
- assert(result > 0);
- }
- body
- {
- x += regionEntrySize + blockEntrySize * 2;
- return x / pageSize * pageSize + pageSize;
- }
-
- @property uint alignment() const @nogc @safe pure nothrow
- {
- return alignment_;
- }
- private enum alignment_ = 8;
-
- private static MmapPool instance_;
-
- 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 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;
+@nogc:
+ 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);
+
+ void* data = findBlock(dataSize);
+ if (data is null)
+ {
+ data = initializeRegion(dataSize);
+ }
+
+ return data is null ? null : data[0..size];
+ }
+
+ ///
+ @safe nothrow unittest
+ {
+ auto p = MmapPool.instance.allocate(20);
+
+ assert(p);
+
+ MmapPool.instance.deallocate(p);
+ }
+
+ /**
+ * Search for a block large enough to keep $(D_PARAM size) and split it
+ * into two blocks if the block is too large.
+ *
+ * Params:
+ * size = Minimum size the block should have.
+ *
+ * Returns: Data the block points to or $(D_KEYWORD null).
+ */
+ private void* findBlock(size_t size) shared nothrow
+ {
+ Block block1;
+ RegionLoop: for (auto r = head; r !is null; r = r.next)
+ {
+ block1 = cast(Block) (cast(void*) r + regionEntrySize);
+ do
+ {
+ if (block1.free && block1.size >= size)
+ {
+ break RegionLoop;
+ }
+ }
+ while ((block1 = block1.next) !is null);
+ }
+ if (block1 is null)
+ {
+ return null;
+ }
+ else if (block1.size >= size + alignment + blockEntrySize)
+ { // Split the block if needed
+ Block block2 = cast(Block) (cast(void*) block1 + blockEntrySize + size);
+ block2.prev = block1;
+ if (block1.next is null)
+ {
+ block2.next = null;
+ }
+ else
+ {
+ block2.next = block1.next.next;
+ }
+ block1.next = block2;
+
+ block1.free = false;
+ block2.free = true;
+
+ 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;
+ }
+
+ /**
+ * 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;
+ }
+
+ 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);
+
+ assert(MmapPool.instance.deallocate(p));
+ }
+
+ /**
+ * Increases or decreases the size of a memory block.
+ *
+ * Params:
+ * p = A pointer to the memory block.
+ * size = Size of the reallocated block.
+ *
+ * Returns: Whether the reallocation was successful.
+ */
+ bool reallocate(ref void[] p, size_t size) shared nothrow @trusted
+ {
+ void[] reallocP;
+
+ if (size == p.length)
+ {
+ 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;
+
+ return true;
+ }
+
+ ///
+ nothrow unittest
+ {
+ void[] p;
+ MmapPool.instance.reallocate(p, 10 * int.sizeof);
+ (cast(int[]) p)[7] = 123;
+
+ assert(p.length == 40);
+
+ MmapPool.instance.reallocate(p, 8 * int.sizeof);
+
+ assert(p.length == 32);
+ assert((cast(int[]) p)[7] == 123);
+
+ 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);
+
+ MmapPool.instance.reallocate(p, 8 * int.sizeof);
+
+ assert(p.length == 32);
+ assert((cast(int[]) p)[7] == 123);
+
+ MmapPool.instance.deallocate(p);
+ }
+
+ /**
+ * Static allocator instance and initializer.
+ *
+ * Returns: Global $(D_PSYMBOL MmapPool) instance.
+ */
+ static @property ref shared(MmapPool) instance() nothrow @trusted
+ {
+ if (instance_ is null)
+ {
+ immutable instanceSize = addAlignment(__traits(classInstanceSize, MmapPool));
+
+ Region head; // Will become soon our region list head
+ 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_;
+ }
+
+ ///
+ @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;
+ }
+ }
+
+ 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;
+
+ // 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;
+
+ // 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;
+ }
+
+ /// Ditto.
+ private void* initializeRegion(size_t size) shared 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 = 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;
+ }
+
+ @property uint alignment() shared const pure nothrow @safe
+ {
+ return alignment_;
+ }
+ private enum alignment_ = 8;
+
+ private static shared MmapPool instance_;
+
+ 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 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;
}
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);
@@ -78,35 +89,13 @@ struct RefCounted(T)
}
/**
- * 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.
*/
this(this) pure nothrow @safe @nogc
{
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)
+ {
+ counter == 1 ? allocator.dispose(payload) : --counter;
+ }
+ else if (!isInitialized)
{
- initialize();
+ 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)
- {
- onOutOfMemoryError();
- }
- scope (failure)
- {
- allocator.deallocate(p);
- }
-
- rc.counter = emplace(cast(uint*) p.ptr, 1);
-
- static if (is(T == class))
+ static if (isReference!T)
{
- 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;
-
- private enum SOCKET_ERROR = -1;
+ 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;
}
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;
-
- 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;
- }
-
- 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;
-
- 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 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;
-
- 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 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 OverlappedSocketEvent
- {
- accept = 1,
- read = 2,
- write = 3,
- }
-
- 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);
- }
-
- /**
- * 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;
-
- 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;
- }
-
- /**
- * 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;
- }
-
- /**
- * 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);
-
- if (result == SOCKET_ERROR && !wouldHaveBlocked)
- {
- disconnected_ = true;
- throw theAllocator.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;
- }
- }
-
- 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;
-
- 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");
- }
- }
-
- /**
- * 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;
-
- // 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;
- }
-
- /**
- * 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;
- }
- }
+ 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.
+ }
+
+ 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);
+
+ private alias GROUP = uint;
+
+ 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 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;
+
+ 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 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 OverlappedSocketEvent
+ {
+ accept = 1,
+ read = 2,
+ write = 3,
+ }
+
+ 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);
+ }
+
+ /**
+ * 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;
+
+ auto result = WSARecv(handle_,
+ &overlapped.buffer,
+ 1u,
+ NULL,
+ &receiveFlags,
+ &overlapped.overlapped,
+ NULL);
+
+ if (result == SOCKET_ERROR && !wouldHaveBlocked)
+ {
+ throw defaultAllocator.make!SocketException("Unable to receive");
+ }
+ return result == 0;
+ }
+
+ /**
+ * 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;
+
+ auto result = WSASend(handle_,
+ &overlapped.buffer,
+ 1u,
+ NULL,
+ cast(DWORD) flags,
+ &overlapped.overlapped,
+ NULL);
+
+ if (result == SOCKET_ERROR && !wouldHaveBlocked)
+ {
+ disconnected_ = true;
+ throw defaultAllocator.make!SocketException("Unable to send");
+ }
+ return result == 0;
+ }
+
+ /**
+ * 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;
+
+ /**
+ * 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;
+
+ 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 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 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)
+ {
+ 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");
-
- getaddrinfoPointer = cast(typeof(getaddrinfoPointer))
- GetProcAddress(ws2Lib, "getaddrinfo");
- freeaddrinfoPointer = cast(typeof(freeaddrinfoPointer))
- GetProcAddress(ws2Lib, "freeaddrinfo");
- }
- else version (Posix)
- {
- getaddrinfoPointer = &getaddrinfo;
- freeaddrinfoPointer = &freeaddrinfo;
- }
+ 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;
+ }
}
/**
@@ -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;
-
- /**
- * 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;
- }
- }
- }
+ 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);
+
+ 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;
-
- /**
- * 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;
- }
-
- /// Socket handle.
- protected socket_t handle_;
-
- /// 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;
-
- // 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_;
- }
-
- /**
- * 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();
- }
-
- /**
- * 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;
- }
-
- /// 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 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");
- }
- }
-
- /// 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, 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_;
- }
- }
-
- /**
- * 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;
- }
- }
-
- /**
- * 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);
- }
-
- /**
- * 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;
- }
-
- /**
- * 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");
- }
- }
-
- /**
- * 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;
- }
+ 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.
+ }
+
+ // 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_;
+
+ /// 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;
+
+ // 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_;
+ }
+
+ /**
+ * 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();
+ }
+
+ /**
+ * 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 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;
+ }
+
+ /**
+ * 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, 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);
+ }
+ }
+
+ /**
+ * 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);
+
+ 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: $(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);
+ }
+
+ /**
+ * 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 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;
+ }
}
/**
@@ -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
- }
-
- alias Flags = BitFlags!Flag;
+ /**
+ * 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;
}
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);
- }
-
- /**
- * 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");
- }
- }
-
- /**
- * 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);
- }
-
- if (sock == socket_t.init)
- {
- if (wouldHaveBlocked())
- {
- return null;
- }
- throw theAllocator.make!SocketException("Unable to accept socket connection");
- }
-
- auto newSocket = theAllocator.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;
- }
+ /**
+ * 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 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;
+
+ version (linux)
+ {
+ int flags;
+ if (!blocking)
+ {
+ flags |= SOCK_NONBLOCK;
+ }
+ sock = cast(socket_t).accept4(handle_, null, null, flags);
+ }
+ else
+ {
+ sock = cast(socket_t).accept(handle_, null, null);
+ }
+
+ if (sock == socket_t.init)
+ {
+ if (wouldHaveBlocked())
+ {
+ return null;
+ }
+ throw make!SocketException(defaultAllocator,
+ "Unable to accept socket connection");
+ }
+
+ auto newSocket = defaultAllocator.make!ConnectedSocket(sock, addressFamily);
+
+ version (linux)
+ { // Blocking mode already set
+ }
+ else version (Posix)
+ {
+ if (!blocking)
+ {
+ try
+ {
+ newSocket.blocking = blocking;
+ }
+ catch (SocketException e)
+ {
+ defaultAllocator.dispose(newSocket);
+ throw e;
+ }
+ }
+ }
+ else version (Windows)
+ { // Inherits blocking mode
+ newSocket.blocking_ = blocking;
+ }
+ return newSocket;
+ }
}
/**
@@ -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_;
-
- /**
- * 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);
- }
-
- 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;
- }
-
- 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;
- }
-
- /**
- * 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;
- }
-
- 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");
- }
+ /**
+ * $(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_;
+ }
+
+ /**
+ * 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;
+ }
+ }
+
+ /**
+ * 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 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;
+
+ static if (is(typeof(MSG_NOSIGNAL)))
+ {
+ sendFlags |= MSG_NOSIGNAL;
+ }
+
+ sent = .send(handle_, buf.ptr, capToMaxBuffer(buf.length), sendFlags);
+ if (sent != SOCKET_ERROR)
+ {
+ return sent;
+ }
+ else if (wouldHaveBlocked())
+ {
+ return 0;
+ }
+ throw defaultAllocator.make!SocketException("Unable to send");
+ }
}
/**
@@ -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: Actual size of underlying $(D_PSYMBOL sockaddr) structure.
- */
- abstract @property inout(socklen_t) length() inout const 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;
}
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_;
-
- 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;
-
- // 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);
- }
-
- // 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");
- }
- }
-
- /**
- * 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: 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_;
- }
+ 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,
+ }
+
+ 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 = 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;
+ }
+
+ 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: 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;
+ }
+
+ @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 username. */
- U user;
-
- /** The password. */
- U pass;
-
- /** The hostname. */
- U host;
-
- /** The port number. */
- ushort port;
-
- /** The path. */
- U path;
-
- /** The query string. */
- U query;
-
- /** 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;
-
- 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;
- }
- }
-
- 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;
- }
-
- // 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];
- }
-
- 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;
- }
- }
- }
-
- // 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");
- }
-
- host = value[start..pos];
-
- if (endPos == value.length)
- {
- return;
- }
-
- 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;
- }
-
- 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;
- }
- }
+ /** The URL scheme. */
+ U scheme;
+
+ /** The username. */
+ U user;
+
+ /** The password. */
+ U pass;
+
+ /** The hostname. */
+ U host;
+
+ /** The port number. */
+ ushort port;
+
+ /** The path. */
+ U path;
+
+ /** The query string. */
+ U query;
+
+ /** 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;
+
+ foreach (i, ref c; source)
+ {
+ if (pos == -1 && c == ':')
+ {
+ pos = i;
+ }
+ if (endPos == value.length && (c == '?' || c == '#'))
+ {
+ endPos = i;
+ }
+ }
+
+ // Check if the colon is a part of the scheme or the port and parse
+ // the appropriate part
+ if (value.length > 1 && value[0] == '/' && value[1] == '/')
+ {
+ // Relative scheme
+ start = 2;
+ }
+ else if (pos > 0)
+ {
+ // Validate scheme
+ // [ toLower(alpha) | digit | "+" | "-" | "." ]
+ foreach (ref c; value[0..pos])
+ {
+ if (!c.isAlphaNum && c != '+' && c != '-' && c != '.')
+ {
+ if (endPos > pos)
+ {
+ if (!parsePort(value[pos..$]))
+ {
+ throw defaultAllocator.make!URIException("Failed to parse port");
+ }
+ }
+ goto ParsePath;
+ }
+ }
+
+ if (value.length == pos + 1) // only scheme is available
+ {
+ scheme = value[0 .. $ - 1];
+ return;
+ }
+ else if (value.length > pos + 1 && value[pos + 1] == '/')
+ {
+ scheme = value[0..pos];
+
+ if (value.length > pos + 2 && value[pos + 2] == '/')
+ {
+ start = pos + 3;
+ if (scheme == "file" && value.length > start && value[start] == '/')
+ {
+ // Windows drive letters
+ if (value.length - start > 2 && value[start + 2] == ':')
+ {
+ ++start;
+ }
+ goto ParsePath;
+ }
+ }
+ else
+ {
+ start = pos + 1;
+ goto ParsePath;
+ }
+ }
+ else // certain schemas like mailto: and zlib: may not have any / after them
+ {
+
+ if (!parsePort(value[pos..$]))
+ {
+ scheme = value[0..pos];
+ start = pos + 1;
+ goto ParsePath;
+ }
+ }
+ }
+ else if (pos == 0 && parsePort(value[pos..$]))
+ {
+ // An URL shouldn't begin with a port number
+ throw defaultAllocator.make!URIException("URL begins with port");
+ }
+ else
+ {
+ goto ParsePath;
+ }
+
+ // Parse host
+ pos = -1;
+ for (ptrdiff_t i = start; i < value.length; ++i)
+ {
+ if (value[i] == '@')
+ {
+ pos = i;
+ }
+ else if (value[i] == '/')
+ {
+ endPos = i;
+ break;
+ }
+ }
+
+ // Check for login and password
+ if (pos != -1)
+ {
+ // *( unreserved / pct-encoded / sub-delims / ":" )
+ foreach (i, c; value[start..pos])
+ {
+ if (c == ':')
+ {
+ if (user is null)
+ {
+ user = value[start .. start + i];
+ pass = value[start + i + 1 .. pos];
+ }
+ }
+ else if (!c.isAlpha &&
+ !c.isNumber &&
+ c != '!' &&
+ c != ';' &&
+ c != '=' &&
+ c != '_' &&
+ c != '~' &&
+ !(c >= '$' && c <= '.'))
+ {
+ if (scheme !is null)
+ {
+ scheme = null;
+ }
+ if (user !is null)
+ {
+ user = null;
+ }
+ if (pass !is null)
+ {
+ pass = null;
+ }
+ throw make!URIException(defaultAllocator,
+ "Restricted characters in user information");
+ }
+ }
+ if (user is null)
+ {
+ user = value[start..pos];
+ }
+
+ start = ++pos;
+ }
+
+ pos = endPos;
+ if (endPos <= 1 || value[start] != '[' || value[endPos - 1] != ']')
+ {
+ // Short circuit portscan
+ // IPv6 embedded address
+ for (ptrdiff_t i = endPos - 1; i >= start; --i)
+ {
+ if (value[i] == ':')
+ {
+ pos = i;
+ if (port == 0 && !parsePort(value[i..endPos]))
+ {
+ if (scheme !is null)
+ {
+ scheme = null;
+ }
+ if (user !is null)
+ {
+ user = null;
+ }
+ if (pass !is null)
+ {
+ pass = null;
+ }
+ throw defaultAllocator.make!URIException("Invalid port");
+ }
+ break;
+ }
+ }
+ }
+
+ // 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];
+
+ if (endPos == value.length)
+ {
+ return;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+}
- /**
- * 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;
- }
+ /**
+ * 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;
+ }
}
///
unittest
{
- auto u = URL!()("example.org");
- assert(u.path == "example.org");
-
- 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);
-
- // 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 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");
+ auto u = URL!()("example.org");
+ assert(u.path == "example.org");
+
+ 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);
+
+ // 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 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");
}
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);