summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2017-05-16 12:12:57 +0200
committerEugen Wissner <belka@caraus.de>2017-05-16 12:12:57 +0200
commit8c42cbfd63724d9d89908b23588673d57c65674d (patch)
tree6d725c95ff89d711c40e3e825b158d26f18bad4b /source
parent58664570f952e63e4b06a04b360c01c5550c24a1 (diff)
downloadtanya-8c42cbfd63724d9d89908b23588673d57c65674d.tar.gz
Rename Vector to Array
For consistency with Phobos.
Diffstat (limited to 'source')
-rw-r--r--source/tanya/async/event/epoll.d292
-rw-r--r--source/tanya/async/event/iocp.d694
-rw-r--r--source/tanya/async/event/kqueue.d468
-rw-r--r--source/tanya/async/event/selector.d731
-rw-r--r--source/tanya/async/iocp.d12
-rw-r--r--source/tanya/async/loop.d412
-rw-r--r--source/tanya/async/protocol.d40
-rw-r--r--source/tanya/async/transport.d94
-rw-r--r--source/tanya/async/watcher.d132
-rw-r--r--source/tanya/container/array.d1646
-rw-r--r--source/tanya/container/package.d2
-rw-r--r--source/tanya/container/vector.d1631
-rw-r--r--source/tanya/math/mp.d38
13 files changed, 3107 insertions, 3085 deletions
diff --git a/source/tanya/async/event/epoll.d b/source/tanya/async/event/epoll.d
index c10b241..5809dd4 100644
--- a/source/tanya/async/event/epoll.d
+++ b/source/tanya/async/event/epoll.d
@@ -18,7 +18,7 @@ import tanya.async.event.selector;
import tanya.async.loop;
import tanya.async.transport;
import tanya.async.watcher;
-import tanya.container.vector;
+import tanya.container.array;
import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket;
@@ -29,153 +29,153 @@ import std.algorithm.comparison;
extern (C) nothrow @nogc
{
- int epoll_create1(int flags);
- int epoll_ctl (int epfd, int op, int fd, epoll_event *event);
- int epoll_wait (int epfd, epoll_event *events, int maxevents, int timeout);
+ int epoll_create1(int flags);
+ int epoll_ctl (int epfd, int op, int fd, epoll_event *event);
+ int epoll_wait (int epfd, epoll_event *events, int maxevents, int timeout);
}
final class EpollLoop : SelectorLoop
{
- protected int fd;
- private Vector!epoll_event events;
-
- /**
- * Initializes the loop.
- */
- this() @nogc
- {
- if ((fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
- {
- throw defaultAllocator.make!BadLoopException("epoll initialization failed");
- }
- super();
- events = Vector!epoll_event(maxEvents, MmapPool.instance);
- }
-
- /**
- * Frees loop internals.
- */
- ~this() @nogc
- {
- close(fd);
- }
-
- /**
- * Should be called if the backend configuration changes.
- *
- * Params:
- * watcher = Watcher.
- * oldEvents = The events were already set.
- * events = The events should be set.
- *
- * Returns: $(D_KEYWORD true) if the operation was successful.
- */
- protected override bool reify(SocketWatcher watcher,
- EventMask oldEvents,
- EventMask events) @nogc
- {
- int op = EPOLL_CTL_DEL;
- epoll_event ev;
-
- if (events == oldEvents)
- {
- return true;
- }
- if (events && oldEvents)
- {
- op = EPOLL_CTL_MOD;
- }
- else if (events && !oldEvents)
- {
- op = EPOLL_CTL_ADD;
- }
-
- ev.data.fd = watcher.socket.handle;
- ev.events = (events & (Event.read | Event.accept) ? EPOLLIN | EPOLLPRI : 0)
- | (events & Event.write ? EPOLLOUT : 0)
- | EPOLLET;
-
- return epoll_ctl(fd, op, watcher.socket.handle, &ev) == 0;
- }
-
- /**
- * Does the actual polling.
- */
- protected override void poll() @nogc
- {
- // Don't block
- immutable timeout = cast(immutable int) blockTime.total!"msecs";
- auto eventCount = epoll_wait(fd, events.get().ptr, maxEvents, timeout);
-
- if (eventCount < 0)
- {
- if (errno != EINTR)
- {
- throw defaultAllocator.make!BadLoopException();
- }
- return;
- }
-
- for (auto i = 0; i < eventCount; ++i)
- {
- auto transport = cast(StreamTransport) connections[events[i].data.fd];
-
- if (transport is null)
- {
- auto connection = cast(ConnectionWatcher) connections[events[i].data.fd];
- assert(connection !is null);
-
- acceptConnections(connection);
- }
- else if (events[i].events & EPOLLERR)
- {
- kill(transport);
- continue;
- }
- else if (events[i].events & (EPOLLIN | EPOLLPRI | EPOLLHUP))
- {
- SocketException exception;
- try
- {
- ptrdiff_t received;
- do
- {
- received = transport.socket.receive(transport.output[]);
- transport.output += received;
- }
- while (received);
- }
- catch (SocketException e)
- {
- exception = e;
- }
- if (transport.socket.disconnected)
- {
- kill(transport, exception);
- continue;
- }
- else if (transport.output.length)
- {
- pendings.enqueue(transport);
- }
- }
- if (events[i].events & EPOLLOUT)
- {
- transport.writeReady = true;
- if (transport.input.length)
- {
- feed(transport);
- }
- }
- }
- }
-
- /**
- * Returns: The blocking time.
- */
- override protected @property inout(Duration) blockTime()
- inout @safe pure nothrow
- {
- return min(super.blockTime, 1.dur!"seconds");
- }
+ protected int fd;
+ private Array!epoll_event events;
+
+ /**
+ * Initializes the loop.
+ */
+ this() @nogc
+ {
+ if ((fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
+ {
+ throw defaultAllocator.make!BadLoopException("epoll initialization failed");
+ }
+ super();
+ events = Array!epoll_event(maxEvents, MmapPool.instance);
+ }
+
+ /**
+ * Frees loop internals.
+ */
+ ~this() @nogc
+ {
+ close(fd);
+ }
+
+ /**
+ * Should be called if the backend configuration changes.
+ *
+ * Params:
+ * watcher = Watcher.
+ * oldEvents = The events were already set.
+ * events = The events should be set.
+ *
+ * Returns: $(D_KEYWORD true) if the operation was successful.
+ */
+ protected override bool reify(SocketWatcher watcher,
+ EventMask oldEvents,
+ EventMask events) @nogc
+ {
+ int op = EPOLL_CTL_DEL;
+ epoll_event ev;
+
+ if (events == oldEvents)
+ {
+ return true;
+ }
+ if (events && oldEvents)
+ {
+ op = EPOLL_CTL_MOD;
+ }
+ else if (events && !oldEvents)
+ {
+ op = EPOLL_CTL_ADD;
+ }
+
+ ev.data.fd = watcher.socket.handle;
+ ev.events = (events & (Event.read | Event.accept) ? EPOLLIN | EPOLLPRI : 0)
+ | (events & Event.write ? EPOLLOUT : 0)
+ | EPOLLET;
+
+ return epoll_ctl(fd, op, watcher.socket.handle, &ev) == 0;
+ }
+
+ /**
+ * Does the actual polling.
+ */
+ protected override void poll() @nogc
+ {
+ // Don't block
+ immutable timeout = cast(immutable int) blockTime.total!"msecs";
+ auto eventCount = epoll_wait(fd, events.get().ptr, maxEvents, timeout);
+
+ if (eventCount < 0)
+ {
+ if (errno != EINTR)
+ {
+ throw defaultAllocator.make!BadLoopException();
+ }
+ return;
+ }
+
+ for (auto i = 0; i < eventCount; ++i)
+ {
+ auto transport = cast(StreamTransport) connections[events[i].data.fd];
+
+ if (transport is null)
+ {
+ auto connection = cast(ConnectionWatcher) connections[events[i].data.fd];
+ assert(connection !is null);
+
+ acceptConnections(connection);
+ }
+ else if (events[i].events & EPOLLERR)
+ {
+ kill(transport);
+ continue;
+ }
+ else if (events[i].events & (EPOLLIN | EPOLLPRI | EPOLLHUP))
+ {
+ SocketException exception;
+ try
+ {
+ ptrdiff_t received;
+ do
+ {
+ received = transport.socket.receive(transport.output[]);
+ transport.output += received;
+ }
+ while (received);
+ }
+ catch (SocketException e)
+ {
+ exception = e;
+ }
+ if (transport.socket.disconnected)
+ {
+ kill(transport, exception);
+ continue;
+ }
+ else if (transport.output.length)
+ {
+ pendings.enqueue(transport);
+ }
+ }
+ if (events[i].events & EPOLLOUT)
+ {
+ transport.writeReady = true;
+ if (transport.input.length)
+ {
+ feed(transport);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns: The blocking time.
+ */
+ override protected @property inout(Duration) blockTime()
+ inout @safe pure nothrow
+ {
+ return min(super.blockTime, 1.dur!"seconds");
+ }
}
diff --git a/source/tanya/async/event/iocp.d b/source/tanya/async/event/iocp.d
index 26678e1..fe05cc3 100644
--- a/source/tanya/async/event/iocp.d
+++ b/source/tanya/async/event/iocp.d
@@ -31,354 +31,354 @@ import core.sys.windows.winsock2;
*/
final class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
{
- private SocketException exception;
-
- private ReadBuffer!ubyte output;
-
- private WriteBuffer!ubyte input;
-
- private Protocol protocol_;
-
- private bool closing;
-
- /**
- * Creates new completion port transport.
- *
- * Params:
- * socket = Socket.
- *
- * Precondition: $(D_INLINECODE socket !is null)
- */
- this(OverlappedConnectedSocket socket) @nogc
- {
- super(socket);
- output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance);
- input = WriteBuffer!ubyte(8192, MmapPool.instance);
- active = true;
- }
-
- /**
- * Returns: Socket.
- *
- * Postcondition: $(D_INLINECODE socket !is null)
- */
- override @property OverlappedConnectedSocket socket() pure nothrow @safe @nogc
- out (socket)
- {
- assert(socket !is null);
- }
- body
- {
- return cast(OverlappedConnectedSocket) socket_;
- }
-
- /**
- * Returns $(D_PARAM true) if the transport is closing or closed.
- */
- bool isClosing() const pure nothrow @safe @nogc
- {
- return closing;
- }
-
- /**
- * Close the transport.
- *
- * Buffered data will be flushed. No more data will be received.
- */
- void close() pure nothrow @safe @nogc
- {
- closing = true;
- }
-
- /**
- * Write some data to the transport.
- *
- * Params:
- * data = Data to send.
- */
- void write(ubyte[] data) @nogc
- {
- input ~= data;
- }
-
- /**
- * Returns: Application protocol.
- */
- @property Protocol protocol() pure nothrow @safe @nogc
- {
- return protocol_;
- }
-
- /**
- * Switches the protocol.
- *
- * The protocol is deallocated by the event loop, it should currently be
- * allocated with $(D_PSYMBOL MmapPool).
- *
- * Params:
- * protocol = Application protocol.
- *
- * Precondition: $(D_INLINECODE protocol !is null)
- */
- @property void protocol(Protocol protocol) pure nothrow @safe @nogc
- in
- {
- assert(protocol !is null);
- }
- body
- {
- protocol_ = protocol;
- }
-
- /**
- * Invokes the watcher callback.
- */
- override void invoke() @nogc
- {
- if (output.length)
- {
- immutable empty = input.length == 0;
- protocol.received(output[0 .. $]);
- output.clear();
- if (empty)
- {
- SocketState overlapped;
- try
- {
- overlapped = MmapPool.instance.make!SocketState;
- socket.beginSend(input[], overlapped);
- }
- catch (SocketException e)
- {
- MmapPool.instance.dispose(overlapped);
- MmapPool.instance.dispose(e);
- }
- }
- }
- else
- {
- protocol.disconnected(exception);
- MmapPool.instance.dispose(protocol_);
- defaultAllocator.dispose(exception);
- active = false;
- }
- }
+ private SocketException exception;
+
+ private ReadBuffer!ubyte output;
+
+ private WriteBuffer!ubyte input;
+
+ private Protocol protocol_;
+
+ private bool closing;
+
+ /**
+ * Creates new completion port transport.
+ *
+ * Params:
+ * socket = Socket.
+ *
+ * Precondition: $(D_INLINECODE socket !is null)
+ */
+ this(OverlappedConnectedSocket socket) @nogc
+ {
+ super(socket);
+ output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance);
+ input = WriteBuffer!ubyte(8192, MmapPool.instance);
+ active = true;
+ }
+
+ /**
+ * Returns: Socket.
+ *
+ * Postcondition: $(D_INLINECODE socket !is null)
+ */
+ override @property OverlappedConnectedSocket socket() pure nothrow @safe @nogc
+ out (socket)
+ {
+ assert(socket !is null);
+ }
+ body
+ {
+ return cast(OverlappedConnectedSocket) socket_;
+ }
+
+ /**
+ * Returns $(D_PARAM true) if the transport is closing or closed.
+ */
+ bool isClosing() const pure nothrow @safe @nogc
+ {
+ return closing;
+ }
+
+ /**
+ * Close the transport.
+ *
+ * Buffered data will be flushed. No more data will be received.
+ */
+ void close() pure nothrow @safe @nogc
+ {
+ closing = true;
+ }
+
+ /**
+ * Write some data to the transport.
+ *
+ * Params:
+ * data = Data to send.
+ */
+ void write(ubyte[] data) @nogc
+ {
+ input ~= data;
+ }
+
+ /**
+ * Returns: Application protocol.
+ */
+ @property Protocol protocol() pure nothrow @safe @nogc
+ {
+ return protocol_;
+ }
+
+ /**
+ * Switches the protocol.
+ *
+ * The protocol is deallocated by the event loop, it should currently be
+ * allocated with $(D_PSYMBOL MmapPool).
+ *
+ * Params:
+ * protocol = Application protocol.
+ *
+ * Precondition: $(D_INLINECODE protocol !is null)
+ */
+ @property void protocol(Protocol protocol) pure nothrow @safe @nogc
+ in
+ {
+ assert(protocol !is null);
+ }
+ body
+ {
+ protocol_ = protocol;
+ }
+
+ /**
+ * Invokes the watcher callback.
+ */
+ override void invoke() @nogc
+ {
+ if (output.length)
+ {
+ immutable empty = input.length == 0;
+ protocol.received(output[0 .. $]);
+ output.clear();
+ if (empty)
+ {
+ SocketState overlapped;
+ try
+ {
+ overlapped = MmapPool.instance.make!SocketState;
+ socket.beginSend(input[], overlapped);
+ }
+ catch (SocketException e)
+ {
+ MmapPool.instance.dispose(overlapped);
+ MmapPool.instance.dispose(e);
+ }
+ }
+ }
+ else
+ {
+ protocol.disconnected(exception);
+ MmapPool.instance.dispose(protocol_);
+ defaultAllocator.dispose(exception);
+ active = false;
+ }
+ }
}
final class IOCPLoop : Loop
{
- protected HANDLE completionPort;
-
- protected OVERLAPPED overlap;
-
- /**
- * Initializes the loop.
- */
- this() @nogc
- {
- super();
-
- completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
- if (!completionPort)
- {
- throw make!BadLoopException(defaultAllocator,
- "Creating completion port failed");
- }
- }
-
- /**
- * Should be called if the backend configuration changes.
- *
- * Params:
- * watcher = Watcher.
- * oldEvents = The events were already set.
- * events = The events should be set.
- *
- * Returns: $(D_KEYWORD true) if the operation was successful.
- */
- override protected bool reify(SocketWatcher watcher,
- EventMask oldEvents,
- EventMask events) @nogc
- {
- SocketState overlapped;
- if (!(oldEvents & Event.accept) && (events & Event.accept))
- {
- auto socket = cast(OverlappedStreamSocket) watcher.socket;
- assert(socket !is null);
-
- if (CreateIoCompletionPort(cast(HANDLE) socket.handle,
- completionPort,
- cast(ULONG_PTR) (cast(void*) watcher),
- 0) !is completionPort)
- {
- return false;
- }
-
- try
- {
- overlapped = MmapPool.instance.make!SocketState;
- socket.beginAccept(overlapped);
- }
- catch (SocketException e)
- {
- MmapPool.instance.dispose(overlapped);
- defaultAllocator.dispose(e);
- return false;
- }
- }
- if (!(oldEvents & Event.read) && (events & Event.read)
- || !(oldEvents & Event.write) && (events & Event.write))
- {
- auto transport = cast(StreamTransport) watcher;
- assert(transport !is null);
-
- if (CreateIoCompletionPort(cast(HANDLE) transport.socket.handle,
- completionPort,
- cast(ULONG_PTR) (cast(void*) watcher),
- 0) !is completionPort)
- {
- return false;
- }
-
- // Begin to read
- if (!(oldEvents & Event.read) && (events & Event.read))
- {
- try
- {
- overlapped = MmapPool.instance.make!SocketState;
- transport.socket.beginReceive(transport.output[], overlapped);
- }
- catch (SocketException e)
- {
- MmapPool.instance.dispose(overlapped);
- defaultAllocator.dispose(e);
- return false;
- }
- }
- }
- return true;
- }
-
- private void kill(StreamTransport transport,
- SocketException exception = null) @nogc
- in
- {
- assert(transport !is null);
- }
- body
- {
- transport.socket.shutdown();
- defaultAllocator.dispose(transport.socket);
- transport.exception = exception;
- pendings.enqueue(transport);
- }
-
- /**
- * Does the actual polling.
- */
- override protected void poll() @nogc
- {
- DWORD lpNumberOfBytes;
- ULONG_PTR key;
- LPOVERLAPPED overlap;
- immutable timeout = cast(immutable int) blockTime.total!"msecs";
-
- auto result = GetQueuedCompletionStatus(completionPort,
- &lpNumberOfBytes,
- &key,
- &overlap,
- timeout);
- if (result == FALSE && overlap == NULL)
- {
- return; // Timeout
- }
-
- auto overlapped = (cast(SocketState) ((cast(void*) overlap) - 8));
- assert(overlapped !is null);
- scope (failure)
- {
- MmapPool.instance.dispose(overlapped);
- }
-
- switch (overlapped.event)
- {
- case OverlappedSocketEvent.accept:
- auto connection = cast(ConnectionWatcher) (cast(void*) key);
- assert(connection !is null);
-
- auto listener = cast(OverlappedStreamSocket) connection.socket;
- assert(listener !is null);
-
- auto socket = listener.endAccept(overlapped);
- auto transport = MmapPool.instance.make!StreamTransport(socket);
-
- connection.incoming.enqueue(transport);
-
- reify(transport, EventMask(Event.none), EventMask(Event.read, Event.write));
-
- pendings.enqueue(connection);
- listener.beginAccept(overlapped);
- break;
- case OverlappedSocketEvent.read:
- auto transport = cast(StreamTransport) (cast(void*) key);
- assert(transport !is null);
-
- if (!transport.active)
- {
- MmapPool.instance.dispose(transport);
- MmapPool.instance.dispose(overlapped);
- return;
- }
-
- int received;
- SocketException exception;
- try
- {
- received = transport.socket.endReceive(overlapped);
- }
- catch (SocketException e)
- {
- exception = e;
- }
- if (transport.socket.disconnected)
- {
- // We want to get one last notification to destroy the watcher.
- transport.socket.beginReceive(transport.output[], overlapped);
- kill(transport, exception);
- }
- else if (received > 0)
- {
- immutable full = transport.output.free == received;
-
- transport.output += received;
- // Receive was interrupted because the buffer is full. We have to continue.
- if (full)
- {
- transport.socket.beginReceive(transport.output[], overlapped);
- }
- pendings.enqueue(transport);
- }
- break;
- case OverlappedSocketEvent.write:
- auto transport = cast(StreamTransport) (cast(void*) key);
- assert(transport !is null);
-
- transport.input += transport.socket.endSend(overlapped);
- if (transport.input.length > 0)
- {
- transport.socket.beginSend(transport.input[], overlapped);
- }
- else
- {
- transport.socket.beginReceive(transport.output[], overlapped);
- if (transport.isClosing())
- {
- kill(transport);
- }
- }
- break;
- default:
- assert(false, "Unknown event");
- }
- }
-} \ No newline at end of file
+ protected HANDLE completionPort;
+
+ protected OVERLAPPED overlap;
+
+ /**
+ * Initializes the loop.
+ */
+ this() @nogc
+ {
+ super();
+
+ completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
+ if (!completionPort)
+ {
+ throw make!BadLoopException(defaultAllocator,
+ "Creating completion port failed");
+ }
+ }
+
+ /**
+ * Should be called if the backend configuration changes.
+ *
+ * Params:
+ * watcher = Watcher.
+ * oldEvents = The events were already set.
+ * events = The events should be set.
+ *
+ * Returns: $(D_KEYWORD true) if the operation was successful.
+ */
+ override protected bool reify(SocketWatcher watcher,
+ EventMask oldEvents,
+ EventMask events) @nogc
+ {
+ SocketState overlapped;
+ if (!(oldEvents & Event.accept) && (events & Event.accept))
+ {
+ auto socket = cast(OverlappedStreamSocket) watcher.socket;
+ assert(socket !is null);
+
+ if (CreateIoCompletionPort(cast(HANDLE) socket.handle,
+ completionPort,
+ cast(ULONG_PTR) (cast(void*) watcher),
+ 0) !is completionPort)
+ {
+ return false;
+ }
+
+ try
+ {
+ overlapped = MmapPool.instance.make!SocketState;
+ socket.beginAccept(overlapped);
+ }
+ catch (SocketException e)
+ {
+ MmapPool.instance.dispose(overlapped);
+ defaultAllocator.dispose(e);
+ return false;
+ }
+ }
+ if (!(oldEvents & Event.read) && (events & Event.read)
+ || !(oldEvents & Event.write) && (events & Event.write))
+ {
+ auto transport = cast(StreamTransport) watcher;
+ assert(transport !is null);
+
+ if (CreateIoCompletionPort(cast(HANDLE) transport.socket.handle,
+ completionPort,
+ cast(ULONG_PTR) (cast(void*) watcher),
+ 0) !is completionPort)
+ {
+ return false;
+ }
+
+ // Begin to read
+ if (!(oldEvents & Event.read) && (events & Event.read))
+ {
+ try
+ {
+ overlapped = MmapPool.instance.make!SocketState;
+ transport.socket.beginReceive(transport.output[], overlapped);
+ }
+ catch (SocketException e)
+ {
+ MmapPool.instance.dispose(overlapped);
+ defaultAllocator.dispose(e);
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private void kill(StreamTransport transport,
+ SocketException exception = null) @nogc
+ in
+ {
+ assert(transport !is null);
+ }
+ body
+ {
+ transport.socket.shutdown();
+ defaultAllocator.dispose(transport.socket);
+ transport.exception = exception;
+ pendings.enqueue(transport);
+ }
+
+ /**
+ * Does the actual polling.
+ */
+ override protected void poll() @nogc
+ {
+ DWORD lpNumberOfBytes;
+ ULONG_PTR key;
+ LPOVERLAPPED overlap;
+ immutable timeout = cast(immutable int) blockTime.total!"msecs";
+
+ auto result = GetQueuedCompletionStatus(completionPort,
+ &lpNumberOfBytes,
+ &key,
+ &overlap,
+ timeout);
+ if (result == FALSE && overlap == NULL)
+ {
+ return; // Timeout
+ }
+
+ auto overlapped = (cast(SocketState) ((cast(void*) overlap) - 8));
+ assert(overlapped !is null);
+ scope (failure)
+ {
+ MmapPool.instance.dispose(overlapped);
+ }
+
+ switch (overlapped.event)
+ {
+ case OverlappedSocketEvent.accept:
+ auto connection = cast(ConnectionWatcher) (cast(void*) key);
+ assert(connection !is null);
+
+ auto listener = cast(OverlappedStreamSocket) connection.socket;
+ assert(listener !is null);
+
+ auto socket = listener.endAccept(overlapped);
+ auto transport = MmapPool.instance.make!StreamTransport(socket);
+
+ connection.incoming.enqueue(transport);
+
+ reify(transport, EventMask(Event.none), EventMask(Event.read, Event.write));
+
+ pendings.enqueue(connection);
+ listener.beginAccept(overlapped);
+ break;
+ case OverlappedSocketEvent.read:
+ auto transport = cast(StreamTransport) (cast(void*) key);
+ assert(transport !is null);
+
+ if (!transport.active)
+ {
+ MmapPool.instance.dispose(transport);
+ MmapPool.instance.dispose(overlapped);
+ return;
+ }
+
+ int received;
+ SocketException exception;
+ try
+ {
+ received = transport.socket.endReceive(overlapped);
+ }
+ catch (SocketException e)
+ {
+ exception = e;
+ }
+ if (transport.socket.disconnected)
+ {
+ // We want to get one last notification to destroy the watcher.
+ transport.socket.beginReceive(transport.output[], overlapped);
+ kill(transport, exception);
+ }
+ else if (received > 0)
+ {
+ immutable full = transport.output.free == received;
+
+ transport.output += received;
+ // Receive was interrupted because the buffer is full. We have to continue.
+ if (full)
+ {
+ transport.socket.beginReceive(transport.output[], overlapped);
+ }
+ pendings.enqueue(transport);
+ }
+ break;
+ case OverlappedSocketEvent.write:
+ auto transport = cast(StreamTransport) (cast(void*) key);
+ assert(transport !is null);
+
+ transport.input += transport.socket.endSend(overlapped);
+ if (transport.input.length > 0)
+ {
+ transport.socket.beginSend(transport.input[], overlapped);
+ }
+ else
+ {
+ transport.socket.beginReceive(transport.output[], overlapped);
+ if (transport.isClosing())
+ {
+ kill(transport);
+ }
+ }
+ break;
+ default:
+ assert(false, "Unknown event");
+ }
+ }
+}
diff --git a/source/tanya/async/event/kqueue.d b/source/tanya/async/event/kqueue.d
index 8673ff7..5cadec6 100644
--- a/source/tanya/async/event/kqueue.d
+++ b/source/tanya/async/event/kqueue.d
@@ -12,31 +12,31 @@ module tanya.async.event.kqueue;
version (OSX)
{
- version = MacBSD;
+ version = MacBSD;
}
else version (iOS)
{
- version = MacBSD;
+ version = MacBSD;
}
else version (TVOS)
{
- version = MacBSD;
+ version = MacBSD;
}
else version (WatchOS)
{
- 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):
@@ -50,62 +50,62 @@ import tanya.async.event.selector;
import tanya.async.loop;
import tanya.async.transport;
import tanya.async.watcher;
-import tanya.container.vector;
+import tanya.container.array;
import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket;
void EV_SET(kevent_t* kevp, typeof(kevent_t.tupleof) args) pure nothrow @nogc
{
- *kevp = kevent_t(args);
+ *kevp = kevent_t(args);
}
enum : short
{
- EVFILT_READ = -1,
- EVFILT_WRITE = -2,
- EVFILT_AIO = -3, /* attached to aio requests */
- EVFILT_VNODE = -4, /* attached to vnodes */
- EVFILT_PROC = -5, /* attached to struct proc */
- EVFILT_SIGNAL = -6, /* attached to struct proc */
- EVFILT_TIMER = -7, /* timers */
- EVFILT_MACHPORT = -8, /* Mach portsets */
- EVFILT_FS = -9, /* filesystem events */
- EVFILT_USER = -10, /* User events */
- EVFILT_VM = -12, /* virtual memory events */
- EVFILT_SYSCOUNT = 11
+ EVFILT_READ = -1,
+ EVFILT_WRITE = -2,
+ EVFILT_AIO = -3, /* attached to aio requests */
+ EVFILT_VNODE = -4, /* attached to vnodes */
+ EVFILT_PROC = -5, /* attached to struct proc */
+ EVFILT_SIGNAL = -6, /* attached to struct proc */
+ EVFILT_TIMER = -7, /* timers */
+ EVFILT_MACHPORT = -8, /* Mach portsets */
+ EVFILT_FS = -9, /* filesystem events */
+ EVFILT_USER = -10, /* User events */
+ EVFILT_VM = -12, /* virtual memory events */
+ EVFILT_SYSCOUNT = 11
}
struct kevent_t
{
- uintptr_t ident; /* identifier for this event */
- short filter; /* filter for event */
- ushort flags;
- uint fflags;
- intptr_t data;
- void *udata; /* opaque user data identifier */
+ uintptr_t ident; /* identifier for this event */
+ short filter; /* filter for event */
+ ushort flags;
+ uint fflags;
+ intptr_t data;
+ void *udata; /* opaque user data identifier */
}
enum
{
- /* actions */
- EV_ADD = 0x0001, /* add event to kq (implies enable) */
- EV_DELETE = 0x0002, /* delete event from kq */
- EV_ENABLE = 0x0004, /* enable event */
- EV_DISABLE = 0x0008, /* disable event (not reported) */
+ /* actions */
+ EV_ADD = 0x0001, /* add event to kq (implies enable) */
+ EV_DELETE = 0x0002, /* delete event from kq */
+ EV_ENABLE = 0x0004, /* enable event */
+ EV_DISABLE = 0x0008, /* disable event (not reported) */
- /* flags */
- EV_ONESHOT = 0x0010, /* only report one occurrence */
- EV_CLEAR = 0x0020, /* clear event state after reporting */
- EV_RECEIPT = 0x0040, /* force EV_ERROR on success, data=0 */
- EV_DISPATCH = 0x0080, /* disable event after reporting */
+ /* flags */
+ EV_ONESHOT = 0x0010, /* only report one occurrence */
+ EV_CLEAR = 0x0020, /* clear event state after reporting */
+ EV_RECEIPT = 0x0040, /* force EV_ERROR on success, data=0 */
+ EV_DISPATCH = 0x0080, /* disable event after reporting */
- EV_SYSFLAGS = 0xF000, /* reserved by system */
- EV_FLAG1 = 0x2000, /* filter-specific flag */
+ EV_SYSFLAGS = 0xF000, /* reserved by system */
+ EV_FLAG1 = 0x2000, /* filter-specific flag */
- /* returned values */
- EV_EOF = 0x8000, /* EOF detected */
- EV_ERROR = 0x4000, /* error, data contains errno */
+ /* returned values */
+ EV_EOF = 0x8000, /* EOF detected */
+ EV_ERROR = 0x4000, /* error, data contains errno */
}
extern(C) int kqueue() nothrow @nogc;
@@ -115,211 +115,211 @@ extern(C) int kevent(int kq, const kevent_t *changelist, int nchanges,
final class KqueueLoop : SelectorLoop
{
- protected int fd;
- private Vector!kevent_t events;
- private Vector!kevent_t changes;
- private size_t changeCount;
+ protected int fd;
+ private Array!kevent_t events;
+ private Array!kevent_t changes;
+ private size_t changeCount;
- /**
- * Returns: Maximal event count can be got at a time
- * (should be supported by the backend).
- */
- override protected @property uint maxEvents()
- const pure nothrow @safe @nogc
- {
- return cast(uint) events.length;
- }
+ /**
+ * Returns: Maximal event count can be got at a time
+ * (should be supported by the backend).
+ */
+ override protected @property uint maxEvents()
+ const pure nothrow @safe @nogc
+ {
+ return cast(uint) events.length;
+ }
- this() @nogc
- {
- super();
+ this() @nogc
+ {
+ super();
- if ((fd = kqueue()) == -1)
- {
- throw make!BadLoopException(defaultAllocator,
- "kqueue initialization failed");
- }
- events = Vector!kevent_t(64, MmapPool.instance);
- changes = Vector!kevent_t(64, MmapPool.instance);
- }
+ if ((fd = kqueue()) == -1)
+ {
+ throw make!BadLoopException(defaultAllocator,
+ "kqueue initialization failed");
+ }
+ events = Array!kevent_t(64, MmapPool.instance);
+ changes = Array!kevent_t(64, MmapPool.instance);
+ }
- /**
- * Frees loop internals.
- */
- ~this() @nogc
- {
- close(fd);
- }
+ /**
+ * Frees loop internals.
+ */
+ ~this() @nogc
+ {
+ close(fd);
+ }
- private void set(socket_t socket, short filter, ushort flags) @nogc
- {
- if (changes.length <= changeCount)
- {
- changes.length = changeCount + maxEvents;
- }
- EV_SET(&changes[changeCount],
- cast(ulong) socket,
- filter,
- flags,
- 0U,
- 0L,
- null);
- ++changeCount;
- }
+ private void set(socket_t socket, short filter, ushort flags) @nogc
+ {
+ if (changes.length <= changeCount)
+ {
+ changes.length = changeCount + maxEvents;
+ }
+ EV_SET(&changes[changeCount],
+ cast(ulong) socket,
+ filter,
+ flags,
+ 0U,
+ 0L,
+ null);
+ ++changeCount;
+ }
- /**
- * Should be called if the backend configuration changes.
- *
- * Params:
- * watcher = Watcher.
- * oldEvents = The events were already set.
- * events = The events should be set.
- *
- * Returns: $(D_KEYWORD true) if the operation was successful.
- */
- override protected bool reify(SocketWatcher watcher,
- EventMask oldEvents,
- EventMask events) @nogc
- {
- if (events != oldEvents)
- {
- if (oldEvents & Event.read || oldEvents & Event.accept)
- {
- set(watcher.socket.handle, EVFILT_READ, EV_DELETE);
- }
- if (oldEvents & Event.write)
- {
- set(watcher.socket.handle, EVFILT_WRITE, EV_DELETE);
- }
- }
- if (events & (Event.read | events & Event.accept))
- {
- set(watcher.socket.handle, EVFILT_READ, EV_ADD | EV_ENABLE);
- }
- if (events & Event.write)
- {
- set(watcher.socket.handle, EVFILT_WRITE, EV_ADD | EV_DISPATCH);
- }
- return true;
- }
+ /**
+ * Should be called if the backend configuration changes.
+ *
+ * Params:
+ * watcher = Watcher.
+ * oldEvents = The events were already set.
+ * events = The events should be set.
+ *
+ * Returns: $(D_KEYWORD true) if the operation was successful.
+ */
+ override protected bool reify(SocketWatcher watcher,
+ EventMask oldEvents,
+ EventMask events) @nogc
+ {
+ if (events != oldEvents)
+ {
+ if (oldEvents & Event.read || oldEvents & Event.accept)
+ {
+ set(watcher.socket.handle, EVFILT_READ, EV_DELETE);
+ }
+ if (oldEvents & Event.write)
+ {
+ set(watcher.socket.handle, EVFILT_WRITE, EV_DELETE);
+ }
+ }
+ if (events & (Event.read | events & Event.accept))
+ {
+ set(watcher.socket.handle, EVFILT_READ, EV_ADD | EV_ENABLE);
+ }
+ if (events & Event.write)
+ {
+ set(watcher.socket.handle, EVFILT_WRITE, EV_ADD | EV_DISPATCH);
+ }
+ return true;
+ }
- /**
- * Does the actual polling.
- */
- protected override void poll() @nogc
- {
- timespec ts;
- blockTime.split!("seconds", "nsecs")(ts.tv_sec, ts.tv_nsec);
+ /**
+ * Does the actual polling.
+ */
+ protected override void poll() @nogc
+ {
+ timespec ts;
+ blockTime.split!("seconds", "nsecs")(ts.tv_sec, ts.tv_nsec);
- if (changeCount > maxEvents)
- {
- events.length = changes.length;
- }
+ if (changeCount > maxEvents)
+ {
+ events.length = changes.length;
+ }
- auto eventCount = kevent(fd,
- changes.get().ptr,
- cast(int) changeCount,
- events.get().ptr,
- maxEvents,
- &ts);
- changeCount = 0;
+ auto eventCount = kevent(fd,
+ changes.get().ptr,
+ cast(int) changeCount,
+ events.get().ptr,
+ maxEvents,
+ &ts);
+ changeCount = 0;
- if (eventCount < 0)
- {
- if (errno != EINTR)
- {
- throw defaultAllocator.make!BadLoopException();
- }
- return;
- }
+ if (eventCount < 0)
+ {
+ if (errno != EINTR)
+ {
+ throw defaultAllocator.make!BadLoopException();
+ }
+ return;
+ }
- for (int i; i < eventCount; ++i)
- {
- assert(connections.length > events[i].ident);
+ for (int i; i < eventCount; ++i)
+ {
+ assert(connections.length > events[i].ident);
- auto transport = cast(StreamTransport) connections[events[i].ident];
- // If it is a ConnectionWatcher. Accept connections.
- if (transport is null)
- {
- auto connection = cast(ConnectionWatcher) connections[events[i].ident];
- assert(connection !is null);
+ auto transport = cast(StreamTransport) connections[events[i].ident];
+ // If it is a ConnectionWatcher. Accept connections.
+ if (transport is null)
+ {
+ auto connection = cast(ConnectionWatcher) connections[events[i].ident];
+ assert(connection !is null);
- acceptConnections(connection);
- }
- else if (events[i].flags & EV_ERROR)
- {
- kill(transport);
- }
- else if (events[i].filter == EVFILT_READ)
- {
- SocketException exception;
- try
- {
- ptrdiff_t received;
- do
- {
- received = transport.socket.receive(transport.output[]);
- transport.output += received;
- }
- while (received);
- }
- catch (SocketException e)
- {
- exception = e;
- }
- if (transport.socket.disconnected)
- {
- kill(transport, exception);
- }
- else if (transport.output.length)
- {
- pendings.enqueue(transport);
- }
- }
- else if (events[i].filter == EVFILT_WRITE)
- {
- transport.writeReady = true;
- if (transport.input.length)
- {
- feed(transport);
- }
- }
- }
- }
+ acceptConnections(connection);
+ }
+ else if (events[i].flags & EV_ERROR)
+ {
+ kill(transport);
+ }
+ else if (events[i].filter == EVFILT_READ)
+ {
+ SocketException exception;
+ try
+ {
+ ptrdiff_t received;
+ do
+ {
+ received = transport.socket.receive(transport.output[]);
+ transport.output += received;
+ }
+ while (received);
+ }
+ catch (SocketException e)
+ {
+ exception = e;
+ }
+ if (transport.socket.disconnected)
+ {
+ kill(transport, exception);
+ }
+ else if (transport.output.length)
+ {
+ pendings.enqueue(transport);
+ }
+ }
+ else if (events[i].filter == EVFILT_WRITE)
+ {
+ transport.writeReady = true;
+ if (transport.input.length)
+ {
+ feed(transport);
+ }
+ }
+ }
+ }
- /**
- * Returns: The blocking time.
- */
- override protected @property inout(Duration) blockTime()
- inout @nogc @safe pure nothrow
- {
- return min(super.blockTime, 1.dur!"seconds");
- }
+ /**
+ * Returns: The blocking time.
+ */
+ override protected @property inout(Duration) blockTime()
+ inout @nogc @safe pure nothrow
+ {
+ return min(super.blockTime, 1.dur!"seconds");
+ }
- /**
- * If the transport couldn't send the data, the further sending should
- * be handled by the event loop.
- *
- * Params:
- * transport = Transport.
- * exception = Exception thrown on sending.
- *
- * Returns: $(D_KEYWORD true) if the operation could be successfully
- * completed or scheduled, $(D_KEYWORD false) otherwise (the
- * transport will be destroyed then).
- */
- protected override bool feed(StreamTransport transport,
- SocketException exception = null) @nogc
- {
- if (!super.feed(transport, exception))
- {
- return false;
- }
- if (!transport.writeReady)
- {
- set(transport.socket.handle, EVFILT_WRITE, EV_DISPATCH);
- return true;
- }
- return false;
- }
+ /**
+ * If the transport couldn't send the data, the further sending should
+ * be handled by the event loop.
+ *
+ * Params:
+ * transport = Transport.
+ * exception = Exception thrown on sending.
+ *
+ * Returns: $(D_KEYWORD true) if the operation could be successfully
+ * completed or scheduled, $(D_KEYWORD false) otherwise (the
+ * transport will be destroyed then).
+ */
+ protected override bool feed(StreamTransport transport,
+ SocketException exception = null) @nogc
+ {
+ if (!super.feed(transport, exception))
+ {
+ return false;
+ }
+ if (!transport.writeReady)
+ {
+ set(transport.socket.handle, EVFILT_WRITE, EV_DISPATCH);
+ return true;
+ }
+ return false;
+ }
}
diff --git a/source/tanya/async/event/selector.d b/source/tanya/async/event/selector.d
index 13254ce..7194756 100644
--- a/source/tanya/async/event/selector.d
+++ b/source/tanya/async/event/selector.d
@@ -17,7 +17,7 @@ import tanya.async.protocol;
import tanya.async.transport;
import tanya.async.watcher;
import tanya.container.buffer;
-import tanya.container.vector;
+import tanya.container.array;
import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket;
@@ -27,371 +27,374 @@ import tanya.network.socket;
*/
package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
{
- private SelectorLoop loop;
-
- private SocketException exception;
-
- package ReadBuffer!ubyte output;
-
- package WriteBuffer!ubyte input;
-
- private Protocol protocol_;
-
- private bool closing;
-
- /// Received notification that the underlying socket is write-ready.
- package bool writeReady;
-
- /**
- * Params:
- * loop = Event loop.
- * socket = Socket.
- *
- * Precondition: $(D_INLINECODE loop !is null && socket !is null)
- */
- this(SelectorLoop loop, ConnectedSocket socket) @nogc
- in
- {
- assert(loop !is null);
- }
- body
- {
- super(socket);
- this.loop = loop;
- output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance);
- input = WriteBuffer!ubyte(8192, MmapPool.instance);
- active = true;
- }
-
- /**
- * Returns: Socket.
- *
- * Postcondition: $(D_INLINECODE socket !is null)
- */
- override @property ConnectedSocket socket() pure nothrow @safe @nogc
- out (socket)
- {
- assert(socket !is null);
- }
- body
- {
- return cast(ConnectedSocket) socket_;
- }
-
- private @property void socket(ConnectedSocket socket) pure nothrow @safe @nogc
- in
- {
- assert(socket !is null);
- }
- body
- {
- socket_ = socket;
- }
-
- /**
- * Returns: Application protocol.
- */
- @property Protocol protocol() pure nothrow @safe @nogc
- {
- return protocol_;
- }
-
- /**
- * Switches the protocol.
- *
- * The protocol is deallocated by the event loop, it should currently be
- * allocated with $(D_PSYMBOL MmapPool).
- *
- * Params:
- * protocol = Application protocol.
- *
- * Precondition: $(D_INLINECODE protocol !is null)
- */
- @property void protocol(Protocol protocol) pure nothrow @safe @nogc
- in
- {
- assert(protocol !is null);
- }
- body
- {
- protocol_ = protocol;
- }
-
- /**
- * Returns $(D_PARAM true) if the transport is closing or closed.
- */
- bool isClosing() const pure nothrow @safe @nogc
- {
- return closing;
- }
-
- /**
- * Close the transport.
- *
- * Buffered data will be flushed. No more data will be received.
- */
- void close() @nogc
- {
- closing = true;
- loop.reify(this, EventMask(Event.read, Event.write), EventMask(Event.write));
- }
-
- /**
- * Invokes the watcher callback.
- */
- override void invoke() @nogc
- {
- if (output.length)
- {
- protocol.received(output[0 .. $]);
- output.clear();
- if (isClosing() && input.length == 0)
- {
- loop.kill(this);
- }
- }
- else
- {
- protocol.disconnected(exception);
- MmapPool.instance.dispose(protocol_);
- defaultAllocator.dispose(exception);
- active = false;
- }
- }
-
- /**
- * Write some data to the transport.
- *
- * Params:
- * data = Data to send.
- */
- void write(ubyte[] data) @nogc
- {
- if (!data.length)
- {
- return;
- }
- // Try to write if the socket is write ready.
- if (writeReady)
- {
- ptrdiff_t sent;
- SocketException exception;
- try
- {
- sent = socket.send(data);
- if (sent == 0)
- {
- writeReady = false;
- }
- }
- catch (SocketException e)
- {
- writeReady = false;
- exception = e;
- }
- if (sent < data.length)
- {
- input ~= data[sent..$];
- loop.feed(this, exception);
- }
- }
- else
- {
- input ~= data;
- }
- }
+ private SelectorLoop loop;
+
+ private SocketException exception;
+
+ package ReadBuffer!ubyte output;
+
+ package WriteBuffer!ubyte input;
+
+ private Protocol protocol_;
+
+ private bool closing;
+
+ /// Received notification that the underlying socket is write-ready.
+ package bool writeReady;
+
+ /**
+ * Params:
+ * loop = Event loop.
+ * socket = Socket.
+ *
+ * Precondition: $(D_INLINECODE loop !is null && socket !is null)
+ */
+ this(SelectorLoop loop, ConnectedSocket socket) @nogc
+ in
+ {
+ assert(loop !is null);
+ }
+ body
+ {
+ super(socket);
+ this.loop = loop;
+ output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance);
+ input = WriteBuffer!ubyte(8192, MmapPool.instance);
+ active = true;
+ }
+
+ /**
+ * Returns: Socket.
+ *
+ * Postcondition: $(D_INLINECODE socket !is null)
+ */
+ override @property ConnectedSocket socket() pure nothrow @safe @nogc
+ out (socket)
+ {
+ assert(socket !is null);
+ }
+ body
+ {
+ return cast(ConnectedSocket) socket_;
+ }
+
+ private @property void socket(ConnectedSocket socket)
+ pure nothrow @safe @nogc
+ in
+ {
+ assert(socket !is null);
+ }
+ body
+ {
+ socket_ = socket;
+ }
+
+ /**
+ * Returns: Application protocol.
+ */
+ @property Protocol protocol() pure nothrow @safe @nogc
+ {
+ return protocol_;
+ }
+
+ /**
+ * Switches the protocol.
+ *
+ * The protocol is deallocated by the event loop, it should currently be
+ * allocated with $(D_PSYMBOL MmapPool).
+ *
+ * Params:
+ * protocol = Application protocol.
+ *
+ * Precondition: $(D_INLINECODE protocol !is null)
+ */
+ @property void protocol(Protocol protocol) pure nothrow @safe @nogc
+ in
+ {
+ assert(protocol !is null);
+ }
+ body
+ {
+ protocol_ = protocol;
+ }
+
+ /**
+ * Returns $(D_PARAM true) if the transport is closing or closed.
+ */
+ bool isClosing() const pure nothrow @safe @nogc
+ {
+ return closing;
+ }
+
+ /**
+ * Close the transport.
+ *
+ * Buffered data will be flushed. No more data will be received.
+ */
+ void close() @nogc
+ {
+ closing = true;
+ loop.reify(this,
+ EventMask(Event.read, Event.write),
+ EventMask(Event.write));
+ }
+
+ /**
+ * Invokes the watcher callback.
+ */
+ override void invoke() @nogc
+ {
+ if (output.length)
+ {
+ protocol.received(output[0 .. $]);
+ output.clear();
+ if (isClosing() && input.length == 0)
+ {
+ loop.kill(this);
+ }
+ }
+ else
+ {
+ protocol.disconnected(exception);
+ MmapPool.instance.dispose(protocol_);
+ defaultAllocator.dispose(exception);
+ active = false;
+ }
+ }
+
+ /**
+ * Write some data to the transport.
+ *
+ * Params:
+ * data = Data to send.
+ */
+ void write(ubyte[] data) @nogc
+ {
+ if (!data.length)
+ {
+ return;
+ }
+ // Try to write if the socket is write ready.
+ if (writeReady)
+ {
+ ptrdiff_t sent;
+ SocketException exception;
+ try
+ {
+ sent = socket.send(data);
+ if (sent == 0)
+ {
+ writeReady = false;
+ }
+ }
+ catch (SocketException e)
+ {
+ writeReady = false;
+ exception = e;
+ }
+ if (sent < data.length)
+ {
+ input ~= data[sent..$];
+ loop.feed(this, exception);
+ }
+ }
+ else
+ {
+ input ~= data;
+ }
+ }
}
abstract class SelectorLoop : Loop
{
- /// Pending connections.
- protected Vector!SocketWatcher connections;
-
- this() @nogc
- {
- super();
- connections = Vector!SocketWatcher(maxEvents, MmapPool.instance);
- }
-
- ~this() @nogc
- {
- foreach (ref connection; connections)
- {
- // We want to free only the transports. ConnectionWatcher are created by the
- // user and should be freed by himself.
- if (cast(StreamTransport) connection !is null)
- {
- MmapPool.instance.dispose(connection);
- }
- }
- }
-
- /**
- * Should be called if the backend configuration changes.
- *
- * Params:
- * watcher = Watcher.
- * oldEvents = The events were already set.
- * events = The events should be set.
- *
- * Returns: $(D_KEYWORD true) if the operation was successful.
- */
- override abstract protected bool reify(SocketWatcher watcher,
- EventMask oldEvents,
- EventMask events) @nogc;
-
- /**
- * Kills the watcher and closes the connection.
- *
- * Params:
- * transport = Transport.
- * exception = Occurred exception.
- */
- protected void kill(StreamTransport transport,
- SocketException exception = null) @nogc
- in
- {
- assert(transport !is null);
- }
- body
- {
- transport.socket.shutdown();
- defaultAllocator.dispose(transport.socket);
- transport.exception = exception;
- pendings.enqueue(transport);
- }
-
- /**
- * If the transport couldn't send the data, the further sending should
- * be handled by the event loop.
- *
- * Params:
- * transport = Transport.
- * exception = Exception thrown on sending.
- *
- * Returns: $(D_KEYWORD true) if the operation could be successfully
- * completed or scheduled, $(D_KEYWORD false) otherwise (the
- * transport will be destroyed then).
- */
- protected bool feed(StreamTransport transport,
- SocketException exception = null) @nogc
- in
- {
- assert(transport !is null);
- }
- body
- {
- while (transport.input.length && transport.writeReady)
- {
- try
- {
- ptrdiff_t sent = transport.socket.send(transport.input[]);
- if (sent == 0)
- {
- transport.writeReady = false;
- }
- else
- {
- transport.input += sent;
- }
- }
- catch (SocketException e)
- {
- exception = e;
- transport.writeReady = false;
- }
- }
- if (exception !is null)
- {
- kill(transport, exception);
- return false;
- }
- if (transport.input.length == 0 && transport.isClosing())
- {
- kill(transport);
- }
- return true;
- }
-
- /**
- * Start watching.
- *
- * Params:
- * watcher = Watcher.
- */
- override void start(ConnectionWatcher watcher) @nogc
- {
- if (watcher.active)
- {
- return;
- }
-
- if (connections.length <= watcher.socket)
- {
- connections.length = watcher.socket.handle + maxEvents / 2;
- }
- connections[watcher.socket.handle] = watcher;
-
- super.start(watcher);
- }
-
- /**
- * Accept incoming connections.
- *
- * Params:
- * connection = Connection watcher ready to accept.
- */
- package void acceptConnections(ConnectionWatcher connection) @nogc
- in
- {
- assert(connection !is null);
- }
- body
- {
- while (true)
- {
- ConnectedSocket client;
- try
- {
- client = (cast(StreamSocket) connection.socket).accept();
- }
- catch (SocketException e)
- {
- defaultAllocator.dispose(e);
- break;
- }
- if (client is null)
- {
- break;
- }
-
- StreamTransport transport;
-
- if (connections.length > client.handle)
- {
- transport = cast(StreamTransport) connections[client.handle];
- }
- else
- {
- connections.length = client.handle + maxEvents / 2;
- }
- if (transport is null)
- {
- transport = MmapPool.instance.make!StreamTransport(this, client);
- connections[client.handle] = transport;
- }
- else
- {
- transport.socket = client;
- }
-
- reify(transport, EventMask(Event.none), EventMask(Event.read, Event.write));
- connection.incoming.enqueue(transport);
- }
-
- if (!connection.incoming.empty)
- {
- pendings.enqueue(connection);
- }
- }
+ /// Pending connections.
+ protected Array!SocketWatcher connections;
+
+ this() @nogc
+ {
+ super();
+ connections = Array!SocketWatcher(maxEvents, MmapPool.instance);
+ }
+
+ ~this() @nogc
+ {
+ foreach (ref connection; connections)
+ {
+ // We want to free only the transports. ConnectionWatcher are
+ // created by the user and should be freed by himself.
+ if (cast(StreamTransport) connection !is null)
+ {
+ MmapPool.instance.dispose(connection);
+ }
+ }
+ }
+
+ /**
+ * Should be called if the backend configuration changes.
+ *
+ * Params:
+ * watcher = Watcher.
+ * oldEvents = The events were already set.
+ * events = The events should be set.
+ *
+ * Returns: $(D_KEYWORD true) if the operation was successful.
+ */
+ override abstract protected bool reify(SocketWatcher watcher,
+ EventMask oldEvents,
+ EventMask events) @nogc;
+
+ /**
+ * Kills the watcher and closes the connection.
+ *
+ * Params:
+ * transport = Transport.
+ * exception = Occurred exception.
+ */
+ protected void kill(StreamTransport transport,
+ SocketException exception = null) @nogc
+ in
+ {
+ assert(transport !is null);
+ }
+ body
+ {
+ transport.socket.shutdown();
+ defaultAllocator.dispose(transport.socket);
+ transport.exception = exception;
+ pendings.enqueue(transport);
+ }
+
+ /**
+ * If the transport couldn't send the data, the further sending should
+ * be handled by the event loop.
+ *
+ * Params:
+ * transport = Transport.
+ * exception = Exception thrown on sending.
+ *
+ * Returns: $(D_KEYWORD true) if the operation could be successfully
+ * completed or scheduled, $(D_KEYWORD false) otherwise (the
+ * transport will be destroyed then).
+ */
+ protected bool feed(StreamTransport transport,
+ SocketException exception = null) @nogc
+ in
+ {
+ assert(transport !is null);
+ }
+ body
+ {
+ while (transport.input.length && transport.writeReady)
+ {
+ try
+ {
+ ptrdiff_t sent = transport.socket.send(transport.input[]);
+ if (sent == 0)
+ {
+ transport.writeReady = false;
+ }
+ else
+ {
+ transport.input += sent;
+ }
+ }
+ catch (SocketException e)
+ {
+ exception = e;
+ transport.writeReady = false;
+ }
+ }
+ if (exception !is null)
+ {
+ kill(transport, exception);
+ return false;
+ }
+ if (transport.input.length == 0 && transport.isClosing())
+ {
+ kill(transport);
+ }
+ return true;
+ }
+
+ /**
+ * Start watching.
+ *
+ * Params:
+ * watcher = Watcher.
+ */
+ override void start(ConnectionWatcher watcher) @nogc
+ {
+ if (watcher.active)
+ {
+ return;
+ }
+
+ if (connections.length <= watcher.socket)
+ {
+ connections.length = watcher.socket.handle + maxEvents / 2;
+ }
+ connections[watcher.socket.handle] = watcher;
+
+ super.start(watcher);
+ }
+
+ /**
+ * Accept incoming connections.
+ *
+ * Params:
+ * connection = Connection watcher ready to accept.
+ */
+ package void acceptConnections(ConnectionWatcher connection) @nogc
+ in
+ {
+ assert(connection !is null);
+ }
+ body
+ {
+ while (true)
+ {
+ ConnectedSocket client;
+ try
+ {
+ client = (cast(StreamSocket) connection.socket).accept();
+ }
+ catch (SocketException e)
+ {
+ defaultAllocator.dispose(e);
+ break;
+ }
+ if (client is null)
+ {
+ break;
+ }
+
+ StreamTransport transport;
+
+ if (connections.length > client.handle)
+ {
+ transport = cast(StreamTransport) connections[client.handle];
+ }
+ else
+ {
+ connections.length = client.handle + maxEvents / 2;
+ }
+ if (transport is null)
+ {
+ transport = MmapPool.instance.make!StreamTransport(this, client);
+ connections[client.handle] = transport;
+ }
+ else
+ {
+ transport.socket = client;
+ }
+
+ reify(transport, EventMask(Event.none), EventMask(Event.read, Event.write));
+ connection.incoming.enqueue(transport);
+ }
+
+ if (!connection.incoming.empty)
+ {
+ pendings.enqueue(connection);
+ }
+ }
}
diff --git a/source/tanya/async/iocp.d b/source/tanya/async/iocp.d
index 0dfcbe8..bd191d3 100644
--- a/source/tanya/async/iocp.d
+++ b/source/tanya/async/iocp.d
@@ -21,12 +21,12 @@ import core.sys.windows.windef;
*/
class State
{
- /// For internal use by Windows API.
- align(1) OVERLAPPED overlapped;
+ /// For internal use by Windows API.
+ align(1) OVERLAPPED overlapped;
- /// File/socket handle.
- HANDLE handle;
+ /// File/socket handle.
+ HANDLE handle;
- /// For keeping events or event masks.
- int event;
+ /// For keeping events or event masks.
+ int event;
}
diff --git a/source/tanya/async/loop.d b/source/tanya/async/loop.d
index 54b5238..04b1422 100644
--- a/source/tanya/async/loop.d
+++ b/source/tanya/async/loop.d
@@ -15,50 +15,50 @@
*
* class EchoProtocol : TransmissionControlProtocol
* {
- * private DuplexTransport transport;
+ * private DuplexTransport transport;
*
- * void received(in ubyte[] data) @nogc
- * {
- * transport.write(data);
- * }
+ * void received(in ubyte[] data) @nogc
+ * {
+ * transport.write(data);
+ * }
*
- * void connected(DuplexTransport transport) @nogc
- * {
- * this.transport = transport;
- * }
+ * void connected(DuplexTransport transport) @nogc
+ * {
+ * this.transport = transport;
+ * }
*
- * void disconnected(SocketException e) @nogc
- * {
- * }
+ * void disconnected(SocketException e) @nogc
+ * {
+ * }
* }
*
* void main()
* {
- * auto address = defaultAllocator.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 = defaultAllocator.make!OverlappedStreamSocket(AddressFamily.INET);
- * }
- * else
- * {
- * auto sock = defaultAllocator.make!StreamSocket(AddressFamily.INET);
- * sock.blocking = false;
- * }
+ * version (Windows)
+ * {
+ * auto sock = defaultAllocator.make!OverlappedStreamSocket(AddressFamily.INET);
+ * }
+ * else
+ * {
+ * auto sock = defaultAllocator.make!StreamSocket(AddressFamily.INET);
+ * sock.blocking = false;
+ * }
*
- * sock.bind(address);
- * sock.listen(5);
+ * sock.bind(address);
+ * sock.listen(5);
*
- * auto io = defaultAllocator.make!ConnectionWatcher(sock);
- * io.setProtocol!EchoProtocol;
+ * auto io = defaultAllocator.make!ConnectionWatcher(sock);
+ * io.setProtocol!EchoProtocol;
*
- * defaultLoop.start(io);
- * defaultLoop.run();
+ * defaultLoop.start(io);
+ * defaultLoop.run();
*
- * sock.shutdown();
- * defaultAllocator.dispose(io);
- * defaultAllocator.dispose(sock);
- * defaultAllocator.dispose(address);
+ * sock.shutdown();
+ * defaultAllocator.dispose(io);
+ * defaultAllocator.dispose(sock);
+ * defaultAllocator.dispose(address);
* }
* ---
*/
@@ -81,33 +81,33 @@ version (DisableBackends)
}
else version (linux)
{
- import tanya.async.event.epoll;
- version = Epoll;
+ import tanya.async.event.epoll;
+ version = Epoll;
}
else version (Windows)
{
- import tanya.async.event.iocp;
- version = IOCP;
+ import tanya.async.event.iocp;
+ version = IOCP;
}
else version (OSX)
{
- version = Kqueue;
+ version = Kqueue;
}
else version (iOS)
{
- version = Kqueue;
+ version = Kqueue;
}
else version (FreeBSD)
{
- version = Kqueue;
+ version = Kqueue;
}
else version (OpenBSD)
{
- version = Kqueue;
+ version = Kqueue;
}
else version (DragonFlyBSD)
{
- version = Kqueue;
+ version = Kqueue;
}
/**
@@ -115,11 +115,11 @@ else version (DragonFlyBSD)
*/
enum Event : uint
{
- none = 0x00, /// No events.
- read = 0x01, /// Non-blocking read call.
- write = 0x02, /// Non-blocking write call.
- accept = 0x04, /// Connection made.
- error = 0x80000000, /// Sent when an error occurs.
+ none = 0x00, /// No events.
+ read = 0x01, /// Non-blocking read call.
+ write = 0x02, /// Non-blocking write call.
+ accept = 0x04, /// Connection made.
+ error = 0x80000000, /// Sent when an error occurs.
}
alias EventMask = BitFlags!Event;
@@ -129,150 +129,150 @@ alias EventMask = BitFlags!Event;
*/
abstract class Loop
{
- private bool done;
+ private bool done;
- /// Pending watchers.
- protected Queue!Watcher pendings;
+ /// Pending watchers.
+ protected Queue!Watcher pendings;
- /**
- * Returns: Maximal event count can be got at a time
- * (should be supported by the backend).
- */
- protected @property uint maxEvents()
- const pure nothrow @safe @nogc
- {
- return 128U;
- }
+ /**
+ * Returns: Maximal event count can be got at a time
+ * (should be supported by the backend).
+ */
+ protected @property uint maxEvents()
+ const pure nothrow @safe @nogc
+ {
+ return 128U;
+ }
- /**
- * Initializes the loop.
- */
- this() @nogc
- {
- pendings = Queue!Watcher(MmapPool.instance);
- }
+ /**
+ * Initializes the loop.
+ */
+ this() @nogc
+ {
+ pendings = Queue!Watcher(MmapPool.instance);
+ }
- /**
- * Frees loop internals.
- */
- ~this() @nogc
- {
- foreach (w; pendings)
- {
- MmapPool.instance.dispose(w);
- }
- }
+ /**
+ * Frees loop internals.
+ */
+ ~this() @nogc
+ {
+ foreach (w; pendings)
+ {
+ MmapPool.instance.dispose(w);
+ }
+ }
- /**
- * Starts the loop.
- */
- void run() @nogc
- {
- done = false;
- do
- {
- poll();
+ /**
+ * Starts the loop.
+ */
+ void run() @nogc
+ {
+ done = false;
+ do
+ {
+ poll();
- // Invoke pendings
- foreach (ref w; pendings)
- {
- w.invoke();
- }
- }
- while (!done);
- }
+ // Invoke pendings
+ foreach (ref w; pendings)
+ {
+ w.invoke();
+ }
+ }
+ while (!done);
+ }
- /**
- * Break out of the loop.
- */
- void unloop() @safe pure nothrow @nogc
- {
- done = true;
- }
+ /**
+ * Break out of the loop.
+ */
+ void unloop() @safe pure nothrow @nogc
+ {
+ done = true;
+ }
- /**
- * Start watching.
- *
- * Params:
- * watcher = Watcher.
- */
- void start(ConnectionWatcher watcher) @nogc
- {
- if (watcher.active)
- {
- return;
- }
- watcher.active = true;
+ /**
+ * Start watching.
+ *
+ * Params:
+ * watcher = Watcher.
+ */
+ void start(ConnectionWatcher watcher) @nogc
+ {
+ if (watcher.active)
+ {
+ return;
+ }
+ watcher.active = true;
- reify(watcher, EventMask(Event.none), EventMask(Event.accept));
- }
+ reify(watcher, EventMask(Event.none), EventMask(Event.accept));
+ }
- /**
- * Stop watching.
- *
- * Params:
- * watcher = Watcher.
- */
- void stop(ConnectionWatcher watcher) @nogc
- {
- if (!watcher.active)
- {
- return;
- }
- watcher.active = false;
+ /**
+ * Stop watching.
+ *
+ * Params:
+ * watcher = Watcher.
+ */
+ void stop(ConnectionWatcher watcher) @nogc
+ {
+ if (!watcher.active)
+ {
+ return;
+ }
+ watcher.active = false;
- reify(watcher, EventMask(Event.accept), EventMask(Event.none));
- }
+ reify(watcher, EventMask(Event.accept), EventMask(Event.none));
+ }
- /**
- * Should be called if the backend configuration changes.
- *
- * Params:
- * watcher = Watcher.
- * oldEvents = The events were already set.
- * events = The events should be set.
- *
- * Returns: $(D_KEYWORD true) if the operation was successful.
- */
- abstract protected bool reify(SocketWatcher watcher,
- EventMask oldEvents,
- EventMask events) @nogc;
+ /**
+ * Should be called if the backend configuration changes.
+ *
+ * Params:
+ * watcher = Watcher.
+ * oldEvents = The events were already set.
+ * events = The events should be set.
+ *
+ * Returns: $(D_KEYWORD true) if the operation was successful.
+ */
+ abstract protected bool reify(SocketWatcher watcher,
+ EventMask oldEvents,
+ EventMask events) @nogc;
- /**
- * Returns: The blocking time.
- */
- protected @property inout(Duration) blockTime()
- inout @safe pure nothrow @nogc
- {
- // Don't block if we have to do.
- return pendings.empty ? blockTime_ : Duration.zero;
- }
+ /**
+ * Returns: The blocking time.
+ */
+ protected @property inout(Duration) blockTime()
+ inout @safe pure nothrow @nogc
+ {
+ // Don't block if we have to do.
+ return pendings.empty ? blockTime_ : Duration.zero;
+ }
- /**
- * Sets the blocking time for IO watchers.
- *
- * Params:
- * blockTime = The blocking time. Cannot be larger than
- * $(D_PSYMBOL maxBlockTime).
- */
- protected @property void blockTime(in Duration blockTime) @safe pure nothrow @nogc
- in
- {
- assert(blockTime <= 1.dur!"hours", "Too long to wait.");
- assert(!blockTime.isNegative);
- }
- body
- {
- blockTime_ = blockTime;
- }
+ /**
+ * Sets the blocking time for IO watchers.
+ *
+ * Params:
+ * blockTime = The blocking time. Cannot be larger than
+ * $(D_PSYMBOL maxBlockTime).
+ */
+ protected @property void blockTime(in Duration blockTime) @safe pure nothrow @nogc
+ in
+ {
+ assert(blockTime <= 1.dur!"hours", "Too long to wait.");
+ assert(!blockTime.isNegative);
+ }
+ body
+ {
+ blockTime_ = blockTime;
+ }
- /**
- * Does the actual polling.
- */
- abstract protected void poll() @nogc;
+ /**
+ * Does the actual polling.
+ */
+ abstract protected void poll() @nogc;
- /// Maximal block time.
- protected Duration blockTime_ = 1.dur!"minutes";
+ /// Maximal block time.
+ protected Duration blockTime_ = 1.dur!"minutes";
}
/**
@@ -280,17 +280,17 @@ abstract class Loop
*/
class BadLoopException : Exception
{
- /**
- * Params:
- * 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 file = __FILE__, size_t line = __LINE__, Throwable next = null)
- pure nothrow const @safe @nogc
- {
- super("Event loop cannot be initialized.", file, line, next);
- }
+ /**
+ * Params:
+ * 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 file = __FILE__, size_t line = __LINE__, Throwable next = null)
+ pure nothrow const @safe @nogc
+ {
+ super("Event loop cannot be initialized.", file, line, next);
+ }
}
/**
@@ -302,24 +302,24 @@ class BadLoopException : Exception
*/
@property Loop defaultLoop() @nogc
{
- if (defaultLoop_ !is null)
- {
- return defaultLoop_;
- }
- version (Epoll)
- {
- defaultLoop_ = MmapPool.instance.make!EpollLoop;
- }
- else version (IOCP)
- {
- defaultLoop_ = MmapPool.instance.make!IOCPLoop;
- }
- else version (Kqueue)
- {
- import tanya.async.event.kqueue;
- defaultLoop_ = MmapPool.instance.make!KqueueLoop;
- }
- return defaultLoop_;
+ if (defaultLoop_ !is null)
+ {
+ return defaultLoop_;
+ }
+ version (Epoll)
+ {
+ defaultLoop_ = MmapPool.instance.make!EpollLoop;
+ }
+ else version (IOCP)
+ {
+ defaultLoop_ = MmapPool.instance.make!IOCPLoop;
+ }
+ else version (Kqueue)
+ {
+ import tanya.async.event.kqueue;
+ defaultLoop_ = MmapPool.instance.make!KqueueLoop;
+ }
+ return defaultLoop_;
}
/**
@@ -331,16 +331,16 @@ class BadLoopException : Exception
* your implementation to this property.
*
* Params:
- * loop = The event loop.
+ * loop = The event loop.
*/
@property void defaultLoop(Loop loop) @nogc
in
{
- assert(loop !is null);
+ assert(loop !is null);
}
body
{
- defaultLoop_ = loop;
+ defaultLoop_ = loop;
}
private Loop defaultLoop_;
diff --git a/source/tanya/async/protocol.d b/source/tanya/async/protocol.d
index 28e90e0..2498d45 100644
--- a/source/tanya/async/protocol.d
+++ b/source/tanya/async/protocol.d
@@ -18,28 +18,28 @@ import tanya.async.transport;
*/
interface Protocol
{
- /**
- * Params:
- * data = Read data.
- */
- void received(in ubyte[] data) @nogc;
+ /**
+ * Params:
+ * data = Read data.
+ */
+ void received(in ubyte[] data) @nogc;
- /**
- * Called when a connection is made.
- *
- * Params:
- * transport = Protocol transport.
- */
- void connected(DuplexTransport transport) @nogc;
+ /**
+ * Called when a connection is made.
+ *
+ * Params:
+ * transport = Protocol transport.
+ */
+ void connected(DuplexTransport transport) @nogc;
- /**
- * Called when a connection is lost.
- *
- * Params:
- * exception = $(D_PSYMBOL Exception) if an error caused
- * the disconnect, $(D_KEYWORD null) otherwise.
- */
- void disconnected(SocketException exception) @nogc;
+ /**
+ * Called when a connection is lost.
+ *
+ * Params:
+ * exception = $(D_PSYMBOL Exception) if an error caused
+ * the disconnect, $(D_KEYWORD null) otherwise.
+ */
+ void disconnected(SocketException exception) @nogc;
}
/**
diff --git a/source/tanya/async/transport.d b/source/tanya/async/transport.d
index 4550522..2d5fbe7 100644
--- a/source/tanya/async/transport.d
+++ b/source/tanya/async/transport.d
@@ -32,13 +32,13 @@ interface ReadTransport : Transport
*/
interface WriteTransport : Transport
{
- /**
- * Write some data to the transport.
- *
- * Params:
- * data = Data to send.
- */
- void write(ubyte[] data) @nogc;
+ /**
+ * Write some data to the transport.
+ *
+ * Params:
+ * data = Data to send.
+ */
+ void write(ubyte[] data) @nogc;
}
/**
@@ -46,46 +46,46 @@ interface WriteTransport : Transport
*/
interface DuplexTransport : ReadTransport, WriteTransport
{
- /**
- * Returns: Application protocol.
- *
- * Postcondition: $(D_INLINECODE protocol !is null)
- */
- @property Protocol protocol() pure nothrow @safe @nogc
- out (protocol)
- {
- assert(protocol !is null);
- }
+ /**
+ * Returns: Application protocol.
+ *
+ * Postcondition: $(D_INLINECODE protocol !is null)
+ */
+ @property Protocol protocol() pure nothrow @safe @nogc
+ out (protocol)
+ {
+ assert(protocol !is null);
+ }
- /**
- * Switches the protocol.
- *
- * The protocol is deallocated by the event loop, it should currently be
- * allocated with $(D_PSYMBOL MmapPool).
- *
- * Params:
- * protocol = Application protocol.
- *
- * Precondition: $(D_INLINECODE protocol !is null)
- */
- @property void protocol(Protocol protocol) pure nothrow @safe @nogc
- in
- {
- assert(protocol !is null);
- }
+ /**
+ * Switches the protocol.
+ *
+ * The protocol is deallocated by the event loop, it should currently be
+ * allocated with $(D_PSYMBOL MmapPool).
+ *
+ * Params:
+ * protocol = Application protocol.
+ *
+ * Precondition: $(D_INLINECODE protocol !is null)
+ */
+ @property void protocol(Protocol protocol) pure nothrow @safe @nogc
+ in
+ {
+ assert(protocol !is null);
+ }
- /**
- * Returns $(D_PARAM true) if the transport is closing or closed.
- */
- bool isClosing() const pure nothrow @safe @nogc;
+ /**
+ * Returns $(D_PARAM true) if the transport is closing or closed.
+ */
+ bool isClosing() const pure nothrow @safe @nogc;
- /**
- * Close the transport.
- *
- * Buffered data will be flushed. No more data will be received.
- */
- void close() @nogc;
+ /**
+ * Close the transport.
+ *
+ * Buffered data will be flushed. No more data will be received.
+ */
+ void close() @nogc;
}
/**
@@ -93,8 +93,8 @@ interface DuplexTransport : ReadTransport, WriteTransport
*/
interface SocketTransport : Transport
{
- /**
- * Returns: Socket.
- */
- @property Socket socket() pure nothrow @safe @nogc;
+ /**
+ * Returns: Socket.
+ */
+ @property Socket socket() pure nothrow @safe @nogc;
}
diff --git a/source/tanya/async/watcher.d b/source/tanya/async/watcher.d
index 9756d99..048c5de 100644
--- a/source/tanya/async/watcher.d
+++ b/source/tanya/async/watcher.d
@@ -27,13 +27,13 @@ import tanya.network.socket;
*/
abstract class Watcher
{
- /// Whether the watcher is active.
- bool active;
+ /// Whether the watcher is active.
+ bool active;
- /**
- * Invoke some action on event.
- */
- void invoke() @nogc;
+ /**
+ * Invoke some action on event.
+ */
+ void invoke() @nogc;
}
/**
@@ -41,32 +41,32 @@ abstract class Watcher
*/
abstract class SocketWatcher : Watcher
{
- /// Watched socket.
- protected Socket socket_;
+ /// Watched socket.
+ protected Socket socket_;
- /**
- * Params:
- * socket = Socket.
- *
- * Precondition: $(D_INLINECODE socket !is null)
- */
- this(Socket socket) pure nothrow @safe @nogc
- in
- {
- assert(socket !is null);
- }
- body
- {
- socket_ = socket;
- }
+ /**
+ * Params:
+ * socket = Socket.
+ *
+ * Precondition: $(D_INLINECODE socket !is null)
+ */
+ this(Socket socket) pure nothrow @safe @nogc
+ in
+ {
+ assert(socket !is null);
+ }
+ body
+ {
+ socket_ = socket;
+ }
- /**
- * Returns: Socket.
- */
- @property Socket socket() pure nothrow @safe @nogc
- {
- return socket_;
- }
+ /**
+ * Returns: Socket.
+ */
+ @property Socket socket() pure nothrow @safe @nogc
+ {
+ return socket_;
+ }
}
/**
@@ -74,44 +74,44 @@ abstract class SocketWatcher : Watcher
*/
class ConnectionWatcher : SocketWatcher
{
- /// Incoming connection queue.
- Queue!DuplexTransport incoming;
+ /// Incoming connection queue.
+ Queue!DuplexTransport incoming;
- private Protocol delegate() @nogc protocolFactory;
+ private Protocol delegate() @nogc protocolFactory;
- /**
- * Params:
- * socket = Socket.
- */
- this(Socket socket) @nogc
- {
- super(socket);
- incoming = Queue!DuplexTransport(MmapPool.instance);
- }
+ /**
+ * Params:
+ * socket = Socket.
+ */
+ this(Socket socket) @nogc
+ {
+ super(socket);
+ incoming = Queue!DuplexTransport(MmapPool.instance);
+ }
- /**
- * Params:
- * P = Protocol should be used.
- */
- void setProtocol(P : Protocol)() @nogc
- {
- this.protocolFactory = () @nogc => cast(Protocol) MmapPool.instance.make!P;
- }
+ /**
+ * Params:
+ * P = Protocol should be used.
+ */
+ void setProtocol(P : Protocol)() @nogc
+ {
+ this.protocolFactory = () @nogc => cast(Protocol) MmapPool.instance.make!P;
+ }
- /**
- * Invokes new connection callback.
- */
- override void invoke() @nogc
- in
- {
- assert(protocolFactory !is null, "Protocol isn't set.");
- }
- body
- {
- foreach (transport; incoming)
- {
- transport.protocol = protocolFactory();
- transport.protocol.connected(transport);
- }
- }
+ /**
+ * Invokes new connection callback.
+ */
+ override void invoke() @nogc
+ in
+ {
+ assert(protocolFactory !is null, "Protocol isn't set.");
+ }
+ body
+ {
+ foreach (transport; incoming)
+ {
+ transport.protocol = protocolFactory();
+ transport.protocol.connected(transport);
+ }
+ }
}
diff --git a/source/tanya/container/array.d b/source/tanya/container/array.d
new file mode 100644
index 0000000..647d510
--- /dev/null
+++ b/source/tanya/container/array.d
@@ -0,0 +1,1646 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Single-dimensioned array.
+ *
+ * Copyright: Eugene Wissner 2016-2017.
+ * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
+ * Mozilla Public License, v. 2.0).
+ * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
+ */
+module tanya.container.array;
+
+import core.checkedint;
+import core.exception;
+import std.algorithm.comparison;
+import std.algorithm.mutation;
+import std.conv;
+import std.range.primitives;
+import std.meta;
+import std.traits;
+import tanya.memory;
+
+deprecated("Use tanya.container.array instead.")
+alias Vector = Array;
+
+/**
+ * Random-access range for the $(D_PSYMBOL Array).
+ *
+ * Params:
+ * E = Element type.
+ */
+struct Range(E)
+{
+ private E* begin, end;
+ private alias ContainerType = CopyConstness!(E, Array!(Unqual!E));
+ private ContainerType* container;
+
+ invariant
+ {
+ assert(this.begin <= this.end);
+ assert(this.container !is null);
+ assert(this.begin >= this.container.data);
+ assert(this.end <= this.container.data + this.container.length);
+ }
+
+ private this(ref ContainerType container, E* begin, E* end) @trusted
+ in
+ {
+ assert(begin <= end);
+ assert(begin >= container.data);
+ assert(end <= container.data + container.length);
+ }
+ body
+ {
+ this.container = &container;
+ this.begin = begin;
+ this.end = end;
+ }
+
+ @disable this();
+
+ @property Range save()
+ {
+ return this;
+ }
+
+ @property bool empty() const
+ {
+ return this.begin == this.end;
+ }
+
+ @property size_t length() const
+ {
+ return this.end - this.begin;
+ }
+
+ alias opDollar = length;
+
+ @property ref inout(E) front() inout
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ return *this.begin;
+ }
+
+ @property ref inout(E) back() inout @trusted
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ return *(this.end - 1);
+ }
+
+ void popFront() @trusted
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ ++this.begin;
+ }
+
+ void popBack() @trusted
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ --this.end;
+ }
+
+ ref inout(E) opIndex(const size_t i) inout @trusted
+ in
+ {
+ assert(i < length);
+ }
+ body
+ {
+ return *(this.begin + i);
+ }
+
+ Range opIndex()
+ {
+ return typeof(return)(*this.container, this.begin, this.end);
+ }
+
+ Range!(const E) opIndex() const
+ {
+ return typeof(return)(*this.container, this.begin, this.end);
+ }
+
+ Range opSlice(const size_t i, const size_t j) @trusted
+ in
+ {
+ assert(i <= j);
+ assert(j <= length);
+ }
+ body
+ {
+ return typeof(return)(*this.container, this.begin + i, this.begin + j);
+ }
+
+ Range!(const E) opSlice(const size_t i, const size_t j) const @trusted
+ in
+ {
+ assert(i <= j);
+ assert(j <= length);
+ }
+ body
+ {
+ return typeof(return)(*this.container, this.begin + i, this.begin + j);
+ }
+
+ inout(E[]) get() inout @trusted
+ {
+ return this.begin[0 .. length];
+ }
+}
+
+/**
+ * One dimensional array.
+ *
+ * Params:
+ * T = Content type.
+ */
+struct Array(T)
+{
+ private size_t length_;
+ private T* data;
+ private size_t capacity_;
+
+ invariant
+ {
+ assert(this.length_ <= this.capacity_);
+ assert(this.capacity_ == 0 || this.data !is null);
+ }
+
+ /**
+ * Creates a new $(D_PSYMBOL Array) with the elements from a static array.
+ *
+ * Params:
+ * R = Static array size.
+ * init = Values to initialize the array with.
+ * allocator = Allocator.
+ */
+ this(size_t R)(T[R] init, shared Allocator allocator = defaultAllocator)
+ {
+ this(allocator);
+ insertBack!(T[])(init[]);
+ }
+
+ /**
+ * Creates a new $(D_PSYMBOL Array) with the elements from an input range.
+ *
+ * Params:
+ * R = Type of the initial range.
+ * init = Values to initialize the array with.
+ * allocator = Allocator.
+ */
+ this(R)(R init, shared Allocator allocator = defaultAllocator)
+ if (!isInfinite!R
+ && isInputRange!R
+ && isImplicitlyConvertible!(ElementType!R, T))
+ {
+ this(allocator);
+ insertBack(init);
+ }
+
+ /**
+ * Initializes this array from another one.
+ *
+ * If $(D_PARAM init) is passed by value, it won't be copied, but moved.
+ * If the allocator of ($D_PARAM init) matches $(D_PARAM allocator),
+ * $(D_KEYWORD this) will just take the ownership over $(D_PARAM init)'s
+ * storage, otherwise, the storage will be allocated with
+ * $(D_PARAM allocator) and all elements will be moved;
+ * $(D_PARAM init) will be destroyed at the end.
+ *
+ * If $(D_PARAM init) is passed by reference, it will be copied.
+ *
+ * Params:
+ * R = Source array type.
+ * init = Source array.
+ * allocator = Allocator.
+ */
+ this(R)(ref R init, shared Allocator allocator = defaultAllocator)
+ if (is(Unqual!R == Array))
+ {
+ this(allocator);
+ insertBack(init[]);
+ }
+
+ /// Ditto.
+ this(R)(R init, shared Allocator allocator = defaultAllocator) @trusted
+ if (is(R == Array))
+ {
+ this(allocator);
+ if (allocator is init.allocator)
+ {
+ // Just steal all references and the allocator.
+ this.data = init.data;
+ this.length_ = init.length_;
+ this.capacity_ = init.capacity_;
+
+ // Reset the source array, so it can't destroy the moved storage.
+ init.length_ = init.capacity_ = 0;
+ init.data = null;
+ }
+ else
+ {
+ // Move each element.
+ reserve(init.length_);
+ moveEmplaceAll(init.data[0 .. init.length_], this.data[0 .. init.length_]);
+ this.length_ = init.length_;
+ // Destructor of init should destroy it here.
+ }
+ }
+
+ ///
+ @trusted @nogc unittest
+ {
+ auto v1 = Array!int([1, 2, 3]);
+ auto v2 = Array!int(v1);
+ assert(v1 == v2);
+
+ auto v3 = Array!int(Array!int([1, 2, 3]));
+ assert(v1 == v3);
+ assert(v3.length == 3);
+ assert(v3.capacity == 3);
+ }
+
+ private @trusted @nogc unittest // const constructor tests
+ {
+ auto v1 = const Array!int([1, 2, 3]);
+ auto v2 = Array!int(v1);
+ assert(v1.data !is v2.data);
+ assert(v1 == v2);
+
+ auto v3 = const Array!int(Array!int([1, 2, 3]));
+ assert(v1 == v3);
+ assert(v3.length == 3);
+ assert(v3.capacity == 3);
+ }
+
+ /**
+ * Creates a new $(D_PSYMBOL Array).
+ *
+ * Params:
+ * len = Initial length of the array.
+ * init = Initial value to fill the array with.
+ * allocator = Allocator.
+ */
+ this(const size_t len, T init, shared Allocator allocator = defaultAllocator) @trusted
+ {
+ this(allocator);
+ reserve(len);
+ uninitializedFill(this.data[0 .. len], init);
+ length_ = len;
+ }
+
+ /// Ditto.
+ this(const size_t len, shared Allocator allocator = defaultAllocator)
+ {
+ this(allocator);
+ length = len;
+ }
+
+ /// Ditto.
+ this(shared Allocator allocator)
+ in
+ {
+ assert(allocator !is null);
+ }
+ body
+ {
+ allocator_ = allocator;
+ }
+
+ ///
+ unittest
+ {
+ auto v = Array!int([3, 8, 2]);
+
+ assert(v.capacity == 3);
+ assert(v.length == 3);
+ assert(v[0] == 3 && v[1] == 8 && v[2] == 2);
+ }
+
+ ///
+ unittest
+ {
+ auto v = Array!int(3, 5);
+
+ assert(v.capacity == 3);
+ assert(v.length == 3);
+ assert(v[0] == 5 && v[1] == 5 && v[2] == 5);
+ }
+
+ @safe unittest
+ {
+ auto v1 = Array!int(defaultAllocator);
+ }
+
+ /**
+ * Destroys this $(D_PSYMBOL Array).
+ */
+ ~this() @trusted
+ {
+ clear();
+ allocator.deallocate(this.data[0 .. capacity]);
+ }
+
+ /**
+ * Copies the array.
+ */
+ this(this)
+ {
+ auto buf = this.data[0 .. this.length_];
+ this.length_ = capacity_ = 0;
+ this.data = null;
+ insertBack(buf);
+ }
+
+ /**
+ * Removes all elements.
+ */
+ void clear()
+ {
+ length = 0;
+ }
+
+ ///
+ unittest
+ {
+ auto v = Array!int([18, 20, 15]);
+ v.clear();
+ assert(v.length == 0);
+ assert(v.capacity == 3);
+ }
+
+ /**
+ * Returns: How many elements the array can contain without reallocating.
+ */
+ @property size_t capacity() const
+ {
+ return capacity_;
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto v = Array!int(4);
+ assert(v.capacity == 4);
+ }
+
+ /**
+ * Returns: Array length.
+ */
+ @property size_t length() const
+ {
+ return length_;
+ }
+
+ /// Ditto.
+ size_t opDollar() const
+ {
+ return length;
+ }
+
+ /**
+ * Expands/shrinks the array.
+ *
+ * Params:
+ * len = New length.
+ */
+ @property void length(const size_t len) @trusted
+ {
+ if (len == length)
+ {
+ return;
+ }
+ else if (len > length)
+ {
+ reserve(len);
+ initializeAll(this.data[length_ .. len]);
+ }
+ else
+ {
+ static if (hasElaborateDestructor!T)
+ {
+ const T* end = this.data + length_ - 1;
+ for (T* e = this.data + len; e != end; ++e)
+ {
+ destroy(*e);
+ }
+ }
+ }
+ length_ = len;
+ }
+
+ ///
+ unittest
+ {
+ Array!int v;
+
+ v.length = 5;
+ assert(v.length == 5);
+ assert(v.capacity == 5);
+
+ v.length = 7;
+ assert(v.length == 7);
+ assert(v.capacity == 7);
+
+ assert(v[$ - 1] == 0);
+ v[$ - 1] = 3;
+ assert(v[$ - 1] == 3);
+
+ v.length = 0;
+ assert(v.length == 0);
+ assert(v.capacity == 7);
+ }
+
+ /**
+ * Reserves space for $(D_PARAM size) elements.
+ *
+ * If $(D_PARAM size) is less than or equal to the $(D_PSYMBOL capacity), the
+ * function call does not cause a reallocation and the array capacity is not
+ * affected.
+ *
+ * Params:
+ * size = Desired size.
+ */
+ void reserve(const size_t size) @trusted
+ {
+ if (capacity_ >= size)
+ {
+ return;
+ }
+ bool overflow;
+ const byteSize = mulu(size, T.sizeof, overflow);
+ assert(!overflow);
+
+ void[] buf = this.data[0 .. this.capacity_];
+ if (!allocator.reallocateInPlace(buf, byteSize))
+ {
+ buf = allocator.allocate(byteSize);
+ if (buf is null)
+ {
+ onOutOfMemoryErrorNoGC();
+ }
+ scope (failure)
+ {
+ allocator.deallocate(buf);
+ }
+ const T* end = this.data + this.length_;
+ for (T* src = this.data, dest = cast(T*) buf; src != end; ++src, ++dest)
+ {
+ moveEmplace(*src, *dest);
+ static if (hasElaborateDestructor!T)
+ {
+ destroy(*src);
+ }
+ }
+ allocator.deallocate(this.data[0 .. this.capacity_]);
+ this.data = cast(T*) buf;
+ }
+ this.capacity_ = size;
+ }
+
+ ///
+ @nogc @safe unittest
+ {
+ Array!int v;
+ assert(v.capacity == 0);
+ assert(v.length == 0);
+
+ v.reserve(3);
+ assert(v.capacity == 3);
+ assert(v.length == 0);
+ }
+
+ /**
+ * Requests the array to reduce its capacity to fit the $(D_PARAM size).
+ *
+ * The request is non-binding. The array won't become smaller than the
+ * $(D_PARAM length).
+ *
+ * Params:
+ * size = Desired size.
+ */
+ void shrink(const size_t size) @trusted
+ {
+ if (capacity <= size)
+ {
+ return;
+ }
+ const n = max(length, size);
+ void[] buf = this.data[0 .. this.capacity_];
+ if (allocator.reallocateInPlace(buf, n * T.sizeof))
+ {
+ this.capacity_ = n;
+ }
+ }
+
+ ///
+ @nogc @safe unittest
+ {
+ Array!int v;
+ assert(v.capacity == 0);
+ assert(v.length == 0);
+
+ v.reserve(5);
+ v.insertBack(1);
+ v.insertBack(3);
+ assert(v.capacity == 5);
+ assert(v.length == 2);
+ }
+
+ /**
+ * Returns: $(D_KEYWORD true) if the array is empty.
+ */
+ @property bool empty() const
+ {
+ return length == 0;
+ }
+
+ /**
+ * Removes the value at the back of the array.
+ *
+ * Returns: The number of elements removed
+ *
+ * Precondition: $(D_INLINECODE !empty).
+ */
+ void removeBack()
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ length = length - 1;
+ }
+
+ /**
+ * Removes $(D_PARAM howMany) elements from the array.
+ *
+ * This method doesn't fail if it could not remove $(D_PARAM howMany)
+ * elements. Instead, if $(D_PARAM howMany) is greater than the array
+ * length, all elements are removed.
+ *
+ * Params:
+ * howMany = How many elements should be removed.
+ *
+ * Returns: The number of elements removed
+ */
+ size_t removeBack(const size_t howMany)
+ out (removed)
+ {
+ assert(removed <= howMany);
+ }
+ body
+ {
+ const toRemove = min(howMany, length);
+
+ length = length - toRemove;
+
+ return toRemove;
+ }
+
+ ///
+ unittest
+ {
+ auto v = Array!int([5, 18, 17]);
+
+ assert(v.removeBack(0) == 0);
+ assert(v.removeBack(2) == 2);
+ assert(v.removeBack(3) == 1);
+ assert(v.removeBack(3) == 0);
+ }
+
+ /**
+ * Remove all elements beloning to $(D_PARAM r).
+ *
+ * Params:
+ * r = Range originally obtained from this array.
+ *
+ * Returns: A range spanning the remaining elements in the array that
+ * initially were right after $(D_PARAM r).
+ *
+ * Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this).
+ */
+ Range!T remove(Range!T r) @trusted
+ in
+ {
+ assert(r.container is &this);
+ assert(r.begin >= this.data);
+ assert(r.end <= this.data + length);
+ }
+ body
+ {
+ auto end = this.data + this.length;
+ moveAll(Range!T(this, r.end, end), Range!T(this, r.begin, end));
+ length = length - r.length;
+ return Range!T(this, r.begin, this.data + length);
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto v = Array!int([5, 18, 17, 2, 4, 6, 1]);
+
+ assert(v.remove(v[1 .. 3]).length == 4);
+ assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6 && v[4] == 1);
+ assert(v.length == 5);
+
+ assert(v.remove(v[4 .. 4]).length == 1);
+ assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6 && v[4] == 1);
+ assert(v.length == 5);
+
+ assert(v.remove(v[4 .. 5]).length == 0);
+ assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6);
+ assert(v.length == 4);
+
+ assert(v.remove(v[]).length == 0);
+
+ }
+
+ private void moveBack(R)(ref R el) @trusted
+ if (isImplicitlyConvertible!(R, T))
+ {
+ reserve(this.length + 1);
+ moveEmplace(el, *(this.data + this.length_));
+ ++this.length_;
+ }
+
+ /**
+ * Inserts the $(D_PARAM el) into the array.
+ *
+ * Params:
+ * R = Type of the inserted value(s) (single value, range or static array).
+ * el = Value(s) should be inserted.
+ *
+ * Returns: The number of elements inserted.
+ */
+ size_t insertBack(R)(R el)
+ if (isImplicitlyConvertible!(R, T))
+ {
+ moveBack(el);
+ return 1;
+ }
+
+ /// Ditto.
+ size_t insertBack(R)(ref R el) @trusted
+ if (isImplicitlyConvertible!(R, T))
+ {
+ reserve(this.length_ + 1);
+ emplace(this.data + this.length_, el);
+ ++this.length_;
+ return 1;
+ }
+
+ /// Ditto.
+ size_t insertBack(R)(R el)
+ if (!isInfinite!R
+ && isInputRange!R
+ && isImplicitlyConvertible!(ElementType!R, T))
+ {
+ static if (hasLength!R)
+ {
+ reserve(length + el.length);
+ }
+ size_t retLength;
+ foreach (e; el)
+ {
+ retLength += insertBack(e);
+ }
+ return retLength;
+ }
+
+ /// Ditto.
+ size_t insertBack(size_t R)(T[R] el)
+ {
+ return insertBack!(T[])(el[]);
+ }
+
+ /// Ditto.
+ alias insert = insertBack;
+
+ ///
+ unittest
+ {
+ struct TestRange
+ {
+ int counter = 6;
+
+ int front()
+ {
+ return counter;
+ }
+
+ void popFront()
+ {
+ counter -= 2;
+ }
+
+ bool empty()
+ {
+ return counter == 0;
+ }
+ }
+
+ Array!int v1;
+
+ assert(v1.insertBack(5) == 1);
+ assert(v1.length == 1);
+ assert(v1.capacity == 1);
+ assert(v1.back == 5);
+
+ assert(v1.insertBack(TestRange()) == 3);
+ assert(v1.length == 4);
+ assert(v1.capacity == 4);
+ assert(v1[0] == 5 && v1[1] == 6 && v1[2] == 4 && v1[3] == 2);
+
+ assert(v1.insertBack([34, 234]) == 2);
+ assert(v1.length == 6);
+ assert(v1.capacity == 6);
+ assert(v1[4] == 34 && v1[5] == 234);
+ }
+
+ /**
+ * Inserts $(D_PARAM el) before or after $(D_PARAM r).
+ *
+ * Params:
+ * R = Type of the inserted value(s) (single value, range or static array).
+ * r = Range originally obtained from this array.
+ * el = Value(s) should be inserted.
+ *
+ * Returns: The number of elements inserted.
+ *
+ * Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this).
+ */
+ size_t insertAfter(R)(Range!T r, R el)
+ if (!isInfinite!R
+ && isInputRange!R
+ && isImplicitlyConvertible!(ElementType!R, T))
+ in
+ {
+ assert(r.container is &this);
+ assert(r.begin >= this.data);
+ assert(r.end <= this.data + length);
+ }
+ body
+ {
+ const oldLen = length;
+ const offset = r.end - this.data;
+ const inserted = insertBack(el);
+ bringToFront(this.data[offset .. oldLen], this.data[oldLen .. length]);
+ return inserted;
+ }
+
+ /// Ditto.
+ size_t insertAfter(size_t R)(Range!T r, T[R] el)
+ in
+ {
+ assert(r.container is &this);
+ assert(r.begin >= this.data);
+ assert(r.end <= this.data + length);
+ }
+ body
+ {
+ return insertAfter!(T[])(r, el[]);
+ }
+
+ /// Ditto.
+ size_t insertAfter(R)(Range!T r, auto ref R el)
+ if (isImplicitlyConvertible!(R, T))
+ in
+ {
+ assert(r.container is &this);
+ assert(r.begin >= this.data);
+ assert(r.end <= this.data + length);
+ }
+ body
+ {
+ const oldLen = length;
+ const offset = r.end - this.data;
+
+ static if (__traits(isRef, el))
+ {
+ insertBack(el);
+ }
+ else
+ {
+ moveBack(el);
+ }
+ bringToFront(this.data[offset .. oldLen], this.data[oldLen .. length]);
+
+ return 1;
+ }
+
+ /// Ditto.
+ size_t insertBefore(R)(Range!T r, R el)
+ if (!isInfinite!R
+ && isInputRange!R
+ && isImplicitlyConvertible!(ElementType!R, T))
+ in
+ {
+ assert(r.container is &this);
+ assert(r.begin >= this.data);
+ assert(r.end <= this.data + length);
+ }
+ body
+ {
+ return insertAfter(Range!T(this, this.data, r.begin), el);
+ }
+
+ /// Ditto.
+ size_t insertBefore(size_t R)(Range!T r, T[R] el)
+ in
+ {
+ assert(r.container is &this);
+ assert(r.begin >= this.data);
+ assert(r.end <= this.data + length);
+ }
+ body
+ {
+ return insertBefore!(T[])(r, el[]);
+ }
+
+ /// Ditto.
+ size_t insertBefore(R)(Range!T r, auto ref R el)
+ if (isImplicitlyConvertible!(R, T))
+ in
+ {
+ assert(r.container is &this);
+ assert(r.begin >= this.data);
+ assert(r.end <= this.data + length);
+ }
+ body
+ {
+ const oldLen = length;
+ const offset = r.begin - this.data;
+
+ static if (__traits(isRef, el))
+ {
+ insertBack(el);
+ }
+ else
+ {
+ moveBack(el);
+ }
+ bringToFront(this.data[offset .. oldLen], this.data[oldLen .. length]);
+
+ return 1;
+ }
+
+ ///
+ unittest
+ {
+ Array!int v1;
+ v1.insertAfter(v1[], [2, 8]);
+ assert(v1[0] == 2);
+ assert(v1[1] == 8);
+ assert(v1.length == 2);
+
+ v1.insertAfter(v1[], [1, 2]);
+ assert(v1[0] == 2);
+ assert(v1[1] == 8);
+ assert(v1[2] == 1);
+ assert(v1[3] == 2);
+ assert(v1.length == 4);
+
+ v1.insertAfter(v1[0 .. 0], [1, 2]);
+ assert(v1[0] == 1);
+ assert(v1[1] == 2);
+ assert(v1[2] == 2);
+ assert(v1[3] == 8);
+ assert(v1[4] == 1);
+ assert(v1[5] == 2);
+ assert(v1.length == 6);
+
+ v1.insertAfter(v1[0 .. 4], 9);
+ assert(v1[0] == 1);
+ assert(v1[1] == 2);
+ assert(v1[2] == 2);
+ assert(v1[3] == 8);
+ assert(v1[4] == 9);
+ assert(v1[5] == 1);
+ assert(v1[6] == 2);
+ assert(v1.length == 7);
+ }
+
+ ///
+ unittest
+ {
+ Array!int v1;
+ v1.insertBefore(v1[], [2, 8]);
+ assert(v1[0] == 2);
+ assert(v1[1] == 8);
+ assert(v1.length == 2);
+
+ v1.insertBefore(v1[], [1, 2]);
+ assert(v1[0] == 1);
+ assert(v1[1] == 2);
+ assert(v1[2] == 2);
+ assert(v1[3] == 8);
+ assert(v1.length == 4);
+
+ v1.insertBefore(v1[0 .. 1], [1, 2]);
+ assert(v1[0] == 1);
+ assert(v1[1] == 2);
+ assert(v1[2] == 1);
+ assert(v1[3] == 2);
+ assert(v1[4] == 2);
+ assert(v1[5] == 8);
+ assert(v1.length == 6);
+
+ v1.insertBefore(v1[2 .. $], 9);
+ assert(v1[0] == 1);
+ assert(v1[1] == 2);
+ assert(v1[2] == 9);
+ assert(v1[3] == 1);
+ assert(v1[4] == 2);
+ assert(v1[5] == 2);
+ assert(v1[6] == 8);
+ assert(v1.length == 7);
+ }
+
+ /**
+ * Assigns a value to the element with the index $(D_PARAM pos).
+ *
+ * Params:
+ * E = Value type.
+ * value = Value.
+ * pos = Position.
+ *
+ * Returns: Assigned value.
+ *
+ * Precondition: $(D_INLINECODE length > pos).
+ */
+ ref T opIndexAssign(E : T)(auto ref E value, const size_t pos)
+ {
+ return opIndex(pos) = value;
+ }
+
+ /// Ditto.
+ Range!T opIndexAssign(E : T)(auto ref E value)
+ {
+ return opSliceAssign(value, 0, length);
+ }
+
+ ///
+ nothrow @safe @nogc unittest
+ {
+ Array!int a = Array!int(1);
+ a[0] = 5;
+ assert(a[0] == 5);
+ }
+
+ /**
+ * Assigns a range or a static array.
+ *
+ * Params:
+ * R = Value type.
+ * value = Value.
+ *
+ * Returns: Assigned value.
+ *
+ * Precondition: $(D_INLINECODE length == value.length).
+ */
+ Range!T opIndexAssign(size_t R)(T[R] value)
+ {
+ return opSliceAssign!R(value, 0, length);
+ }
+
+ /// Ditto.
+ Range!T opIndexAssign(Range!T value)
+ {
+ return opSliceAssign(value, 0, length);
+ }
+
+ ///
+ @nogc unittest
+ {
+ auto v1 = Array!int([12, 1, 7]);
+
+ v1[] = 3;
+ assert(v1[0] == 3);
+ assert(v1[1] == 3);
+ assert(v1[2] == 3);
+
+ v1[] = [7, 1, 12];
+ assert(v1[0] == 7);
+ assert(v1[1] == 1);
+ assert(v1[2] == 12);
+ }
+
+ /**
+ * Params:
+ * pos = Index.
+ *
+ * Returns: The value at a specified index.
+ *
+ * Precondition: $(D_INLINECODE length > pos).
+ */
+ ref inout(T) opIndex(const size_t pos) inout @trusted
+ in
+ {
+ assert(length > pos);
+ }
+ body
+ {
+ return *(this.data + pos);
+ }
+
+ /**
+ * Returns: Random access range that iterates over elements of the array,
+ * in forward order.
+ */
+ Range!T opIndex() @trusted
+ {
+ return typeof(return)(this, this.data, this.data + length);
+ }
+
+ /// Ditto.
+ Range!(const T) opIndex() const @trusted
+ {
+ return typeof(return)(this, this.data, this.data + length);
+ }
+
+ ///
+ unittest
+ {
+ const v1 = Array!int([6, 123, 34, 5]);
+
+ assert(v1[0] == 6);
+ assert(v1[1] == 123);
+ assert(v1[2] == 34);
+ assert(v1[3] == 5);
+ static assert(is(typeof(v1[0]) == const(int)));
+ static assert(is(typeof(v1[])));
+ }
+
+ /**
+ * Comparison for equality.
+ *
+ * Params:
+ * that = The array to compare with.
+ *
+ * Returns: $(D_KEYWORD true) if the arrays are equal, $(D_KEYWORD false)
+ * otherwise.
+ */
+ bool opEquals()(auto ref typeof(this) that) @trusted
+ {
+ return equal(this.data[0 .. length], that.data[0 .. that.length]);
+ }
+
+ /// Ditto.
+ bool opEquals()(const auto ref typeof(this) that) const @trusted
+ {
+ return equal(this.data[0 .. length], that.data[0 .. that.length]);
+ }
+
+ /// Ditto.
+ bool opEquals(Range!T that)
+ {
+ return equal(opIndex(), that);
+ }
+
+ /**
+ * Comparison for equality.
+ *
+ * Params:
+ * R = Right hand side type.
+ * that = Right hand side array range.
+ *
+ * Returns: $(D_KEYWORD true) if the array and the range are equal,
+ * $(D_KEYWORD false) otherwise.
+ */
+ bool opEquals(R)(Range!R that) const
+ if (is(Unqual!R == T))
+ {
+ return equal(opIndex(), that);
+ }
+
+ ///
+ unittest
+ {
+ Array!int v1, v2;
+ assert(v1 == v2);
+
+ v1.length = 1;
+ v2.length = 2;
+ assert(v1 != v2);
+
+ v1.length = 2;
+ v1[0] = v2[0] = 2;
+ v1[1] = 3;
+ v2[1] = 4;
+ assert(v1 != v2);
+
+ v2[1] = 3;
+ assert(v1 == v2);
+ }
+
+ /**
+ * Returns: The first element.
+ *
+ * Precondition: $(D_INLINECODE !empty).
+ */
+ @property ref inout(T) front() inout
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ return *this.data;
+ }
+
+ ///
+ @safe unittest
+ {
+ auto v = Array!int([5]);
+
+ assert(v.front == 5);
+
+ v.length = 2;
+ v[1] = 15;
+ assert(v.front == 5);
+ }
+
+ /**
+ * Returns: The last element.
+ *
+ * Precondition: $(D_INLINECODE !empty).
+ */
+ @property ref inout(T) back() inout @trusted
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ return *(this.data + length - 1);
+ }
+
+ ///
+ unittest
+ {
+ auto v = Array!int([5]);
+
+ assert(v.back == 5);
+
+ v.length = 2;
+ v[1] = 15;
+ assert(v.back == 15);
+ }
+
+ /**
+ * Params:
+ * i = Slice start.
+ * j = Slice end.
+ *
+ * Returns: A range that iterates over elements of the container from
+ * index $(D_PARAM i) up to (excluding) index $(D_PARAM j).
+ *
+ * Precondition: $(D_INLINECODE i <= j && j <= length).
+ */
+ Range!T opSlice(const size_t i, const size_t j) @trusted
+ in
+ {
+ assert(i <= j);
+ assert(j <= length);
+ }
+ body
+ {
+ return typeof(return)(this, this.data + i, this.data + j);
+ }
+
+ /// Ditto.
+ Range!(const T) opSlice(const size_t i, const size_t j) const @trusted
+ in
+ {
+ assert(i <= j);
+ assert(j <= length);
+ }
+ body
+ {
+ return typeof(return)(this, this.data + i, this.data + j);
+ }
+
+ ///
+ unittest
+ {
+ Array!int v;
+ auto r = v[];
+ assert(r.length == 0);
+ assert(r.empty);
+ }
+
+ ///
+ unittest
+ {
+ auto v = Array!int([1, 2, 3]);
+ auto r = v[];
+
+ assert(r.front == 1);
+ assert(r.back == 3);
+
+ r.popFront();
+ assert(r.front == 2);
+
+ r.popBack();
+ assert(r.back == 2);
+
+ assert(r.length == 1);
+ }
+
+ ///
+ unittest
+ {
+ auto v = Array!int([1, 2, 3, 4]);
+ auto r = v[1 .. 4];
+ assert(r.length == 3);
+ assert(r[0] == 2);
+ assert(r[1] == 3);
+ assert(r[2] == 4);
+
+ r = v[0 .. 0];
+ assert(r.length == 0);
+
+ r = v[4 .. 4];
+ assert(r.length == 0);
+ }
+
+ /**
+ * Slicing assignment.
+ *
+ * Params:
+ * R = Type of the assigned slice or length of the static array should
+ * be assigned.
+ * value = New value (single value, range or static array).
+ * i = Slice start.
+ * j = Slice end.
+ *
+ * Returns: Slice with the assigned part of the array.
+ *
+ * Precondition: $(D_INLINECODE i <= j && j <= length
+ * && value.length == j - i)
+ */
+ Range!T opSliceAssign(size_t R)(T[R] value, const size_t i, const size_t j)
+ @trusted
+ in
+ {
+ assert(i <= j);
+ assert(j <= length);
+ }
+ body
+ {
+ copy(value[], this.data[i .. j]);
+ return opSlice(i, j);
+ }
+
+ /// Ditto.
+ Range!T opSliceAssign(R : T)(auto ref R value, const size_t i, const size_t j)
+ @trusted
+ in
+ {
+ assert(i <= j);
+ assert(j <= length);
+ }
+ body
+ {
+ fill(this.data[i .. j], value);
+ return opSlice(i, j);
+ }
+
+ /// Ditto.
+ Range!T opSliceAssign(Range!T value, const size_t i, const size_t j) @trusted
+ in
+ {
+ assert(i <= j);
+ assert(j <= length);
+ assert(j - i == value.length);
+ }
+ body
+ {
+ copy(value, this.data[i .. j]);
+ return opSlice(i, j);
+ }
+
+ ///
+ @nogc @safe unittest
+ {
+ auto v1 = Array!int([3, 3, 3]);
+ auto v2 = Array!int([1, 2]);
+
+ v1[0 .. 2] = 286;
+ assert(v1[0] == 286);
+ assert(v1[1] == 286);
+ assert(v1[2] == 3);
+
+ v2[0 .. $] = v1[1 .. 3];
+ assert(v2[0] == 286);
+ assert(v2[1] == 3);
+
+ v1[0 .. 2] = [5, 8];
+ assert(v1[0] == 5);
+ assert(v1[1] == 8);
+ assert(v1[2] == 3);
+ }
+
+ /**
+ * Returns an array used internally by the array to store its owned elements.
+ * The length of the returned array may differ from the size of the allocated
+ * memory for the array: the array contains only initialized elements, but
+ * not the reserved memory.
+ *
+ * Returns: The array with elements of this array.
+ */
+ inout(T[]) get() inout @trusted
+ {
+ return this.data[0 .. length];
+ }
+
+ ///
+ unittest
+ {
+ auto v = Array!int([1, 2, 4]);
+ auto data = v.get();
+
+ assert(data[0] == 1);
+ assert(data[1] == 2);
+ assert(data[2] == 4);
+ assert(data.length == 3);
+
+ data = v[1 .. 2].get();
+ assert(data[0] == 2);
+ assert(data.length == 1);
+ }
+
+ /**
+ * Assigns another array.
+ *
+ * If $(D_PARAM that) is passed by value, it won't be copied, but moved.
+ * This array will take the ownership over $(D_PARAM that)'s storage and
+ * the allocator.
+ *
+ * If $(D_PARAM that) is passed by reference, it will be copied.
+ *
+ * Params:
+ * R = Content type.
+ * that = The value should be assigned.
+ *
+ * Returns: $(D_KEYWORD this).
+ */
+ ref typeof(this) opAssign(R)(ref R that)
+ if (is(Unqual!R == Array))
+ {
+ return this = that[];
+ }
+
+ /// Ditto.
+ ref typeof(this) opAssign(R)(R that) @trusted
+ if (is(R == Array))
+ {
+ swap(this.data, that.data);
+ swap(this.length_, that.length_);
+ swap(this.capacity_, that.capacity_);
+ swap(this.allocator_, that.allocator_);
+ return this;
+ }
+
+ /**
+ * Assigns a range to the array.
+ *
+ * Params:
+ * R = Content type.
+ * that = The value should be assigned.
+ *
+ * Returns: $(D_KEYWORD this).
+ */
+ ref typeof(this) opAssign(R)(R that)
+ if (!isInfinite!R
+ && isInputRange!R
+ && isImplicitlyConvertible!(ElementType!R, T))
+ {
+ length = 0;
+ insertBack(that);
+ return this;
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto v1 = const Array!int([5, 15, 8]);
+ Array!int v2;
+ v2 = v1;
+ assert(v1 == v2);
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto v1 = const Array!int([5, 15, 8]);
+ Array!int v2;
+ v2 = v1[0 .. 2];
+ assert(equal(v1[0 .. 2], v2[]));
+ }
+
+ // Move assignment.
+ private @safe @nogc unittest
+ {
+ Array!int v1;
+ v1 = Array!int([5, 15, 8]);
+ }
+
+ /**
+ * Assigns a static array.
+ *
+ * Params:
+ * R = Static array size.
+ * that = Values to initialize the array with.
+ *
+ * Returns: $(D_KEYWORD this).
+ */
+ ref typeof(this) opAssign(size_t R)(T[R] that)
+ {
+ return opAssign!(T[])(that[]);
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto v1 = Array!int([5, 15, 8]);
+ Array!int v2;
+
+ v2 = [5, 15, 8];
+ assert(v1 == v2);
+ }
+
+ mixin DefaultAllocator;
+}
+
+///
+unittest
+{
+ auto v = Array!int([5, 15, 8]);
+
+ assert(v.front == 5);
+ assert(v[1] == 15);
+ assert(v.back == 8);
+
+ auto r = v[];
+ r[0] = 7;
+ assert(r.front == 7);
+ assert(r.front == v.front);
+}
+
+@nogc unittest
+{
+ const v1 = Array!int();
+ const Array!int v2;
+ const v3 = Array!int([1, 5, 8]);
+ static assert(is(PointerTarget!(typeof(v3.data)) == const(int)));
+}
+
+@nogc unittest
+{
+ // Test that const arrays return usable ranges.
+ auto v = const Array!int([1, 2, 4]);
+ auto r1 = v[];
+
+ assert(r1.back == 4);
+ r1.popBack();
+ assert(r1.back == 2);
+ r1.popBack();
+ assert(r1.back == 1);
+ r1.popBack();
+ assert(r1.length == 0);
+
+ static assert(!is(typeof(r1[0] = 5)));
+ static assert(!is(typeof(v[0] = 5)));
+
+ const r2 = r1[];
+ static assert(is(typeof(r2[])));
+}
+
+@nogc unittest
+{
+ Array!int v1;
+ const Array!int v2;
+
+ auto r1 = v1[];
+ auto r2 = v1[];
+
+ assert(r1.length == 0);
+ assert(r2.empty);
+ assert(r1 == r2);
+
+ v1.insertBack([1, 2, 4]);
+ assert(v1[] == v1);
+ assert(v2[] == v2);
+ assert(v2[] != v1);
+ assert(v1[] != v2);
+ assert(v1[].equal(v1[]));
+ assert(v2[].equal(v2[]));
+ assert(!v1[].equal(v2[]));
+}
+
+@nogc unittest
+{
+ struct MutableEqualsStruct
+ {
+ int opEquals(typeof(this) that) @nogc
+ {
+ return true;
+ }
+ }
+ struct ConstEqualsStruct
+ {
+ int opEquals(const typeof(this) that) const @nogc
+ {
+ return true;
+ }
+ }
+ auto v1 = Array!ConstEqualsStruct();
+ auto v2 = Array!ConstEqualsStruct();
+ assert(v1 == v2);
+ assert(v1[] == v2);
+ assert(v1 == v2[]);
+ assert(v1[].equal(v2[]));
+
+ auto v3 = const Array!ConstEqualsStruct();
+ auto v4 = const Array!ConstEqualsStruct();
+ assert(v3 == v4);
+ assert(v3[] == v4);
+ assert(v3 == v4[]);
+ assert(v3[].equal(v4[]));
+
+ auto v7 = Array!MutableEqualsStruct(1, MutableEqualsStruct());
+ auto v8 = Array!MutableEqualsStruct(1, MutableEqualsStruct());
+ assert(v7 == v8);
+ assert(v7[] == v8);
+ assert(v7 == v8[]);
+ assert(v7[].equal(v8[]));
+}
+
+@nogc unittest
+{
+ struct SWithDtor
+ {
+ ~this() @nogc
+ {
+ }
+ }
+ auto v = Array!SWithDtor(); // Destructor can destroy empty arrays.
+}
+
+private unittest
+{
+ class A
+ {
+ }
+ A a1, a2;
+ auto v1 = Array!A([a1, a2]);
+}
+
+private @safe @nogc unittest
+{
+ auto v = Array!int([5, 15, 8]);
+ {
+ size_t i;
+
+ foreach (e; v)
+ {
+ assert(i != 0 || e == 5);
+ assert(i != 1 || e == 15);
+ assert(i != 2 || e == 8);
+ ++i;
+ }
+ assert(i == 3);
+ }
+ {
+ size_t i = 3;
+
+ foreach_reverse (e; v)
+ {
+ --i;
+ assert(i != 2 || e == 8);
+ assert(i != 1 || e == 15);
+ assert(i != 0 || e == 5);
+ }
+ assert(i == 0);
+ }
+}
diff --git a/source/tanya/container/package.d b/source/tanya/container/package.d
index 35400d5..a8adf35 100644
--- a/source/tanya/container/package.d
+++ b/source/tanya/container/package.d
@@ -12,8 +12,8 @@
*/
module tanya.container;
+public import tanya.container.array;
public import tanya.container.buffer;
public import tanya.container.list;
public import tanya.container.string;
-public import tanya.container.vector;
public import tanya.container.queue;
diff --git a/source/tanya/container/vector.d b/source/tanya/container/vector.d
index 7f5e3a5..a957fe5 100644
--- a/source/tanya/container/vector.d
+++ b/source/tanya/container/vector.d
@@ -10,1634 +10,7 @@
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
*/
+deprecated("Use tanya.container.array instead.")
module tanya.container.vector;
-import core.checkedint;
-import core.exception;
-import std.algorithm.comparison;
-import std.algorithm.mutation;
-import std.conv;
-import std.range.primitives;
-import std.meta;
-import std.traits;
-import tanya.memory;
-
-/**
- * Random-access range for the $(D_PSYMBOL Vector).
- *
- * Params:
- * E = Element type.
- */
-struct Range(E)
-{
- private E* begin, end;
- private alias ContainerType = CopyConstness!(E, Vector!(Unqual!E));
- private ContainerType* container;
-
- invariant
- {
- assert(this.begin <= this.end);
- assert(this.container !is null);
- assert(this.begin >= this.container.data);
- assert(this.end <= this.container.data + this.container.length);
- }
-
- private this(ref ContainerType container, E* begin, E* end) @trusted
- in
- {
- assert(begin <= end);
- assert(begin >= container.data);
- assert(end <= container.data + container.length);
- }
- body
- {
- this.container = &container;
- this.begin = begin;
- this.end = end;
- }
-
- @disable this();
-
- @property Range save()
- {
- return this;
- }
-
- @property bool empty() const
- {
- return this.begin == this.end;
- }
-
- @property size_t length() const
- {
- return this.end - this.begin;
- }
-
- alias opDollar = length;
-
- @property ref inout(E) front() inout
- in
- {
- assert(!empty);
- }
- body
- {
- return *this.begin;
- }
-
- @property ref inout(E) back() inout @trusted
- in
- {
- assert(!empty);
- }
- body
- {
- return *(this.end - 1);
- }
-
- void popFront() @trusted
- in
- {
- assert(!empty);
- }
- body
- {
- ++this.begin;
- }
-
- void popBack() @trusted
- in
- {
- assert(!empty);
- }
- body
- {
- --this.end;
- }
-
- ref inout(E) opIndex(const size_t i) inout @trusted
- in
- {
- assert(i < length);
- }
- body
- {
- return *(this.begin + i);
- }
-
- Range opIndex()
- {
- return typeof(return)(*this.container, this.begin, this.end);
- }
-
- Range!(const E) opIndex() const
- {
- return typeof(return)(*this.container, this.begin, this.end);
- }
-
- Range opSlice(const size_t i, const size_t j) @trusted
- in
- {
- assert(i <= j);
- assert(j <= length);
- }
- body
- {
- return typeof(return)(*this.container, this.begin + i, this.begin + j);
- }
-
- Range!(const E) opSlice(const size_t i, const size_t j) const @trusted
- in
- {
- assert(i <= j);
- assert(j <= length);
- }
- body
- {
- return typeof(return)(*this.container, this.begin + i, this.begin + j);
- }
-
- inout(E[]) get() inout @trusted
- {
- return this.begin[0 .. length];
- }
-}
-
-/**
- * One dimensional array.
- *
- * Params:
- * T = Content type.
- */
-struct Vector(T)
-{
- private size_t length_;
- private T* data;
- private size_t capacity_;
-
- invariant
- {
- assert(this.length_ <= this.capacity_);
- assert(this.capacity_ == 0 || this.data !is null);
- }
-
- /**
- * Creates a new $(D_PSYMBOL Vector) with the elements from a static array.
- *
- * Params:
- * R = Static array size.
- * init = Values to initialize the vector with.
- * allocator = Allocator.
- */
- this(size_t R)(T[R] init, shared Allocator allocator = defaultAllocator)
- {
- this(allocator);
- insertBack!(T[])(init[]);
- }
-
- /**
- * Creates a new $(D_PSYMBOL Vector) with the elements from an input range.
- *
- * Params:
- * R = Type of the initial range.
- * init = Values to initialize the vector with.
- * allocator = Allocator.
- */
- this(R)(R init, shared Allocator allocator = defaultAllocator)
- if (!isInfinite!R
- && isInputRange!R
- && isImplicitlyConvertible!(ElementType!R, T))
- {
- this(allocator);
- insertBack(init);
- }
-
- /**
- * Initializes this vector from another one.
- *
- * If $(D_PARAM init) is passed by value, it won't be copied, but moved.
- * If the allocator of ($D_PARAM init) matches $(D_PARAM allocator),
- * $(D_KEYWORD this) will just take the ownership over $(D_PARAM init)'s
- * storage, otherwise, the storage will be allocated with
- * $(D_PARAM allocator) and all elements will be moved;
- * $(D_PARAM init) will be destroyed at the end.
- *
- * If $(D_PARAM init) is passed by reference, it will be copied.
- *
- * Params:
- * R = Source vector type.
- * init = Source vector.
- * allocator = Allocator.
- */
- this(R)(ref R init, shared Allocator allocator = defaultAllocator)
- if (is(Unqual!R == Vector))
- {
- this(allocator);
- insertBack(init[]);
- }
-
- /// Ditto.
- this(R)(R init, shared Allocator allocator = defaultAllocator) @trusted
- if (is(R == Vector))
- {
- this(allocator);
- if (allocator is init.allocator)
- {
- // Just steal all references and the allocator.
- this.data = init.data;
- this.length_ = init.length_;
- this.capacity_ = init.capacity_;
-
- // Reset the source vector, so it can't destroy the moved storage.
- init.length_ = init.capacity_ = 0;
- init.data = null;
- }
- else
- {
- // Move each element.
- reserve(init.length_);
- moveEmplaceAll(init.data[0 .. init.length_], this.data[0 .. init.length_]);
- this.length_ = init.length_;
- // Destructor of init should destroy it here.
- }
- }
-
- ///
- @trusted @nogc unittest
- {
- auto v1 = Vector!int([1, 2, 3]);
- auto v2 = Vector!int(v1);
- assert(v1 == v2);
-
- auto v3 = Vector!int(Vector!int([1, 2, 3]));
- assert(v1 == v3);
- assert(v3.length == 3);
- assert(v3.capacity == 3);
- }
-
- private @trusted @nogc unittest // const constructor tests
- {
- auto v1 = const Vector!int([1, 2, 3]);
- auto v2 = Vector!int(v1);
- assert(v1.data !is v2.data);
- assert(v1 == v2);
-
- auto v3 = const Vector!int(Vector!int([1, 2, 3]));
- assert(v1 == v3);
- assert(v3.length == 3);
- assert(v3.capacity == 3);
- }
-
- /**
- * Creates a new $(D_PSYMBOL Vector).
- *
- * Params:
- * len = Initial length of the vector.
- * init = Initial value to fill the vector with.
- * allocator = Allocator.
- */
- this(const size_t len, T init, shared Allocator allocator = defaultAllocator) @trusted
- {
- this(allocator);
- reserve(len);
- uninitializedFill(this.data[0 .. len], init);
- length_ = len;
- }
-
- /// Ditto.
- this(const size_t len, shared Allocator allocator = defaultAllocator)
- {
- this(allocator);
- length = len;
- }
-
- /// Ditto.
- this(shared Allocator allocator)
- in
- {
- assert(allocator !is null);
- }
- body
- {
- allocator_ = allocator;
- }
-
- ///
- unittest
- {
- auto v = Vector!int([3, 8, 2]);
-
- assert(v.capacity == 3);
- assert(v.length == 3);
- assert(v[0] == 3 && v[1] == 8 && v[2] == 2);
- }
-
- ///
- unittest
- {
- auto v = Vector!int(3, 5);
-
- assert(v.capacity == 3);
- assert(v.length == 3);
- assert(v[0] == 5 && v[1] == 5 && v[2] == 5);
- }
-
- @safe unittest
- {
- auto v1 = Vector!int(defaultAllocator);
- }
-
- /**
- * Destroys this $(D_PSYMBOL Vector).
- */
- ~this() @trusted
- {
- clear();
- allocator.deallocate(this.data[0 .. capacity]);
- }
-
- /**
- * Copies the vector.
- */
- this(this)
- {
- auto buf = this.data[0 .. this.length_];
- this.length_ = capacity_ = 0;
- this.data = null;
- insertBack(buf);
- }
-
- /**
- * Removes all elements.
- */
- void clear()
- {
- length = 0;
- }
-
- ///
- unittest
- {
- auto v = Vector!int([18, 20, 15]);
- v.clear();
- assert(v.length == 0);
- assert(v.capacity == 3);
- }
-
- /**
- * Returns: How many elements the vector can contain without reallocating.
- */
- @property size_t capacity() const
- {
- return capacity_;
- }
-
- ///
- @safe @nogc unittest
- {
- auto v = Vector!int(4);
- assert(v.capacity == 4);
- }
-
- /**
- * Returns: Vector length.
- */
- @property size_t length() const
- {
- return length_;
- }
-
- /// Ditto.
- size_t opDollar() const
- {
- return length;
- }
-
- /**
- * Expands/shrinks the vector.
- *
- * Params:
- * len = New length.
- */
- @property void length(const size_t len) @trusted
- {
- if (len == length)
- {
- return;
- }
- else if (len > length)
- {
- reserve(len);
- initializeAll(this.data[length_ .. len]);
- }
- else
- {
- static if (hasElaborateDestructor!T)
- {
- const T* end = this.data + length_ - 1;
- for (T* e = this.data + len; e != end; ++e)
- {
- destroy(*e);
- }
- }
- }
- length_ = len;
- }
-
- ///
- unittest
- {
- Vector!int v;
-
- v.length = 5;
- assert(v.length == 5);
- assert(v.capacity == 5);
-
- v.length = 7;
- assert(v.length == 7);
- assert(v.capacity == 7);
-
- assert(v[$ - 1] == 0);
- v[$ - 1] = 3;
- assert(v[$ - 1] == 3);
-
- v.length = 0;
- assert(v.length == 0);
- assert(v.capacity == 7);
- }
-
- /**
- * Reserves space for $(D_PARAM size) elements.
- *
- * If $(D_PARAM size) is less than or equal to the $(D_PSYMBOL capacity), the
- * function call does not cause a reallocation and the vector capacity is not
- * affected.
- *
- * Params:
- * size = Desired size.
- */
- void reserve(const size_t size) @trusted
- {
- if (capacity_ >= size)
- {
- return;
- }
- bool overflow;
- const byteSize = mulu(size, T.sizeof, overflow);
- assert(!overflow);
-
- void[] buf = this.data[0 .. this.capacity_];
- if (!allocator.reallocateInPlace(buf, byteSize))
- {
- buf = allocator.allocate(byteSize);
- if (buf is null)
- {
- onOutOfMemoryErrorNoGC();
- }
- scope (failure)
- {
- allocator.deallocate(buf);
- }
- const T* end = this.data + this.length_;
- for (T* src = this.data, dest = cast(T*) buf; src != end; ++src, ++dest)
- {
- moveEmplace(*src, *dest);
- static if (hasElaborateDestructor!T)
- {
- destroy(*src);
- }
- }
- allocator.deallocate(this.data[0 .. this.capacity_]);
- this.data = cast(T*) buf;
- }
- this.capacity_ = size;
- }
-
- ///
- @nogc @safe unittest
- {
- Vector!int v;
- assert(v.capacity == 0);
- assert(v.length == 0);
-
- v.reserve(3);
- assert(v.capacity == 3);
- assert(v.length == 0);
- }
-
- /**
- * Requests the vector to reduce its capacity to fit the $(D_PARAM size).
- *
- * The request is non-binding. The vector won't become smaller than the
- * $(D_PARAM length).
- *
- * Params:
- * size = Desired size.
- */
- void shrink(const size_t size) @trusted
- {
- if (capacity <= size)
- {
- return;
- }
- const n = max(length, size);
- void[] buf = this.data[0 .. this.capacity_];
- if (allocator.reallocateInPlace(buf, n * T.sizeof))
- {
- this.capacity_ = n;
- }
- }
-
- ///
- @nogc @safe unittest
- {
- Vector!int v;
- assert(v.capacity == 0);
- assert(v.length == 0);
-
- v.reserve(5);
- v.insertBack(1);
- v.insertBack(3);
- assert(v.capacity == 5);
- assert(v.length == 2);
- }
-
- /**
- * Returns: $(D_KEYWORD true) if the vector is empty.
- */
- @property bool empty() const
- {
- return length == 0;
- }
-
- /**
- * Removes the value at the back of the vector.
- *
- * Returns: The number of elements removed
- *
- * Precondition: $(D_INLINECODE !empty).
- */
- void removeBack()
- in
- {
- assert(!empty);
- }
- body
- {
- length = length - 1;
- }
-
- /**
- * Removes $(D_PARAM howMany) elements from the vector.
- *
- * This method doesn't fail if it could not remove $(D_PARAM howMany)
- * elements. Instead, if $(D_PARAM howMany) is greater than the vector
- * length, all elements are removed.
- *
- * Params:
- * howMany = How many elements should be removed.
- *
- * Returns: The number of elements removed
- */
- size_t removeBack(const size_t howMany)
- out (removed)
- {
- assert(removed <= howMany);
- }
- body
- {
- const toRemove = min(howMany, length);
-
- length = length - toRemove;
-
- return toRemove;
- }
-
- ///
- unittest
- {
- auto v = 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);
- }
-
- /**
- * Remove all elements beloning to $(D_PARAM r).
- *
- * Params:
- * r = Range originally obtained from this vector.
- *
- * Returns: A range spanning the remaining elements in the array that
- * initially were right after $(D_PARAM r).
- *
- * Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this).
- */
- Range!T remove(Range!T r) @trusted
- in
- {
- assert(r.container is &this);
- assert(r.begin >= this.data);
- assert(r.end <= this.data + length);
- }
- body
- {
- auto end = this.data + this.length;
- moveAll(Range!T(this, r.end, end), Range!T(this, r.begin, end));
- length = length - r.length;
- return Range!T(this, r.begin, this.data + length);
- }
-
- ///
- @safe @nogc unittest
- {
- auto v = Vector!int([5, 18, 17, 2, 4, 6, 1]);
-
- assert(v.remove(v[1 .. 3]).length == 4);
- assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6 && v[4] == 1);
- assert(v.length == 5);
-
- assert(v.remove(v[4 .. 4]).length == 1);
- assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6 && v[4] == 1);
- assert(v.length == 5);
-
- assert(v.remove(v[4 .. 5]).length == 0);
- assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6);
- assert(v.length == 4);
-
- assert(v.remove(v[]).length == 0);
-
- }
-
- private void moveBack(R)(ref R el) @trusted
- if (isImplicitlyConvertible!(R, T))
- {
- reserve(this.length + 1);
- moveEmplace(el, *(this.data + this.length_));
- ++this.length_;
- }
-
- /**
- * Inserts the $(D_PARAM el) into the vector.
- *
- * Params:
- * R = Type of the inserted value(s) (single value, range or static array).
- * el = Value(s) should be inserted.
- *
- * Returns: The number of elements inserted.
- */
- size_t insertBack(R)(R el)
- if (isImplicitlyConvertible!(R, T))
- {
- moveBack(el);
- return 1;
- }
-
- /// Ditto.
- size_t insertBack(R)(ref R el) @trusted
- if (isImplicitlyConvertible!(R, T))
- {
- reserve(this.length_ + 1);
- emplace(this.data + this.length_, el);
- ++this.length_;
- return 1;
- }
-
- /// Ditto.
- size_t insertBack(R)(R el)
- if (!isInfinite!R
- && isInputRange!R
- && isImplicitlyConvertible!(ElementType!R, T))
- {
- static if (hasLength!R)
- {
- reserve(length + el.length);
- }
- size_t retLength;
- foreach (e; el)
- {
- retLength += insertBack(e);
- }
- return retLength;
- }
-
- /// Ditto.
- size_t insertBack(size_t R)(T[R] el)
- {
- return insertBack!(T[])(el[]);
- }
-
- /// Ditto.
- alias insert = insertBack;
-
- ///
- unittest
- {
- struct TestRange
- {
- int counter = 6;
-
- int front()
- {
- return counter;
- }
-
- void popFront()
- {
- counter -= 2;
- }
-
- bool empty()
- {
- return counter == 0;
- }
- }
-
- Vector!int v1;
-
- assert(v1.insertBack(5) == 1);
- assert(v1.length == 1);
- assert(v1.capacity == 1);
- assert(v1.back == 5);
-
- assert(v1.insertBack(TestRange()) == 3);
- assert(v1.length == 4);
- assert(v1.capacity == 4);
- assert(v1[0] == 5 && v1[1] == 6 && v1[2] == 4 && v1[3] == 2);
-
- assert(v1.insertBack([34, 234]) == 2);
- assert(v1.length == 6);
- assert(v1.capacity == 6);
- assert(v1[4] == 34 && v1[5] == 234);
- }
-
- /**
- * Inserts $(D_PARAM el) before or after $(D_PARAM r).
- *
- * Params:
- * R = Type of the inserted value(s) (single value, range or static array).
- * r = Range originally obtained from this vector.
- * el = Value(s) should be inserted.
- *
- * Returns: The number of elements inserted.
- *
- * Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this).
- */
- size_t insertAfter(R)(Range!T r, R el)
- if (!isInfinite!R
- && isInputRange!R
- && isImplicitlyConvertible!(ElementType!R, T))
- in
- {
- assert(r.container is &this);
- assert(r.begin >= this.data);
- assert(r.end <= this.data + length);
- }
- body
- {
- const oldLen = length;
- const offset = r.end - this.data;
- const inserted = insertBack(el);
- bringToFront(this.data[offset .. oldLen], this.data[oldLen .. length]);
- return inserted;
- }
-
- /// Ditto.
- size_t insertAfter(size_t R)(Range!T r, T[R] el)
- in
- {
- assert(r.container is &this);
- assert(r.begin >= this.data);
- assert(r.end <= this.data + length);
- }
- body
- {
- return insertAfter!(T[])(r, el[]);
- }
-
- /// Ditto.
- size_t insertAfter(R)(Range!T r, auto ref R el)
- if (isImplicitlyConvertible!(R, T))
- in
- {
- assert(r.container is &this);
- assert(r.begin >= this.data);
- assert(r.end <= this.data + length);
- }
- body
- {
- const oldLen = length;
- const offset = r.end - this.data;
-
- static if (__traits(isRef, el))
- {
- insertBack(el);
- }
- else
- {
- moveBack(el);
- }
- bringToFront(this.data[offset .. oldLen], this.data[oldLen .. length]);
-
- return 1;
- }
-
- /// Ditto.
- size_t insertBefore(R)(Range!T r, R el)
- if (!isInfinite!R
- && isInputRange!R
- && isImplicitlyConvertible!(ElementType!R, T))
- in
- {
- assert(r.container is &this);
- assert(r.begin >= this.data);
- assert(r.end <= this.data + length);
- }
- body
- {
- return insertAfter(Range!T(this, this.data, r.begin), el);
- }
-
- /// Ditto.
- size_t insertBefore(size_t R)(Range!T r, T[R] el)
- in
- {
- assert(r.container is &this);
- assert(r.begin >= this.data);
- assert(r.end <= this.data + length);
- }
- body
- {
- return insertBefore!(T[])(r, el[]);
- }
-
- /// Ditto.
- size_t insertBefore(R)(Range!T r, auto ref R el)
- if (isImplicitlyConvertible!(R, T))
- in
- {
- assert(r.container is &this);
- assert(r.begin >= this.data);
- assert(r.end <= this.data + length);
- }
- body
- {
- const oldLen = length;
- const offset = r.begin - this.data;
-
- static if (__traits(isRef, el))
- {
- insertBack(el);
- }
- else
- {
- moveBack(el);
- }
- bringToFront(this.data[offset .. oldLen], this.data[oldLen .. length]);
-
- return 1;
- }
-
- ///
- unittest
- {
- Vector!int v1;
- v1.insertAfter(v1[], [2, 8]);
- assert(v1[0] == 2);
- assert(v1[1] == 8);
- assert(v1.length == 2);
-
- v1.insertAfter(v1[], [1, 2]);
- assert(v1[0] == 2);
- assert(v1[1] == 8);
- assert(v1[2] == 1);
- assert(v1[3] == 2);
- assert(v1.length == 4);
-
- v1.insertAfter(v1[0 .. 0], [1, 2]);
- assert(v1[0] == 1);
- assert(v1[1] == 2);
- assert(v1[2] == 2);
- assert(v1[3] == 8);
- assert(v1[4] == 1);
- assert(v1[5] == 2);
- assert(v1.length == 6);
-
- v1.insertAfter(v1[0 .. 4], 9);
- assert(v1[0] == 1);
- assert(v1[1] == 2);
- assert(v1[2] == 2);
- assert(v1[3] == 8);
- assert(v1[4] == 9);
- assert(v1[5] == 1);
- assert(v1[6] == 2);
- assert(v1.length == 7);
- }
-
- ///
- unittest
- {
- Vector!int v1;
- v1.insertBefore(v1[], [2, 8]);
- assert(v1[0] == 2);
- assert(v1[1] == 8);
- assert(v1.length == 2);
-
- v1.insertBefore(v1[], [1, 2]);
- assert(v1[0] == 1);
- assert(v1[1] == 2);
- assert(v1[2] == 2);
- assert(v1[3] == 8);
- assert(v1.length == 4);
-
- v1.insertBefore(v1[0 .. 1], [1, 2]);
- assert(v1[0] == 1);
- assert(v1[1] == 2);
- assert(v1[2] == 1);
- assert(v1[3] == 2);
- assert(v1[4] == 2);
- assert(v1[5] == 8);
- assert(v1.length == 6);
-
- v1.insertBefore(v1[2 .. $], 9);
- assert(v1[0] == 1);
- assert(v1[1] == 2);
- assert(v1[2] == 9);
- assert(v1[3] == 1);
- assert(v1[4] == 2);
- assert(v1[5] == 2);
- assert(v1[6] == 8);
- assert(v1.length == 7);
- }
-
- /**
- * Assigns a value to the element with the index $(D_PARAM pos).
- *
- * Params:
- * E = Value type.
- * value = Value.
- * pos = Position.
- *
- * Returns: Assigned value.
- *
- * Precondition: $(D_INLINECODE length > pos).
- */
- ref T opIndexAssign(E : T)(auto ref E value, const size_t pos)
- {
- return opIndex(pos) = value;
- }
-
- /// Ditto.
- Range!T opIndexAssign(E : T)(auto ref E value)
- {
- return opSliceAssign(value, 0, length);
- }
-
- ///
- nothrow @safe @nogc unittest
- {
- Vector!int a = Vector!int(1);
- a[0] = 5;
- assert(a[0] == 5);
- }
-
- /**
- * Assigns a range or a static array.
- *
- * Params:
- * R = Value type.
- * value = Value.
- *
- * Returns: Assigned value.
- *
- * Precondition: $(D_INLINECODE length == value.length).
- */
- Range!T opIndexAssign(size_t R)(T[R] value)
- {
- return opSliceAssign!R(value, 0, length);
- }
-
- /// Ditto.
- Range!T opIndexAssign(Range!T value)
- {
- return opSliceAssign(value, 0, length);
- }
-
- ///
- @nogc unittest
- {
- auto v1 = Vector!int([12, 1, 7]);
-
- v1[] = 3;
- assert(v1[0] == 3);
- assert(v1[1] == 3);
- assert(v1[2] == 3);
-
- v1[] = [7, 1, 12];
- assert(v1[0] == 7);
- assert(v1[1] == 1);
- assert(v1[2] == 12);
- }
-
- /**
- * Params:
- * pos = Index.
- *
- * Returns: The value at a specified index.
- *
- * Precondition: $(D_INLINECODE length > pos).
- */
- ref inout(T) opIndex(const size_t pos) inout @trusted
- in
- {
- assert(length > pos);
- }
- body
- {
- return *(this.data + pos);
- }
-
- /**
- * Returns: Random access range that iterates over elements of the vector, in
- * forward order.
- */
- Range!T opIndex() @trusted
- {
- return typeof(return)(this, this.data, this.data + length);
- }
-
- /// Ditto.
- Range!(const T) opIndex() const @trusted
- {
- return typeof(return)(this, this.data, this.data + length);
- }
-
- ///
- unittest
- {
- const v1 = Vector!int([6, 123, 34, 5]);
-
- assert(v1[0] == 6);
- assert(v1[1] == 123);
- assert(v1[2] == 34);
- assert(v1[3] == 5);
- static assert(is(typeof(v1[0]) == const(int)));
- static assert(is(typeof(v1[])));
- }
-
- /**
- * Comparison for equality.
- *
- * Params:
- * that = The vector to compare with.
- *
- * Returns: $(D_KEYWORD true) if the vectors are equal, $(D_KEYWORD false)
- * otherwise.
- */
- bool opEquals()(auto ref typeof(this) that) @trusted
- {
- return equal(this.data[0 .. length], that.data[0 .. that.length]);
- }
-
- /// Ditto.
- bool opEquals()(const auto ref typeof(this) that) const @trusted
- {
- return equal(this.data[0 .. length], that.data[0 .. that.length]);
- }
-
- /// Ditto.
- bool opEquals(Range!T that)
- {
- return equal(opIndex(), that);
- }
-
- /**
- * Comparison for equality.
- *
- * Params:
- * R = Right hand side type.
- * that = Right hand side vector range.
- *
- * Returns: $(D_KEYWORD true) if the vector and the range are equal,
- * $(D_KEYWORD false) otherwise.
- */
- bool opEquals(R)(Range!R that) const
- if (is(Unqual!R == T))
- {
- return equal(opIndex(), that);
- }
-
- ///
- unittest
- {
- Vector!int v1, v2;
- assert(v1 == v2);
-
- v1.length = 1;
- v2.length = 2;
- assert(v1 != v2);
-
- v1.length = 2;
- v1[0] = v2[0] = 2;
- v1[1] = 3;
- v2[1] = 4;
- assert(v1 != v2);
-
- v2[1] = 3;
- assert(v1 == v2);
- }
-
- /**
- * Returns: The first element.
- *
- * Precondition: $(D_INLINECODE !empty).
- */
- @property ref inout(T) front() inout
- in
- {
- assert(!empty);
- }
- body
- {
- return *this.data;
- }
-
- ///
- @safe unittest
- {
- auto v = Vector!int([5]);
-
- assert(v.front == 5);
-
- v.length = 2;
- v[1] = 15;
- assert(v.front == 5);
- }
-
- /**
- * Returns: The last element.
- *
- * Precondition: $(D_INLINECODE !empty).
- */
- @property ref inout(T) back() inout @trusted
- in
- {
- assert(!empty);
- }
- body
- {
- return *(this.data + length - 1);
- }
-
- ///
- unittest
- {
- auto v = Vector!int([5]);
-
- assert(v.back == 5);
-
- v.length = 2;
- v[1] = 15;
- assert(v.back == 15);
- }
-
- /**
- * Params:
- * i = Slice start.
- * j = Slice end.
- *
- * Returns: A range that iterates over elements of the container from
- * index $(D_PARAM i) up to (excluding) index $(D_PARAM j).
- *
- * Precondition: $(D_INLINECODE i <= j && j <= length).
- */
- Range!T opSlice(const size_t i, const size_t j) @trusted
- in
- {
- assert(i <= j);
- assert(j <= length);
- }
- body
- {
- return typeof(return)(this, this.data + i, this.data + j);
- }
-
- /// Ditto.
- Range!(const T) opSlice(const size_t i, const size_t j) const @trusted
- in
- {
- assert(i <= j);
- assert(j <= length);
- }
- body
- {
- return typeof(return)(this, this.data + i, this.data + j);
- }
-
- ///
- unittest
- {
- Vector!int v;
- auto r = v[];
- assert(r.length == 0);
- assert(r.empty);
- }
-
- ///
- unittest
- {
- auto v = Vector!int([1, 2, 3]);
- auto r = v[];
-
- assert(r.front == 1);
- assert(r.back == 3);
-
- r.popFront();
- assert(r.front == 2);
-
- r.popBack();
- assert(r.back == 2);
-
- assert(r.length == 1);
- }
-
- ///
- unittest
- {
- auto v = Vector!int([1, 2, 3, 4]);
- auto r = v[1 .. 4];
- assert(r.length == 3);
- assert(r[0] == 2);
- assert(r[1] == 3);
- assert(r[2] == 4);
-
- r = v[0 .. 0];
- assert(r.length == 0);
-
- r = v[4 .. 4];
- assert(r.length == 0);
- }
-
- /**
- * Slicing assignment.
- *
- * Params:
- * R = Type of the assigned slice or length of the static array should
- * be assigned.
- * value = New value (single value, range or static array).
- * i = Slice start.
- * j = Slice end.
- *
- * Returns: Slice with the assigned part of the vector.
- *
- * Precondition: $(D_INLINECODE i <= j && j <= length
- * && value.length == j - i)
- */
- Range!T opSliceAssign(size_t R)(T[R] value, const size_t i, const size_t j)
- @trusted
- in
- {
- assert(i <= j);
- assert(j <= length);
- }
- body
- {
- copy(value[], this.data[i .. j]);
- return opSlice(i, j);
- }
-
- /// Ditto.
- Range!T opSliceAssign(R : T)(auto ref R value, const size_t i, const size_t j)
- @trusted
- in
- {
- assert(i <= j);
- assert(j <= length);
- }
- body
- {
- fill(this.data[i .. j], value);
- return opSlice(i, j);
- }
-
- /// Ditto.
- Range!T opSliceAssign(Range!T value, const size_t i, const size_t j) @trusted
- in
- {
- assert(i <= j);
- assert(j <= length);
- assert(j - i == value.length);
- }
- body
- {
- copy(value, this.data[i .. j]);
- return opSlice(i, j);
- }
-
- ///
- @nogc @safe unittest
- {
- auto v1 = Vector!int([3, 3, 3]);
- auto v2 = Vector!int([1, 2]);
-
- v1[0 .. 2] = 286;
- assert(v1[0] == 286);
- assert(v1[1] == 286);
- assert(v1[2] == 3);
-
- v2[0 .. $] = v1[1 .. 3];
- assert(v2[0] == 286);
- assert(v2[1] == 3);
-
- v1[0 .. 2] = [5, 8];
- assert(v1[0] == 5);
- assert(v1[1] == 8);
- assert(v1[2] == 3);
- }
-
- /**
- * Returns an array used internally by the vector to store its owned elements.
- * The length of the returned array may differ from the size of the allocated
- * memory for the vector: the array contains only initialized elements, but
- * not the reserved memory.
- *
- * Returns: The array with elements of this vector.
- */
- inout(T[]) get() inout @trusted
- {
- return this.data[0 .. length];
- }
-
- ///
- unittest
- {
- auto v = Vector!int([1, 2, 4]);
- auto data = v.get();
-
- assert(data[0] == 1);
- assert(data[1] == 2);
- assert(data[2] == 4);
- assert(data.length == 3);
-
- data = v[1 .. 2].get();
- assert(data[0] == 2);
- assert(data.length == 1);
- }
-
- /**
- * Assigns another vector.
- *
- * If $(D_PARAM that) is passed by value, it won't be copied, but moved.
- * This vector will take the ownership over $(D_PARAM that)'s storage and
- * the allocator.
- *
- * If $(D_PARAM that) is passed by reference, it will be copied.
- *
- * Params:
- * R = Content type.
- * that = The value should be assigned.
- *
- * Returns: $(D_KEYWORD this).
- */
- ref typeof(this) opAssign(R)(ref R that)
- if (is(Unqual!R == Vector))
- {
- return this = that[];
- }
-
- /// Ditto.
- ref typeof(this) opAssign(R)(R that) @trusted
- if (is(R == Vector))
- {
- swap(this.data, that.data);
- swap(this.length_, that.length_);
- swap(this.capacity_, that.capacity_);
- swap(this.allocator_, that.allocator_);
- return this;
- }
-
- /**
- * Assigns a range to the vector.
- *
- * Params:
- * R = Content type.
- * that = The value should be assigned.
- *
- * Returns: $(D_KEYWORD this).
- */
- ref typeof(this) opAssign(R)(R that)
- if (!isInfinite!R
- && isInputRange!R
- && isImplicitlyConvertible!(ElementType!R, T))
- {
- length = 0;
- insertBack(that);
- return this;
- }
-
- ///
- @safe @nogc unittest
- {
- auto v1 = const Vector!int([5, 15, 8]);
- Vector!int v2;
- v2 = v1;
- assert(v1 == v2);
- }
-
- ///
- @safe @nogc unittest
- {
- auto v1 = const Vector!int([5, 15, 8]);
- Vector!int v2;
- v2 = v1[0 .. 2];
- assert(equal(v1[0 .. 2], v2[]));
- }
-
- // Move assignment.
- private @safe @nogc unittest
- {
- Vector!int v1;
- v1 = Vector!int([5, 15, 8]);
- }
-
- /**
- * Assigns a static array.
- *
- * Params:
- * R = Static array size.
- * that = Values to initialize the vector with.
- *
- * Returns: $(D_KEYWORD this).
- */
- ref typeof(this) opAssign(size_t R)(T[R] that)
- {
- return opAssign!(T[])(that[]);
- }
-
- ///
- @safe @nogc unittest
- {
- auto v1 = Vector!int([5, 15, 8]);
- Vector!int v2;
-
- v2 = [5, 15, 8];
- assert(v1 == v2);
- }
-
- mixin DefaultAllocator;
-}
-
-///
-unittest
-{
- auto v = Vector!int([5, 15, 8]);
-
- assert(v.front == 5);
- assert(v[1] == 15);
- assert(v.back == 8);
-
- auto r = v[];
- r[0] = 7;
- assert(r.front == 7);
- assert(r.front == v.front);
-}
-
-@nogc unittest
-{
- const v1 = Vector!int();
- const Vector!int v2;
- const v3 = Vector!int([1, 5, 8]);
- static assert(is(PointerTarget!(typeof(v3.data)) == const(int)));
-}
-
-@nogc unittest
-{
- // Test that const vectors return usable ranges.
- auto v = const Vector!int([1, 2, 4]);
- auto r1 = v[];
-
- assert(r1.back == 4);
- r1.popBack();
- assert(r1.back == 2);
- r1.popBack();
- assert(r1.back == 1);
- r1.popBack();
- assert(r1.length == 0);
-
- static assert(!is(typeof(r1[0] = 5)));
- static assert(!is(typeof(v[0] = 5)));
-
- const r2 = r1[];
- static assert(is(typeof(r2[])));
-}
-
-@nogc unittest
-{
- Vector!int v1;
- const Vector!int v2;
-
- auto r1 = v1[];
- auto r2 = v1[];
-
- assert(r1.length == 0);
- assert(r2.empty);
- assert(r1 == r2);
-
- v1.insertBack([1, 2, 4]);
- assert(v1[] == v1);
- assert(v2[] == v2);
- assert(v2[] != v1);
- assert(v1[] != v2);
- assert(v1[].equal(v1[]));
- assert(v2[].equal(v2[]));
- assert(!v1[].equal(v2[]));
-}
-
-@nogc unittest
-{
- struct MutableEqualsStruct
- {
- int opEquals(typeof(this) that) @nogc
- {
- return true;
- }
- }
- struct ConstEqualsStruct
- {
- int opEquals(const typeof(this) that) const @nogc
- {
- return true;
- }
- }
- auto v1 = Vector!ConstEqualsStruct();
- auto v2 = Vector!ConstEqualsStruct();
- assert(v1 == v2);
- assert(v1[] == v2);
- assert(v1 == v2[]);
- assert(v1[].equal(v2[]));
-
- auto v3 = const Vector!ConstEqualsStruct();
- auto v4 = const Vector!ConstEqualsStruct();
- assert(v3 == v4);
- assert(v3[] == v4);
- assert(v3 == v4[]);
- assert(v3[].equal(v4[]));
-
- auto v7 = Vector!MutableEqualsStruct(1, MutableEqualsStruct());
- auto v8 = Vector!MutableEqualsStruct(1, MutableEqualsStruct());
- assert(v7 == v8);
- assert(v7[] == v8);
- assert(v7 == v8[]);
- assert(v7[].equal(v8[]));
-}
-
-@nogc unittest
-{
- struct SWithDtor
- {
- ~this() @nogc
- {
- }
- }
- auto v = Vector!SWithDtor(); // Destructor can destroy empty vectors.
-}
-
-private unittest
-{
- class A
- {
- }
- A a1, a2;
- auto v1 = Vector!A([a1, a2]);
-}
-
-private @safe @nogc unittest
-{
- auto v = Vector!int([5, 15, 8]);
- {
- size_t i;
-
- foreach (e; v)
- {
- assert(i != 0 || e == 5);
- assert(i != 1 || e == 15);
- assert(i != 2 || e == 8);
- ++i;
- }
- assert(i == 3);
- }
- {
- size_t i = 3;
-
- foreach_reverse (e; v)
- {
- --i;
- assert(i != 2 || e == 8);
- assert(i != 1 || e == 15);
- assert(i != 0 || e == 5);
- }
- assert(i == 0);
- }
-}
+public import tanya.container.array;
diff --git a/source/tanya/math/mp.d b/source/tanya/math/mp.d
index 057a10d..d22b8be 100644
--- a/source/tanya/math/mp.d
+++ b/source/tanya/math/mp.d
@@ -16,7 +16,7 @@ import std.algorithm;
import std.ascii;
import std.range;
import std.traits;
-import tanya.container.vector;
+import tanya.container.array;
import tanya.memory;
/**
@@ -1444,26 +1444,26 @@ struct Integer
/**
* Returns: Two's complement representation of the integer.
*/
- Vector!ubyte toVector() const nothrow @safe @nogc
- out (vector)
+ Array!ubyte toArray() const nothrow @safe @nogc
+ out (array)
{
- assert(vector.length == length);
+ assert(array.length == length);
}
body
{
- Vector!ubyte vector;
+ Array!ubyte array;
if (this.size == 0)
{
- return vector;
+ return array;
}
const bc = countBits();
const remainingBits = bc & 0x07;
- vector.reserve(bc / 8);
+ array.reserve(bc / 8);
if (remainingBits == 0)
{
- vector.insertBack(ubyte.init);
+ array.insertBack(ubyte.init);
}
@@ -1486,14 +1486,14 @@ struct Integer
do
{
- vector.insertBack(cast(ubyte) (tmp.rep[0] & 0xff));
+ array.insertBack(cast(ubyte) (tmp.rep[0] & 0xff));
tmp >>= 8;
}
while (tmp != 0);
- vector[].reverse();
+ array[].reverse();
- return vector;
+ return array;
}
///
@@ -1503,15 +1503,15 @@ struct Integer
auto integer = Integer(0x66778899aabbddee);
ubyte[8] expected = [ 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xdd, 0xee ];
- auto vector = integer.toVector();
- assert(equal(vector[], expected[]));
+ auto array = integer.toArray();
+ assert(equal(array[], expected[]));
}
{
auto integer = Integer(0x03);
ubyte[1] expected = [ 0x03 ];
- auto vector = integer.toVector();
- assert(equal(vector[], expected[]));
+ auto array = integer.toArray();
+ assert(equal(array[], expected[]));
}
{
ubyte[63] expected = [
@@ -1526,8 +1526,8 @@ struct Integer
];
auto integer = Integer(Sign.positive, expected[]);
- auto vector = integer.toVector();
- assert(equal(vector[], expected[]));
+ auto array = integer.toArray();
+ assert(equal(array[], expected[]));
}
{
ubyte[14] expected = [
@@ -1536,8 +1536,8 @@ struct Integer
];
auto integer = Integer(Sign.positive, expected[]);
- auto vector = integer.toVector();
- assert(equal(vector[], expected[]));
+ auto array = integer.toArray();
+ assert(equal(array[], expected[]));
}
}