Compare commits
41 Commits
Author | SHA1 | Date | |
---|---|---|---|
e77a499fa2 | |||
54bcec216e | |||
fbbdb36853 | |||
b795267e75 | |||
81cbb96d45 | |||
467335460e | |||
dc3b083097 | |||
16c5fa12df | |||
9bf8754711 | |||
760cea163d | |||
03c40ecace | |||
9c70e9a058 | |||
5ae20512af | |||
d30de300d6 | |||
464a0fecbb | |||
84d6e207c5 | |||
af942116e4 | |||
7ee4af9e79 | |||
9876d9245c | |||
bd2b88f16e | |||
2946fd7f81 | |||
2cda82eeea | |||
e9f70853c6 | |||
4aaa71a7d0 | |||
cbc68c2c43 | |||
048ddf21ff | |||
fd02c411e1 | |||
b69d737845 | |||
904451ccaa | |||
c1864cf473 | |||
8db1851c5c | |||
12de700706 | |||
78a8afdf75 | |||
3c996d7c57 | |||
2a68048fc1 | |||
907f7a4e61 | |||
670328c047 | |||
7fe69ccc5c | |||
26c3532e28 | |||
75ce854192 | |||
9e16d84f9e |
2
.gitignore
vendored
2
.gitignore
vendored
@ -4,6 +4,8 @@
|
||||
|
||||
# D
|
||||
.dub
|
||||
dub.selections.json
|
||||
|
||||
__test__*__
|
||||
__test__*__.core
|
||||
/tanya-test-*
|
||||
|
@ -7,9 +7,10 @@ os:
|
||||
language: d
|
||||
|
||||
d:
|
||||
- dmd-2.077.0
|
||||
- dmd-2.079.0
|
||||
- dmd-2.078.3
|
||||
- dmd-2.077.1
|
||||
- dmd-2.076.1
|
||||
- dmd-2.075.1
|
||||
|
||||
env:
|
||||
matrix:
|
||||
@ -22,7 +23,7 @@ addons:
|
||||
- gcc-multilib
|
||||
|
||||
before_script:
|
||||
- if [ "$PS1" = '(dmd-2.077.0)' ]; then
|
||||
- if [ "$PS1" = '(dmd-2.079.0)' ]; then
|
||||
export UNITTEST="unittest-cov";
|
||||
fi
|
||||
|
||||
|
16
README.md
16
README.md
@ -12,8 +12,8 @@ Tanya is a general purpose library for D programming language.
|
||||
Its aim is to simplify the manual memory management in D and to provide a
|
||||
guarantee with @nogc attribute that there are no hidden allocations on the
|
||||
Garbage Collector heap. Everything in the library is usable in @nogc code.
|
||||
Tanya extends Phobos functionality and provides alternative implementations for
|
||||
data structures and utilities that depend on the Garbage Collector in Phobos.
|
||||
Tanya provides data structures and utilities to facilitate painless systems
|
||||
programming in D.
|
||||
|
||||
* [API Documentation](https://docs.caraus.io/tanya)
|
||||
* [Contribution guidelines](CONTRIBUTING.md)
|
||||
@ -149,9 +149,10 @@ There are more containers in the `tanya.container` package.
|
||||
|
||||
| DMD | GCC |
|
||||
|:-------:|:--------------:|
|
||||
| 2.077.0 | *gdc-5* branch |
|
||||
| 2.079.0 | *master* |
|
||||
| 2.078.3 | |
|
||||
| 2.077.1 | |
|
||||
| 2.076.1 | |
|
||||
| 2.075.1 | |
|
||||
|
||||
### Current status
|
||||
|
||||
@ -161,17 +162,14 @@ Following modules are under development:
|
||||
|----------|:---------:|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| BitArray | bitvector | [](https://travis-ci.org/caraus-ecms/tanya) [](https://ci.appveyor.com/project/belka-ew/tanya/branch/bitvector) |
|
||||
| TLS | crypto | [](https://travis-ci.org/caraus-ecms/tanya) [](https://ci.appveyor.com/project/belka-ew/tanya/branch/crypto) |
|
||||
| File IO | io | [](https://travis-ci.org/caraus-ecms/tanya) [](https://ci.appveyor.com/project/belka-ew/tanya/branch/io) |
|
||||
|
||||
### Release management
|
||||
|
||||
3-week release cycle.
|
||||
|
||||
Deprecated features are removed after one release (in approximately 6 weeks after deprecating).
|
||||
Deprecated features are removed after one release that includes these deprecations.
|
||||
|
||||
## Further characteristics
|
||||
|
||||
* Tanya is a native D library without any external dependencies.
|
||||
* Tanya is a native D library
|
||||
|
||||
* Tanya is cross-platform. The development happens on a 64-bit Linux, but it
|
||||
is being tested on Windows and FreeBSD as well.
|
||||
|
22
appveyor.yml
22
appveyor.yml
@ -4,10 +4,22 @@ os: Visual Studio 2015
|
||||
environment:
|
||||
matrix:
|
||||
- DC: dmd
|
||||
DVersion: 2.077.0
|
||||
DVersion: 2.079.0
|
||||
arch: x64
|
||||
- DC: dmd
|
||||
DVersion: 2.077.0
|
||||
DVersion: 2.079.0
|
||||
arch: x86
|
||||
- DC: dmd
|
||||
DVersion: 2.078.3
|
||||
arch: x64
|
||||
- DC: dmd
|
||||
DVersion: 2.078.3
|
||||
arch: x86
|
||||
- DC: dmd
|
||||
DVersion: 2.077.1
|
||||
arch: x64
|
||||
- DC: dmd
|
||||
DVersion: 2.077.1
|
||||
arch: x86
|
||||
- DC: dmd
|
||||
DVersion: 2.076.1
|
||||
@ -15,12 +27,6 @@ environment:
|
||||
- DC: dmd
|
||||
DVersion: 2.076.1
|
||||
arch: x86
|
||||
- DC: dmd
|
||||
DVersion: 2.075.1
|
||||
arch: x64
|
||||
- DC: dmd
|
||||
DVersion: 2.075.1
|
||||
arch: x86
|
||||
|
||||
skip_tags: true
|
||||
|
||||
|
@ -2,10 +2,10 @@
|
||||
|
||||
|
||||
// logl.
|
||||
.globl _D5tanya4math8nbtheory2lnFNaNbNiNfeZe
|
||||
.type _D5tanya4math8nbtheory2lnFNaNbNiNfeZe, @function
|
||||
.globl _D5tanya4math8nbtheory9__T2lnTeZ2lnFNaNbNiNfeZe
|
||||
.type _D5tanya4math8nbtheory9__T2lnTeZ2lnFNaNbNiNfeZe, @function
|
||||
|
||||
_D5tanya4math8nbtheory2lnFNaNbNiNfeZe:
|
||||
_D5tanya4math8nbtheory9__T2lnTeZ2lnFNaNbNiNfeZe:
|
||||
fldln2 // Put lb(e) onto the FPU stack
|
||||
fldt 8(%rsp) // Put the argument onto the FPU stack
|
||||
fyl2x // %st1 * lb(%st0)
|
||||
@ -13,10 +13,10 @@ _D5tanya4math8nbtheory2lnFNaNbNiNfeZe:
|
||||
|
||||
|
||||
// log.
|
||||
.globl _D5tanya4math8nbtheory2lnFNaNbNiNfdZd
|
||||
.type _D5tanya4math8nbtheory2lnFNaNbNiNfdZd, @function
|
||||
.globl _D5tanya4math8nbtheory9__T2lnTdZ2lnFNaNbNiNfdZd
|
||||
.type _D5tanya4math8nbtheory9__T2lnTdZ2lnFNaNbNiNfdZd, @function
|
||||
|
||||
_D5tanya4math8nbtheory2lnFNaNbNiNfdZd:
|
||||
_D5tanya4math8nbtheory9__T2lnTdZ2lnFNaNbNiNfdZd:
|
||||
movsd %xmm0, -8(%rsp) // Put the argument onto the stack
|
||||
|
||||
fldln2 // Put lb(e) onto the FPU stack
|
||||
@ -31,10 +31,10 @@ _D5tanya4math8nbtheory2lnFNaNbNiNfdZd:
|
||||
|
||||
|
||||
// logf.
|
||||
.globl _D5tanya4math8nbtheory2lnFNaNbNiNffZf
|
||||
.type _D5tanya4math8nbtheory2lnFNaNbNiNffZf, @function
|
||||
.globl _D5tanya4math8nbtheory9__T2lnTfZ2lnFNaNbNiNffZf
|
||||
.type _D5tanya4math8nbtheory9__T2lnTfZ2lnFNaNbNiNffZf, @function
|
||||
|
||||
_D5tanya4math8nbtheory2lnFNaNbNiNffZf:
|
||||
_D5tanya4math8nbtheory9__T2lnTfZ2lnFNaNbNiNffZf:
|
||||
movss %xmm0, -4(%rsp) // Put the argument onto the stack
|
||||
|
||||
fldln2 // Put lb(e) onto the FPU stack
|
||||
|
@ -37,6 +37,11 @@ _D5tanya6memory2op10fillMemoryFNaNbNiAvmZv:
|
||||
|
||||
mov %rsi, %r8
|
||||
|
||||
// If the length is less than the number of misaligned bytes,
|
||||
// write one byte at a time and exit
|
||||
cmp %rax, %rcx
|
||||
jg aligned_1
|
||||
|
||||
naligned:
|
||||
mov %dl, (%r8) // Write a byte
|
||||
|
||||
|
14
dub.json
14
dub.json
@ -9,10 +9,19 @@
|
||||
|
||||
"targetType": "library",
|
||||
|
||||
"dependencies-linux": {
|
||||
"mir-linux-kernel": "~>1.0.0",
|
||||
},
|
||||
|
||||
"configurations": [
|
||||
{
|
||||
"name": "library",
|
||||
"targetType": "library",
|
||||
"targetType": "staticLibrary",
|
||||
"versions": ["TanyaPhobos"]
|
||||
},
|
||||
{
|
||||
"name": "dynamic",
|
||||
"targetType": "dynamicLibrary",
|
||||
"versions": ["TanyaPhobos"]
|
||||
},
|
||||
{
|
||||
@ -23,5 +32,6 @@
|
||||
"lflags": ["arch/tanya.a"],
|
||||
"versions": ["TanyaNative"]
|
||||
}
|
||||
]
|
||||
],
|
||||
"libs-windows": ["advapi32"]
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ in
|
||||
{
|
||||
assert(&source !is &target, "Source and target must be different");
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
static if (is(T == struct) || isStaticArray!T)
|
||||
{
|
||||
|
@ -31,7 +31,6 @@ import tanya.async.transport;
|
||||
import tanya.async.watcher;
|
||||
import tanya.container.array;
|
||||
import tanya.memory;
|
||||
import tanya.memory.mmappool;
|
||||
import tanya.network.socket;
|
||||
|
||||
extern (C) nothrow @nogc
|
||||
@ -56,7 +55,7 @@ final class EpollLoop : SelectorLoop
|
||||
throw defaultAllocator.make!BadLoopException("epoll initialization failed");
|
||||
}
|
||||
super();
|
||||
events = Array!epoll_event(maxEvents, MmapPool.instance);
|
||||
events = Array!epoll_event(maxEvents);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -27,7 +27,6 @@ import tanya.async.transport;
|
||||
import tanya.async.watcher;
|
||||
import tanya.container.buffer;
|
||||
import tanya.memory;
|
||||
import tanya.memory.mmappool;
|
||||
import tanya.network.socket;
|
||||
import tanya.sys.windows.winbase;
|
||||
|
||||
@ -57,8 +56,8 @@ final class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
|
||||
this(OverlappedConnectedSocket socket) @nogc
|
||||
{
|
||||
super(socket);
|
||||
output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance);
|
||||
input = WriteBuffer!ubyte(8192, MmapPool.instance);
|
||||
output = ReadBuffer!ubyte(8192, 1024);
|
||||
input = WriteBuffer!ubyte(8192);
|
||||
active = true;
|
||||
}
|
||||
|
||||
@ -72,7 +71,7 @@ final class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
|
||||
{
|
||||
assert(socket !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return cast(OverlappedConnectedSocket) socket_;
|
||||
}
|
||||
@ -117,8 +116,7 @@ final class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
|
||||
/**
|
||||
* Switches the protocol.
|
||||
*
|
||||
* The protocol is deallocated by the event loop, it should currently be
|
||||
* allocated with $(D_PSYMBOL MmapPool).
|
||||
* The protocol is deallocated by the event loop.
|
||||
*
|
||||
* Params:
|
||||
* protocol = Application protocol.
|
||||
@ -130,7 +128,7 @@ final class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
|
||||
{
|
||||
assert(protocol !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
protocol_ = protocol;
|
||||
}
|
||||
@ -150,20 +148,20 @@ final class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
|
||||
SocketState overlapped;
|
||||
try
|
||||
{
|
||||
overlapped = MmapPool.instance.make!SocketState;
|
||||
overlapped = defaultAllocator.make!SocketState;
|
||||
socket.beginSend(input[], overlapped);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
MmapPool.instance.dispose(overlapped);
|
||||
MmapPool.instance.dispose(e);
|
||||
defaultAllocator.dispose(overlapped);
|
||||
defaultAllocator.dispose(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
protocol.disconnected(exception);
|
||||
MmapPool.instance.dispose(protocol_);
|
||||
defaultAllocator.dispose(protocol_);
|
||||
defaultAllocator.dispose(exception);
|
||||
active = false;
|
||||
}
|
||||
@ -221,12 +219,12 @@ final class IOCPLoop : Loop
|
||||
|
||||
try
|
||||
{
|
||||
overlapped = MmapPool.instance.make!SocketState;
|
||||
overlapped = defaultAllocator.make!SocketState;
|
||||
socket.beginAccept(overlapped);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
MmapPool.instance.dispose(overlapped);
|
||||
defaultAllocator.dispose(overlapped);
|
||||
defaultAllocator.dispose(e);
|
||||
return false;
|
||||
}
|
||||
@ -250,12 +248,12 @@ final class IOCPLoop : Loop
|
||||
{
|
||||
try
|
||||
{
|
||||
overlapped = MmapPool.instance.make!SocketState;
|
||||
overlapped = defaultAllocator.make!SocketState;
|
||||
transport.socket.beginReceive(transport.output[], overlapped);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
MmapPool.instance.dispose(overlapped);
|
||||
defaultAllocator.dispose(overlapped);
|
||||
defaultAllocator.dispose(e);
|
||||
return false;
|
||||
}
|
||||
@ -270,7 +268,7 @@ final class IOCPLoop : Loop
|
||||
{
|
||||
assert(transport !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
transport.socket.shutdown();
|
||||
defaultAllocator.dispose(transport.socket);
|
||||
@ -302,7 +300,7 @@ final class IOCPLoop : Loop
|
||||
assert(overlapped !is null);
|
||||
scope (failure)
|
||||
{
|
||||
MmapPool.instance.dispose(overlapped);
|
||||
defaultAllocator.dispose(overlapped);
|
||||
}
|
||||
|
||||
switch (overlapped.event)
|
||||
@ -315,7 +313,7 @@ final class IOCPLoop : Loop
|
||||
assert(listener !is null);
|
||||
|
||||
auto socket = listener.endAccept(overlapped);
|
||||
auto transport = MmapPool.instance.make!StreamTransport(socket);
|
||||
auto transport = defaultAllocator.make!StreamTransport(socket);
|
||||
|
||||
connection.incoming.enqueue(transport);
|
||||
|
||||
@ -330,8 +328,8 @@ final class IOCPLoop : Loop
|
||||
|
||||
if (!transport.active)
|
||||
{
|
||||
MmapPool.instance.dispose(transport);
|
||||
MmapPool.instance.dispose(overlapped);
|
||||
defaultAllocator.dispose(transport);
|
||||
defaultAllocator.dispose(overlapped);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,6 @@ import tanya.async.transport;
|
||||
import tanya.async.watcher;
|
||||
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
|
||||
@ -146,8 +145,8 @@ final class KqueueLoop : SelectorLoop
|
||||
throw make!BadLoopException(defaultAllocator,
|
||||
"kqueue initialization failed");
|
||||
}
|
||||
events = Array!kevent_t(64, MmapPool.instance);
|
||||
changes = Array!kevent_t(64, MmapPool.instance);
|
||||
events = Array!kevent_t(64);
|
||||
changes = Array!kevent_t(64);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,7 +26,6 @@ import tanya.async.watcher;
|
||||
import tanya.container.array;
|
||||
import tanya.container.buffer;
|
||||
import tanya.memory;
|
||||
import tanya.memory.mmappool;
|
||||
import tanya.network.socket;
|
||||
|
||||
/**
|
||||
@ -61,12 +60,12 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
|
||||
{
|
||||
assert(loop !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
super(socket);
|
||||
this.loop = loop;
|
||||
output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance);
|
||||
input = WriteBuffer!ubyte(8192, MmapPool.instance);
|
||||
output = ReadBuffer!ubyte(8192, 1024);
|
||||
input = WriteBuffer!ubyte(8192);
|
||||
active = true;
|
||||
}
|
||||
|
||||
@ -80,7 +79,7 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
|
||||
{
|
||||
assert(socket !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return cast(ConnectedSocket) socket_;
|
||||
}
|
||||
@ -91,7 +90,7 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
|
||||
{
|
||||
assert(socket !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
socket_ = socket;
|
||||
}
|
||||
@ -107,8 +106,7 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
|
||||
/**
|
||||
* Switches the protocol.
|
||||
*
|
||||
* The protocol is deallocated by the event loop, it should currently be
|
||||
* allocated with $(D_PSYMBOL MmapPool).
|
||||
* The protocol is deallocated by the event loop.
|
||||
*
|
||||
* Params:
|
||||
* protocol = Application protocol.
|
||||
@ -120,7 +118,7 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
|
||||
{
|
||||
assert(protocol !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
protocol_ = protocol;
|
||||
}
|
||||
@ -163,7 +161,7 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
|
||||
else
|
||||
{
|
||||
protocol.disconnected(exception);
|
||||
MmapPool.instance.dispose(protocol_);
|
||||
defaultAllocator.dispose(protocol_);
|
||||
defaultAllocator.dispose(exception);
|
||||
active = false;
|
||||
}
|
||||
@ -220,7 +218,7 @@ abstract class SelectorLoop : Loop
|
||||
this() @nogc
|
||||
{
|
||||
super();
|
||||
connections = Array!SocketWatcher(maxEvents, MmapPool.instance);
|
||||
connections = Array!SocketWatcher(maxEvents);
|
||||
}
|
||||
|
||||
~this() @nogc
|
||||
@ -231,7 +229,7 @@ abstract class SelectorLoop : Loop
|
||||
// created by the user and should be freed by himself.
|
||||
if (cast(StreamTransport) connection !is null)
|
||||
{
|
||||
MmapPool.instance.dispose(connection);
|
||||
defaultAllocator.dispose(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -263,7 +261,7 @@ abstract class SelectorLoop : Loop
|
||||
{
|
||||
assert(transport !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
transport.socket.shutdown();
|
||||
defaultAllocator.dispose(transport.socket);
|
||||
@ -289,7 +287,7 @@ abstract class SelectorLoop : Loop
|
||||
{
|
||||
assert(transport !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
while (transport.input.length && transport.writeReady)
|
||||
{
|
||||
@ -356,7 +354,7 @@ abstract class SelectorLoop : Loop
|
||||
{
|
||||
assert(connection !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
@ -387,7 +385,7 @@ abstract class SelectorLoop : Loop
|
||||
}
|
||||
if (transport is null)
|
||||
{
|
||||
transport = MmapPool.instance.make!StreamTransport(this, client);
|
||||
transport = defaultAllocator.make!StreamTransport(this, client);
|
||||
connections[client.handle] = transport;
|
||||
}
|
||||
else
|
||||
|
@ -78,7 +78,6 @@ import tanya.async.watcher;
|
||||
import tanya.container.buffer;
|
||||
import tanya.container.queue;
|
||||
import tanya.memory;
|
||||
import tanya.memory.mmappool;
|
||||
import tanya.network.socket;
|
||||
|
||||
version (DisableBackends)
|
||||
@ -189,7 +188,7 @@ abstract class Loop
|
||||
*/
|
||||
this() @nogc
|
||||
{
|
||||
pendings = Queue!Watcher(MmapPool.instance);
|
||||
pendings = Queue!Watcher();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -199,7 +198,7 @@ abstract class Loop
|
||||
{
|
||||
foreach (w; pendings)
|
||||
{
|
||||
MmapPool.instance.dispose(w);
|
||||
defaultAllocator.dispose(w);
|
||||
}
|
||||
}
|
||||
|
||||
@ -326,7 +325,7 @@ abstract class Loop
|
||||
assert(blockTime <= 1.dur!"hours", "Too long to wait.");
|
||||
assert(!blockTime.isNegative);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
blockTime_ = blockTime;
|
||||
}
|
||||
@ -384,16 +383,16 @@ class BadLoopException : Exception
|
||||
}
|
||||
version (Epoll)
|
||||
{
|
||||
defaultLoop_ = MmapPool.instance.make!EpollLoop;
|
||||
defaultLoop_ = defaultAllocator.make!EpollLoop;
|
||||
}
|
||||
else version (IOCP)
|
||||
{
|
||||
defaultLoop_ = MmapPool.instance.make!IOCPLoop;
|
||||
defaultLoop_ = defaultAllocator.make!IOCPLoop;
|
||||
}
|
||||
else version (Kqueue)
|
||||
{
|
||||
import tanya.async.event.kqueue;
|
||||
defaultLoop_ = MmapPool.instance.make!KqueueLoop;
|
||||
defaultLoop_ = defaultAllocator.make!KqueueLoop;
|
||||
}
|
||||
return defaultLoop_;
|
||||
}
|
||||
@ -414,7 +413,7 @@ in
|
||||
{
|
||||
assert(loop !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
defaultLoop_ = loop;
|
||||
}
|
||||
|
@ -65,8 +65,7 @@ interface DuplexTransport : ReadTransport, WriteTransport
|
||||
/**
|
||||
* Switches the protocol.
|
||||
*
|
||||
* The protocol is deallocated by the event loop, it should currently be
|
||||
* allocated with $(D_PSYMBOL MmapPool).
|
||||
* The protocol is deallocated by the event loop.
|
||||
*
|
||||
* Params:
|
||||
* protocol = Application protocol.
|
||||
|
@ -22,7 +22,6 @@ import tanya.async.transport;
|
||||
import tanya.container.buffer;
|
||||
import tanya.container.queue;
|
||||
import tanya.memory;
|
||||
import tanya.memory.mmappool;
|
||||
import tanya.network.socket;
|
||||
|
||||
/**
|
||||
@ -72,7 +71,7 @@ abstract class SocketWatcher : Watcher
|
||||
{
|
||||
assert(socket !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
socket_ = socket;
|
||||
}
|
||||
@ -103,7 +102,7 @@ class ConnectionWatcher : SocketWatcher
|
||||
this(Socket socket) @nogc
|
||||
{
|
||||
super(socket);
|
||||
incoming = Queue!DuplexTransport(MmapPool.instance);
|
||||
incoming = Queue!DuplexTransport();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -112,7 +111,7 @@ class ConnectionWatcher : SocketWatcher
|
||||
*/
|
||||
void setProtocol(P : Protocol)() @nogc
|
||||
{
|
||||
this.protocolFactory = () @nogc => cast(Protocol) MmapPool.instance.make!P;
|
||||
this.protocolFactory = () @nogc => cast(Protocol) defaultAllocator.make!P;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -123,7 +122,7 @@ class ConnectionWatcher : SocketWatcher
|
||||
{
|
||||
assert(protocolFactory !is null, "Protocol isn't set.");
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
foreach (transport; incoming)
|
||||
{
|
||||
|
@ -56,7 +56,7 @@ struct Range(A)
|
||||
assert(begin >= container.data);
|
||||
assert(end <= container.data + container.length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
this.container = &container;
|
||||
this.begin = begin;
|
||||
@ -87,7 +87,7 @@ struct Range(A)
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return *this.begin;
|
||||
}
|
||||
@ -97,7 +97,7 @@ struct Range(A)
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return *(this.end - 1);
|
||||
}
|
||||
@ -107,7 +107,7 @@ struct Range(A)
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
++this.begin;
|
||||
}
|
||||
@ -117,7 +117,7 @@ struct Range(A)
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
--this.end;
|
||||
}
|
||||
@ -127,7 +127,7 @@ struct Range(A)
|
||||
{
|
||||
assert(i < length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return *(this.begin + i);
|
||||
}
|
||||
@ -148,7 +148,7 @@ struct Range(A)
|
||||
assert(i <= j);
|
||||
assert(j <= length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return typeof(return)(*this.container, this.begin + i, this.begin + j);
|
||||
}
|
||||
@ -159,7 +159,7 @@ struct Range(A)
|
||||
assert(i <= j);
|
||||
assert(j <= length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return typeof(return)(*this.container, this.begin + i, this.begin + j);
|
||||
}
|
||||
@ -333,7 +333,7 @@ struct Array(T)
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
allocator_ = allocator;
|
||||
}
|
||||
@ -597,7 +597,7 @@ struct Array(T)
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
length = length - 1;
|
||||
}
|
||||
@ -619,7 +619,7 @@ struct Array(T)
|
||||
{
|
||||
assert(removed <= howMany);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
const toRemove = min(howMany, length);
|
||||
|
||||
@ -662,7 +662,7 @@ struct Array(T)
|
||||
assert(r.begin >= this.data);
|
||||
assert(r.end <= this.data + length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto target = r.begin;
|
||||
for (auto source = r.end; source != end; ++source, ++target)
|
||||
@ -821,7 +821,7 @@ struct Array(T)
|
||||
assert(r.begin >= this.data);
|
||||
assert(r.end <= this.data + length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
const oldLen = length;
|
||||
const offset = r.end - this.data;
|
||||
@ -838,7 +838,7 @@ struct Array(T)
|
||||
assert(r.begin >= this.data);
|
||||
assert(r.end <= this.data + length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return insertAfter!(T[])(r, el[]);
|
||||
}
|
||||
@ -852,7 +852,7 @@ struct Array(T)
|
||||
assert(r.begin >= this.data);
|
||||
assert(r.end <= this.data + length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
const oldLen = length;
|
||||
const offset = r.end - this.data;
|
||||
@ -881,7 +881,7 @@ struct Array(T)
|
||||
assert(r.begin >= this.data);
|
||||
assert(r.end <= this.data + length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return insertAfter(Range(this, this.data, r.begin), el);
|
||||
}
|
||||
@ -894,7 +894,7 @@ struct Array(T)
|
||||
assert(r.begin >= this.data);
|
||||
assert(r.end <= this.data + length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return insertBefore!(T[])(r, el[]);
|
||||
}
|
||||
@ -908,7 +908,7 @@ struct Array(T)
|
||||
assert(r.begin >= this.data);
|
||||
assert(r.end <= this.data + length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
const oldLen = length;
|
||||
const offset = r.begin - this.data;
|
||||
@ -1080,7 +1080,7 @@ struct Array(T)
|
||||
{
|
||||
assert(length > pos);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return *(this.data + pos);
|
||||
}
|
||||
@ -1185,7 +1185,7 @@ struct Array(T)
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return *this.data;
|
||||
}
|
||||
@ -1212,7 +1212,7 @@ struct Array(T)
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return *(this.data + length - 1);
|
||||
}
|
||||
@ -1245,7 +1245,7 @@ struct Array(T)
|
||||
assert(i <= j);
|
||||
assert(j <= length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return typeof(return)(this, this.data + i, this.data + j);
|
||||
}
|
||||
@ -1257,7 +1257,7 @@ struct Array(T)
|
||||
assert(i <= j);
|
||||
assert(j <= length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return typeof(return)(this, this.data + i, this.data + j);
|
||||
}
|
||||
@ -1328,7 +1328,7 @@ struct Array(T)
|
||||
assert(i <= j);
|
||||
assert(j <= length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
copy(value[], this.data[i .. j]);
|
||||
return opSlice(i, j);
|
||||
@ -1342,7 +1342,7 @@ struct Array(T)
|
||||
assert(i <= j);
|
||||
assert(j <= length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
fill(this.data[i .. j], value);
|
||||
return opSlice(i, j);
|
||||
@ -1356,7 +1356,7 @@ struct Array(T)
|
||||
assert(j <= length);
|
||||
assert(j - i == value.length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
copy(value, this.data[i .. j]);
|
||||
return opSlice(i, j);
|
||||
|
@ -27,7 +27,7 @@ version (unittest)
|
||||
{
|
||||
assert(start < end);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto numberRead = end - start;
|
||||
for (ubyte i; i < numberRead; ++i)
|
||||
@ -106,7 +106,7 @@ struct ReadBuffer(T = ubyte)
|
||||
{
|
||||
assert(allocator_ is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
allocator_ = allocator;
|
||||
}
|
||||
@ -349,7 +349,7 @@ struct WriteBuffer(T = ubyte)
|
||||
assert(size > 0);
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
blockSize = size;
|
||||
ring = size - 1;
|
||||
@ -549,7 +549,7 @@ struct WriteBuffer(T = ubyte)
|
||||
{
|
||||
assert(length <= this.length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto afterRing = ring + 1;
|
||||
auto oldStart = start;
|
||||
|
@ -64,7 +64,7 @@ struct SRange(L)
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return (*this.head).content;
|
||||
}
|
||||
@ -74,7 +74,7 @@ struct SRange(L)
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
this.head = &(*this.head).next;
|
||||
}
|
||||
@ -124,7 +124,7 @@ struct SList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l = SList!int([5, 8, 15]);
|
||||
assert(l.front == 5);
|
||||
@ -172,19 +172,13 @@ struct SList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l = SList!int(2, 3);
|
||||
assert(l.length == 2);
|
||||
assert(l.front == 3);
|
||||
}
|
||||
|
||||
private @safe @nogc unittest
|
||||
{
|
||||
auto l = SList!int(0, 0);
|
||||
assert(l.empty);
|
||||
}
|
||||
|
||||
/// ditto
|
||||
this(const size_t len, shared Allocator allocator = defaultAllocator)
|
||||
{
|
||||
@ -192,7 +186,7 @@ struct SList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l = SList!int(2);
|
||||
assert(l.length == 2);
|
||||
@ -205,7 +199,7 @@ struct SList(T)
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
this.allocator_ = allocator;
|
||||
}
|
||||
@ -263,7 +257,7 @@ struct SList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = SList!int([5, 1, 234]);
|
||||
auto l2 = SList!int(l1);
|
||||
@ -289,7 +283,7 @@ struct SList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = SList!int([5, 1, 234]);
|
||||
auto l2 = l1;
|
||||
@ -308,7 +302,7 @@ struct SList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
SList!int l = SList!int([8, 5]);
|
||||
|
||||
@ -325,7 +319,7 @@ struct SList(T)
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return this.head.content;
|
||||
}
|
||||
@ -366,7 +360,7 @@ struct SList(T)
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
SList!int l;
|
||||
int value = 5;
|
||||
@ -419,7 +413,7 @@ struct SList(T)
|
||||
alias insert = insertFront;
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
SList!int l1;
|
||||
|
||||
@ -467,13 +461,13 @@ struct SList(T)
|
||||
{
|
||||
assert(checkRangeBelonging(r));
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return moveEntry(*r.head, el);
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = SList!int([234, 5, 1]);
|
||||
auto l2 = SList!int([5, 1]);
|
||||
@ -490,7 +484,7 @@ struct SList(T)
|
||||
{
|
||||
assert(checkRangeBelonging(r));
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
size_t inserted;
|
||||
foreach (e; el)
|
||||
@ -502,7 +496,7 @@ struct SList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = SList!int([5, 234, 30, 1]);
|
||||
auto l2 = SList!int([5, 1]);
|
||||
@ -519,14 +513,14 @@ struct SList(T)
|
||||
{
|
||||
assert(checkRangeBelonging(r));
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
*r.head = allocator.make!Entry(el, *r.head);
|
||||
return 1;
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = SList!int([234, 5, 1]);
|
||||
auto l2 = SList!int([5, 1]);
|
||||
@ -553,7 +547,7 @@ struct SList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = SList!int([5, 234, 30, 1]);
|
||||
auto l2 = SList!int([5, 1]);
|
||||
@ -572,7 +566,7 @@ struct SList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
SList!int l;
|
||||
|
||||
@ -600,7 +594,7 @@ struct SList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
SList!int l1, l2;
|
||||
|
||||
@ -641,16 +635,16 @@ struct SList(T)
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto n = this.head.next;
|
||||
|
||||
this.allocator.dispose(this.head);
|
||||
allocator.dispose(this.head);
|
||||
this.head = n;
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
SList!int l;
|
||||
|
||||
@ -680,7 +674,7 @@ struct SList(T)
|
||||
{
|
||||
assert(removed <= howMany);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
size_t i;
|
||||
for (; i < howMany && !empty; ++i)
|
||||
@ -691,7 +685,7 @@ struct SList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
SList!int l = SList!int([8, 5, 4]);
|
||||
|
||||
@ -716,7 +710,7 @@ struct SList(T)
|
||||
{
|
||||
assert(checkRangeBelonging(r));
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto outOfScopeList = typeof(this)(allocator);
|
||||
outOfScopeList.head = *r.head;
|
||||
@ -726,7 +720,7 @@ struct SList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = SList!int([5, 234, 30, 1]);
|
||||
auto l2 = SList!int([5]);
|
||||
@ -784,7 +778,7 @@ struct SList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
{
|
||||
auto l1 = SList!int([5, 4, 9]);
|
||||
@ -834,7 +828,7 @@ struct SList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = SList!int([5, 4, 9]);
|
||||
auto l2 = SList!int([9, 4]);
|
||||
@ -842,14 +836,6 @@ struct SList(T)
|
||||
assert(l1 == l2);
|
||||
}
|
||||
|
||||
private @safe @nogc unittest
|
||||
{
|
||||
auto l1 = SList!int();
|
||||
auto l2 = SList!int([9, 4]);
|
||||
l1 = l2[];
|
||||
assert(l1 == l2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a static array.
|
||||
*
|
||||
@ -865,7 +851,7 @@ struct SList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = SList!int([5, 4, 9]);
|
||||
auto l2 = SList!int([9, 4]);
|
||||
@ -877,7 +863,7 @@ struct SList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
SList!int l;
|
||||
size_t i;
|
||||
@ -895,7 +881,7 @@ struct SList(T)
|
||||
assert(i == 3);
|
||||
}
|
||||
|
||||
@safe @nogc private unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
interface Stuff
|
||||
{
|
||||
@ -903,8 +889,14 @@ struct SList(T)
|
||||
static assert(is(SList!Stuff));
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l = SList!int(0, 0);
|
||||
assert(l.empty);
|
||||
}
|
||||
|
||||
// foreach called using opIndex().
|
||||
private @nogc @safe unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
SList!int l;
|
||||
size_t i;
|
||||
@ -921,6 +913,14 @@ private @nogc @safe unittest
|
||||
}
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = SList!int();
|
||||
auto l2 = SList!int([9, 4]);
|
||||
l1 = l2[];
|
||||
assert(l1 == l2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward range for the $(D_PSYMBOL DList).
|
||||
*
|
||||
@ -963,7 +963,7 @@ struct DRange(L)
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return (*this.head).content;
|
||||
}
|
||||
@ -973,7 +973,7 @@ struct DRange(L)
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return (*this.tail).content;
|
||||
}
|
||||
@ -983,7 +983,7 @@ struct DRange(L)
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
this.head = &(*this.head).next;
|
||||
}
|
||||
@ -993,7 +993,7 @@ struct DRange(L)
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
this.tail = &(*this.tail).prev;
|
||||
}
|
||||
@ -1051,7 +1051,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l = DList!int([5, 8, 15]);
|
||||
assert(l.front == 5);
|
||||
@ -1101,7 +1101,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l = DList!int(2, 3);
|
||||
assert(l.length == 2);
|
||||
@ -1109,12 +1109,6 @@ struct DList(T)
|
||||
assert(l.back == 3);
|
||||
}
|
||||
|
||||
private @safe @nogc unittest
|
||||
{
|
||||
auto l = DList!int(0, 0);
|
||||
assert(l.empty);
|
||||
}
|
||||
|
||||
/// ditto
|
||||
this(const size_t len, shared Allocator allocator = defaultAllocator)
|
||||
{
|
||||
@ -1122,7 +1116,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l = DList!int(2);
|
||||
assert(l.length == 2);
|
||||
@ -1135,7 +1129,7 @@ struct DList(T)
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
this.allocator_ = allocator;
|
||||
}
|
||||
@ -1196,7 +1190,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = DList!int([5, 1, 234]);
|
||||
auto l2 = DList!int(l1);
|
||||
@ -1223,7 +1217,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = DList!int([5, 1, 234]);
|
||||
auto l2 = l1;
|
||||
@ -1242,7 +1236,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
DList!int l = DList!int([8, 5]);
|
||||
|
||||
@ -1259,7 +1253,7 @@ struct DList(T)
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return this.head.content;
|
||||
}
|
||||
@ -1272,13 +1266,13 @@ struct DList(T)
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return this.tail.content;
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l = DList!int([25]);
|
||||
assert(l.front == 25);
|
||||
@ -1311,17 +1305,17 @@ struct DList(T)
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Creates a lsit of linked entries from a range.
|
||||
// Returns count of the elements in the list.
|
||||
private size_t makeList(R)(ref R el, out Entry* head, out Entry* tail) @trusted
|
||||
out (retLength)
|
||||
{
|
||||
assert((retLength == 0 && head is null && tail is null)
|
||||
|| (retLength > 0 && head !is null && tail !is null));
|
||||
}
|
||||
body
|
||||
{
|
||||
size_t retLength;
|
||||
// Creates a lsit of linked entries from a range.
|
||||
// Returns count of the elements in the list.
|
||||
private size_t makeList(R)(ref R el, out Entry* head, out Entry* tail) @trusted
|
||||
out (retLength)
|
||||
{
|
||||
assert((retLength == 0 && head is null && tail is null)
|
||||
|| (retLength > 0 && head !is null && tail !is null));
|
||||
}
|
||||
do
|
||||
{
|
||||
size_t retLength;
|
||||
|
||||
if (!el.empty)
|
||||
{
|
||||
@ -1336,8 +1330,8 @@ struct DList(T)
|
||||
tail = tail.next;
|
||||
++retLength;
|
||||
}
|
||||
return retLength;
|
||||
}
|
||||
return retLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a new element at the beginning.
|
||||
@ -1371,7 +1365,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
DList!int l;
|
||||
int value = 5;
|
||||
@ -1408,12 +1402,6 @@ struct DList(T)
|
||||
return inserted;
|
||||
}
|
||||
|
||||
private @safe @nogc unittest
|
||||
{
|
||||
auto l1 = DList!int([5, 234]);
|
||||
assert(l1.head is l1.head.next.prev);
|
||||
}
|
||||
|
||||
/// ditto
|
||||
size_t insertFront(size_t R)(T[R] el)
|
||||
{
|
||||
@ -1421,7 +1409,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
DList!int l1;
|
||||
|
||||
@ -1497,7 +1485,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
DList!int l;
|
||||
int value = 5;
|
||||
@ -1525,10 +1513,10 @@ struct DList(T)
|
||||
{
|
||||
this.head = begin;
|
||||
}
|
||||
else
|
||||
{
|
||||
else
|
||||
{
|
||||
this.tail.next = begin;
|
||||
}
|
||||
}
|
||||
if (begin !is null)
|
||||
{
|
||||
this.tail = end;
|
||||
@ -1544,7 +1532,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
DList!int l1;
|
||||
|
||||
@ -1595,13 +1583,13 @@ struct DList(T)
|
||||
{
|
||||
assert(checkRangeBelonging(r));
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return moveFront(*r.head, el);
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = DList!int([234, 5, 1]);
|
||||
auto l2 = DList!int([5, 1]);
|
||||
@ -1615,7 +1603,7 @@ struct DList(T)
|
||||
{
|
||||
assert(checkRangeBelonging(r));
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto temp = allocator.make!Entry(el, *r.head);
|
||||
|
||||
@ -1634,7 +1622,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = DList!int([234, 5, 1]);
|
||||
auto l2 = DList!int([5, 1]);
|
||||
@ -1653,7 +1641,7 @@ struct DList(T)
|
||||
{
|
||||
assert(checkRangeBelonging(r));
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
size_t inserted;
|
||||
foreach (e; el)
|
||||
@ -1665,7 +1653,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = DList!int([5, 234, 30, 1]);
|
||||
auto l2 = DList!int([5, 1]);
|
||||
@ -1710,13 +1698,13 @@ struct DList(T)
|
||||
{
|
||||
assert(checkRangeBelonging(r));
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return moveBack(*r.tail, el);
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = DList!int([5, 234, 1]);
|
||||
auto l2 = DList!int([5, 1]);
|
||||
@ -1726,22 +1714,13 @@ struct DList(T)
|
||||
assert(l1 == l2);
|
||||
}
|
||||
|
||||
private @safe @nogc unittest
|
||||
{
|
||||
DList!int l;
|
||||
l.insertAfter(l[], 234);
|
||||
assert(l.front == 234);
|
||||
assert(l.back == 234);
|
||||
assert(l.length == 1);
|
||||
}
|
||||
|
||||
/// ditto
|
||||
size_t insertAfter(Range r, ref T el) @trusted
|
||||
in
|
||||
{
|
||||
assert(checkRangeBelonging(r));
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto temp = allocator.make!Entry(el, null, *r.tail);
|
||||
|
||||
@ -1760,7 +1739,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = DList!int([5, 1, 234]);
|
||||
auto l2 = DList!int([5, 1]);
|
||||
@ -1779,7 +1758,7 @@ struct DList(T)
|
||||
{
|
||||
assert(checkRangeBelonging(r));
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
size_t inserted;
|
||||
foreach (e; el)
|
||||
@ -1790,7 +1769,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = DList!int([5, 234, 30, 1]);
|
||||
auto l2 = DList!int([5, 1]);
|
||||
@ -1827,7 +1806,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
DList!int l;
|
||||
|
||||
@ -1855,7 +1834,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
DList!int l1, l2;
|
||||
|
||||
@ -1896,11 +1875,11 @@ struct DList(T)
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto n = this.head.next;
|
||||
|
||||
allocator.dispose(this.head);
|
||||
this.allocator_.dispose(this.head);
|
||||
this.head = n;
|
||||
if (this.head is null)
|
||||
{
|
||||
@ -1913,7 +1892,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
DList!int l;
|
||||
|
||||
@ -1932,7 +1911,7 @@ struct DList(T)
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto n = this.tail.prev;
|
||||
|
||||
@ -1949,7 +1928,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l = DList!int([9, 8]);
|
||||
|
||||
@ -1978,7 +1957,7 @@ struct DList(T)
|
||||
{
|
||||
assert(removed <= howMany);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
size_t i;
|
||||
for (; i < howMany && !empty; ++i)
|
||||
@ -1989,7 +1968,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
DList!int l = DList!int([8, 5, 4]);
|
||||
|
||||
@ -2005,7 +1984,7 @@ struct DList(T)
|
||||
{
|
||||
assert(removed <= howMany);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
size_t i;
|
||||
for (; i < howMany && !empty; ++i)
|
||||
@ -2016,7 +1995,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
DList!int l = DList!int([8, 5, 4]);
|
||||
|
||||
@ -2041,13 +2020,14 @@ struct DList(T)
|
||||
{
|
||||
assert(checkRangeBelonging(r));
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
// Save references to the elements before and after the range.
|
||||
Entry* tailNext, headPrev;
|
||||
if (*r.tail !is null && (*r.tail).next !is null)
|
||||
Entry* headPrev;
|
||||
Entry** tailNext;
|
||||
if (*r.tail !is null)
|
||||
{
|
||||
tailNext = (*r.tail).next;
|
||||
tailNext = &(*r.tail).next;
|
||||
}
|
||||
if (*r.head !is null)
|
||||
{
|
||||
@ -2056,26 +2036,42 @@ struct DList(T)
|
||||
|
||||
// Remove the elements.
|
||||
Entry* e = *r.head;
|
||||
while (e !is tailNext)
|
||||
while (e !is *tailNext)
|
||||
{
|
||||
auto next = e.next;
|
||||
allocator.dispose(e);
|
||||
/* Workaround for dmd 2.076.1 bug on OSX. Invariants fail when
|
||||
the allocator is called. Here it should be safe to use
|
||||
allocator_ directory, since the list isn't empty.
|
||||
See also removeFront. */
|
||||
this.allocator_.dispose(e);
|
||||
e = next;
|
||||
}
|
||||
|
||||
// Connect the elements before and after the removed range.
|
||||
if (tailNext !is null)
|
||||
if (*tailNext !is null)
|
||||
{
|
||||
tailNext.prev = headPrev;
|
||||
(*tailNext).prev = headPrev;
|
||||
}
|
||||
*r.head = tailNext;
|
||||
*r.tail = tail;
|
||||
else
|
||||
{
|
||||
this.tail = headPrev;
|
||||
}
|
||||
if (headPrev !is null)
|
||||
{
|
||||
headPrev.next = *tailNext;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.head = *tailNext;
|
||||
}
|
||||
r.head = tailNext;
|
||||
r.tail = &this.tail;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = DList!int([5, 234, 30, 1]);
|
||||
auto l2 = DList!int([5]);
|
||||
@ -2087,22 +2083,6 @@ struct DList(T)
|
||||
assert(l1 == l2);
|
||||
}
|
||||
|
||||
// Issue 260: https://issues.caraus.io/issues/260.
|
||||
private @safe @nogc unittest
|
||||
{
|
||||
auto l1 = DList!int([5, 234, 30, 1]);
|
||||
auto l2 = DList!int([5, 1]);
|
||||
auto r = l1[];
|
||||
|
||||
r.popFront();
|
||||
r.popBack();
|
||||
assert(r.front == 234);
|
||||
assert(r.back == 30);
|
||||
|
||||
assert(!l1.remove(r).empty);
|
||||
assert(l1 == l2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Range that iterates over all elements of the container, in
|
||||
* forward order.
|
||||
@ -2183,7 +2163,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = DList!int([5, 4, 9]);
|
||||
auto l2 = DList!int([9, 4]);
|
||||
@ -2191,14 +2171,6 @@ struct DList(T)
|
||||
assert(l1 == l2);
|
||||
}
|
||||
|
||||
private @safe @nogc unittest
|
||||
{
|
||||
auto l1 = DList!int();
|
||||
auto l2 = DList!int([9, 4]);
|
||||
l1 = l2[];
|
||||
assert(l1 == l2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a static array.
|
||||
*
|
||||
@ -2214,7 +2186,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = DList!int([5, 4, 9]);
|
||||
auto l2 = DList!int([9, 4]);
|
||||
@ -2226,7 +2198,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
///
|
||||
@nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
DList!int l;
|
||||
size_t i;
|
||||
@ -2244,8 +2216,7 @@ struct DList(T)
|
||||
assert(i == 3);
|
||||
}
|
||||
|
||||
// Issue 232: https://issues.caraus.io/issues/232.
|
||||
private @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
class A
|
||||
{
|
||||
@ -2253,3 +2224,67 @@ private @nogc unittest
|
||||
static assert(is(SList!(A*)));
|
||||
static assert(is(DList!(A*)));
|
||||
}
|
||||
|
||||
// Removes all elements
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l = DList!int([5]);
|
||||
assert(l.remove(l[]).empty);
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = DList!int([5, 234, 30, 1]);
|
||||
auto l2 = DList!int([5, 1]);
|
||||
auto r = l1[];
|
||||
|
||||
r.popFront();
|
||||
r.popBack();
|
||||
assert(r.front == 234);
|
||||
assert(r.back == 30);
|
||||
|
||||
assert(!l1.remove(r).empty);
|
||||
assert(l1 == l2);
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l = DList!int(0, 0);
|
||||
assert(l.empty);
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = DList!int([5, 234]);
|
||||
assert(l1.head is l1.head.next.prev);
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
DList!int l;
|
||||
l.insertAfter(l[], 234);
|
||||
assert(l.front == 234);
|
||||
assert(l.back == 234);
|
||||
assert(l.length == 1);
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = DList!int();
|
||||
auto l2 = DList!int([9, 4]);
|
||||
l1 = l2[];
|
||||
assert(l1 == l2);
|
||||
}
|
||||
|
||||
// Sets the new head
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l1 = DList!int([5, 234, 30, 1]);
|
||||
auto l2 = DList!int([1]);
|
||||
auto r = l1[];
|
||||
|
||||
r.popBack();
|
||||
|
||||
assert(!l1.remove(r).empty);
|
||||
assert(l1 == l2);
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ struct Queue(T)
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto n = first.next;
|
||||
T ret = move(first.content);
|
||||
|
@ -76,7 +76,7 @@ struct Range(E)
|
||||
assert(this.dataRange.empty
|
||||
|| this.dataRange.back.status == BucketStatus.used);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
do
|
||||
{
|
||||
@ -96,7 +96,7 @@ struct Range(E)
|
||||
assert(this.dataRange.empty
|
||||
|| this.dataRange.back.status == BucketStatus.used);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
do
|
||||
{
|
||||
@ -111,7 +111,7 @@ struct Range(E)
|
||||
assert(!this.dataRange.empty);
|
||||
assert(this.dataRange.front.status == BucketStatus.used);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return dataRange.front.content;
|
||||
}
|
||||
@ -122,7 +122,7 @@ struct Range(E)
|
||||
assert(!this.dataRange.empty);
|
||||
assert(this.dataRange.back.status == BucketStatus.used);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return dataRange.back.content;
|
||||
}
|
||||
@ -180,7 +180,7 @@ struct Set(T)
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
this(allocator);
|
||||
rehash(n);
|
||||
@ -192,7 +192,7 @@ struct Set(T)
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
this.data = typeof(this.data)(allocator);
|
||||
}
|
||||
@ -227,7 +227,7 @@ struct Set(T)
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
this.data = typeof(this.data)(init.data, allocator);
|
||||
}
|
||||
@ -239,7 +239,7 @@ struct Set(T)
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
this.data = typeof(this.data)(move(init.data), allocator);
|
||||
this.lengthIndex = init.lengthIndex;
|
||||
@ -285,7 +285,7 @@ struct Set(T)
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return cast(shared Allocator) this.data.allocator;
|
||||
}
|
||||
@ -371,7 +371,7 @@ struct Set(T)
|
||||
{
|
||||
assert(buckets.length > 0);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return hash % buckets.length;
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ if (is(Unqual!E == char))
|
||||
assert(begin >= container.data);
|
||||
assert(end <= container.data + container.length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
this.container = &container;
|
||||
this.begin = begin;
|
||||
@ -122,7 +122,7 @@ if (is(Unqual!E == char))
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return *this.begin;
|
||||
}
|
||||
@ -132,7 +132,7 @@ if (is(Unqual!E == char))
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return *(this.end - 1);
|
||||
}
|
||||
@ -142,7 +142,7 @@ if (is(Unqual!E == char))
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
++this.begin;
|
||||
}
|
||||
@ -152,7 +152,7 @@ if (is(Unqual!E == char))
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
--this.end;
|
||||
}
|
||||
@ -162,7 +162,7 @@ if (is(Unqual!E == char))
|
||||
{
|
||||
assert(i < length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return *(this.begin + i);
|
||||
}
|
||||
@ -183,7 +183,7 @@ if (is(Unqual!E == char))
|
||||
assert(i <= j);
|
||||
assert(j <= length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return typeof(return)(*this.container, this.begin + i, this.begin + j);
|
||||
}
|
||||
@ -194,7 +194,7 @@ if (is(Unqual!E == char))
|
||||
assert(i <= j);
|
||||
assert(j <= length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return typeof(return)(*this.container, this.begin + i, this.begin + j);
|
||||
}
|
||||
@ -233,7 +233,7 @@ if (is(Unqual!E == char))
|
||||
assert(begin >= container.data);
|
||||
assert(end <= container.data + container.length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
this.container = &container;
|
||||
this.begin = begin;
|
||||
@ -261,10 +261,11 @@ if (is(Unqual!E == char))
|
||||
{
|
||||
assert(chr < 0xd800 || chr > 0xdfff);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
dchar chr;
|
||||
ubyte units, mask;
|
||||
ubyte units;
|
||||
int mask;
|
||||
const(char)* it = this.begin;
|
||||
|
||||
if (*it & 0x80)
|
||||
@ -294,7 +295,7 @@ if (is(Unqual!E == char))
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
ubyte units;
|
||||
if ((*begin & 0xf0) == 0xf0)
|
||||
@ -437,7 +438,7 @@ struct String
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
this.allocator_ = allocator;
|
||||
}
|
||||
@ -527,7 +528,7 @@ struct String
|
||||
assert(capacity - length >= 4);
|
||||
assert(src - 0x10000 < 0x100000);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto dst = this.data + length;
|
||||
|
||||
@ -545,7 +546,7 @@ struct String
|
||||
{
|
||||
assert(capacity - length >= 3);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto dst = this.data + length;
|
||||
if (chr < 0x80)
|
||||
@ -923,7 +924,7 @@ struct String
|
||||
assert(j <= length);
|
||||
assert(j - i == value.length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto target = opSlice(i, j);
|
||||
copy(value, target);
|
||||
@ -940,7 +941,7 @@ struct String
|
||||
assert(i <= j);
|
||||
assert(j <= length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
copy(value[], this.data[i .. j]);
|
||||
return opSlice(i, j);
|
||||
@ -956,7 +957,7 @@ struct String
|
||||
assert(i <= j);
|
||||
assert(j <= length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
for (auto p = this.data + i; p < this.data + j; ++p)
|
||||
{
|
||||
@ -1037,7 +1038,7 @@ struct String
|
||||
{
|
||||
assert(length > pos);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return *(this.data + pos);
|
||||
}
|
||||
@ -1187,7 +1188,7 @@ struct String
|
||||
assert(i <= j);
|
||||
assert(j <= length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return typeof(return)(this, this.data + i, this.data + j);
|
||||
}
|
||||
@ -1200,7 +1201,7 @@ struct String
|
||||
assert(i <= j);
|
||||
assert(j <= length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return typeof(return)(this, this.data + i, this.data + j);
|
||||
}
|
||||
@ -1508,7 +1509,7 @@ struct String
|
||||
assert(r.begin >= this.data);
|
||||
assert(r.end <= this.data + length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto end = this.data + this.length;
|
||||
copy(ByCodeUnit!char(this, r.end, end), ByCodeUnit!char(this, r.begin, end));
|
||||
@ -1561,12 +1562,14 @@ struct String
|
||||
assert(r.begin >= this.data);
|
||||
assert(r.end <= this.data + length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto oldLen = this.data + length;
|
||||
const oldLength = length;
|
||||
const rangeEnd = r.end - this.data;
|
||||
const inserted = insertBack(el);
|
||||
bringToFront(ByCodeUnit!char(this, r.end, oldLen),
|
||||
ByCodeUnit!char(this, oldLen, this.data + length));
|
||||
auto containerEnd = this.data + oldLength;
|
||||
bringToFront(ByCodeUnit!char(this, this.data + rangeEnd, containerEnd),
|
||||
ByCodeUnit!char(this, containerEnd, this.data + length));
|
||||
return inserted;
|
||||
}
|
||||
|
||||
@ -1594,7 +1597,7 @@ struct String
|
||||
assert(r.begin >= this.data);
|
||||
assert(r.end <= this.data + length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return insertAfter(R(this, this.data, r.begin), el);
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ out (result)
|
||||
{
|
||||
assert(memory.ptr is (() @trusted => cast(void*) result)());
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
copy(typeid(T).initializer, memory);
|
||||
|
||||
@ -91,7 +91,8 @@ in
|
||||
out (result)
|
||||
{
|
||||
assert(memory.ptr is (() @trusted => cast(void*) result)());
|
||||
}body
|
||||
}
|
||||
do
|
||||
{
|
||||
copy(typeid(T).initializer, memory);
|
||||
|
||||
@ -144,7 +145,7 @@ out (result)
|
||||
{
|
||||
assert(memory.ptr is result);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto result = (() @trusted => cast(T*) memory.ptr)();
|
||||
static if (Args.length == 1)
|
||||
@ -169,7 +170,7 @@ out (result)
|
||||
{
|
||||
assert(memory.ptr is result);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto result = (() @trusted => cast(T*) memory.ptr)();
|
||||
static if (!hasElaborateAssign!T && isAssignable!T)
|
||||
|
@ -16,7 +16,6 @@ module tanya.format;
|
||||
|
||||
import tanya.container.string;
|
||||
import tanya.encoding.ascii;
|
||||
public import tanya.format.conv;
|
||||
import tanya.math;
|
||||
import tanya.memory.op;
|
||||
import tanya.meta.metafunction;
|
||||
@ -323,9 +322,9 @@ private HP raise2Power10(const HP value, int power)
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a float value, returns the significant bits in bits, and the position
|
||||
* of the decimal point in $(D_PARAM exponent). +/-Inf and NaN are specified
|
||||
* by special values returned in the $(D_PARAM exponent). Sing bit is set in
|
||||
* Given a float value, returns the significant bits, and the position of the
|
||||
* decimal point in $(D_PARAM exponent). +/-Inf and NaN are specified by
|
||||
* special values returned in the $(D_PARAM exponent). Sing bit is set in
|
||||
* $(D_PARAM sign).
|
||||
*/
|
||||
private const(char)[] real2String(double value,
|
||||
@ -505,86 +504,120 @@ if (T.sizeof == U.sizeof)
|
||||
copy((&src)[0 .. 1], (&dest)[0 .. 1]);
|
||||
}
|
||||
|
||||
package(tanya) String format(string fmt, Args...)(auto ref Args args)
|
||||
private void formatReal(T)(ref T arg, ref String result)
|
||||
if (isFloatingPoint!T)
|
||||
{
|
||||
String result;
|
||||
char[512] buffer; // Big enough for e+308 or e-307.
|
||||
char[8] tail = 0;
|
||||
char[] bufferSlice = buffer[64 .. $];
|
||||
uint precision = 6;
|
||||
bool negative;
|
||||
int decimalPoint;
|
||||
|
||||
static if (is(Unqual!(Args[0]) == typeof(null)))
|
||||
// Read the double into a string.
|
||||
auto realString = real2String(arg, buffer, decimalPoint, negative);
|
||||
auto length = cast(uint) realString.length;
|
||||
|
||||
// Clamp the precision and delete extra zeros after clamp.
|
||||
uint n = precision;
|
||||
if (length > precision)
|
||||
{
|
||||
result.insertBack("null");
|
||||
length = precision;
|
||||
}
|
||||
else static if(is(Unqual!(Args[0]) == bool))
|
||||
while ((length > 1) && (precision != 0) && (realString[length - 1] == '0'))
|
||||
{
|
||||
result.insertBack(args[0] ? "true" : "false");
|
||||
--precision;
|
||||
--length;
|
||||
}
|
||||
else static if (isSomeString!(Args[0])) // String
|
||||
|
||||
if (negative)
|
||||
{
|
||||
if (args[0] is null)
|
||||
result.insertBack('-');
|
||||
}
|
||||
if (decimalPoint == special)
|
||||
{
|
||||
result.insertBack(realString);
|
||||
return;
|
||||
}
|
||||
|
||||
// Should we use sceintific notation?
|
||||
if ((decimalPoint <= -4) || (decimalPoint > cast(int) n))
|
||||
{
|
||||
if (precision > length)
|
||||
{
|
||||
result.insertBack("null");
|
||||
precision = length - 1;
|
||||
}
|
||||
else if (precision > 0)
|
||||
{
|
||||
// When using scientific notation, there is one digit before the
|
||||
// decimal.
|
||||
--precision;
|
||||
}
|
||||
|
||||
// Handle leading chars.
|
||||
bufferSlice.front = realString[0];
|
||||
bufferSlice.popFront();
|
||||
|
||||
if (precision != 0)
|
||||
{
|
||||
bufferSlice.front = period;
|
||||
bufferSlice.popFront();
|
||||
}
|
||||
|
||||
// Handle after decimal.
|
||||
if ((length - 1) > precision)
|
||||
{
|
||||
length = precision + 1;
|
||||
}
|
||||
realString[1 .. length].copy(bufferSlice);
|
||||
bufferSlice.popFrontExactly(length - 1);
|
||||
|
||||
// Dump the exponent.
|
||||
tail[1] = 'e';
|
||||
--decimalPoint;
|
||||
if (decimalPoint < 0)
|
||||
{
|
||||
tail[2] = '-';
|
||||
decimalPoint = -decimalPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.insertBack(args[0]);
|
||||
tail[2] = '+';
|
||||
}
|
||||
|
||||
n = decimalPoint >= 100 ? 5 : 4;
|
||||
|
||||
tail[0] = cast(char) n;
|
||||
while (true)
|
||||
{
|
||||
tail[n] = '0' + decimalPoint % 10;
|
||||
if (n <= 3)
|
||||
{
|
||||
break;
|
||||
}
|
||||
--n;
|
||||
decimalPoint /= 10;
|
||||
}
|
||||
}
|
||||
else static if (isSomeChar!(Args[0])) // Char
|
||||
else
|
||||
{
|
||||
result.insertBack(args[0]);
|
||||
}
|
||||
else static if (isFloatingPoint!(Args[0])) // Float
|
||||
{
|
||||
char[512] buffer; // Big enough for e+308 or e-307.
|
||||
char[8] tail = 0;
|
||||
char[] bufferSlice = buffer[64 .. $];
|
||||
uint precision = 6;
|
||||
bool negative;
|
||||
int decimalPoint;
|
||||
|
||||
// Read the double into a string.
|
||||
auto realString = real2String(args[0], buffer, decimalPoint, negative);
|
||||
auto length = cast(uint) realString.length;
|
||||
|
||||
// Clamp the precision and delete extra zeros after clamp.
|
||||
uint n = precision;
|
||||
if (length > precision)
|
||||
if (decimalPoint > 0)
|
||||
{
|
||||
length = precision;
|
||||
precision = decimalPoint < (cast(int) length)
|
||||
? length - decimalPoint
|
||||
: 0;
|
||||
}
|
||||
while ((length > 1)
|
||||
&& (precision != 0)
|
||||
&& (realString[length - 1] == '0'))
|
||||
else
|
||||
{
|
||||
--precision;
|
||||
--length;
|
||||
precision = -decimalPoint
|
||||
+ (precision > length ? length : precision);
|
||||
}
|
||||
|
||||
if (negative)
|
||||
// Handle the three decimal varieties.
|
||||
if (decimalPoint <= 0)
|
||||
{
|
||||
result.insertBack('-');
|
||||
}
|
||||
if (decimalPoint == special)
|
||||
{
|
||||
result.insertBack(realString);
|
||||
goto ParamEnd;
|
||||
}
|
||||
|
||||
// Should we use sceintific notation?
|
||||
if ((decimalPoint <= -4) || (decimalPoint > cast(int) n))
|
||||
{
|
||||
if (precision > length)
|
||||
{
|
||||
precision = length - 1;
|
||||
}
|
||||
else if (precision > 0)
|
||||
{
|
||||
// When using scientific notation, there is one digit before the
|
||||
// decimal.
|
||||
--precision;
|
||||
}
|
||||
|
||||
// Handle leading chars.
|
||||
bufferSlice.front = realString[0];
|
||||
// Handle 0.000*000xxxx.
|
||||
bufferSlice.front = '0';
|
||||
bufferSlice.popFront();
|
||||
|
||||
if (precision != 0)
|
||||
@ -592,143 +625,205 @@ package(tanya) String format(string fmt, Args...)(auto ref Args args)
|
||||
bufferSlice.front = period;
|
||||
bufferSlice.popFront();
|
||||
}
|
||||
|
||||
// Handle after decimal.
|
||||
if ((length - 1) > precision)
|
||||
n = -decimalPoint;
|
||||
if (n > precision)
|
||||
{
|
||||
length = precision + 1;
|
||||
}
|
||||
realString[1 .. length].copy(bufferSlice);
|
||||
bufferSlice.popFrontExactly(length - 1);
|
||||
|
||||
// Dump the exponent.
|
||||
tail[1] = 'e';
|
||||
--decimalPoint;
|
||||
if (decimalPoint < 0)
|
||||
{
|
||||
tail[2] = '-';
|
||||
decimalPoint = -decimalPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
tail[2] = '+';
|
||||
n = precision;
|
||||
}
|
||||
|
||||
n = decimalPoint >= 100 ? 5 : 4;
|
||||
fill!'0'(bufferSlice[0 .. n]);
|
||||
bufferSlice.popFrontExactly(n);
|
||||
|
||||
tail[0] = cast(char) n;
|
||||
while (true)
|
||||
if ((length + n) > precision)
|
||||
{
|
||||
tail[n] = '0' + decimalPoint % 10;
|
||||
if (n <= 3)
|
||||
{
|
||||
break;
|
||||
}
|
||||
--n;
|
||||
decimalPoint /= 10;
|
||||
length = precision - n;
|
||||
}
|
||||
|
||||
realString[0 .. length].copy(bufferSlice);
|
||||
bufferSlice.popFrontExactly(length);
|
||||
}
|
||||
else if (cast(uint) decimalPoint >= length)
|
||||
{
|
||||
// Handle xxxx000*000.0.
|
||||
n = 0;
|
||||
do
|
||||
{
|
||||
bufferSlice.front = realString[n];
|
||||
bufferSlice.popFront();
|
||||
++n;
|
||||
}
|
||||
while (n < length);
|
||||
if (n < cast(uint) decimalPoint)
|
||||
{
|
||||
n = decimalPoint - n;
|
||||
|
||||
fill!'0'(bufferSlice[0 .. n]);
|
||||
bufferSlice.popFrontExactly(n);
|
||||
}
|
||||
if (precision != 0)
|
||||
{
|
||||
bufferSlice.front = period;
|
||||
bufferSlice.popFront();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (decimalPoint > 0)
|
||||
// Handle xxxxx.xxxx000*000.
|
||||
n = 0;
|
||||
do
|
||||
{
|
||||
precision = decimalPoint < (cast(int) length)
|
||||
? length - decimalPoint
|
||||
: 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
precision = -decimalPoint
|
||||
+ (precision > length ? length : precision);
|
||||
}
|
||||
|
||||
// Handle the three decimal varieties.
|
||||
if (decimalPoint <= 0)
|
||||
{
|
||||
// Handle 0.000*000xxxx.
|
||||
bufferSlice.front = '0';
|
||||
bufferSlice.front = realString[n];
|
||||
bufferSlice.popFront();
|
||||
|
||||
if (precision != 0)
|
||||
{
|
||||
bufferSlice.front = period;
|
||||
bufferSlice.popFront();
|
||||
}
|
||||
n = -decimalPoint;
|
||||
if (n > precision)
|
||||
{
|
||||
n = precision;
|
||||
}
|
||||
|
||||
fill!'0'(bufferSlice[0 .. n]);
|
||||
bufferSlice.popFrontExactly(n);
|
||||
|
||||
if ((length + n) > precision)
|
||||
{
|
||||
length = precision - n;
|
||||
}
|
||||
|
||||
realString[0 .. length].copy(bufferSlice);
|
||||
bufferSlice.popFrontExactly(length);
|
||||
++n;
|
||||
}
|
||||
else if (cast(uint) decimalPoint >= length)
|
||||
{
|
||||
// Handle xxxx000*000.0.
|
||||
n = 0;
|
||||
do
|
||||
{
|
||||
bufferSlice.front = realString[n];
|
||||
bufferSlice.popFront();
|
||||
++n;
|
||||
}
|
||||
while (n < length);
|
||||
if (n < cast(uint) decimalPoint)
|
||||
{
|
||||
n = decimalPoint - n;
|
||||
while (n < cast(uint) decimalPoint);
|
||||
|
||||
fill!'0'(bufferSlice[0 .. n]);
|
||||
bufferSlice.popFrontExactly(n);
|
||||
}
|
||||
if (precision != 0)
|
||||
{
|
||||
bufferSlice.front = period;
|
||||
bufferSlice.popFront();
|
||||
}
|
||||
if (precision > 0)
|
||||
{
|
||||
bufferSlice.front = period;
|
||||
bufferSlice.popFront();
|
||||
}
|
||||
else
|
||||
if ((length - decimalPoint) > precision)
|
||||
{
|
||||
// Handle xxxxx.xxxx000*000.
|
||||
n = 0;
|
||||
do
|
||||
{
|
||||
bufferSlice.front = realString[n];
|
||||
bufferSlice.popFront();
|
||||
++n;
|
||||
}
|
||||
while (n < cast(uint) decimalPoint);
|
||||
length = precision + decimalPoint;
|
||||
}
|
||||
|
||||
if (precision > 0)
|
||||
{
|
||||
bufferSlice.front = period;
|
||||
bufferSlice.popFront();
|
||||
}
|
||||
if ((length - decimalPoint) > precision)
|
||||
{
|
||||
length = precision + decimalPoint;
|
||||
}
|
||||
realString[n .. length].copy(bufferSlice);
|
||||
bufferSlice.popFrontExactly(length - n);
|
||||
}
|
||||
}
|
||||
|
||||
realString[n .. length].copy(bufferSlice);
|
||||
bufferSlice.popFrontExactly(length - n);
|
||||
// Get the length that we've copied.
|
||||
length = cast(uint) (buffer.length - bufferSlice.length);
|
||||
|
||||
result.insertBack(buffer[64 .. length]); // Number.
|
||||
result.insertBack(tail[1 .. tail[0] + 1]); // Tail.
|
||||
}
|
||||
|
||||
private void formatStruct(T)(ref T arg, ref String result)
|
||||
if (is(T == struct))
|
||||
{
|
||||
template pred(alias f)
|
||||
{
|
||||
static if (f == "this")
|
||||
{
|
||||
// Exclude context pointer from nested structs.
|
||||
enum bool pred = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
enum bool pred = !isSomeFunction!(__traits(getMember, arg, f));
|
||||
}
|
||||
}
|
||||
alias fields = Filter!(pred, __traits(allMembers, T));
|
||||
|
||||
result.insertBack(T.stringof);
|
||||
result.insertBack('(');
|
||||
static if (fields.length > 0)
|
||||
{
|
||||
printToString!"{}"(result, __traits(getMember, arg, fields[0]));
|
||||
foreach (field; fields[1 .. $])
|
||||
{
|
||||
result.insertBack(", ");
|
||||
printToString!"{}"(result, __traits(getMember, arg, field));
|
||||
}
|
||||
}
|
||||
result.insertBack(')');
|
||||
}
|
||||
|
||||
private void formatRange(T)(ref T arg, ref String result)
|
||||
if (isInputRange!T && !isInfinite!T)
|
||||
{
|
||||
result.insertBack('[');
|
||||
if (!arg.empty)
|
||||
{
|
||||
printToString!"{}"(result, arg.front);
|
||||
arg.popFront();
|
||||
}
|
||||
foreach (e; arg)
|
||||
{
|
||||
result.insertBack(", ");
|
||||
printToString!"{}"(result, e);
|
||||
}
|
||||
result.insertBack(']');
|
||||
}
|
||||
|
||||
private ref String printToString(string fmt, Args...)(return ref String result,
|
||||
auto ref Args args)
|
||||
{
|
||||
alias Arg = Args[0];
|
||||
|
||||
static if (is(Unqual!Arg == typeof(null))) // null
|
||||
{
|
||||
result.insertBack("null");
|
||||
}
|
||||
else static if (is(Unqual!Arg == bool)) // Boolean
|
||||
{
|
||||
result.insertBack(args[0] ? "true" : "false");
|
||||
}
|
||||
else static if (is(Arg == enum)) // Enum
|
||||
{
|
||||
foreach (m; __traits(allMembers, Arg))
|
||||
{
|
||||
if (args[0] == __traits(getMember, Arg, m))
|
||||
{
|
||||
result.insertBack(m);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the length that we've copied.
|
||||
length = cast(uint) (buffer.length - bufferSlice.length);
|
||||
|
||||
result.insertBack(buffer[64 .. length]); // Number.
|
||||
result.insertBack(tail[1 .. tail[0] + 1]); // Tail.
|
||||
}
|
||||
else static if (isPointer!(Args[0])) // Pointer
|
||||
else static if (isSomeChar!Arg || isSomeString!Arg) // String or char
|
||||
{
|
||||
result.insertBack(args[0]);
|
||||
}
|
||||
else static if (isInputRange!Arg
|
||||
&& !isInfinite!Arg
|
||||
&& isSomeChar!(ElementType!Arg)) // Stringish range
|
||||
{
|
||||
result.insertBack(args[0]);
|
||||
}
|
||||
else static if (isInputRange!Arg && !isInfinite!Arg)
|
||||
{
|
||||
formatRange(args[0], result);
|
||||
}
|
||||
else static if (is(Unqual!(typeof(args[0].stringify())) == String))
|
||||
{
|
||||
static if (is(Arg == class) || is(Arg == interface))
|
||||
{
|
||||
if (args[0] is null)
|
||||
{
|
||||
result.insertBack("null");
|
||||
}
|
||||
else
|
||||
{
|
||||
result.insertBack(args[0].stringify()[]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.insertBack(args[0].stringify()[]);
|
||||
}
|
||||
}
|
||||
else static if (is(Arg == class))
|
||||
{
|
||||
result.insertBack(args[0] is null ? "null" : args[0].toString());
|
||||
}
|
||||
else static if (is(Arg == interface))
|
||||
{
|
||||
result.insertBack(Arg.classinfo.name);
|
||||
}
|
||||
else static if (is(Arg == struct))
|
||||
{
|
||||
formatStruct(args[0], result);
|
||||
}
|
||||
else static if (is(Arg == union))
|
||||
{
|
||||
result.insertBack(Arg.stringof);
|
||||
}
|
||||
else static if (isFloatingPoint!Arg) // Float
|
||||
{
|
||||
formatReal(args[0], result);
|
||||
}
|
||||
else static if (isPointer!Arg) // Pointer
|
||||
{
|
||||
char[size_t.sizeof * 2] buffer;
|
||||
size_t position = buffer.length;
|
||||
@ -744,20 +839,40 @@ package(tanya) String format(string fmt, Args...)(auto ref Args args)
|
||||
result.insertBack("0x");
|
||||
result.insertBack(buffer[position .. $]);
|
||||
}
|
||||
else static if (isIntegral!(Args[0])) // Integer
|
||||
else static if (isIntegral!Arg) // Integer
|
||||
{
|
||||
char[21] buffer;
|
||||
result.insertBack(integral2String(args[0], buffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
static assert(false);
|
||||
static assert(false,
|
||||
"Formatting type " ~ Arg.stringof ~ " is not supported");
|
||||
}
|
||||
ParamEnd:
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
package(tanya) String format(string fmt, Args...)(auto ref Args args)
|
||||
{
|
||||
String formatted;
|
||||
return printToString!fmt(formatted, args);
|
||||
}
|
||||
|
||||
// Enum.
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
enum E1 : int
|
||||
{
|
||||
one,
|
||||
two,
|
||||
}
|
||||
assert(format!"{}"(E1.one) == "one");
|
||||
|
||||
const E1 e1;
|
||||
assert(format!"{}"(e1) == "one");
|
||||
}
|
||||
|
||||
// One argument tests.
|
||||
@nogc pure @safe unittest
|
||||
{
|
||||
@ -772,7 +887,7 @@ ParamEnd:
|
||||
|
||||
// String printing.
|
||||
assert(format!"{}"("Some weired string") == "Some weired string");
|
||||
assert(format!"{}"(cast(string) null) == "null");
|
||||
assert(format!"{}"(cast(string) null) == "");
|
||||
assert(format!"{}"('c') == "c");
|
||||
|
||||
// Integer.
|
||||
@ -826,6 +941,145 @@ ParamEnd:
|
||||
assert(format!"{}"(cast(void*) null) == "0x0");
|
||||
}
|
||||
|
||||
// Structs.
|
||||
@nogc pure @safe unittest
|
||||
{
|
||||
static struct WithoutStringify1
|
||||
{
|
||||
int a;
|
||||
void func()
|
||||
{
|
||||
}
|
||||
}
|
||||
assert(format!"{}"(WithoutStringify1(6)) == "WithoutStringify1(6)");
|
||||
|
||||
static struct WithoutStringify2
|
||||
{
|
||||
}
|
||||
assert(format!"{}"(WithoutStringify2()) == "WithoutStringify2()");
|
||||
|
||||
static struct WithoutStringify3
|
||||
{
|
||||
int a = -2;
|
||||
int b = 8;
|
||||
}
|
||||
assert(format!"{}"(WithoutStringify3()) == "WithoutStringify3(-2, 8)");
|
||||
|
||||
struct Nested
|
||||
{
|
||||
int i;
|
||||
|
||||
void func()
|
||||
{
|
||||
}
|
||||
}
|
||||
assert(format!"{}"(Nested()) == "Nested(0)");
|
||||
|
||||
static struct WithStringify
|
||||
{
|
||||
String stringify() const @nogc nothrow pure @safe
|
||||
{
|
||||
return String("stringify method");
|
||||
}
|
||||
}
|
||||
assert(format!"{}"(WithStringify()) == "stringify method");
|
||||
}
|
||||
|
||||
// Aggregate types.
|
||||
@system unittest // Object.toString has no attributes.
|
||||
{
|
||||
import tanya.memory;
|
||||
import tanya.memory.smartref;
|
||||
|
||||
interface I
|
||||
{
|
||||
}
|
||||
class A : I
|
||||
{
|
||||
}
|
||||
auto instance = defaultAllocator.unique!A();
|
||||
assert(format!"{}"(instance.get()) == instance.get().toString());
|
||||
assert(format!"{}"(cast(I) instance.get()) == I.classinfo.name);
|
||||
assert(format!"{}"(cast(A) null) == "null");
|
||||
|
||||
class B
|
||||
{
|
||||
String stringify() @nogc nothrow pure @safe
|
||||
{
|
||||
return String("Class B");
|
||||
}
|
||||
}
|
||||
assert(format!"{}"(cast(B) null) == "null");
|
||||
}
|
||||
|
||||
// Unions.
|
||||
unittest
|
||||
{
|
||||
union U
|
||||
{
|
||||
int i;
|
||||
char c;
|
||||
}
|
||||
assert(format!"{}"(U(2)) == "U");
|
||||
}
|
||||
|
||||
// Ranges.
|
||||
@nogc pure @safe unittest
|
||||
{
|
||||
static struct Stringish
|
||||
{
|
||||
private string content = "Some content";
|
||||
|
||||
immutable(char) front() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.content[0];
|
||||
}
|
||||
|
||||
void popFront() @nogc nothrow pure @safe
|
||||
{
|
||||
this.content = this.content[1 .. $];
|
||||
}
|
||||
|
||||
bool empty() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.content.length == 0;
|
||||
}
|
||||
}
|
||||
assert(format!"{}"(Stringish()) == "Some content");
|
||||
|
||||
static struct Intish
|
||||
{
|
||||
private int front_ = 3;
|
||||
|
||||
int front() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.front_;
|
||||
}
|
||||
|
||||
void popFront() @nogc nothrow pure @safe
|
||||
{
|
||||
--this.front_;
|
||||
}
|
||||
|
||||
bool empty() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.front == 0;
|
||||
}
|
||||
}
|
||||
assert(format!"{}"(Intish()) == "[3, 2, 1]");
|
||||
}
|
||||
|
||||
// Typeid.
|
||||
nothrow pure @safe unittest
|
||||
{
|
||||
assert(format!"{}"(typeid(int[])) == "int[]");
|
||||
|
||||
class C
|
||||
{
|
||||
}
|
||||
assert(format!"{}"(typeid(C)) == typeid(C).toString());
|
||||
}
|
||||
|
||||
private struct FormatSpec
|
||||
{
|
||||
}
|
@ -1,293 +0,0 @@
|
||||
/* 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/. */
|
||||
|
||||
/**
|
||||
* This module provides functions for converting between different types.
|
||||
*
|
||||
* Copyright: Eugene Wissner 2017.
|
||||
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
||||
* Mozilla Public License, v. 2.0).
|
||||
* Authors: Jeff Roberts, $(LINK2 mailto:info@caraus.de, Eugene Wissner)
|
||||
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/format/conv.d,
|
||||
* tanya/format/conv.d)
|
||||
*/
|
||||
module tanya.format.conv;
|
||||
|
||||
import tanya.container.string;
|
||||
import tanya.memory;
|
||||
import tanya.memory.op;
|
||||
import tanya.meta.trait;
|
||||
import tanya.meta.transform;
|
||||
|
||||
version (unittest)
|
||||
{
|
||||
import tanya.test.assertion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown if a type conversion fails.
|
||||
*/
|
||||
final class ConvException : Exception
|
||||
{
|
||||
/**
|
||||
* Params:
|
||||
* msg = The message for the exception.
|
||||
* file = The file where the exception occurred.
|
||||
* line = The line number where the exception occurred.
|
||||
* next = The previous exception in the chain of exceptions, if any.
|
||||
*/
|
||||
this(string msg,
|
||||
string file = __FILE__,
|
||||
size_t line = __LINE__,
|
||||
Throwable next = null) @nogc @safe pure nothrow
|
||||
{
|
||||
super(msg, file, line, next);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the source type $(D_PARAM From) and the target type $(D_PARAM To) are
|
||||
* equal, does nothing. If $(D_PARAM From) can be implicitly converted to
|
||||
* $(D_PARAM To), just returns $(D_PARAM from).
|
||||
*
|
||||
* Params:
|
||||
* To = Target type.
|
||||
*
|
||||
* Returns: $(D_PARAM from).
|
||||
*/
|
||||
deprecated("Use tanya.conv.to instead")
|
||||
template to(To)
|
||||
{
|
||||
/**
|
||||
* Params:
|
||||
* From = Source type.
|
||||
* from = Source value.
|
||||
*/
|
||||
ref To to(From)(ref From from)
|
||||
if (is(To == From))
|
||||
{
|
||||
return from;
|
||||
}
|
||||
|
||||
/// ditto
|
||||
To to(From)(From from)
|
||||
if (is(Unqual!To == Unqual!From) || (isNumeric!From && isFloatingPoint!To))
|
||||
{
|
||||
return from;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs checked conversion from an integral type $(D_PARAM From) to an
|
||||
* integral type $(D_PARAM To).
|
||||
*
|
||||
* Params:
|
||||
* From = Source type.
|
||||
* To = Target type.
|
||||
* from = Source value.
|
||||
*
|
||||
* Returns: $(D_PARAM from) converted to $(D_PARAM To).
|
||||
*
|
||||
* Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) is too small or too
|
||||
* large to be represented by $(D_PARAM To).
|
||||
*/
|
||||
deprecated("Use tanya.conv.to instead")
|
||||
To to(To, From)(From from)
|
||||
if (isIntegral!From
|
||||
&& isIntegral!To
|
||||
&& !is(Unqual!To == Unqual!From)
|
||||
&& !is(To == enum))
|
||||
{
|
||||
static if ((isUnsigned!From && isSigned!To && From.sizeof == To.sizeof)
|
||||
|| From.sizeof > To.sizeof)
|
||||
{
|
||||
if (from > To.max)
|
||||
{
|
||||
throw make!ConvException(defaultAllocator,
|
||||
"Positive integer overflow");
|
||||
}
|
||||
}
|
||||
static if (isSigned!From)
|
||||
{
|
||||
static if (isUnsigned!To)
|
||||
{
|
||||
if (from < 0)
|
||||
{
|
||||
throw make!ConvException(defaultAllocator,
|
||||
"Negative integer overflow");
|
||||
}
|
||||
}
|
||||
else static if (From.sizeof > To.sizeof)
|
||||
{
|
||||
if (from < To.min)
|
||||
{
|
||||
throw make!ConvException(defaultAllocator,
|
||||
"Negative integer overflow");
|
||||
}
|
||||
}
|
||||
}
|
||||
static if (From.sizeof <= To.sizeof)
|
||||
{
|
||||
return from;
|
||||
}
|
||||
else static if (isSigned!To)
|
||||
{
|
||||
return cast(To) from;
|
||||
}
|
||||
else
|
||||
{
|
||||
return from & To.max;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts $(D_PARAM from) to a boolean.
|
||||
*
|
||||
* If $(D_PARAM From) is a numeric type, then `1` becomes $(D_KEYWORD true),
|
||||
* `0` $(D_KEYWORD false). Otherwise $(D_PSYMBOL ConvException) is thrown.
|
||||
*
|
||||
* If $(D_PARAM To) is a string (built-in string or $(D_PSYMBOL String)),
|
||||
* then `"true"` or `"false"` are converted to the appropriate boolean value.
|
||||
* Otherwise $(D_PSYMBOL ConvException) is thrown.
|
||||
*
|
||||
* Params:
|
||||
* From = Source type.
|
||||
* To = Target type.
|
||||
* from = Source value.
|
||||
*
|
||||
* Returns: $(D_KEYWORD from) converted to a boolean.
|
||||
*
|
||||
* Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) isn't convertible.
|
||||
*/
|
||||
deprecated("Use tanya.conv.to instead")
|
||||
To to(To, From)(From from)
|
||||
if (isNumeric!From && is(Unqual!To == bool) && !is(Unqual!To == Unqual!From))
|
||||
{
|
||||
if (from == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (from < 0)
|
||||
{
|
||||
throw make!ConvException(defaultAllocator,
|
||||
"Negative number overflow");
|
||||
}
|
||||
else if (from <= 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
throw make!ConvException(defaultAllocator,
|
||||
"Positive number overflow");
|
||||
}
|
||||
|
||||
/// ditto
|
||||
deprecated("Use tanya.conv.to instead")
|
||||
To to(To, From)(auto ref const From from)
|
||||
if ((is(From == String) || isSomeString!From) && is(Unqual!To == bool))
|
||||
{
|
||||
if (from == "true")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (from == "false")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
throw make!ConvException(defaultAllocator,
|
||||
"String doesn't contain a boolean value");
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a boolean to $(D_PARAM To).
|
||||
*
|
||||
* If $(D_PARAM To) is a numeric type, then $(D_KEYWORD true) becomes `1`,
|
||||
* $(D_KEYWORD false) `0`.
|
||||
*
|
||||
* If $(D_PARAM To) is a $(D_PSYMBOL String), then `"true"` or `"false"`
|
||||
* is returned.
|
||||
*
|
||||
* Params:
|
||||
* From = Source type.
|
||||
* To = Target type.
|
||||
* from = Source value.
|
||||
*
|
||||
* Returns: $(D_PARAM from) converted to $(D_PARAM To).
|
||||
*/
|
||||
deprecated("Use tanya.conv.to instead")
|
||||
To to(To, From)(const From from)
|
||||
if (is(Unqual!From == bool) && isNumeric!To && !is(Unqual!To == Unqual!From))
|
||||
{
|
||||
return from;
|
||||
}
|
||||
|
||||
/// ditto
|
||||
deprecated("Use tanya.conv.to instead")
|
||||
To to(To, From)(const From from)
|
||||
if (is(Unqual!From == bool) && is(Unqual!To == String))
|
||||
{
|
||||
return String(from ? "true" : "false");
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a floating point number to an integral type.
|
||||
*
|
||||
* Params:
|
||||
* From = Source type.
|
||||
* To = Target type.
|
||||
* from = Source value.
|
||||
*
|
||||
* Returns: Truncated $(D_PARAM from) (everything after the decimal point is
|
||||
* dropped).
|
||||
*
|
||||
* Throws: $(D_PSYMBOL ConvException) if
|
||||
* $(D_INLINECODE from < To.min || from > To.max).
|
||||
*/
|
||||
deprecated("Use tanya.conv.to instead")
|
||||
To to(To, From)(From from)
|
||||
if (isFloatingPoint!From
|
||||
&& isIntegral!To
|
||||
&& !is(Unqual!To == Unqual!From)
|
||||
&& !is(To == enum))
|
||||
{
|
||||
if (from > To.max)
|
||||
{
|
||||
throw make!ConvException(defaultAllocator,
|
||||
"Positive number overflow");
|
||||
}
|
||||
else if (from < To.min)
|
||||
{
|
||||
throw make!ConvException(defaultAllocator,
|
||||
"Negative number overflow");
|
||||
}
|
||||
return cast(To) from;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs checked conversion from an integral type $(D_PARAM From) to an
|
||||
* $(D_KEYWORD enum).
|
||||
*
|
||||
* Params:
|
||||
* From = Source type.
|
||||
* To = Target type.
|
||||
* from = Source value.
|
||||
*
|
||||
* Returns: $(D_KEYWORD enum) value.
|
||||
*
|
||||
* Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) is not a member of
|
||||
* $(D_PSYMBOL To).
|
||||
*/
|
||||
deprecated("Use tanya.conv.to instead")
|
||||
To to(To, From)(From from)
|
||||
if (isIntegral!From && is(To == enum))
|
||||
{
|
||||
foreach (m; EnumMembers!To)
|
||||
{
|
||||
if (from == m)
|
||||
{
|
||||
return m;
|
||||
}
|
||||
}
|
||||
throw make!ConvException(defaultAllocator,
|
||||
"Value not found in enum '" ~ To.stringof ~ "'");
|
||||
}
|
@ -112,7 +112,7 @@ struct Integer
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
this.allocator_ = allocator;
|
||||
}
|
||||
@ -393,7 +393,9 @@ struct Integer
|
||||
T opCast(T)() const
|
||||
if (isIntegral!T && isSigned!T)
|
||||
{
|
||||
return this.sign ? -(cast(Unsigned!T) this) : cast(Unsigned!T) this;
|
||||
return this.sign
|
||||
? cast(T) -(cast(Promoted!(Unsigned!T)) (cast(Unsigned!T) this))
|
||||
: cast(Unsigned!T) this;
|
||||
}
|
||||
|
||||
///
|
||||
@ -871,7 +873,7 @@ struct Integer
|
||||
{
|
||||
assert(operand.length > 0, "Division by zero.");
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
divide(operand, this);
|
||||
return this;
|
||||
@ -883,7 +885,7 @@ struct Integer
|
||||
{
|
||||
assert(operand.length > 0, "Division by zero.");
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
divide(operand, null, this);
|
||||
return this;
|
||||
@ -1072,7 +1074,7 @@ struct Integer
|
||||
assert(h1 == 79);
|
||||
|
||||
h2 = ~h1;
|
||||
assert(h2 == ~cast(ubyte) 79);
|
||||
assert(h2 == cast(ubyte) ~79);
|
||||
}
|
||||
|
||||
/// ditto
|
||||
@ -1166,7 +1168,7 @@ struct Integer
|
||||
{
|
||||
assert(operand.length > 0, "Division by zero.");
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
mixin("return Integer(this, allocator) " ~ op ~ "= operand;");
|
||||
}
|
||||
@ -1285,7 +1287,7 @@ struct Integer
|
||||
{
|
||||
assert(divisor != 0, "Division by zero.");
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
if (compare(divisor) < 0)
|
||||
{
|
||||
@ -1470,7 +1472,7 @@ struct Integer
|
||||
{
|
||||
assert(array.length == length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
Array!ubyte array;
|
||||
|
||||
|
@ -30,15 +30,15 @@ else
|
||||
* Calculates the absolute value of a number.
|
||||
*
|
||||
* Params:
|
||||
* I = Value type.
|
||||
* x = Value.
|
||||
* T = Argument type.
|
||||
* x = Argument.
|
||||
*
|
||||
* Returns: Absolute value of $(D_PARAM x).
|
||||
*/
|
||||
I abs(I)(I x)
|
||||
if (isIntegral!I)
|
||||
T abs(T)(T x)
|
||||
if (isIntegral!T)
|
||||
{
|
||||
static if (isSigned!I)
|
||||
static if (isSigned!T)
|
||||
{
|
||||
return x >= 0 ? x : -x;
|
||||
}
|
||||
@ -49,7 +49,7 @@ if (isIntegral!I)
|
||||
}
|
||||
|
||||
///
|
||||
pure nothrow @safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
int i = -1;
|
||||
assert(i.abs == 1);
|
||||
@ -63,25 +63,25 @@ pure nothrow @safe @nogc unittest
|
||||
version (D_Ddoc)
|
||||
{
|
||||
/// ditto
|
||||
I abs(I)(I x)
|
||||
if (isFloatingPoint!I);
|
||||
T abs(T)(T x)
|
||||
if (isFloatingPoint!T);
|
||||
}
|
||||
else version (TanyaNative)
|
||||
{
|
||||
extern I abs(I)(I number) pure nothrow @safe @nogc
|
||||
if (isFloatingPoint!I);
|
||||
extern T abs(T)(T number) @nogc nothrow pure @safe
|
||||
if (isFloatingPoint!T);
|
||||
}
|
||||
else
|
||||
{
|
||||
I abs(I)(I x)
|
||||
if (isFloatingPoint!I)
|
||||
T abs(T)(T x)
|
||||
if (isFloatingPoint!T)
|
||||
{
|
||||
return fabs(cast(real) x);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pure nothrow @safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
float f = -1.64;
|
||||
assert(f.abs == 1.64F);
|
||||
@ -97,7 +97,7 @@ pure nothrow @safe @nogc unittest
|
||||
}
|
||||
|
||||
/// ditto
|
||||
I abs(I : Integer)(const auto ref I x)
|
||||
T abs(T : Integer)(const auto ref T x)
|
||||
{
|
||||
auto result = Integer(x, x.allocator);
|
||||
result.sign = Sign.positive;
|
||||
@ -105,7 +105,7 @@ I abs(I : Integer)(const auto ref I x)
|
||||
}
|
||||
|
||||
/// ditto
|
||||
I abs(I : Integer)(I x)
|
||||
T abs(T : Integer)(T x)
|
||||
{
|
||||
x.sign = Sign.positive;
|
||||
return x;
|
||||
@ -117,37 +117,30 @@ version (D_Ddoc)
|
||||
* Calculates natural logarithm of $(D_PARAM x).
|
||||
*
|
||||
* Params:
|
||||
* T = Argument type.
|
||||
* x = Argument.
|
||||
*
|
||||
* Returns: Natural logarithm of $(D_PARAM x).
|
||||
*/
|
||||
float ln(float x) pure nothrow @safe @nogc;
|
||||
/// ditto
|
||||
double ln(double x) pure nothrow @safe @nogc;
|
||||
/// ditto
|
||||
real ln(real x) pure nothrow @safe @nogc;
|
||||
T ln(T)(T x)
|
||||
if (isFloatingPoint!T);
|
||||
}
|
||||
else version (TanyaNative)
|
||||
{
|
||||
extern float ln(float x) pure nothrow @safe @nogc;
|
||||
extern double ln(double x) pure nothrow @safe @nogc;
|
||||
extern real ln(real x) pure nothrow @safe @nogc;
|
||||
extern T ln(T)(T x) @nogc nothrow pure @safe
|
||||
if (isFloatingPoint!T);
|
||||
}
|
||||
else
|
||||
{
|
||||
float ln(float x) pure nothrow @safe @nogc
|
||||
T ln(T)(T x)
|
||||
if (isFloatingPoint!T)
|
||||
{
|
||||
return log(x);
|
||||
}
|
||||
double ln(double x) pure nothrow @safe @nogc
|
||||
{
|
||||
return log(x);
|
||||
}
|
||||
alias ln = log;
|
||||
}
|
||||
|
||||
///
|
||||
pure nothrow @safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
import tanya.math;
|
||||
|
||||
|
@ -586,7 +586,7 @@ in
|
||||
{
|
||||
assert(z > 0, "Division by zero.");
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
G mask = G.max / 2 + 1;
|
||||
H result;
|
||||
@ -627,7 +627,7 @@ in
|
||||
{
|
||||
assert(z.length > 0, "Division by zero.");
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
size_t i;
|
||||
auto tmp1 = Integer(x, x.allocator);
|
||||
|
@ -94,9 +94,31 @@ abstract class EntropySource
|
||||
Nullable!ubyte poll(out ubyte[maxGather] output) @nogc;
|
||||
}
|
||||
|
||||
version (CRuntime_Bionic)
|
||||
{
|
||||
version = SecureARC4Random;
|
||||
}
|
||||
else version (OSX)
|
||||
{
|
||||
version = SecureARC4Random;
|
||||
}
|
||||
else version (OpenBSD)
|
||||
{
|
||||
version = SecureARC4Random;
|
||||
}
|
||||
else version (NetBSD)
|
||||
{
|
||||
version = SecureARC4Random;
|
||||
}
|
||||
else version (Solaris)
|
||||
{
|
||||
version = SecureARC4Random;
|
||||
}
|
||||
|
||||
version (linux)
|
||||
{
|
||||
extern (C) long syscall(long number, ...) nothrow @system @nogc;
|
||||
import core.stdc.config : c_long;
|
||||
extern (C) c_long syscall(c_long number, ...) nothrow @system @nogc;
|
||||
|
||||
/**
|
||||
* Uses getrandom system call.
|
||||
@ -134,10 +156,11 @@ version (linux)
|
||||
{
|
||||
assert(length <= maxGather);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
// int getrandom(void *buf, size_t buflen, unsigned int flags);
|
||||
auto length = syscall(318, output.ptr, output.length, 0);
|
||||
import mir.linux._asm.unistd : NR_getrandom;
|
||||
auto length = syscall(NR_getrandom, output.ptr, output.length, 0);
|
||||
Nullable!ubyte ret;
|
||||
|
||||
if (length >= 0)
|
||||
@ -148,16 +171,180 @@ version (linux)
|
||||
}
|
||||
}
|
||||
|
||||
version (X86_64)
|
||||
@nogc @system unittest
|
||||
{
|
||||
private unittest
|
||||
{
|
||||
auto entropy = defaultAllocator.make!Entropy();
|
||||
ubyte[blockSize] output;
|
||||
output = entropy.random;
|
||||
auto entropy = defaultAllocator.make!Entropy();
|
||||
ubyte[blockSize] output;
|
||||
output = entropy.random;
|
||||
|
||||
defaultAllocator.dispose(entropy);
|
||||
defaultAllocator.dispose(entropy);
|
||||
}
|
||||
}
|
||||
else version (SecureARC4Random)
|
||||
{
|
||||
private extern (C) void arc4random_buf(scope void* buf, size_t nbytes) nothrow @nogc @system;
|
||||
|
||||
/**
|
||||
* Uses arc4random_buf.
|
||||
*/
|
||||
class PlatformEntropySource : EntropySource
|
||||
{
|
||||
/**
|
||||
* Returns: Minimum bytes required from the entropy source.
|
||||
*/
|
||||
override @property ubyte threshold() const pure nothrow @safe @nogc
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Whether this entropy source is strong.
|
||||
*/
|
||||
override @property bool strong() const pure nothrow @safe @nogc
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Poll the entropy source.
|
||||
*
|
||||
* Params:
|
||||
* output = Buffer to save the generate random sequence (the method will
|
||||
* to fill the buffer).
|
||||
*
|
||||
* Returns: Number of bytes that were copied to the $(D_PARAM output)
|
||||
* or $(D_PSYMBOL Nullable!ubyte.init) on error.
|
||||
*/
|
||||
override Nullable!ubyte poll(out ubyte[maxGather] output) nothrow @nogc @safe
|
||||
{
|
||||
(() @trusted => arc4random_buf(output.ptr, output.length))();
|
||||
return Nullable!ubyte(cast(ubyte) (output.length));
|
||||
}
|
||||
}
|
||||
|
||||
@nogc @system unittest
|
||||
{
|
||||
auto entropy = defaultAllocator.make!Entropy();
|
||||
ubyte[blockSize] output;
|
||||
output = entropy.random;
|
||||
|
||||
defaultAllocator.dispose(entropy);
|
||||
}
|
||||
}
|
||||
else version (Windows)
|
||||
{
|
||||
import core.sys.windows.basetsd : ULONG_PTR;
|
||||
import core.sys.windows.windef : BOOL, DWORD, PBYTE;
|
||||
import core.sys.windows.winnt : LPCSTR, LPCWSTR;
|
||||
import core.sys.windows.wincrypt;
|
||||
private extern(Windows) @nogc nothrow
|
||||
{
|
||||
BOOL CryptGenRandom(HCRYPTPROV, DWORD, PBYTE);
|
||||
BOOL CryptAcquireContextA(HCRYPTPROV*, LPCSTR, LPCSTR, DWORD, DWORD);
|
||||
BOOL CryptAcquireContextW(HCRYPTPROV*, LPCWSTR, LPCWSTR, DWORD, DWORD);
|
||||
BOOL CryptReleaseContext(HCRYPTPROV, ULONG_PTR);
|
||||
}
|
||||
|
||||
private bool initCryptGenRandom(scope ref HCRYPTPROV hProvider) @nogc nothrow @trusted
|
||||
{
|
||||
import core.sys.windows.winbase : GetLastError;
|
||||
import core.sys.windows.winerror : NTE_BAD_KEYSET;
|
||||
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa379886(v=vs.85).aspx
|
||||
// For performance reasons, we recommend that you set the pszContainer
|
||||
// parameter to NULL and the dwFlags parameter to CRYPT_VERIFYCONTEXT
|
||||
// in all situations where you do not require a persisted key.
|
||||
// CRYPT_SILENT is intended for use with applications for which the UI cannot be displayed by the CSP.
|
||||
if (!CryptAcquireContextW(&hProvider, null, null, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
|
||||
{
|
||||
if (GetLastError() == NTE_BAD_KEYSET)
|
||||
{
|
||||
// Attempt to create default container
|
||||
if (!CryptAcquireContextA(&hProvider, null, null, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_SILENT))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class PlatformEntropySource : EntropySource
|
||||
{
|
||||
private HCRYPTPROV hProvider;
|
||||
|
||||
/**
|
||||
* Uses CryptGenRandom.
|
||||
*/
|
||||
this() @nogc
|
||||
{
|
||||
if (!initCryptGenRandom(hProvider))
|
||||
{
|
||||
throw defaultAllocator.make!EntropyException("CryptAcquireContextW failed.");
|
||||
}
|
||||
assert(hProvider > 0, "hProvider not properly initialized.");
|
||||
}
|
||||
|
||||
~this() @nogc nothrow @safe
|
||||
{
|
||||
if (hProvider > 0)
|
||||
{
|
||||
(() @trusted => CryptReleaseContext(hProvider, 0))();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Minimum bytes required from the entropy source.
|
||||
*/
|
||||
override @property ubyte threshold() const pure nothrow @safe @nogc
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Whether this entropy source is strong.
|
||||
*/
|
||||
override @property bool strong() const pure nothrow @safe @nogc
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Poll the entropy source.
|
||||
*
|
||||
* Params:
|
||||
* output = Buffer to save the generate random sequence (the method will
|
||||
* to fill the buffer).
|
||||
*
|
||||
* Returns: Number of bytes that were copied to the $(D_PARAM output)
|
||||
* or $(D_PSYMBOL Nullable!ubyte.init) on error.
|
||||
*/
|
||||
override Nullable!ubyte poll(out ubyte[maxGather] output) @nogc nothrow @safe
|
||||
in
|
||||
{
|
||||
assert(hProvider > 0, "hProvider not properly initialized.");
|
||||
}
|
||||
do
|
||||
{
|
||||
Nullable!ubyte ret;
|
||||
if ((() @trusted => CryptGenRandom(hProvider, output.length, cast(PBYTE) output.ptr))())
|
||||
{
|
||||
ret = cast(ubyte) (output.length);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@nogc @system unittest
|
||||
{
|
||||
auto entropy = defaultAllocator.make!Entropy();
|
||||
ubyte[blockSize] output;
|
||||
output = entropy.random;
|
||||
|
||||
defaultAllocator.dispose(entropy);
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,8 +367,6 @@ class Entropy
|
||||
|
||||
private ubyte sourceCount_;
|
||||
|
||||
private shared Allocator allocator;
|
||||
|
||||
/// Entropy accumulator.
|
||||
protected SHA!(maxGather * 8, 512) accumulator;
|
||||
|
||||
@ -198,11 +383,11 @@ class Entropy
|
||||
assert(maxSources > 0 && maxSources <= ubyte.max);
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
allocator.resize(sources, maxSources);
|
||||
|
||||
version (linux)
|
||||
static if (is(PlatformEntropySource))
|
||||
{
|
||||
this ~= allocator.make!PlatformEntropySource;
|
||||
}
|
||||
@ -234,7 +419,7 @@ class Entropy
|
||||
{
|
||||
assert(sourceCount_ <= sources.length);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
sources[sourceCount_++] = source;
|
||||
return this;
|
||||
@ -251,7 +436,7 @@ class Entropy
|
||||
{
|
||||
assert(sourceCount_ > 0, "No entropy sources defined.");
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
bool haveStrong;
|
||||
ushort done;
|
||||
@ -289,7 +474,7 @@ class Entropy
|
||||
|
||||
if (!haveStrong)
|
||||
{
|
||||
throw allocator.make!EntropyException("No strong entropy source defined.");
|
||||
throw defaultAllocator.make!EntropyException("No strong entropy source defined.");
|
||||
}
|
||||
|
||||
output = accumulator.finish();
|
||||
|
@ -14,7 +14,10 @@
|
||||
*/
|
||||
module tanya.memory.mallocator;
|
||||
|
||||
version (TanyaPhobos):
|
||||
version (TanyaNative)
|
||||
{
|
||||
}
|
||||
else:
|
||||
|
||||
import core.stdc.stdlib;
|
||||
import tanya.memory.allocator;
|
||||
|
@ -2,7 +2,7 @@
|
||||
* 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/. */
|
||||
|
||||
/**
|
||||
/*
|
||||
* Native allocator for Posix and Windows.
|
||||
*
|
||||
* Copyright: Eugene Wissner 2016-2017.
|
||||
@ -82,7 +82,7 @@ else version (Windows)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* This allocator allocates memory in regions (multiple of 64 KB for example).
|
||||
* Each region is then splitted in blocks. So it doesn't request the memory
|
||||
* from the operating system on each call, but only if there are no large
|
||||
@ -106,6 +106,7 @@ else version (Windows)
|
||||
* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
* </pre>
|
||||
*/
|
||||
deprecated("Use tanya.memory.mallocator instead")
|
||||
final class MmapPool : Allocator
|
||||
{
|
||||
version (none)
|
||||
@ -126,7 +127,7 @@ final class MmapPool : Allocator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Allocates $(D_PARAM size) bytes of memory.
|
||||
*
|
||||
* Params:
|
||||
@ -155,8 +156,7 @@ final class MmapPool : Allocator
|
||||
return data is null ? null : data[0 .. size];
|
||||
}
|
||||
|
||||
///
|
||||
nothrow unittest
|
||||
version (TanyaNative) @nogc nothrow pure unittest
|
||||
{
|
||||
auto p = MmapPool.instance.allocate(20);
|
||||
assert(p);
|
||||
@ -167,7 +167,7 @@ final class MmapPool : Allocator
|
||||
}
|
||||
|
||||
// Issue 245: https://issues.caraus.io/issues/245.
|
||||
private @nogc unittest
|
||||
version (TanyaNative) @nogc nothrow pure unittest
|
||||
{
|
||||
// allocate() check.
|
||||
size_t tooMuchMemory = size_t.max
|
||||
@ -245,7 +245,7 @@ final class MmapPool : Allocator
|
||||
block.next = block.next.next;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Deallocates a memory block.
|
||||
*
|
||||
* Params:
|
||||
@ -299,15 +299,14 @@ final class MmapPool : Allocator
|
||||
return true;
|
||||
}
|
||||
|
||||
///
|
||||
nothrow unittest
|
||||
version (TanyaNative) @nogc nothrow pure unittest
|
||||
{
|
||||
auto p = MmapPool.instance.allocate(20);
|
||||
|
||||
assert(MmapPool.instance.deallocate(p));
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Reallocates a memory block in place if possible or returns
|
||||
* $(D_KEYWORD false). This function cannot be used to allocate or
|
||||
* deallocate memory, so if $(D_PARAM p) is $(D_KEYWORD null) or
|
||||
@ -383,8 +382,7 @@ final class MmapPool : Allocator
|
||||
return true;
|
||||
}
|
||||
|
||||
///
|
||||
nothrow unittest
|
||||
version (TanyaNative) @nogc nothrow pure unittest
|
||||
{
|
||||
void[] p;
|
||||
assert(!MmapPool.instance.reallocateInPlace(p, 5));
|
||||
@ -408,7 +406,7 @@ final class MmapPool : Allocator
|
||||
MmapPool.instance.deallocate(p);
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Increases or decreases the size of a memory block.
|
||||
*
|
||||
* Params:
|
||||
@ -449,8 +447,7 @@ final class MmapPool : Allocator
|
||||
return true;
|
||||
}
|
||||
|
||||
///
|
||||
nothrow unittest
|
||||
version (TanyaNative) @nogc nothrow pure unittest
|
||||
{
|
||||
void[] p;
|
||||
MmapPool.instance.reallocate(p, 10 * int.sizeof);
|
||||
@ -514,7 +511,7 @@ final class MmapPool : Allocator
|
||||
return instance_;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Static allocator instance and initializer.
|
||||
*
|
||||
* Returns: Global $(D_PSYMBOL MmapPool) instance.
|
||||
@ -524,8 +521,7 @@ final class MmapPool : Allocator
|
||||
return (cast(GetPureInstance!MmapPool) &instantiate)();
|
||||
}
|
||||
|
||||
///
|
||||
nothrow unittest
|
||||
version (TanyaNative) @nogc nothrow pure unittest
|
||||
{
|
||||
assert(instance is instance);
|
||||
}
|
||||
@ -622,7 +618,7 @@ final class MmapPool : Allocator
|
||||
/ pageSize * pageSize + pageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Returns: Alignment offered.
|
||||
*/
|
||||
@property uint alignment() shared const pure nothrow @safe @nogc
|
||||
@ -630,7 +626,7 @@ final class MmapPool : Allocator
|
||||
return alignment_;
|
||||
}
|
||||
|
||||
private nothrow @nogc unittest
|
||||
version (TanyaNative) @nogc nothrow pure unittest
|
||||
{
|
||||
assert(MmapPool.instance.alignment == MmapPool.alignment_);
|
||||
}
|
||||
@ -661,9 +657,11 @@ final class MmapPool : Allocator
|
||||
private alias Block = shared BlockEntry*;
|
||||
}
|
||||
|
||||
version (TanyaNative):
|
||||
|
||||
// A lot of allocations/deallocations, but it is the minimum caused a
|
||||
// segmentation fault because MmapPool reallocateInPlace moves a block wrong.
|
||||
private @nogc unittest
|
||||
@nogc nothrow pure unittest
|
||||
{
|
||||
auto a = MmapPool.instance.allocate(16);
|
||||
auto d = MmapPool.instance.allocate(16);
|
||||
|
@ -32,6 +32,16 @@ else
|
||||
import core.stdc.string;
|
||||
}
|
||||
|
||||
version (TanyaNative)
|
||||
{
|
||||
@nogc nothrow pure @system unittest
|
||||
{
|
||||
ubyte[2] buffer = 1;
|
||||
fillMemory(buffer[1 .. $], 0);
|
||||
assert(buffer[0] == 1 && buffer[1] == 0);
|
||||
}
|
||||
}
|
||||
|
||||
private enum alignMask = size_t.sizeof - 1;
|
||||
|
||||
/**
|
||||
@ -58,7 +68,7 @@ in
|
||||
assert(source.length == 0 || source.ptr !is null);
|
||||
assert(target.length == 0 || target.ptr !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
version (TanyaNative)
|
||||
{
|
||||
@ -126,7 +136,7 @@ in
|
||||
{
|
||||
assert(memory.length == 0 || memory.ptr !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
version (TanyaNative)
|
||||
{
|
||||
@ -149,31 +159,6 @@ body
|
||||
}
|
||||
}
|
||||
|
||||
// Stress test. Checks that `fill` can handle unaligned pointers and different
|
||||
// lengths.
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
ubyte[192] memory;
|
||||
|
||||
foreach (j; 0 .. 192)
|
||||
{
|
||||
foreach (ubyte i, ref ubyte v; memory[j .. $])
|
||||
{
|
||||
v = i;
|
||||
}
|
||||
fill(memory[j .. $]);
|
||||
foreach (ubyte v; memory[j .. $])
|
||||
{
|
||||
assert(v == 0);
|
||||
}
|
||||
fill!1(memory[j .. $]);
|
||||
foreach (ubyte v; memory[j .. $])
|
||||
{
|
||||
assert(v == 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies starting from the end of $(D_PARAM source) into the end of
|
||||
* $(D_PARAM target).
|
||||
@ -203,7 +188,7 @@ in
|
||||
assert(source.length == 0 || source.ptr !is null);
|
||||
assert(target.length == 0 || target.ptr !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
version (TanyaNative)
|
||||
{
|
||||
@ -257,7 +242,7 @@ in
|
||||
assert(r1.length == 0 || r1.ptr !is null);
|
||||
assert(r2.length == 0 || r2.ptr !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
version (TanyaNative)
|
||||
{
|
||||
@ -321,7 +306,7 @@ in
|
||||
{
|
||||
assert(haystack.length == 0 || haystack.ptr !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto length = haystack.length;
|
||||
const size_t needleWord = size_t.max * needle;
|
||||
|
@ -14,12 +14,10 @@
|
||||
*/
|
||||
module tanya.memory;
|
||||
|
||||
import std.algorithm.iteration;
|
||||
import std.algorithm.mutation;
|
||||
import tanya.conv;
|
||||
import tanya.exception;
|
||||
public import tanya.memory.allocator;
|
||||
import tanya.memory.mmappool;
|
||||
import tanya.meta.trait;
|
||||
import tanya.range.primitive;
|
||||
|
||||
@ -45,7 +43,7 @@ mixin template DefaultAllocator()
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
this.allocator_ = allocator;
|
||||
}
|
||||
@ -63,7 +61,7 @@ mixin template DefaultAllocator()
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
if (allocator_ is null)
|
||||
{
|
||||
@ -78,7 +76,7 @@ mixin template DefaultAllocator()
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
if (allocator_ is null)
|
||||
{
|
||||
@ -94,13 +92,21 @@ private void _d_monitordelete(Object h, bool det) pure nothrow @nogc;
|
||||
|
||||
shared Allocator allocator;
|
||||
|
||||
shared static this() nothrow @nogc
|
||||
{
|
||||
allocator = MmapPool.instance;
|
||||
}
|
||||
|
||||
private shared(Allocator) getAllocatorInstance() nothrow @nogc
|
||||
{
|
||||
if (allocator is null)
|
||||
{
|
||||
version (TanyaNative)
|
||||
{
|
||||
import tanya.memory.mmappool;
|
||||
defaultAllocator = MmapPool.instance;
|
||||
}
|
||||
else
|
||||
{
|
||||
import tanya.memory.mallocator;
|
||||
defaultAllocator = Mallocator.instance;
|
||||
}
|
||||
}
|
||||
return allocator;
|
||||
}
|
||||
|
||||
@ -114,7 +120,7 @@ out (allocator)
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return (cast(GetPureInstance!Allocator) &getAllocatorInstance)();
|
||||
}
|
||||
@ -132,7 +138,7 @@ in
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
.allocator = allocator;
|
||||
}
|
||||
@ -244,7 +250,7 @@ package(tanya) T[] resize(T)(shared Allocator allocator,
|
||||
return array;
|
||||
}
|
||||
|
||||
private unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
int[] p;
|
||||
|
||||
@ -267,6 +273,10 @@ private unittest
|
||||
*/
|
||||
package(tanya) void[] finalize(T)(ref T* p)
|
||||
{
|
||||
if (p is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
static if (hasElaborateDestructor!T)
|
||||
{
|
||||
destroy(*p);
|
||||
@ -333,7 +343,10 @@ package(tanya) void[] finalize(T)(ref T[] p)
|
||||
{
|
||||
static if (hasElaborateDestructor!(typeof(p[0])))
|
||||
{
|
||||
p.each!((ref e) => destroy(e));
|
||||
foreach (ref e; p)
|
||||
{
|
||||
destroy(e);
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
@ -354,11 +367,11 @@ void dispose(T)(shared Allocator allocator, auto ref T p)
|
||||
p = null;
|
||||
}
|
||||
|
||||
private unittest
|
||||
@nogc nothrow pure @system unittest
|
||||
{
|
||||
struct S
|
||||
static struct S
|
||||
{
|
||||
~this()
|
||||
~this() @nogc nothrow pure @safe
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -368,7 +381,7 @@ private unittest
|
||||
}
|
||||
|
||||
// Works with interfaces.
|
||||
private pure unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
interface I
|
||||
{
|
||||
@ -403,7 +416,7 @@ in
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto mem = (() @trusted => allocator.allocate(stateSize!T))();
|
||||
if (mem is null)
|
||||
@ -442,7 +455,7 @@ in
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto mem = (() @trusted => allocator.allocate(stateSize!T))();
|
||||
if (mem is null)
|
||||
@ -457,7 +470,7 @@ body
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
int* i = defaultAllocator.make!int(5);
|
||||
assert(*i == 5);
|
||||
@ -484,7 +497,7 @@ in
|
||||
assert(allocator !is null);
|
||||
assert(n <= size_t.max / ElementType!T.sizeof);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto ret = allocator.resize!(ElementType!T)(null, n);
|
||||
ret.uninitializedFill(ElementType!T.init);
|
||||
@ -492,7 +505,7 @@ body
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
int[] i = defaultAllocator.make!(int[])(2);
|
||||
assert(i.length == 2);
|
||||
|
@ -54,7 +54,7 @@ private final class RefCountedStore(T)
|
||||
{
|
||||
assert(this.counter > 0);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
mixin("return " ~ op ~ "counter;");
|
||||
}
|
||||
@ -139,7 +139,7 @@ struct RefCounted(T)
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
this.allocator_ = allocator;
|
||||
}
|
||||
@ -247,7 +247,7 @@ struct RefCounted(T)
|
||||
{
|
||||
assert(count > 0, "Attempted to access an uninitialized reference");
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return this.storage.payload;
|
||||
}
|
||||
@ -501,7 +501,7 @@ in
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto rc = typeof(return)(allocator);
|
||||
|
||||
@ -546,7 +546,7 @@ in
|
||||
assert(allocator !is null);
|
||||
assert(size <= size_t.max / ElementType!T.sizeof);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return RefCounted!T(allocator.make!T(size), allocator);
|
||||
}
|
||||
@ -664,7 +664,7 @@ struct Unique(T)
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
this.allocator_ = allocator;
|
||||
}
|
||||
@ -849,7 +849,7 @@ in
|
||||
{
|
||||
assert(allocator !is null);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto payload = allocator.make!(T, A)(args);
|
||||
return Unique!T(payload, allocator);
|
||||
@ -877,7 +877,7 @@ in
|
||||
assert(allocator !is null);
|
||||
assert(size <= size_t.max / ElementType!T.sizeof);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
auto payload = allocator.resize!(ElementType!T)(null, size);
|
||||
return Unique!T(payload, allocator);
|
||||
|
@ -275,15 +275,50 @@ enum bool isTemplate(alias T) = __traits(isTemplate, T);
|
||||
static assert(!isTemplate!(S!int));
|
||||
}
|
||||
|
||||
deprecated("Use is(T == interface) instead")
|
||||
/**
|
||||
* Tests whether $(D_PARAM T) is an interface.
|
||||
*
|
||||
* Params:
|
||||
* T = A type.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is an interface,
|
||||
* $(D_KEYWORD false) otherwise.
|
||||
*/
|
||||
enum bool isInterface(T) = is(T == interface);
|
||||
|
||||
deprecated("Use is(T == class) instead")
|
||||
/**
|
||||
* Tests whether $(D_PARAM T) is a class.
|
||||
*
|
||||
* Params:
|
||||
* T = A type.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a class,
|
||||
* $(D_KEYWORD false) otherwise.
|
||||
*/
|
||||
enum bool isClass(T) = is(T == class);
|
||||
|
||||
deprecated("Use is(T == struct) instead")
|
||||
/**
|
||||
* Tests whether $(D_PARAM T) is a struct.
|
||||
*
|
||||
* Params:
|
||||
* T = A type.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a struct,
|
||||
* $(D_KEYWORD false) otherwise.
|
||||
*/
|
||||
enum bool isStruct(T) = is(T == struct);
|
||||
|
||||
/**
|
||||
* Tests whether $(D_PARAM T) is a enum.
|
||||
*
|
||||
* Params:
|
||||
* T = A type.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is an enum,
|
||||
* $(D_KEYWORD false) otherwise.
|
||||
*/
|
||||
enum bool isEnum(T) = is(T == enum);
|
||||
|
||||
/**
|
||||
* Determines whether $(D_PARAM T) is a polymorphic type, i.e. a
|
||||
* $(D_KEYWORD class) or an $(D_KEYWORD interface).
|
||||
|
@ -77,7 +77,7 @@ struct NetworkOrder(uint L)
|
||||
{
|
||||
assert(value <= pow(2, L * 8) - 1);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
this.value = value & StorageType.max;
|
||||
}
|
||||
@ -92,7 +92,7 @@ struct NetworkOrder(uint L)
|
||||
{
|
||||
assert(this.length > 0);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return this.value & 0xff;
|
||||
}
|
||||
@ -107,7 +107,7 @@ struct NetworkOrder(uint L)
|
||||
{
|
||||
assert(this.length > 0);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return (this.value >> ((this.length - 1) * 8)) & 0xff;
|
||||
}
|
||||
@ -122,7 +122,7 @@ struct NetworkOrder(uint L)
|
||||
{
|
||||
assert(this.length > 0);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
this.value >>= 8;
|
||||
--this.size;
|
||||
@ -138,7 +138,7 @@ struct NetworkOrder(uint L)
|
||||
{
|
||||
assert(this.length > 0);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
this.value &= StorageType.max >> ((StorageType.sizeof - this.length) * 8);
|
||||
--this.size;
|
||||
|
@ -185,7 +185,7 @@ else version (Windows)
|
||||
{
|
||||
assert(count >= 0);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
DWORD lpNumber;
|
||||
BOOL result = GetOverlappedResult(overlapped.handle,
|
||||
@ -259,7 +259,7 @@ else version (Windows)
|
||||
{
|
||||
assert(count >= 0);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
DWORD lpNumber;
|
||||
BOOL result = GetOverlappedResult(overlapped.handle,
|
||||
@ -720,7 +720,7 @@ abstract class Socket
|
||||
assert(handle != SocketType.init);
|
||||
assert(handle_ == SocketType.init, "Socket handle cannot be changed");
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
handle_ = handle;
|
||||
|
||||
@ -749,7 +749,7 @@ abstract class Socket
|
||||
{
|
||||
assert(handle != SocketType.init);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
scope (failure)
|
||||
{
|
||||
|
@ -5,7 +5,7 @@
|
||||
/**
|
||||
* This module provides a portable way of using operating system error codes.
|
||||
*
|
||||
* Copyright: Eugene Wissner 2017.
|
||||
* Copyright: Eugene Wissner 2017-2018.
|
||||
* 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)
|
||||
@ -14,6 +14,8 @@
|
||||
*/
|
||||
module tanya.os.error;
|
||||
|
||||
import tanya.meta.trait;
|
||||
|
||||
// Socket API error.
|
||||
private template SAError(int posix, int wsa = posix)
|
||||
{
|
||||
@ -120,7 +122,7 @@ struct ErrorCode
|
||||
/// Protocol not available.
|
||||
noProtocolOption = SAError!(92, 42),
|
||||
|
||||
/// The protocol is not implemented orR has not been configured.
|
||||
/// The protocol is not implemented or has not been configured.
|
||||
protocolNotSupported = SAError!(93, 43),
|
||||
|
||||
/// The support for the specified socket type does not exist in this
|
||||
@ -186,19 +188,66 @@ struct ErrorCode
|
||||
cancelled = SAError!(125, 103),
|
||||
}
|
||||
|
||||
/**
|
||||
* Error descriptions.
|
||||
*/
|
||||
private enum ErrorStr : string
|
||||
{
|
||||
success = "The operation completed successfully",
|
||||
noPermission = "Operation not permitted",
|
||||
interrupted = "Interrupted system call",
|
||||
badDescriptor = "Bad file descriptor",
|
||||
wouldBlock = "An operation on a non-blocking socket would block",
|
||||
noMemory = "Out of memory",
|
||||
accessDenied = "Access denied",
|
||||
fault = "An invalid pointer address detected",
|
||||
noSuchDevice = "No such device",
|
||||
invalidArgument = "An invalid argument was supplied",
|
||||
tooManyDescriptors = "The limit on the number of open file descriptors",
|
||||
noDescriptors = "The limit on the number of open file descriptors",
|
||||
brokenPipe = "Broken pipe",
|
||||
nameTooLong = "The name was too long",
|
||||
notSocket = "A socket operation was attempted on a non-socket",
|
||||
protocolError = "Protocol error",
|
||||
messageTooLong = "Message too long",
|
||||
wrongProtocolType = "Wrong protocol type for socket",
|
||||
noProtocolOption = "Protocol not available",
|
||||
protocolNotSupported = "The protocol is not implemented or has not been configured",
|
||||
socketNotSupported = "Socket type not supported",
|
||||
operationNotSupported = "The address family is no supported by the protocol family",
|
||||
addressFamilyNotSupported = "Address family specified is not supported",
|
||||
addressInUse = "Address already in use",
|
||||
networkDown = "The network is not available",
|
||||
networkUnreachable = "No route to host",
|
||||
networkReset = "Network dropped connection because of reset",
|
||||
connectionAborted = "The connection has been aborted",
|
||||
connectionReset = "Connection reset by peer",
|
||||
noBufferSpace = "No free buffer space is available for a socket operation",
|
||||
alreadyConnected = "Transport endpoint is already connected",
|
||||
notConnected = "Transport endpoint is not connected",
|
||||
shutdown = "Cannot send after transport endpoint shutdown",
|
||||
timedOut = "Operation timed out",
|
||||
connectionRefused = "Connection refused",
|
||||
hostDown = "Host is down",
|
||||
hostUnreachable = "No route to host",
|
||||
alreadyStarted = "Operation already in progress",
|
||||
inProgress = "Operation now in progress",
|
||||
cancelled = "Operation cancelled",
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Params:
|
||||
* value = Numeric error code.
|
||||
*/
|
||||
this(const ErrorNo value) pure nothrow @safe @nogc
|
||||
this(const ErrorNo value) @nogc nothrow pure @safe
|
||||
{
|
||||
this.value_ = value;
|
||||
}
|
||||
|
||||
///
|
||||
pure nothrow @safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
ErrorCode ec;
|
||||
assert(ec == ErrorCode.success);
|
||||
@ -211,13 +260,13 @@ struct ErrorCode
|
||||
* Resets this $(D_PSYMBOL ErrorCode) to default
|
||||
* ($(D_PSYMBOL ErrorCode.success)).
|
||||
*/
|
||||
void reset() pure nothrow @safe @nogc
|
||||
void reset() @nogc nothrow pure @safe
|
||||
{
|
||||
this.value_ = ErrorNo.success;
|
||||
}
|
||||
|
||||
///
|
||||
pure nothrow @safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto ec = ErrorCode(ErrorCode.fault);
|
||||
assert(ec == ErrorCode.fault);
|
||||
@ -241,7 +290,7 @@ struct ErrorCode
|
||||
}
|
||||
|
||||
///
|
||||
pure nothrow @safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
ErrorCode ec = ErrorCode.fault;
|
||||
auto errorNo = cast(ErrorCode.ErrorNo) ec;
|
||||
@ -258,38 +307,38 @@ struct ErrorCode
|
||||
*
|
||||
* Returns: $(D_KEYWORD this).
|
||||
*/
|
||||
ref ErrorCode opAssign(const ErrorNo that) pure nothrow @safe @nogc
|
||||
ref ErrorCode opAssign(const ErrorNo that) @nogc nothrow pure @safe
|
||||
{
|
||||
this.value_ = that;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// ditto
|
||||
ref ErrorCode opAssign()(auto ref const ErrorCode that)
|
||||
pure nothrow @safe @nogc
|
||||
ref ErrorCode opAssign(const ErrorCode that) @nogc nothrow pure @safe
|
||||
{
|
||||
this.value_ = that.value_;
|
||||
return this;
|
||||
}
|
||||
|
||||
///
|
||||
pure nothrow @safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
{
|
||||
ErrorCode ec;
|
||||
assert(ec == ErrorCode.success);
|
||||
ErrorCode ec;
|
||||
assert(ec == ErrorCode.success);
|
||||
|
||||
ec = ErrorCode.fault;
|
||||
assert(ec == ErrorCode.fault);
|
||||
}
|
||||
{
|
||||
auto ec1 = ErrorCode(ErrorCode.fault);
|
||||
ErrorCode ec2;
|
||||
assert(ec2 == ErrorCode.success);
|
||||
ec = ErrorCode.fault;
|
||||
assert(ec == ErrorCode.fault);
|
||||
}
|
||||
|
||||
ec2 = ec1;
|
||||
assert(ec1 == ec2);
|
||||
}
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto ec1 = ErrorCode(ErrorCode.fault);
|
||||
ErrorCode ec2;
|
||||
assert(ec2 == ErrorCode.success);
|
||||
|
||||
ec2 = ec1;
|
||||
assert(ec1 == ec2);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -300,37 +349,68 @@ struct ErrorCode
|
||||
*
|
||||
* Returns: Whether $(D_KEYWORD this) and $(D_PARAM that) are equal.
|
||||
*/
|
||||
bool opEquals(const ErrorNo that) const pure nothrow @safe @nogc
|
||||
bool opEquals(const ErrorNo that) const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.value_ == that;
|
||||
}
|
||||
|
||||
/// ditto
|
||||
bool opEquals()(auto ref const ErrorCode that)
|
||||
const pure nothrow @safe @nogc
|
||||
bool opEquals(const ErrorCode that) const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.value_ == that.value_;
|
||||
}
|
||||
|
||||
///
|
||||
pure nothrow @safe @nogc unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
{
|
||||
ErrorCode ec1 = ErrorCode.fault;
|
||||
ErrorCode ec2 = ErrorCode.accessDenied;
|
||||
ErrorCode ec1 = ErrorCode.fault;
|
||||
ErrorCode ec2 = ErrorCode.accessDenied;
|
||||
|
||||
assert(ec1 != ec2);
|
||||
assert(ec1 != ErrorCode.accessDenied);
|
||||
assert(ErrorCode.fault != ec2);
|
||||
}
|
||||
{
|
||||
ErrorCode ec1 = ErrorCode.fault;
|
||||
ErrorCode ec2 = ErrorCode.fault;
|
||||
assert(ec1 != ec2);
|
||||
assert(ec1 != ErrorCode.accessDenied);
|
||||
assert(ErrorCode.fault != ec2);
|
||||
}
|
||||
|
||||
assert(ec1 == ec2);
|
||||
assert(ec1 == ErrorCode.fault);
|
||||
assert(ErrorCode.fault == ec2);
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
ErrorCode ec1 = ErrorCode.fault;
|
||||
ErrorCode ec2 = ErrorCode.fault;
|
||||
|
||||
assert(ec1 == ec2);
|
||||
assert(ec1 == ErrorCode.fault);
|
||||
assert(ErrorCode.fault == ec2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string describing the error number. If a description for a
|
||||
* specific error number is not available, returns $(D_KEYWORD null).
|
||||
*
|
||||
* Returns: String describing the error number.
|
||||
*/
|
||||
string toString() const @nogc nothrow pure @safe
|
||||
{
|
||||
foreach (e; __traits(allMembers, ErrorNo))
|
||||
{
|
||||
if (__traits(getMember, ErrorNo, e) == this.value_)
|
||||
{
|
||||
return __traits(getMember, ErrorStr, e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
ErrorCode ec = ErrorCode.fault;
|
||||
assert(ec.toString() == "An invalid pointer address detected");
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
ErrorCode ec = cast(ErrorCode.ErrorNo) -1;
|
||||
assert(ec.toString() is null);
|
||||
}
|
||||
|
||||
private ErrorNo value_ = ErrorNo.success;
|
||||
|
@ -59,7 +59,7 @@ in
|
||||
{
|
||||
assert(array.length > 0);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return array[0];
|
||||
}
|
||||
@ -99,7 +99,7 @@ in
|
||||
{
|
||||
assert(array.length > 0);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
return array[$ - 1];
|
||||
}
|
||||
@ -138,7 +138,7 @@ in
|
||||
{
|
||||
assert(array.length > 0);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
array = array[1 .. $];
|
||||
}
|
||||
@ -149,7 +149,7 @@ in
|
||||
{
|
||||
assert(array.length > 0);
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
array = array[0 .. $ - 1];
|
||||
}
|
||||
|
@ -633,8 +633,8 @@ template isBidirectionalRange(R)
|
||||
*
|
||||
* A random-access range is a range that allows random access to its
|
||||
* elements by index using $(D_INLINECODE [])-operator (defined with
|
||||
* $(D_INLINECODE opIndex())). Further a random access range should be a
|
||||
* bidirectional range that also has a length or an infinite forward range.
|
||||
* $(D_INLINECODE opIndex())). Further a random access range should
|
||||
* have a length or be infinite.
|
||||
*
|
||||
* Params:
|
||||
* R = The type to be tested.
|
||||
@ -642,19 +642,21 @@ template isBidirectionalRange(R)
|
||||
* Returns: $(D_KEYWORD true) if $(D_PARAM R) is a random-access range,
|
||||
* $(D_KEYWORD false) otherwise.
|
||||
*
|
||||
* See_Also: $(D_PSYMBOL isBidirectionalRange),
|
||||
* $(D_PSYMBOL isForwardRange),
|
||||
* $(D_PSYMBOL isInfinite),
|
||||
* See_Also: $(D_PSYMBOL isInfinite),
|
||||
* $(D_PSYMBOL hasLength).
|
||||
*
|
||||
* Note: This definition differs from `std.range.primitives.isRandomAccessRange`
|
||||
* in the D standard library in that it does not also require $(D_PARAM R) to
|
||||
* be a forward range and a bidirectional range. Those properties may be tested
|
||||
* separately with $(D_PSYMBOL isForwardRange) and
|
||||
* $(D_PSYMBOL isBidirectionalRange).
|
||||
*/
|
||||
template isRandomAccessRange(R)
|
||||
{
|
||||
static if (is(ReturnType!((R r) => r.opIndex(size_t.init)) U))
|
||||
{
|
||||
private enum bool isBidirectional = isBidirectionalRange!R
|
||||
&& hasLength!R;
|
||||
private enum bool isForward = isInfinite!R && isForwardRange!R;
|
||||
enum bool isRandomAccessRange = (isBidirectional || isForward)
|
||||
enum bool isRandomAccessRange = isInputRange!R
|
||||
&& (hasLength!R || isInfinite!R)
|
||||
&& is(U == ReturnType!((R r) => r.front()));
|
||||
}
|
||||
else
|
||||
@ -1053,7 +1055,7 @@ in
|
||||
assert(count <= range.length);
|
||||
}
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
static if (hasSlicing!R)
|
||||
{
|
||||
@ -1112,7 +1114,7 @@ in
|
||||
assert(count <= range.length);
|
||||
}
|
||||
}
|
||||
body
|
||||
do
|
||||
{
|
||||
static if (hasSlicing!R)
|
||||
{
|
||||
|
@ -92,7 +92,7 @@ template Pair(Specs...)
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
static assert(is(Pair!(int, int)));
|
||||
static assert(!is(Pair!(int, 5)));
|
||||
|
Reference in New Issue
Block a user