Compare commits
54 Commits
Author | SHA1 | Date | |
---|---|---|---|
a648e2120a | |||
bc61809050 | |||
8c42cbfd63 | |||
58664570f9 | |||
decb82f437 | |||
357c7e279d | |||
32e19c8b58 | |||
f5c6c5b483 | |||
ba2d086fb8 | |||
7a0241b484 | |||
36dad80e18 | |||
29d883150e | |||
e2bed0cfcb | |||
38afeac071 | |||
001c7c3e33 | |||
d4ab339feb | |||
8477312769 | |||
67f90e137d | |||
f264fd5597 | |||
9e75620f1b | |||
45825946c0 | |||
8afb552d59 | |||
e4091669f8 | |||
1cb9349226 | |||
06620dc5df | |||
708d95db49 | |||
85d9361bfb | |||
a6a6f496eb | |||
db12f03264 | |||
231aedb8ad | |||
c3b63ee40d | |||
6f405c5e08 | |||
16cf8478cf | |||
8915a0c7a7 | |||
e5c7edb72c | |||
64e0d666ed | |||
f2aac680c5 | |||
d629525a4b | |||
33d321f0d7 | |||
3d64d59ba9 | |||
4635835a99 | |||
8725ec5f20 | |||
9a4c8cea06 | |||
eb360bda38 | |||
4b1cd2cbfd | |||
cd944a61b7 | |||
47ef787353 | |||
6436ad49df | |||
e1964e47a5 | |||
43319e4e3a | |||
33dbf042c2 | |||
885fca9b5e | |||
074d027629 | |||
f4b90d8b51 |
9
.editorconfig
Normal file
9
.editorconfig
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
trim_trailing_whitespace = true
|
@ -17,4 +17,7 @@ env:
|
|||||||
- ARCH=x86_64
|
- ARCH=x86_64
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- dub test --arch=$ARCH
|
- dub test -b unittest-cov --arch=$ARCH
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- bash <(curl -s https://codecov.io/bash)
|
||||||
|
35
README.md
35
README.md
@ -1,6 +1,8 @@
|
|||||||
# Tanya
|
# Tanya
|
||||||
|
|
||||||
[](https://travis-ci.org/caraus-ecms/tanya)
|
[](https://travis-ci.org/caraus-ecms/tanya)
|
||||||
|
[](https://ci.appveyor.com/project/belka-ew/tanya/branch/master)
|
||||||
|
[](https://codecov.io/gh/caraus-ecms/tanya)
|
||||||
[](https://code.dlang.org/packages/tanya)
|
[](https://code.dlang.org/packages/tanya)
|
||||||
[](https://code.dlang.org/packages/tanya)
|
[](https://code.dlang.org/packages/tanya)
|
||||||
[](https://raw.githubusercontent.com/caraus-ecms/tanya/master/LICENSE)
|
[](https://raw.githubusercontent.com/caraus-ecms/tanya/master/LICENSE)
|
||||||
@ -21,11 +23,12 @@ data structures and utilities that depend on the Garbage Collector in Phobos.
|
|||||||
Tanya consists of the following packages:
|
Tanya consists of the following packages:
|
||||||
|
|
||||||
* `async`: Event loop (epoll, kqueue and IOCP).
|
* `async`: Event loop (epoll, kqueue and IOCP).
|
||||||
* `container`: Queue, Vector, Singly linked list, buffers.
|
* `container`: Queue, Array, Singly and doubly linked lists, Buffers, UTF-8
|
||||||
|
string.
|
||||||
* `math`: Arbitrary precision integer and a set of functions.
|
* `math`: Arbitrary precision integer and a set of functions.
|
||||||
* `memory`: Tools for manual memory management (allocator, reference counting,
|
* `memory`: Tools for manual memory management (allocator, reference counting,
|
||||||
helper functions).
|
helper functions).
|
||||||
* `network`: URL-Parsing, sockets.
|
* `network`: URL-Parsing, sockets, utilities.
|
||||||
|
|
||||||
### Supported compilers
|
### Supported compilers
|
||||||
|
|
||||||
@ -38,20 +41,14 @@ helper functions).
|
|||||||
|
|
||||||
### Current status
|
### Current status
|
||||||
|
|
||||||
The library is currently under development, but the API is becoming gradually
|
Following modules are under development:
|
||||||
stable.
|
|
||||||
|
|
||||||
Following modules are coming soon:
|
| Feature | Branch | Build status |
|
||||||
|
|--------------|:------------:|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| Feature | Build status |
|
| BitVector | 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) |
|
||||||
| UTF-8 string | [](https://travis-ci.org/caraus-ecms/tanya) |
|
| File IO | io | [](https://travis-ci.org/caraus-ecms/tanya) [](https://ci.appveyor.com/project/belka-ew/tanya/branch/io) |
|
||||||
| BitVector | [](https://travis-ci.org/caraus-ecms/tanya) |
|
| Hash table | horton-table | [](https://travis-ci.org/caraus-ecms/tanya) [](https://ci.appveyor.com/project/belka-ew/tanya/branch/horton-table) |
|
||||||
| Hash table | N/A |
|
|
||||||
|
|
||||||
`math` package contains an arbitrary precision integer implementation that
|
|
||||||
needs more test cases, better performance and some additional features
|
|
||||||
(constructing from a string and an ubyte array, and converting it back).
|
|
||||||
|
|
||||||
### Further characteristics
|
### Further characteristics
|
||||||
|
|
||||||
@ -62,10 +59,14 @@ is being tested on Windows and FreeBSD as well.
|
|||||||
|
|
||||||
* The library isn't thread-safe. Thread-safity should be added later.
|
* The library isn't thread-safe. Thread-safity should be added later.
|
||||||
|
|
||||||
|
## Release management
|
||||||
|
|
||||||
|
3-week release cycle.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Since I'm mostly busy writing new code and implementing new features I would
|
Since I'm mostly busy writing new code and implementing new features I would
|
||||||
appreciate, if anyone uses the library. It would help me to improve the
|
appreciate, if anyone uses the library. It would help me to improve the
|
||||||
codebase and fix issues.
|
codebase and fix issues.
|
||||||
|
|
||||||
Feel free to contact me if you have any questions.
|
Feel free to contact me if you have any questions: info@caraus.de.
|
||||||
|
52
appveyor.yml
Normal file
52
appveyor.yml
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
platform: x64
|
||||||
|
os: Visual Studio 2017
|
||||||
|
|
||||||
|
environment:
|
||||||
|
matrix:
|
||||||
|
- DC: dmd
|
||||||
|
DVersion: 2.074.0
|
||||||
|
arch: x86
|
||||||
|
- DC: dmd
|
||||||
|
DVersion: 2.073.2
|
||||||
|
arch: x86
|
||||||
|
- DC: dmd
|
||||||
|
DVersion: 2.072.2
|
||||||
|
arch: x86
|
||||||
|
- DC: dmd
|
||||||
|
DVersion: 2.071.2
|
||||||
|
arch: x86
|
||||||
|
|
||||||
|
skip_tags: true
|
||||||
|
|
||||||
|
install:
|
||||||
|
- ps: function SetUpDCompiler
|
||||||
|
{
|
||||||
|
$env:toolchain = "msvc";
|
||||||
|
$version = $env:DVersion;
|
||||||
|
Invoke-WebRequest "http://downloads.dlang.org/releases/2.x/$($version)/dmd.$($version).windows.7z" -OutFile "c:\dmd.7z";
|
||||||
|
echo "finished.";
|
||||||
|
pushd c:\\;
|
||||||
|
7z x dmd.7z > $null;
|
||||||
|
popd;
|
||||||
|
}
|
||||||
|
- ps: SetUpDCompiler
|
||||||
|
|
||||||
|
- ps: if($env:DVersion -eq "2.071.2"){
|
||||||
|
Invoke-WebRequest "http://code.dlang.org/files/dub-1.2.1-windows-x86.zip" -OutFile "dub.zip";
|
||||||
|
7z x dub.zip -odub > $null;
|
||||||
|
Move-Item "dub/dub.exe" "C:\dmd2\windows\bin"
|
||||||
|
}
|
||||||
|
|
||||||
|
before_build:
|
||||||
|
- ps: $env:PATH += ";C:\dmd2\windows\bin;";
|
||||||
|
- call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\VsDevCmd.bat" -arch=%arch%
|
||||||
|
|
||||||
|
build_script:
|
||||||
|
- echo dummy build script - dont remove me
|
||||||
|
|
||||||
|
test_script:
|
||||||
|
- echo %DC%
|
||||||
|
- echo %PATH%
|
||||||
|
- 'dub --version'
|
||||||
|
- '%DC% --version'
|
||||||
|
- dub test --arch=x86 --compiler=%DC%
|
@ -18,7 +18,7 @@ import tanya.async.event.selector;
|
|||||||
import tanya.async.loop;
|
import tanya.async.loop;
|
||||||
import tanya.async.transport;
|
import tanya.async.transport;
|
||||||
import tanya.async.watcher;
|
import tanya.async.watcher;
|
||||||
import tanya.container.vector;
|
import tanya.container.array;
|
||||||
import tanya.memory;
|
import tanya.memory;
|
||||||
import tanya.memory.mmappool;
|
import tanya.memory.mmappool;
|
||||||
import tanya.network.socket;
|
import tanya.network.socket;
|
||||||
@ -37,7 +37,7 @@ extern (C) nothrow @nogc
|
|||||||
final class EpollLoop : SelectorLoop
|
final class EpollLoop : SelectorLoop
|
||||||
{
|
{
|
||||||
protected int fd;
|
protected int fd;
|
||||||
private Vector!epoll_event events;
|
private Array!epoll_event events;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the loop.
|
* Initializes the loop.
|
||||||
@ -49,7 +49,7 @@ final class EpollLoop : SelectorLoop
|
|||||||
throw defaultAllocator.make!BadLoopException("epoll initialization failed");
|
throw defaultAllocator.make!BadLoopException("epoll initialization failed");
|
||||||
}
|
}
|
||||||
super();
|
super();
|
||||||
events = Vector!epoll_event(maxEvents, MmapPool.instance);
|
events = Array!epoll_event(maxEvents, MmapPool.instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -50,7 +50,7 @@ import tanya.async.event.selector;
|
|||||||
import tanya.async.loop;
|
import tanya.async.loop;
|
||||||
import tanya.async.transport;
|
import tanya.async.transport;
|
||||||
import tanya.async.watcher;
|
import tanya.async.watcher;
|
||||||
import tanya.container.vector;
|
import tanya.container.array;
|
||||||
import tanya.memory;
|
import tanya.memory;
|
||||||
import tanya.memory.mmappool;
|
import tanya.memory.mmappool;
|
||||||
import tanya.network.socket;
|
import tanya.network.socket;
|
||||||
@ -116,8 +116,8 @@ extern(C) int kevent(int kq, const kevent_t *changelist, int nchanges,
|
|||||||
final class KqueueLoop : SelectorLoop
|
final class KqueueLoop : SelectorLoop
|
||||||
{
|
{
|
||||||
protected int fd;
|
protected int fd;
|
||||||
private Vector!kevent_t events;
|
private Array!kevent_t events;
|
||||||
private Vector!kevent_t changes;
|
private Array!kevent_t changes;
|
||||||
private size_t changeCount;
|
private size_t changeCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -139,8 +139,8 @@ final class KqueueLoop : SelectorLoop
|
|||||||
throw make!BadLoopException(defaultAllocator,
|
throw make!BadLoopException(defaultAllocator,
|
||||||
"kqueue initialization failed");
|
"kqueue initialization failed");
|
||||||
}
|
}
|
||||||
events = Vector!kevent_t(64, MmapPool.instance);
|
events = Array!kevent_t(64, MmapPool.instance);
|
||||||
changes = Vector!kevent_t(64, MmapPool.instance);
|
changes = Array!kevent_t(64, MmapPool.instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,7 +17,7 @@ import tanya.async.protocol;
|
|||||||
import tanya.async.transport;
|
import tanya.async.transport;
|
||||||
import tanya.async.watcher;
|
import tanya.async.watcher;
|
||||||
import tanya.container.buffer;
|
import tanya.container.buffer;
|
||||||
import tanya.container.vector;
|
import tanya.container.array;
|
||||||
import tanya.memory;
|
import tanya.memory;
|
||||||
import tanya.memory.mmappool;
|
import tanya.memory.mmappool;
|
||||||
import tanya.network.socket;
|
import tanya.network.socket;
|
||||||
@ -78,7 +78,8 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
|
|||||||
return cast(ConnectedSocket) socket_;
|
return cast(ConnectedSocket) socket_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private @property void socket(ConnectedSocket socket) pure nothrow @safe @nogc
|
private @property void socket(ConnectedSocket socket)
|
||||||
|
pure nothrow @safe @nogc
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assert(socket !is null);
|
assert(socket !is null);
|
||||||
@ -133,7 +134,9 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
|
|||||||
void close() @nogc
|
void close() @nogc
|
||||||
{
|
{
|
||||||
closing = true;
|
closing = true;
|
||||||
loop.reify(this, EventMask(Event.read, Event.write), EventMask(Event.write));
|
loop.reify(this,
|
||||||
|
EventMask(Event.read, Event.write),
|
||||||
|
EventMask(Event.write));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -205,20 +208,20 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
|
|||||||
abstract class SelectorLoop : Loop
|
abstract class SelectorLoop : Loop
|
||||||
{
|
{
|
||||||
/// Pending connections.
|
/// Pending connections.
|
||||||
protected Vector!SocketWatcher connections;
|
protected Array!SocketWatcher connections;
|
||||||
|
|
||||||
this() @nogc
|
this() @nogc
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
connections = Vector!SocketWatcher(maxEvents, MmapPool.instance);
|
connections = Array!SocketWatcher(maxEvents, MmapPool.instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
~this() @nogc
|
~this() @nogc
|
||||||
{
|
{
|
||||||
foreach (ref connection; connections)
|
foreach (ref connection; connections)
|
||||||
{
|
{
|
||||||
// We want to free only the transports. ConnectionWatcher are created by the
|
// We want to free only the transports. ConnectionWatcher are
|
||||||
// user and should be freed by himself.
|
// created by the user and should be freed by himself.
|
||||||
if (cast(StreamTransport) connection !is null)
|
if (cast(StreamTransport) connection !is null)
|
||||||
{
|
{
|
||||||
MmapPool.instance.dispose(connection);
|
MmapPool.instance.dispose(connection);
|
||||||
|
1646
source/tanya/container/array.d
Normal file
1646
source/tanya/container/array.d
Normal file
File diff suppressed because it is too large
Load Diff
@ -14,9 +14,18 @@ module tanya.container.entry;
|
|||||||
|
|
||||||
package struct SEntry(T)
|
package struct SEntry(T)
|
||||||
{
|
{
|
||||||
/// Item content.
|
// Item content.
|
||||||
T content;
|
T content;
|
||||||
|
|
||||||
/// Next item.
|
// Next item.
|
||||||
SEntry* next;
|
SEntry* next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
package struct DEntry(T)
|
||||||
|
{
|
||||||
|
// Item content.
|
||||||
|
T content;
|
||||||
|
|
||||||
|
// Previous and next item.
|
||||||
|
DEntry* next, prev;
|
||||||
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -12,7 +12,8 @@
|
|||||||
*/
|
*/
|
||||||
module tanya.container;
|
module tanya.container;
|
||||||
|
|
||||||
|
public import tanya.container.array;
|
||||||
public import tanya.container.buffer;
|
public import tanya.container.buffer;
|
||||||
public import tanya.container.list;
|
public import tanya.container.list;
|
||||||
public import tanya.container.vector;
|
public import tanya.container.string;
|
||||||
public import tanya.container.queue;
|
public import tanya.container.queue;
|
||||||
|
1517
source/tanya/container/string.d
Normal file
1517
source/tanya/container/string.d
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -16,7 +16,7 @@ import std.algorithm;
|
|||||||
import std.ascii;
|
import std.ascii;
|
||||||
import std.range;
|
import std.range;
|
||||||
import std.traits;
|
import std.traits;
|
||||||
import tanya.container.vector;
|
import tanya.container.array;
|
||||||
import tanya.memory;
|
import tanya.memory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -160,6 +160,45 @@ struct Integer
|
|||||||
assert(integer == 7383520307673030126);
|
assert(integer == 7383520307673030126);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the integer from a two's complement representation.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* R = Range type.
|
||||||
|
* value = Range.
|
||||||
|
* allocator = Allocator.
|
||||||
|
*
|
||||||
|
* Precondition: $(D_INLINECODE allocator !is null)
|
||||||
|
*/
|
||||||
|
this(R)(R value,
|
||||||
|
shared Allocator allocator = defaultAllocator)
|
||||||
|
if (isBidirectionalRange!R && hasLength!R
|
||||||
|
&& is(Unqual!(ElementType!R) == ubyte))
|
||||||
|
{
|
||||||
|
this(Sign.positive, value, allocator);
|
||||||
|
|
||||||
|
if (!value.empty && ((value.front & 0x80) != 0))
|
||||||
|
{
|
||||||
|
// Negative number.
|
||||||
|
opOpAssign!"-"(exp2(countBits()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
nothrow @safe @nogc unittest
|
||||||
|
{
|
||||||
|
{
|
||||||
|
ubyte[8] range = [ 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xdd, 0xee ];
|
||||||
|
auto integer = Integer(range[]);
|
||||||
|
assert(integer == 7383520307673030126);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ubyte[8] range = [ 0xe6, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xdd, 0xee ];
|
||||||
|
auto integer = Integer(range[]);
|
||||||
|
assert(integer == -1839851729181745682);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copies the integer.
|
* Copies the integer.
|
||||||
*/
|
*/
|
||||||
@ -178,12 +217,64 @@ struct Integer
|
|||||||
allocator.resize(this.rep, 0);
|
allocator.resize(this.rep, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static private const short[16] bitCounts = [
|
||||||
|
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
|
||||||
|
];
|
||||||
|
|
||||||
|
// Counts the number of LSBs before the first non-zero bit.
|
||||||
|
private ptrdiff_t countLSBs() const pure nothrow @safe @nogc
|
||||||
|
{
|
||||||
|
if (this.size == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ptrdiff_t bits;
|
||||||
|
for (bits = 0; (bits < this.size) && (this.rep[bits] == 0); ++bits)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
digit nonZero = this.rep[bits];
|
||||||
|
bits *= digitBitCount;
|
||||||
|
|
||||||
|
/* now scan this digit until a 1 is found */
|
||||||
|
if ((nonZero & 0x01) == 0)
|
||||||
|
{
|
||||||
|
digit bitCountsPos;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
bitCountsPos = nonZero & 0x0f;
|
||||||
|
bits += bitCounts[bitCountsPos];
|
||||||
|
nonZero >>= 4;
|
||||||
|
}
|
||||||
|
while (bitCountsPos == 0);
|
||||||
|
}
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns: Integer byte length.
|
* Returns: Number of bytes in the two's complement representation.
|
||||||
*/
|
*/
|
||||||
@property size_t length() const pure nothrow @safe @nogc
|
@property size_t length() const pure nothrow @safe @nogc
|
||||||
{
|
{
|
||||||
return (countBits() + 7) / 8; // Round up.
|
if (this.sign)
|
||||||
|
{
|
||||||
|
const bc = countBits();
|
||||||
|
auto length = bc + (8 - (bc & 0x07));
|
||||||
|
|
||||||
|
if (((countLSBs() + 1) == bc) && ((bc & 0x07) == 0))
|
||||||
|
{
|
||||||
|
--length;
|
||||||
|
}
|
||||||
|
return length / 8;
|
||||||
|
}
|
||||||
|
else if (this.size == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (countBits() / 8) + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -244,17 +335,11 @@ struct Integer
|
|||||||
/// Ditto.
|
/// Ditto.
|
||||||
ref Integer opAssign(T)(T value) nothrow @safe @nogc
|
ref Integer opAssign(T)(T value) nothrow @safe @nogc
|
||||||
if (is(T == Integer))
|
if (is(T == Integer))
|
||||||
{
|
|
||||||
if (this.allocator is value.allocator)
|
|
||||||
{
|
{
|
||||||
swap(this.rep, value.rep);
|
swap(this.rep, value.rep);
|
||||||
swap(this.sign, value.sign);
|
swap(this.sign, value.sign);
|
||||||
swap(this.size, value.size);
|
swap(this.size, value.size);
|
||||||
}
|
swap(this.allocator_, value.allocator_);
|
||||||
else
|
|
||||||
{
|
|
||||||
this = value;
|
|
||||||
}
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1343,79 +1428,72 @@ struct Integer
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector!ubyte toVector() const nothrow @safe @nogc
|
// Returns 2^^n.
|
||||||
|
private Integer exp2(size_t n) const nothrow @safe @nogc
|
||||||
{
|
{
|
||||||
Vector!ubyte vector;
|
auto ret = Integer(allocator);
|
||||||
bool firstBit;
|
const bytes = n / digitBitCount;
|
||||||
ubyte carry;
|
|
||||||
|
ret.grow(bytes + 1);
|
||||||
|
ret.size = bytes + 1;
|
||||||
|
ret.rep[bytes] = (cast(digit) 1) << (n % digitBitCount);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns: Two's complement representation of the integer.
|
||||||
|
*/
|
||||||
|
Array!ubyte toArray() const nothrow @safe @nogc
|
||||||
|
out (array)
|
||||||
|
{
|
||||||
|
assert(array.length == length);
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
Array!ubyte array;
|
||||||
|
|
||||||
if (this.size == 0)
|
if (this.size == 0)
|
||||||
{
|
{
|
||||||
return vector;
|
return array;
|
||||||
}
|
}
|
||||||
vector.reserve(this.size * digit.sizeof);
|
const bc = countBits();
|
||||||
|
const remainingBits = bc & 0x07;
|
||||||
|
|
||||||
// The first digit needs extra handling since it can have leading
|
array.reserve(bc / 8);
|
||||||
// non significant zeros.
|
if (remainingBits == 0)
|
||||||
int digitCount = digitBitCount - 8;
|
{
|
||||||
const first = this.rep[this.size - 1];
|
array.insertBack(ubyte.init);
|
||||||
const prevBitCount = ((this.size - 1) * digitBitCount);
|
|
||||||
const fullBytesBitCount = ((prevBitCount - 1) / 8 + 1) * 8;
|
|
||||||
|
|
||||||
// Find out the right alignment of the first byte.
|
|
||||||
if ((fullBytesBitCount - prevBitCount) == 0)
|
|
||||||
{
|
|
||||||
digitCount -= digit.sizeof * 8 - digitBitCount;
|
|
||||||
}
|
}
|
||||||
for (; digitCount >= 0; digitCount -= 8)
|
|
||||||
|
Integer tmp;
|
||||||
|
if (this.sign)
|
||||||
{
|
{
|
||||||
if (firstBit || ((first >> digitCount) != 0))
|
auto length = bc + (8 - remainingBits);
|
||||||
|
|
||||||
|
if (((countLSBs() + 1) == bc) && (remainingBits == 0))
|
||||||
{
|
{
|
||||||
firstBit = true;
|
length -= 8;
|
||||||
vector.insertBack(cast(ubyte) (first >> digitCount));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (digitCount >= -8)
|
tmp = exp2(length) + this;
|
||||||
{
|
|
||||||
carry = (first << -digitCount) & 0xff;
|
|
||||||
digitCount += digitBitCount;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
carry = 0;
|
tmp = this;
|
||||||
digitCount = digitBitCount - 8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach_reverse (d; this.rep[0 .. this.size - 1])
|
do
|
||||||
{
|
{
|
||||||
if (carry != 0) // Check the carry from the previous digit.
|
array.insertBack(cast(ubyte) (tmp.rep[0] & 0xff));
|
||||||
{
|
tmp >>= 8;
|
||||||
vector.insertBack(cast(ubyte) (carry | (d >> digitCount)));
|
|
||||||
digitCount -= 8;
|
|
||||||
}
|
|
||||||
// Write the digit by bytes.
|
|
||||||
for (; digitCount >= 0; digitCount -= 8)
|
|
||||||
{
|
|
||||||
vector.insertBack(cast(ubyte) (d >> digitCount));
|
|
||||||
}
|
|
||||||
// Check for an incomplete byte.
|
|
||||||
if (digitCount >= -8)
|
|
||||||
{
|
|
||||||
carry = (d << -digitCount) & 0xff;
|
|
||||||
digitCount += digitBitCount;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
carry = 0;
|
|
||||||
digitCount = digitBitCount - 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (carry != 0)
|
|
||||||
{
|
|
||||||
vector.insertBack(cast(ubyte) (carry >> (digitBitCount - digitCount)));
|
|
||||||
}
|
}
|
||||||
|
while (tmp != 0);
|
||||||
|
|
||||||
return vector;
|
array[].reverse();
|
||||||
|
|
||||||
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@ -1425,15 +1503,15 @@ struct Integer
|
|||||||
auto integer = Integer(0x66778899aabbddee);
|
auto integer = Integer(0x66778899aabbddee);
|
||||||
ubyte[8] expected = [ 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xdd, 0xee ];
|
ubyte[8] expected = [ 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xdd, 0xee ];
|
||||||
|
|
||||||
auto vector = integer.toVector();
|
auto array = integer.toArray();
|
||||||
assert(equal(vector[], expected[]));
|
assert(equal(array[], expected[]));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto integer = Integer(0x03);
|
auto integer = Integer(0x03);
|
||||||
ubyte[1] expected = [ 0x03 ];
|
ubyte[1] expected = [ 0x03 ];
|
||||||
|
|
||||||
auto vector = integer.toVector();
|
auto array = integer.toArray();
|
||||||
assert(equal(vector[], expected[]));
|
assert(equal(array[], expected[]));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
ubyte[63] expected = [
|
ubyte[63] expected = [
|
||||||
@ -1448,8 +1526,8 @@ struct Integer
|
|||||||
];
|
];
|
||||||
auto integer = Integer(Sign.positive, expected[]);
|
auto integer = Integer(Sign.positive, expected[]);
|
||||||
|
|
||||||
auto vector = integer.toVector();
|
auto array = integer.toArray();
|
||||||
assert(equal(vector[], expected[]));
|
assert(equal(array[], expected[]));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
ubyte[14] expected = [
|
ubyte[14] expected = [
|
||||||
@ -1458,8 +1536,8 @@ struct Integer
|
|||||||
];
|
];
|
||||||
auto integer = Integer(Sign.positive, expected[]);
|
auto integer = Integer(Sign.positive, expected[]);
|
||||||
|
|
||||||
auto vector = integer.toVector();
|
auto array = integer.toArray();
|
||||||
assert(equal(vector[], expected[]));
|
assert(equal(array[], expected[]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,19 @@ import std.range;
|
|||||||
import std.traits;
|
import std.traits;
|
||||||
import tanya.memory;
|
import tanya.memory;
|
||||||
|
|
||||||
package(tanya) final class RefCountedStore(T)
|
private template Payload(T)
|
||||||
|
{
|
||||||
|
static if (is(T == class) || is(T == interface) || isArray!T)
|
||||||
|
{
|
||||||
|
alias Payload = T;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
alias Payload = T*;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class RefCountedStore(T)
|
||||||
{
|
{
|
||||||
T payload;
|
T payload;
|
||||||
size_t counter = 1;
|
size_t counter = 1;
|
||||||
@ -34,7 +46,7 @@ package(tanya) final class RefCountedStore(T)
|
|||||||
mixin("return " ~ op ~ "counter;");
|
mixin("return " ~ op ~ "counter;");
|
||||||
}
|
}
|
||||||
|
|
||||||
int opCmp(size_t counter)
|
int opCmp(const size_t counter)
|
||||||
{
|
{
|
||||||
if (this.counter > counter)
|
if (this.counter > counter)
|
||||||
{
|
{
|
||||||
@ -50,7 +62,7 @@ package(tanya) final class RefCountedStore(T)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int opEquals(size_t counter)
|
int opEquals(const size_t counter)
|
||||||
{
|
{
|
||||||
return this.counter == counter;
|
return this.counter == counter;
|
||||||
}
|
}
|
||||||
@ -81,15 +93,7 @@ private void unifiedDeleter(T)(RefCountedStore!T storage,
|
|||||||
*/
|
*/
|
||||||
struct RefCounted(T)
|
struct RefCounted(T)
|
||||||
{
|
{
|
||||||
static if (is(T == class) || is(T == interface) || isArray!T)
|
private alias Storage = RefCountedStore!(Payload!T);
|
||||||
{
|
|
||||||
private alias Payload = T;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
private alias Payload = T*;
|
|
||||||
}
|
|
||||||
private alias Storage = RefCountedStore!Payload;
|
|
||||||
|
|
||||||
private Storage storage;
|
private Storage storage;
|
||||||
private void function(Storage storage,
|
private void function(Storage storage,
|
||||||
@ -112,13 +116,18 @@ struct RefCounted(T)
|
|||||||
*
|
*
|
||||||
* Precondition: $(D_INLINECODE allocator !is null)
|
* Precondition: $(D_INLINECODE allocator !is null)
|
||||||
*/
|
*/
|
||||||
this()(auto ref Payload value,
|
this()(auto ref Payload!T value,
|
||||||
shared Allocator allocator = defaultAllocator)
|
shared Allocator allocator = defaultAllocator)
|
||||||
{
|
{
|
||||||
this(allocator);
|
this(allocator);
|
||||||
this.storage = allocator.make!Storage();
|
this.storage = allocator.make!Storage();
|
||||||
this.deleter = &separateDeleter!Payload;
|
this.deleter = &separateDeleter!(Payload!T);
|
||||||
|
|
||||||
move(value, this.storage.payload);
|
move(value, this.storage.payload);
|
||||||
|
static if (__traits(isRef, value))
|
||||||
|
{
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
@ -139,7 +148,7 @@ struct RefCounted(T)
|
|||||||
{
|
{
|
||||||
if (count != 0)
|
if (count != 0)
|
||||||
{
|
{
|
||||||
++storage;
|
++this.storage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,7 +161,7 @@ struct RefCounted(T)
|
|||||||
{
|
{
|
||||||
if (this.storage !is null && !(this.storage.counter && --this.storage))
|
if (this.storage !is null && !(this.storage.counter && --this.storage))
|
||||||
{
|
{
|
||||||
deleter(storage, allocator);
|
deleter(this.storage, allocator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,34 +172,34 @@ struct RefCounted(T)
|
|||||||
* If it is the last reference of the previously owned object,
|
* If it is the last reference of the previously owned object,
|
||||||
* it will be destroyed.
|
* it will be destroyed.
|
||||||
*
|
*
|
||||||
* To reset the $(D_PSYMBOL RefCounted) assign $(D_KEYWORD null).
|
* To reset $(D_PSYMBOL RefCounted) assign $(D_KEYWORD null).
|
||||||
*
|
*
|
||||||
* If the allocator wasn't set before, $(D_PSYMBOL defaultAllocator) will
|
* If the allocator wasn't set before, $(D_PSYMBOL defaultAllocator) will
|
||||||
* be used. If you need a different allocator, create a new
|
* be used. If you need a different allocator, create a new
|
||||||
* $(D_PSYMBOL RefCounted) and assign it.
|
* $(D_PSYMBOL RefCounted) and assign it.
|
||||||
*
|
*
|
||||||
* Params:
|
* Params:
|
||||||
* rhs = $(D_KEYWORD this).
|
* rhs = New object.
|
||||||
|
*
|
||||||
|
* Returns: $(D_KEYWORD this).
|
||||||
*/
|
*/
|
||||||
ref typeof(this) opAssign()(auto ref Payload rhs)
|
ref typeof(this) opAssign()(auto ref Payload!T rhs)
|
||||||
{
|
{
|
||||||
if (this.storage is null)
|
if (this.storage is null)
|
||||||
{
|
{
|
||||||
this.storage = allocator.make!Storage();
|
this.storage = allocator.make!Storage();
|
||||||
this.deleter = &separateDeleter!Payload;
|
this.deleter = &separateDeleter!(Payload!T);
|
||||||
}
|
}
|
||||||
else if (this.storage > 1)
|
else if (this.storage > 1)
|
||||||
{
|
{
|
||||||
--this.storage;
|
--this.storage;
|
||||||
this.storage = allocator.make!Storage();
|
this.storage = allocator.make!Storage();
|
||||||
this.deleter = &separateDeleter!Payload;
|
this.deleter = &separateDeleter!(Payload!T);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Created with refCounted. Always destroyed togethter with the pointer.
|
|
||||||
assert(this.storage.counter != 0);
|
|
||||||
finalize(this.storage.payload);
|
finalize(this.storage.payload);
|
||||||
this.storage.payload = Payload.init;
|
this.storage.payload = Payload!T.init;
|
||||||
}
|
}
|
||||||
move(rhs, this.storage.payload);
|
move(rhs, this.storage.payload);
|
||||||
return this;
|
return this;
|
||||||
@ -206,15 +215,13 @@ struct RefCounted(T)
|
|||||||
else if (this.storage > 1)
|
else if (this.storage > 1)
|
||||||
{
|
{
|
||||||
--this.storage;
|
--this.storage;
|
||||||
this.storage = null;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Created with refCounted. Always destroyed togethter with the pointer.
|
deleter(this.storage, allocator);
|
||||||
assert(this.storage.counter != 0);
|
|
||||||
finalize(this.storage.payload);
|
|
||||||
this.storage.payload = Payload.init;
|
|
||||||
}
|
}
|
||||||
|
this.storage = null;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,8 +236,10 @@ struct RefCounted(T)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns: Reference to the owned object.
|
* Returns: Reference to the owned object.
|
||||||
|
*
|
||||||
|
* Precondition: $(D_INLINECODE cound > 0).
|
||||||
*/
|
*/
|
||||||
inout(Payload) get() inout pure nothrow @safe @nogc
|
Payload!T get() pure nothrow @safe @nogc
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assert(count > 0, "Attempted to access an uninitialized reference.");
|
assert(count > 0, "Attempted to access an uninitialized reference.");
|
||||||
@ -240,7 +249,7 @@ struct RefCounted(T)
|
|||||||
return storage.payload;
|
return storage.payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
static if (isPointer!Payload)
|
version (D_Ddoc)
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Params:
|
* Params:
|
||||||
@ -251,6 +260,11 @@ struct RefCounted(T)
|
|||||||
*
|
*
|
||||||
* Returns: Reference to the pointed value.
|
* Returns: Reference to the pointed value.
|
||||||
*/
|
*/
|
||||||
|
ref T opUnary(string op)()
|
||||||
|
if (op == "*");
|
||||||
|
}
|
||||||
|
else static if (isPointer!(Payload!T))
|
||||||
|
{
|
||||||
ref T opUnary(string op)()
|
ref T opUnary(string op)()
|
||||||
if (op == "*")
|
if (op == "*")
|
||||||
{
|
{
|
||||||
@ -352,6 +366,44 @@ private unittest
|
|||||||
assert(rc.count == 1);
|
assert(rc.count == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private unittest
|
||||||
|
{
|
||||||
|
auto rc = defaultAllocator.refCounted!int(5);
|
||||||
|
assert(rc.count == 1);
|
||||||
|
|
||||||
|
void func(RefCounted!int rc)
|
||||||
|
{
|
||||||
|
assert(rc.count == 2);
|
||||||
|
rc = null;
|
||||||
|
assert(!rc.isInitialized);
|
||||||
|
assert(rc.count == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(rc.count == 1);
|
||||||
|
func(rc);
|
||||||
|
assert(rc.count == 1);
|
||||||
|
|
||||||
|
rc = null;
|
||||||
|
assert(!rc.isInitialized);
|
||||||
|
assert(rc.count == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private unittest
|
||||||
|
{
|
||||||
|
auto rc = defaultAllocator.refCounted!int(5);
|
||||||
|
assert(*rc == 5);
|
||||||
|
|
||||||
|
void func(RefCounted!int rc)
|
||||||
|
{
|
||||||
|
assert(rc.count == 2);
|
||||||
|
rc = defaultAllocator.refCounted!int(4);
|
||||||
|
assert(*rc == 4);
|
||||||
|
assert(rc.count == 1);
|
||||||
|
}
|
||||||
|
func(rc);
|
||||||
|
assert(*rc == 5);
|
||||||
|
}
|
||||||
|
|
||||||
private unittest
|
private unittest
|
||||||
{
|
{
|
||||||
static assert(is(typeof(RefCounted!int.storage.payload) == int*));
|
static assert(is(typeof(RefCounted!int.storage.payload) == int*));
|
||||||
@ -414,13 +466,13 @@ body
|
|||||||
auto ptr = (() @trusted => (cast(T*) mem[storageSize .. $].ptr))();
|
auto ptr = (() @trusted => (cast(T*) mem[storageSize .. $].ptr))();
|
||||||
rc.storage.payload = emplace!T(ptr, args);
|
rc.storage.payload = emplace!T(ptr, args);
|
||||||
}
|
}
|
||||||
rc.deleter = &unifiedDeleter!(RefCounted!T.Payload);
|
rc.deleter = &unifiedDeleter!(Payload!T);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new array with $(D_PARAM size) elements and wraps it in a
|
* Constructs a new array with $(D_PARAM size) elements and wraps it in a
|
||||||
* $(D_PSYMBOL RefCounted) using.
|
* $(D_PSYMBOL RefCounted).
|
||||||
*
|
*
|
||||||
* Params:
|
* Params:
|
||||||
* T = Array type.
|
* T = Array type.
|
||||||
@ -432,7 +484,8 @@ body
|
|||||||
* Precondition: $(D_INLINECODE allocator !is null
|
* Precondition: $(D_INLINECODE allocator !is null
|
||||||
* && size <= size_t.max / ElementType!T.sizeof)
|
* && size <= size_t.max / ElementType!T.sizeof)
|
||||||
*/
|
*/
|
||||||
RefCounted!T refCounted(T)(shared Allocator allocator, const size_t size)
|
RefCounted!T refCounted(T)(shared Allocator allocator, const size_t size)
|
||||||
|
@trusted
|
||||||
if (isArray!T)
|
if (isArray!T)
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@ -441,8 +494,7 @@ in
|
|||||||
}
|
}
|
||||||
body
|
body
|
||||||
{
|
{
|
||||||
auto payload = cast(T) allocator.allocate(ElementType!T.sizeof * size);
|
auto payload = allocator.resize!(ElementType!T)(null, size);
|
||||||
initializeAll(payload);
|
|
||||||
return RefCounted!T(payload, allocator);
|
return RefCounted!T(payload, allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -495,3 +547,266 @@ private @nogc unittest
|
|||||||
auto rc = defaultAllocator.refCounted!(int[])(5);
|
auto rc = defaultAllocator.refCounted!(int[])(5);
|
||||||
assert(rc.length == 5);
|
assert(rc.length == 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private @nogc unittest
|
||||||
|
{
|
||||||
|
static bool destroyed = false;
|
||||||
|
|
||||||
|
struct F
|
||||||
|
{
|
||||||
|
~this() @nogc
|
||||||
|
{
|
||||||
|
destroyed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto rc = defaultAllocator.refCounted!F();
|
||||||
|
}
|
||||||
|
assert(destroyed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* $(D_PSYMBOL Scoped) stores an object that gets destroyed at the end of its scope.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* T = Value type.
|
||||||
|
*/
|
||||||
|
struct Scoped(T)
|
||||||
|
{
|
||||||
|
private Payload!T payload;
|
||||||
|
|
||||||
|
invariant
|
||||||
|
{
|
||||||
|
assert(payload is null || allocator_ !is null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes ownership over $(D_PARAM value), setting the counter to 1.
|
||||||
|
* $(D_PARAM value) may be a pointer, an object or a dynamic array.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* value = Value whose ownership is taken over.
|
||||||
|
* allocator = Allocator used to destroy the $(D_PARAM value) and to
|
||||||
|
* allocate/deallocate internal storage.
|
||||||
|
*
|
||||||
|
* Precondition: $(D_INLINECODE allocator !is null)
|
||||||
|
*/
|
||||||
|
this()(auto ref Payload!T value,
|
||||||
|
shared Allocator allocator = defaultAllocator)
|
||||||
|
{
|
||||||
|
this(allocator);
|
||||||
|
|
||||||
|
move(value, this.payload);
|
||||||
|
static if (__traits(isRef, value))
|
||||||
|
{
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ditto.
|
||||||
|
this(shared Allocator allocator)
|
||||||
|
in
|
||||||
|
{
|
||||||
|
assert(allocator !is null);
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
this.allocator_ = allocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* $(D_PSYMBOL Scoped) is noncopyable.
|
||||||
|
*/
|
||||||
|
@disable this(this);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys the owned object.
|
||||||
|
*/
|
||||||
|
~this()
|
||||||
|
{
|
||||||
|
if (this.payload !is null)
|
||||||
|
{
|
||||||
|
allocator.dispose(this.payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialized this $(D_PARAM Scoped) and takes ownership over
|
||||||
|
* $(D_PARAM rhs).
|
||||||
|
*
|
||||||
|
* To reset $(D_PSYMBOL Scoped) assign $(D_KEYWORD null).
|
||||||
|
*
|
||||||
|
* If the allocator wasn't set before, $(D_PSYMBOL defaultAllocator) will
|
||||||
|
* be used. If you need a different allocator, create a new
|
||||||
|
* $(D_PSYMBOL Scoped) and assign it.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* rhs = New object.
|
||||||
|
*
|
||||||
|
* Returns: $(D_KEYWORD this).
|
||||||
|
*/
|
||||||
|
ref typeof(this) opAssign()(auto ref Payload!T rhs)
|
||||||
|
{
|
||||||
|
allocator.dispose(this.payload);
|
||||||
|
move(rhs, this.payload);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ditto.
|
||||||
|
ref typeof(this) opAssign(typeof(null))
|
||||||
|
{
|
||||||
|
allocator.dispose(this.payload);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ditto.
|
||||||
|
ref typeof(this) opAssign(typeof(this) rhs)
|
||||||
|
{
|
||||||
|
swap(this.allocator_, rhs.allocator_);
|
||||||
|
swap(this.payload, rhs.payload);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns: Reference to the owned object.
|
||||||
|
*/
|
||||||
|
Payload!T get() pure nothrow @safe @nogc
|
||||||
|
{
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
version (D_Ddoc)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Params:
|
||||||
|
* op = Operation.
|
||||||
|
*
|
||||||
|
* Dereferences the pointer. It is defined only for pointers, not for
|
||||||
|
* reference types like classes, that can be accessed directly.
|
||||||
|
*
|
||||||
|
* Returns: Reference to the pointed value.
|
||||||
|
*/
|
||||||
|
ref T opUnary(string op)()
|
||||||
|
if (op == "*");
|
||||||
|
}
|
||||||
|
else static if (isPointer!(Payload!T))
|
||||||
|
{
|
||||||
|
ref T opUnary(string op)()
|
||||||
|
if (op == "*")
|
||||||
|
{
|
||||||
|
return *payload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mixin DefaultAllocator;
|
||||||
|
alias get this;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
@nogc unittest
|
||||||
|
{
|
||||||
|
auto p = defaultAllocator.make!int(5);
|
||||||
|
auto s = Scoped!int(p, defaultAllocator);
|
||||||
|
assert(p is null);
|
||||||
|
assert(*s == 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
@nogc unittest
|
||||||
|
{
|
||||||
|
static bool destroyed = false;
|
||||||
|
|
||||||
|
struct F
|
||||||
|
{
|
||||||
|
~this() @nogc
|
||||||
|
{
|
||||||
|
destroyed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto s = Scoped!F(defaultAllocator.make!F(), defaultAllocator);
|
||||||
|
}
|
||||||
|
assert(destroyed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new object of type $(D_PARAM T) and wraps it in a
|
||||||
|
* $(D_PSYMBOL Scoped) using $(D_PARAM args) as the parameter list for
|
||||||
|
* the constructor of $(D_PARAM T).
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* T = Type of the constructed object.
|
||||||
|
* A = Types of the arguments to the constructor of $(D_PARAM T).
|
||||||
|
* allocator = Allocator.
|
||||||
|
* args = Constructor arguments of $(D_PARAM T).
|
||||||
|
*
|
||||||
|
* Returns: Newly created $(D_PSYMBOL Scoped!T).
|
||||||
|
*
|
||||||
|
* Precondition: $(D_INLINECODE allocator !is null)
|
||||||
|
*/
|
||||||
|
Scoped!T scoped(T, A...)(shared Allocator allocator, auto ref A args)
|
||||||
|
if (!is(T == interface) && !isAbstractClass!T
|
||||||
|
&& !isAssociativeArray!T && !isArray!T)
|
||||||
|
in
|
||||||
|
{
|
||||||
|
assert(allocator !is null);
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
auto payload = allocator.make!(T, shared Allocator, A)(args);
|
||||||
|
return Scoped!T(payload, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new array with $(D_PARAM size) elements and wraps it in a
|
||||||
|
* $(D_PSYMBOL Scoped).
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* T = Array type.
|
||||||
|
* size = Array size.
|
||||||
|
* allocator = Allocator.
|
||||||
|
*
|
||||||
|
* Returns: Newly created $(D_PSYMBOL Scoped!T).
|
||||||
|
*
|
||||||
|
* Precondition: $(D_INLINECODE allocator !is null
|
||||||
|
* && size <= size_t.max / ElementType!T.sizeof)
|
||||||
|
*/
|
||||||
|
Scoped!T scoped(T)(shared Allocator allocator, const size_t size)
|
||||||
|
@trusted
|
||||||
|
if (isArray!T)
|
||||||
|
in
|
||||||
|
{
|
||||||
|
assert(allocator !is null);
|
||||||
|
assert(size <= size_t.max / ElementType!T.sizeof);
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
auto payload = allocator.resize!(ElementType!T)(null, size);
|
||||||
|
return Scoped!T(payload, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
private unittest
|
||||||
|
{
|
||||||
|
static assert(is(typeof(defaultAllocator.scoped!B(5))));
|
||||||
|
static assert(is(typeof(defaultAllocator.scoped!(int[])(5))));
|
||||||
|
}
|
||||||
|
|
||||||
|
private unittest
|
||||||
|
{
|
||||||
|
auto s = defaultAllocator.scoped!int(5);
|
||||||
|
assert(*s == 5);
|
||||||
|
|
||||||
|
s = null;
|
||||||
|
assert(s is null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private unittest
|
||||||
|
{
|
||||||
|
auto s = defaultAllocator.scoped!int(5);
|
||||||
|
assert(*s == 5);
|
||||||
|
|
||||||
|
s = defaultAllocator.scoped!int(4);
|
||||||
|
assert(*s == 4);
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user