Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
531cae51a3 | |||
1b203507f6 | |||
99e06e0d04 | |||
158a47d54a | |||
5865e355cd | |||
a94b1b0af4 | |||
3df4eb6259 | |||
a332d727af | |||
8241943a58 | |||
d54e06f43c | |||
5e901f505c | |||
533fa3b023 | |||
adf2d8b689 | |||
74ece7ddf4 | |||
411e45ec5c | |||
f51e9405c9 | |||
de15281ccb | |||
a86b6690f0 | |||
15f7994187 | |||
37b0afe290 | |||
cd9960db2a | |||
7357503c5a | |||
173ae115ee | |||
7561b964d3 | |||
c663703221 |
@ -7,10 +7,10 @@ os:
|
||||
language: d
|
||||
|
||||
d:
|
||||
- dmd-2.080.0
|
||||
- dmd-2.081.0
|
||||
- dmd-2.080.1
|
||||
- dmd-2.079.1
|
||||
- dmd-2.078.3
|
||||
- dmd-2.077.1
|
||||
|
||||
env:
|
||||
matrix:
|
||||
@ -23,7 +23,7 @@ addons:
|
||||
- gcc-multilib
|
||||
|
||||
before_script:
|
||||
- if [ "`$DC --version | head -n 1 | grep 'v2.080.0'`" ]; then
|
||||
- if [ "`$DC --version | head -n 1 | grep 'v2.081.0'`" ]; then
|
||||
export UNITTEST="unittest-cov";
|
||||
fi
|
||||
|
||||
|
@ -32,6 +32,8 @@ types.
|
||||
* `encoding`: This package provides tools to work with text encodings.
|
||||
* `exception`: Common exceptions and errors.
|
||||
* `format`: Formatting and conversion functions.
|
||||
* `functional`: Functions that manipulate other functions and their argument
|
||||
lists.
|
||||
* `hash`: Hash algorithms.
|
||||
* `math`: Arbitrary precision integer and a set of functions.
|
||||
* `memory`: Tools for manual memory management (allocators, smart pointers).
|
||||
@ -172,10 +174,10 @@ parameter is used)
|
||||
|
||||
| DMD | GCC |
|
||||
|:-------:|:---------:|
|
||||
| 2.080.0 | *master* |
|
||||
| 2.081.1 | *master* |
|
||||
| 2.080.1 | |
|
||||
| 2.079.1 | |
|
||||
| 2.078.3 | |
|
||||
| 2.077.1 | |
|
||||
|
||||
### Release management
|
||||
|
||||
|
16
appveyor.yml
16
appveyor.yml
@ -4,10 +4,16 @@ os: Visual Studio 2015
|
||||
environment:
|
||||
matrix:
|
||||
- DC: dmd
|
||||
DVersion: 2.080.0
|
||||
DVersion: 2.081.1
|
||||
arch: x64
|
||||
- DC: dmd
|
||||
DVersion: 2.080.0
|
||||
DVersion: 2.081.1
|
||||
arch: x86
|
||||
- DC: dmd
|
||||
DVersion: 2.080.1
|
||||
arch: x64
|
||||
- DC: dmd
|
||||
DVersion: 2.080.1
|
||||
arch: x86
|
||||
- DC: dmd
|
||||
DVersion: 2.079.1
|
||||
@ -21,12 +27,6 @@ environment:
|
||||
- DC: dmd
|
||||
DVersion: 2.078.3
|
||||
arch: x86
|
||||
- DC: dmd
|
||||
DVersion: 2.077.1
|
||||
arch: x64
|
||||
- DC: dmd
|
||||
DVersion: 2.077.1
|
||||
arch: x86
|
||||
|
||||
skip_tags: true
|
||||
|
||||
|
@ -47,6 +47,7 @@ _D5tanya6memory2op9cmpMemoryFNaNbNixAvxAvZi:
|
||||
|
||||
aligned_1: // Compare the remaining bytes
|
||||
mov %rdx, %rcx
|
||||
cmp $0x0, %rcx
|
||||
|
||||
repe cmpsb
|
||||
jl less
|
||||
|
@ -16,6 +16,7 @@ module tanya.algorithm.comparison;
|
||||
|
||||
import tanya.algorithm.mutation;
|
||||
import tanya.math : isNaN;
|
||||
import tanya.memory.op;
|
||||
import tanya.meta.metafunction;
|
||||
import tanya.meta.trait;
|
||||
import tanya.meta.transform;
|
||||
@ -270,3 +271,63 @@ if (isForwardRange!Range && isOrderingComparable!(ElementType!Range))
|
||||
assert(max(s2, s3).s == 3);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares element-wise two ranges for equality.
|
||||
*
|
||||
* If the ranges have different lengths, they aren't equal.
|
||||
*
|
||||
* Params:
|
||||
* R1 = First range type.
|
||||
* R2 = Second range type.
|
||||
* r1 = First range.
|
||||
* r2 = Second range.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if both ranges are equal, $(D_KEYWORD false)
|
||||
* otherwise.
|
||||
*/
|
||||
bool equal(R1, R2)(R1 r1, R2 r2)
|
||||
if (allSatisfy!(isInputRange, R1, R2) && is(typeof(r1.front == r2.front)))
|
||||
{
|
||||
static if (isDynamicArray!R1
|
||||
&& is(R1 == R2)
|
||||
&& __traits(isPOD, ElementType!R1))
|
||||
{
|
||||
return cmp(r1, r2) == 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
static if (hasLength!R1 && hasLength!R2)
|
||||
{
|
||||
if (r1.length != r2.length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (; !r1.empty && !r2.empty; r1.popFront(), r2.popFront())
|
||||
{
|
||||
if (r1.front != r2.front)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static if (hasLength!R1 && hasLength!R2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return r1.empty && r2.empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
int[2] range1 = [1, 2];
|
||||
assert(equal(range1[], range1[]));
|
||||
|
||||
int[3] range2 = [1, 2, 3];
|
||||
assert(!equal(range1[], range2[]));
|
||||
}
|
||||
|
@ -84,12 +84,12 @@ enum : short
|
||||
|
||||
struct kevent_t
|
||||
{
|
||||
uintptr_t ident; /* identifier for this event */
|
||||
short filter; /* filter for event */
|
||||
ushort flags;
|
||||
uint fflags;
|
||||
intptr_t data;
|
||||
void *udata; /* opaque user data identifier */
|
||||
uintptr_t ident; // Identifier for this event
|
||||
short filter; // Filter for event
|
||||
ushort flags;
|
||||
uint fflags;
|
||||
intptr_t data;
|
||||
void* udata; // Opaque user data identifier
|
||||
}
|
||||
|
||||
enum
|
||||
@ -168,7 +168,7 @@ final class KqueueLoop : SelectorLoop
|
||||
filter,
|
||||
flags,
|
||||
0U,
|
||||
0L,
|
||||
0,
|
||||
null);
|
||||
++changeCount;
|
||||
}
|
||||
|
@ -13,50 +13,52 @@
|
||||
*
|
||||
* class EchoProtocol : TransmissionControlProtocol
|
||||
* {
|
||||
* private DuplexTransport transport;
|
||||
* private DuplexTransport transport;
|
||||
*
|
||||
* void received(in ubyte[] data) @nogc
|
||||
* {
|
||||
* transport.write(data);
|
||||
* }
|
||||
* void received(in ubyte[] data) @nogc
|
||||
* {
|
||||
* ubyte[512] buffer;
|
||||
* buffer[0 .. data.length] = data;
|
||||
* transport.write(buffer[]);
|
||||
* }
|
||||
*
|
||||
* void connected(DuplexTransport transport) @nogc
|
||||
* {
|
||||
* this.transport = transport;
|
||||
* }
|
||||
* void connected(DuplexTransport transport) @nogc
|
||||
* {
|
||||
* this.transport = transport;
|
||||
* }
|
||||
*
|
||||
* void disconnected(SocketException e) @nogc
|
||||
* {
|
||||
* }
|
||||
* void disconnected(SocketException e) @nogc
|
||||
* {
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* void main()
|
||||
* {
|
||||
* auto address = defaultAllocator.make!InternetAddress("127.0.0.1", cast(ushort) 8192);
|
||||
* auto address = defaultAllocator.make!InternetAddress("127.0.0.1", cast(ushort) 8192);
|
||||
*
|
||||
* version (Windows)
|
||||
* {
|
||||
* auto sock = defaultAllocator.make!OverlappedStreamSocket(AddressFamily.inet);
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* auto sock = defaultAllocator.make!StreamSocket(AddressFamily.inet);
|
||||
* sock.blocking = false;
|
||||
* }
|
||||
* version (Windows)
|
||||
* {
|
||||
* auto sock = defaultAllocator.make!OverlappedStreamSocket(AddressFamily.inet);
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* auto sock = defaultAllocator.make!StreamSocket(AddressFamily.inet);
|
||||
* sock.blocking = false;
|
||||
* }
|
||||
*
|
||||
* sock.bind(address);
|
||||
* sock.listen(5);
|
||||
* sock.bind(address);
|
||||
* sock.listen(5);
|
||||
*
|
||||
* auto io = defaultAllocator.make!ConnectionWatcher(sock);
|
||||
* io.setProtocol!EchoProtocol;
|
||||
* auto io = defaultAllocator.make!ConnectionWatcher(sock);
|
||||
* io.setProtocol!EchoProtocol;
|
||||
*
|
||||
* defaultLoop.start(io);
|
||||
* defaultLoop.run();
|
||||
* defaultLoop.start(io);
|
||||
* defaultLoop.run();
|
||||
*
|
||||
* sock.shutdown();
|
||||
* defaultAllocator.dispose(io);
|
||||
* defaultAllocator.dispose(sock);
|
||||
* defaultAllocator.dispose(address);
|
||||
* sock.shutdown();
|
||||
* defaultAllocator.dispose(io);
|
||||
* defaultAllocator.dispose(sock);
|
||||
* defaultAllocator.dispose(address);
|
||||
* }
|
||||
* ---
|
||||
*
|
||||
|
@ -15,7 +15,6 @@
|
||||
module tanya.container.array;
|
||||
|
||||
import core.checkedint;
|
||||
import std.algorithm.comparison : equal;
|
||||
import std.algorithm.mutation : bringToFront,
|
||||
copy,
|
||||
fill,
|
||||
|
@ -51,7 +51,7 @@ version (unittest)
|
||||
* T = Buffer type.
|
||||
*/
|
||||
struct ReadBuffer(T = ubyte)
|
||||
if (isScalarType!T)
|
||||
if (isScalarType!T)
|
||||
{
|
||||
/// Internal buffer.
|
||||
private T[] buffer_;
|
||||
@ -66,16 +66,16 @@ struct ReadBuffer(T = ubyte)
|
||||
private size_t ring;
|
||||
|
||||
/// Available space.
|
||||
private immutable size_t minAvailable = 1024;
|
||||
private size_t minAvailable = 1024;
|
||||
|
||||
/// Size by which the buffer will grow.
|
||||
private immutable size_t blockSize = 8192;
|
||||
private size_t blockSize = 8192;
|
||||
|
||||
invariant
|
||||
{
|
||||
assert(length_ <= buffer_.length);
|
||||
assert(blockSize > 0);
|
||||
assert(minAvailable > 0);
|
||||
assert(this.length_ <= this.buffer_.length);
|
||||
assert(this.blockSize > 0);
|
||||
assert(this.minAvailable > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -89,14 +89,14 @@ struct ReadBuffer(T = ubyte)
|
||||
* $(D_PSYMBOL free) < $(D_PARAM minAvailable)).
|
||||
* allocator = Allocator.
|
||||
*/
|
||||
this(in size_t size,
|
||||
in size_t minAvailable = 1024,
|
||||
this(size_t size,
|
||||
size_t minAvailable = 1024,
|
||||
shared Allocator allocator = defaultAllocator) @trusted
|
||||
{
|
||||
this(allocator);
|
||||
this.minAvailable = minAvailable;
|
||||
this.blockSize = size;
|
||||
buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof);
|
||||
this.buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof);
|
||||
}
|
||||
|
||||
/// ditto
|
||||
@ -115,7 +115,7 @@ struct ReadBuffer(T = ubyte)
|
||||
*/
|
||||
~this() @trusted
|
||||
{
|
||||
allocator.deallocate(buffer_);
|
||||
allocator.deallocate(this.buffer_);
|
||||
}
|
||||
|
||||
///
|
||||
@ -131,7 +131,7 @@ struct ReadBuffer(T = ubyte)
|
||||
*/
|
||||
@property size_t capacity() const
|
||||
{
|
||||
return buffer_.length;
|
||||
return this.buffer_.length;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -139,7 +139,7 @@ struct ReadBuffer(T = ubyte)
|
||||
*/
|
||||
@property size_t length() const
|
||||
{
|
||||
return length_ - start;
|
||||
return this.length_ - start;
|
||||
}
|
||||
|
||||
/// ditto
|
||||
@ -152,7 +152,7 @@ struct ReadBuffer(T = ubyte)
|
||||
*/
|
||||
void clear()
|
||||
{
|
||||
start = length_ = ring;
|
||||
start = this.length_ = ring;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -187,10 +187,10 @@ struct ReadBuffer(T = ubyte)
|
||||
*
|
||||
* Returns: $(D_KEYWORD this).
|
||||
*/
|
||||
ref ReadBuffer opOpAssign(string op)(in size_t length)
|
||||
ref ReadBuffer opOpAssign(string op)(size_t length)
|
||||
if (op == "+")
|
||||
{
|
||||
length_ += length;
|
||||
this.length_ += length;
|
||||
ring = start;
|
||||
return this;
|
||||
}
|
||||
@ -234,9 +234,9 @@ struct ReadBuffer(T = ubyte)
|
||||
*
|
||||
* Returns: Array between $(D_PARAM start) and $(D_PARAM end).
|
||||
*/
|
||||
T[] opSlice(in size_t start, in size_t end)
|
||||
T[] opSlice(size_t start, size_t end)
|
||||
{
|
||||
return buffer_[this.start + start .. this.start + end];
|
||||
return this.buffer_[this.start + start .. this.start + end];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -250,23 +250,24 @@ struct ReadBuffer(T = ubyte)
|
||||
{
|
||||
if (start > 0)
|
||||
{
|
||||
auto ret = buffer_[0 .. start];
|
||||
auto ret = this.buffer_[0 .. start];
|
||||
ring = 0;
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (capacity - length < minAvailable)
|
||||
if (capacity - length < this.minAvailable)
|
||||
{
|
||||
void[] buf = buffer_;
|
||||
immutable cap = capacity;
|
||||
void[] buf = this.buffer_;
|
||||
const cap = capacity;
|
||||
() @trusted {
|
||||
allocator.reallocate(buf, (cap + blockSize) * T.sizeof);
|
||||
buffer_ = cast(T[]) buf;
|
||||
allocator.reallocate(buf,
|
||||
(cap + this.blockSize) * T.sizeof);
|
||||
this.buffer_ = cast(T[]) buf;
|
||||
}();
|
||||
}
|
||||
ring = length_;
|
||||
return buffer_[length_ .. $];
|
||||
ring = this.length_;
|
||||
return this.buffer_[this.length_ .. $];
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,7 +311,7 @@ struct ReadBuffer(T = ubyte)
|
||||
* T = Buffer type.
|
||||
*/
|
||||
struct WriteBuffer(T = ubyte)
|
||||
if (isScalarType!T)
|
||||
if (isScalarType!T)
|
||||
{
|
||||
/// Internal buffer.
|
||||
private T[] buffer_;
|
||||
@ -322,16 +323,16 @@ struct WriteBuffer(T = ubyte)
|
||||
private size_t ring;
|
||||
|
||||
/// Size by which the buffer will grow.
|
||||
private immutable size_t blockSize;
|
||||
private const size_t blockSize;
|
||||
|
||||
/// The position of the free area in the buffer.
|
||||
private size_t position;
|
||||
|
||||
invariant
|
||||
{
|
||||
assert(blockSize > 0);
|
||||
assert(this.blockSize > 0);
|
||||
// Position can refer to an element outside the buffer if the buffer is full.
|
||||
assert(position <= buffer_.length);
|
||||
assert(this.position <= this.buffer_.length);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -342,7 +343,7 @@ struct WriteBuffer(T = ubyte)
|
||||
*
|
||||
* Precondition: $(D_INLINECODE size > 0 && allocator !is null)
|
||||
*/
|
||||
this(in size_t size, shared Allocator allocator = defaultAllocator) @trusted
|
||||
this(size_t size, shared Allocator allocator = defaultAllocator) @trusted
|
||||
in
|
||||
{
|
||||
assert(size > 0);
|
||||
@ -350,10 +351,10 @@ struct WriteBuffer(T = ubyte)
|
||||
}
|
||||
do
|
||||
{
|
||||
blockSize = size;
|
||||
this.blockSize = size;
|
||||
ring = size - 1;
|
||||
allocator_ = allocator;
|
||||
buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof);
|
||||
this.buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof);
|
||||
}
|
||||
|
||||
@disable this();
|
||||
@ -363,7 +364,7 @@ struct WriteBuffer(T = ubyte)
|
||||
*/
|
||||
~this()
|
||||
{
|
||||
allocator.deallocate(buffer_);
|
||||
allocator.deallocate(this.buffer_);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -371,7 +372,7 @@ struct WriteBuffer(T = ubyte)
|
||||
*/
|
||||
@property size_t capacity() const
|
||||
{
|
||||
return buffer_.length;
|
||||
return this.buffer_.length;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -384,13 +385,13 @@ struct WriteBuffer(T = ubyte)
|
||||
*/
|
||||
@property size_t length() const
|
||||
{
|
||||
if (position > ring || position < start) // Buffer overflowed
|
||||
if (this.position > ring || this.position < start) // Buffer overflowed
|
||||
{
|
||||
return ring - start + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return position - start;
|
||||
return this.position - start;
|
||||
}
|
||||
}
|
||||
|
||||
@ -433,61 +434,62 @@ struct WriteBuffer(T = ubyte)
|
||||
* Params:
|
||||
* buffer = Buffer chunk got with $(D_PSYMBOL opIndex).
|
||||
*/
|
||||
ref WriteBuffer opOpAssign(string op)(in T[] buffer)
|
||||
ref WriteBuffer opOpAssign(string op)(const T[] buffer)
|
||||
if (op == "~")
|
||||
{
|
||||
size_t end, start;
|
||||
|
||||
if (position >= this.start && position <= ring)
|
||||
if (this.position >= this.start && this.position <= ring)
|
||||
{
|
||||
auto afterRing = ring + 1;
|
||||
|
||||
end = position + buffer.length;
|
||||
end = this.position + buffer.length;
|
||||
if (end > afterRing)
|
||||
{
|
||||
end = afterRing;
|
||||
}
|
||||
start = end - position;
|
||||
buffer_[position .. end] = buffer[0 .. start];
|
||||
start = end - this.position;
|
||||
this.buffer_[this.position .. end] = buffer[0 .. start];
|
||||
if (end == afterRing)
|
||||
{
|
||||
position = this.start == 0 ? afterRing : 0;
|
||||
this.position = this.start == 0 ? afterRing : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
position = end;
|
||||
this.position = end;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we have some free space at the beginning
|
||||
if (start < buffer.length && position < this.start)
|
||||
if (start < buffer.length && this.position < this.start)
|
||||
{
|
||||
end = position + buffer.length - start;
|
||||
end = this.position + buffer.length - start;
|
||||
if (end > this.start)
|
||||
{
|
||||
end = this.start;
|
||||
}
|
||||
auto areaEnd = end - position + start;
|
||||
buffer_[position .. end] = buffer[start .. areaEnd];
|
||||
position = end == this.start ? ring + 1 : end - position;
|
||||
auto areaEnd = end - this.position + start;
|
||||
this.buffer_[this.position .. end] = buffer[start .. areaEnd];
|
||||
this.position = end == this.start ? ring + 1 : end - this.position;
|
||||
start = areaEnd;
|
||||
}
|
||||
|
||||
// And if we still haven't found any place, save the rest in the overflow area
|
||||
if (start < buffer.length)
|
||||
{
|
||||
end = position + buffer.length - start;
|
||||
end = this.position + buffer.length - start;
|
||||
if (end > capacity)
|
||||
{
|
||||
auto newSize = (end / blockSize * blockSize + blockSize) * T.sizeof;
|
||||
const newSize = end / this.blockSize * this.blockSize
|
||||
+ this.blockSize;
|
||||
() @trusted {
|
||||
void[] buf = buffer_;
|
||||
allocator.reallocate(buf, newSize);
|
||||
buffer_ = cast(T[]) buf;
|
||||
void[] buf = this.buffer_;
|
||||
allocator.reallocate(buf, newSize * T.sizeof);
|
||||
this.buffer_ = cast(T[]) buf;
|
||||
}();
|
||||
}
|
||||
buffer_[position .. end] = buffer[start .. $];
|
||||
position = end;
|
||||
this.buffer_[this.position .. end] = buffer[start .. $];
|
||||
this.position = end;
|
||||
if (this.start == 0)
|
||||
{
|
||||
ring = capacity - 1;
|
||||
@ -506,7 +508,7 @@ struct WriteBuffer(T = ubyte)
|
||||
*
|
||||
* Returns: $(D_KEYWORD this).
|
||||
*/
|
||||
ref WriteBuffer opOpAssign(string op)(in size_t length)
|
||||
ref WriteBuffer opOpAssign(string op)(size_t length)
|
||||
if (op == "+")
|
||||
in
|
||||
{
|
||||
@ -521,42 +523,42 @@ struct WriteBuffer(T = ubyte)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
else if (position <= afterRing)
|
||||
else if (this.position <= afterRing)
|
||||
{
|
||||
start += length;
|
||||
if (start > 0 && position == afterRing)
|
||||
if (start > 0 && this.position == afterRing)
|
||||
{
|
||||
position = oldStart;
|
||||
this.position = oldStart;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto overflow = position - afterRing;
|
||||
auto overflow = this.position - afterRing;
|
||||
|
||||
if (overflow > length)
|
||||
{
|
||||
immutable afterLength = afterRing + length;
|
||||
buffer_[start .. start + length] = buffer_[afterRing .. afterLength];
|
||||
buffer_[afterRing .. afterLength] = buffer_[afterLength .. position];
|
||||
position -= length;
|
||||
const afterLength = afterRing + length;
|
||||
this.buffer_[start .. start + length] = this.buffer_[afterRing .. afterLength];
|
||||
this.buffer_[afterRing .. afterLength] = this.buffer_[afterLength .. this.position];
|
||||
this.position -= length;
|
||||
}
|
||||
else if (overflow == length)
|
||||
{
|
||||
buffer_[start .. start + overflow] = buffer_[afterRing .. position];
|
||||
position -= overflow;
|
||||
this.buffer_[start .. start + overflow] = this.buffer_[afterRing .. this.position];
|
||||
this.position -= overflow;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer_[start .. start + overflow] = buffer_[afterRing .. position];
|
||||
position = overflow;
|
||||
this.buffer_[start .. start + overflow] = this.buffer_[afterRing .. this.position];
|
||||
this.position = overflow;
|
||||
}
|
||||
start += length;
|
||||
|
||||
if (start == position)
|
||||
if (start == this.position)
|
||||
{
|
||||
if (position != afterRing)
|
||||
if (this.position != afterRing)
|
||||
{
|
||||
position = 0;
|
||||
this.position = 0;
|
||||
}
|
||||
start = 0;
|
||||
ring = capacity - 1;
|
||||
@ -596,15 +598,15 @@ struct WriteBuffer(T = ubyte)
|
||||
*
|
||||
* Returns: A chunk of data buffer.
|
||||
*/
|
||||
T[] opSlice(in size_t start, in size_t end)
|
||||
T[] opSlice(size_t start, size_t end)
|
||||
{
|
||||
if (position > ring || position < start) // Buffer overflowed
|
||||
if (this.position > ring || this.position < start) // Buffer overflowed
|
||||
{
|
||||
return buffer_[this.start .. ring + 1 - length + end];
|
||||
return this.buffer_[this.start .. ring + 1 - length + end];
|
||||
}
|
||||
else
|
||||
{
|
||||
return buffer_[this.start .. this.start + end];
|
||||
return this.buffer_[this.start .. this.start + end];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,12 +82,18 @@ package struct Bucket(K, V = void)
|
||||
}
|
||||
}
|
||||
|
||||
bool opEquals(ref inout(K) key) inout
|
||||
void moveKey(ref K key)
|
||||
{
|
||||
move(key, this.key());
|
||||
this.status = BucketStatus.used;
|
||||
}
|
||||
|
||||
bool opEquals(T)(ref const T key) const
|
||||
{
|
||||
return this.status == BucketStatus.used && this.key == key;
|
||||
}
|
||||
|
||||
bool opEquals(ref inout(typeof(this)) that) inout
|
||||
bool opEquals(ref const(typeof(this)) that) const
|
||||
{
|
||||
return key == that.key && this.status == that.status;
|
||||
}
|
||||
@ -171,24 +177,29 @@ package struct HashArray(alias hasher, K, V = void)
|
||||
this.length = that.length;
|
||||
}
|
||||
|
||||
@property size_t bucketCount() const
|
||||
{
|
||||
return primes[this.lengthIndex];
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns bucket position for `hash`. `0` may mean the 0th position or an
|
||||
* empty `buckets` array.
|
||||
*/
|
||||
size_t locateBucket(ref const Key key) const
|
||||
size_t locateBucket(T)(ref const T key) const
|
||||
{
|
||||
return this.array.length == 0
|
||||
? 0
|
||||
: hasher(key) % primes[this.lengthIndex];
|
||||
return this.array.length == 0 ? 0 : hasher(key) % bucketCount;
|
||||
}
|
||||
|
||||
/*
|
||||
* Inserts a key into an empty or deleted bucket. If the key is
|
||||
* already in there, does nothing. Returns the bucket with the key.
|
||||
* If the key doesn't already exists, returns an empty bucket the key can
|
||||
* be inserted in and adjusts the element count. Otherwise returns the
|
||||
* bucket containing the key.
|
||||
*/
|
||||
ref Bucket insert(ref Key key)
|
||||
{
|
||||
while ((this.lengthIndex + 1) != primes.length)
|
||||
const newLengthIndex = this.lengthIndex + 1;
|
||||
if (newLengthIndex != primes.length)
|
||||
{
|
||||
foreach (ref e; this.array[locateBucket(key) .. $])
|
||||
{
|
||||
@ -203,17 +214,29 @@ package struct HashArray(alias hasher, K, V = void)
|
||||
}
|
||||
}
|
||||
|
||||
if (this.rehashToSize(this.lengthIndex + 1))
|
||||
this.rehashToSize(newLengthIndex);
|
||||
}
|
||||
|
||||
foreach (ref e; this.array[locateBucket(key) .. $])
|
||||
{
|
||||
if (e == key)
|
||||
{
|
||||
++this.lengthIndex;
|
||||
return e;
|
||||
}
|
||||
else if (e.status != BucketStatus.used)
|
||||
{
|
||||
++this.length;
|
||||
return e;
|
||||
}
|
||||
}
|
||||
this.array.insertBack(Bucket(key));
|
||||
|
||||
this.array.length = this.array.length + 1;
|
||||
++this.length;
|
||||
return this.array[$ - 1];
|
||||
}
|
||||
|
||||
// Takes an index in the primes array.
|
||||
bool rehashToSize(const size_t n)
|
||||
void rehashToSize(const size_t n)
|
||||
in
|
||||
{
|
||||
assert(n < primes.length);
|
||||
@ -231,15 +254,15 @@ package struct HashArray(alias hasher, K, V = void)
|
||||
{
|
||||
if (e2.status != BucketStatus.used) // Insert the key
|
||||
{
|
||||
e2 = e1;
|
||||
.move(e1, e2);
|
||||
continue DataLoop;
|
||||
}
|
||||
}
|
||||
return false; // Rehashing failed.
|
||||
storage.insertBack(.move(e1));
|
||||
}
|
||||
}
|
||||
.move(storage, this.array);
|
||||
return true;
|
||||
this.lengthIndex = n;
|
||||
}
|
||||
|
||||
void rehash(const size_t n)
|
||||
@ -252,9 +275,9 @@ package struct HashArray(alias hasher, K, V = void)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (this.rehashToSize(lengthIndex))
|
||||
if (lengthIndex > this.lengthIndex)
|
||||
{
|
||||
this.lengthIndex = lengthIndex;
|
||||
this.rehashToSize(lengthIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@ -287,7 +310,7 @@ package struct HashArray(alias hasher, K, V = void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool opBinaryRight(string op : "in")(ref inout(Key) key) inout
|
||||
bool opBinaryRight(string op : "in", T)(ref const T key) const
|
||||
{
|
||||
foreach (ref e; this.array[locateBucket(key) .. $])
|
||||
{
|
||||
|
@ -14,6 +14,7 @@
|
||||
*/
|
||||
module tanya.container.hashtable;
|
||||
|
||||
import tanya.algorithm.mutation;
|
||||
import tanya.container.array;
|
||||
import tanya.container.entry;
|
||||
import tanya.hash.lookup;
|
||||
@ -138,6 +139,236 @@ struct Range(T)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bidirectional range iterating over the key of a $(D_PSYMBOL HashTable).
|
||||
*
|
||||
* Params:
|
||||
* T = Type of the internal hash storage.
|
||||
*/
|
||||
struct ByKey(T)
|
||||
{
|
||||
private alias Key = CopyConstness!(T, T.Key);
|
||||
static if (isMutable!T)
|
||||
{
|
||||
private alias DataRange = T.array.Range;
|
||||
}
|
||||
else
|
||||
{
|
||||
private alias DataRange = T.array.ConstRange;
|
||||
}
|
||||
private DataRange dataRange;
|
||||
|
||||
@disable this();
|
||||
|
||||
private this(DataRange dataRange)
|
||||
{
|
||||
while (!dataRange.empty && dataRange.front.status != BucketStatus.used)
|
||||
{
|
||||
dataRange.popFront();
|
||||
}
|
||||
while (!dataRange.empty && dataRange.back.status != BucketStatus.used)
|
||||
{
|
||||
dataRange.popBack();
|
||||
}
|
||||
this.dataRange = dataRange;
|
||||
}
|
||||
|
||||
@property ByKey save()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@property bool empty() const
|
||||
{
|
||||
return this.dataRange.empty();
|
||||
}
|
||||
|
||||
@property void popFront()
|
||||
in
|
||||
{
|
||||
assert(!empty);
|
||||
assert(this.dataRange.front.status == BucketStatus.used);
|
||||
}
|
||||
out
|
||||
{
|
||||
assert(empty || this.dataRange.back.status == BucketStatus.used);
|
||||
}
|
||||
do
|
||||
{
|
||||
do
|
||||
{
|
||||
this.dataRange.popFront();
|
||||
}
|
||||
while (!empty && dataRange.front.status != BucketStatus.used);
|
||||
}
|
||||
|
||||
@property void popBack()
|
||||
in
|
||||
{
|
||||
assert(!empty);
|
||||
assert(this.dataRange.back.status == BucketStatus.used);
|
||||
}
|
||||
out
|
||||
{
|
||||
assert(empty || this.dataRange.back.status == BucketStatus.used);
|
||||
}
|
||||
do
|
||||
{
|
||||
do
|
||||
{
|
||||
this.dataRange.popBack();
|
||||
}
|
||||
while (!empty && dataRange.back.status != BucketStatus.used);
|
||||
}
|
||||
|
||||
@property ref inout(Key) front() inout
|
||||
in
|
||||
{
|
||||
assert(!empty);
|
||||
assert(this.dataRange.front.status == BucketStatus.used);
|
||||
}
|
||||
do
|
||||
{
|
||||
return this.dataRange.front.key;
|
||||
}
|
||||
|
||||
@property ref inout(Key) back() inout
|
||||
in
|
||||
{
|
||||
assert(!empty);
|
||||
assert(this.dataRange.back.status == BucketStatus.used);
|
||||
}
|
||||
do
|
||||
{
|
||||
return this.dataRange.back.key;
|
||||
}
|
||||
|
||||
ByKey opIndex()
|
||||
{
|
||||
return typeof(return)(this.dataRange[]);
|
||||
}
|
||||
|
||||
ByKey!(const T) opIndex() const
|
||||
{
|
||||
return typeof(return)(this.dataRange[]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bidirectional range iterating over the key of a $(D_PSYMBOL HashTable).
|
||||
*
|
||||
* Params:
|
||||
* T = Type of the internal hash storage.
|
||||
*/
|
||||
struct ByValue(T)
|
||||
{
|
||||
private alias Value = CopyConstness!(T, T.Value);
|
||||
static if (isMutable!T)
|
||||
{
|
||||
private alias DataRange = T.array.Range;
|
||||
}
|
||||
else
|
||||
{
|
||||
private alias DataRange = T.array.ConstRange;
|
||||
}
|
||||
private DataRange dataRange;
|
||||
|
||||
@disable this();
|
||||
|
||||
private this(DataRange dataRange)
|
||||
{
|
||||
while (!dataRange.empty && dataRange.front.status != BucketStatus.used)
|
||||
{
|
||||
dataRange.popFront();
|
||||
}
|
||||
while (!dataRange.empty && dataRange.back.status != BucketStatus.used)
|
||||
{
|
||||
dataRange.popBack();
|
||||
}
|
||||
this.dataRange = dataRange;
|
||||
}
|
||||
|
||||
@property ByValue save()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@property bool empty() const
|
||||
{
|
||||
return this.dataRange.empty();
|
||||
}
|
||||
|
||||
@property void popFront()
|
||||
in
|
||||
{
|
||||
assert(!empty);
|
||||
assert(this.dataRange.front.status == BucketStatus.used);
|
||||
}
|
||||
out
|
||||
{
|
||||
assert(empty || this.dataRange.back.status == BucketStatus.used);
|
||||
}
|
||||
do
|
||||
{
|
||||
do
|
||||
{
|
||||
this.dataRange.popFront();
|
||||
}
|
||||
while (!empty && dataRange.front.status != BucketStatus.used);
|
||||
}
|
||||
|
||||
@property void popBack()
|
||||
in
|
||||
{
|
||||
assert(!empty);
|
||||
assert(this.dataRange.back.status == BucketStatus.used);
|
||||
}
|
||||
out
|
||||
{
|
||||
assert(empty || this.dataRange.back.status == BucketStatus.used);
|
||||
}
|
||||
do
|
||||
{
|
||||
do
|
||||
{
|
||||
this.dataRange.popBack();
|
||||
}
|
||||
while (!empty && dataRange.back.status != BucketStatus.used);
|
||||
}
|
||||
|
||||
@property ref inout(Value) front() inout
|
||||
in
|
||||
{
|
||||
assert(!empty);
|
||||
assert(this.dataRange.front.status == BucketStatus.used);
|
||||
}
|
||||
do
|
||||
{
|
||||
return this.dataRange.front.kv.value;
|
||||
}
|
||||
|
||||
@property ref inout(Value) back() inout
|
||||
in
|
||||
{
|
||||
assert(!empty);
|
||||
assert(this.dataRange.back.status == BucketStatus.used);
|
||||
}
|
||||
do
|
||||
{
|
||||
return this.dataRange.back.kv.value;
|
||||
}
|
||||
|
||||
ByValue opIndex()
|
||||
{
|
||||
return typeof(return)(this.dataRange[]);
|
||||
}
|
||||
|
||||
ByValue!(const T) opIndex() const
|
||||
{
|
||||
return typeof(return)(this.dataRange[]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash table is a data structure that stores pairs of keys and values without
|
||||
* any particular order.
|
||||
@ -155,7 +386,7 @@ struct Range(T)
|
||||
* hasher = Hash function for $(D_PARAM Key).
|
||||
*/
|
||||
struct HashTable(Key, Value, alias hasher = hash)
|
||||
if (is(typeof(hasher(Key.init)) == size_t))
|
||||
if (is(typeof(((Key k) => hasher(k))(Key.init)) == size_t))
|
||||
{
|
||||
private alias HashArray = .HashArray!(hasher, Key, Value);
|
||||
private alias Buckets = HashArray.Buckets;
|
||||
@ -171,11 +402,18 @@ if (is(typeof(hasher(Key.init)) == size_t))
|
||||
/// ditto
|
||||
alias ConstRange = .Range!(const HashArray);
|
||||
|
||||
/// ditto
|
||||
alias ByKey = .ByKey!(const HashArray);
|
||||
|
||||
/// ditto
|
||||
alias ByValue = .ByValue!HashArray;
|
||||
|
||||
/// ditto
|
||||
alias ConstByValue = .ByValue!(const HashArray);
|
||||
|
||||
invariant
|
||||
{
|
||||
assert(this.data.lengthIndex < primes.length);
|
||||
assert(this.data.array.length == 0
|
||||
|| this.data.array.length == primes[this.data.lengthIndex]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -440,6 +678,23 @@ if (is(typeof(hasher(Key.init)) == size_t))
|
||||
assert(hashTable.empty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current bucket count in the container.
|
||||
*
|
||||
* Bucket count equals to the number of the elements can be saved in the
|
||||
* container in the best case scenario for key distribution, i.d. every key
|
||||
* has a unique hash value. In a worse case the bucket count can be less
|
||||
* than the number of elements stored in the container.
|
||||
*
|
||||
* Returns: Current bucket count.
|
||||
*
|
||||
* See_Also: $(D_PSYMBOL rehash).
|
||||
*/
|
||||
@property size_t bucketCount() const
|
||||
{
|
||||
return this.data.bucketCount;
|
||||
}
|
||||
|
||||
/// The maximum number of buckets the container can have.
|
||||
enum size_t maxBucketCount = primes[$ - 1];
|
||||
|
||||
@ -453,14 +708,28 @@ if (is(typeof(hasher(Key.init)) == size_t))
|
||||
*
|
||||
* Returns: Just inserted element.
|
||||
*/
|
||||
ref Value opIndexAssign(Value value, Key key)
|
||||
ref Value opIndexAssign()(auto ref Value value, auto ref Key key)
|
||||
{
|
||||
auto e = ((ref v) @trusted => &this.data.insert(v))(key);
|
||||
if (e.status != BucketStatus.used)
|
||||
{
|
||||
e.key = key;
|
||||
static if (__traits(isRef, key))
|
||||
{
|
||||
e.key = key;
|
||||
}
|
||||
else
|
||||
{
|
||||
e.moveKey(key);
|
||||
}
|
||||
}
|
||||
static if (__traits(isRef, value))
|
||||
{
|
||||
return e.kv.value = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return e.kv.value = move(value);
|
||||
}
|
||||
return e.kv.value = value;
|
||||
}
|
||||
|
||||
///
|
||||
@ -490,7 +759,7 @@ if (is(typeof(hasher(Key.init)) == size_t))
|
||||
*
|
||||
* Returns: The number of the inserted elements with a unique key.
|
||||
*/
|
||||
size_t insert(KeyValue keyValue)
|
||||
size_t insert(ref KeyValue keyValue)
|
||||
{
|
||||
auto e = ((ref v) @trusted => &this.data.insert(v))(keyValue.key);
|
||||
size_t inserted;
|
||||
@ -503,6 +772,20 @@ if (is(typeof(hasher(Key.init)) == size_t))
|
||||
return inserted;
|
||||
}
|
||||
|
||||
/// ditto
|
||||
size_t insert(KeyValue keyValue)
|
||||
{
|
||||
auto e = ((ref v) @trusted => &this.data.insert(v))(keyValue.key);
|
||||
size_t inserted;
|
||||
if (e.status != BucketStatus.used)
|
||||
{
|
||||
e.moveKey(keyValue.key);
|
||||
inserted = 1;
|
||||
}
|
||||
move(keyValue.value, e.kv.value);
|
||||
return inserted;
|
||||
}
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
@ -557,13 +840,15 @@ if (is(typeof(hasher(Key.init)) == size_t))
|
||||
* Find the element with the key $(D_PARAM key).
|
||||
*
|
||||
* Params:
|
||||
* T = Type comparable with the key type, used for the lookup.
|
||||
* key = The key to be find.
|
||||
*
|
||||
* Returns: The value associated with $(D_PARAM key).
|
||||
*
|
||||
* Precondition: Element with $(D_PARAM key) is in this hash table.
|
||||
*/
|
||||
ref Value opIndex(Key key)
|
||||
ref Value opIndex(T)(auto ref const T key)
|
||||
if (ifTestable!(T, a => Key.init == a))
|
||||
{
|
||||
const code = this.data.locateBucket(key);
|
||||
|
||||
@ -619,12 +904,14 @@ if (is(typeof(hasher(Key.init)) == size_t))
|
||||
* Looks for $(D_PARAM key) in this hash table.
|
||||
*
|
||||
* Params:
|
||||
* T = Type comparable with the key type, used for the lookup.
|
||||
* key = The key to look for.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if $(D_PARAM key) exists in the hash table,
|
||||
* $(D_KEYWORD false) otherwise.
|
||||
*/
|
||||
bool opBinaryRight(string op : "in")(auto ref inout(Key) key) inout
|
||||
bool opBinaryRight(string op : "in", T)(auto ref const T key) const
|
||||
if (ifTestable!(T, a => Key.init == a))
|
||||
{
|
||||
return key in this.data;
|
||||
}
|
||||
@ -645,18 +932,15 @@ if (is(typeof(hasher(Key.init)) == size_t))
|
||||
* Sets the number of buckets in the container to at least $(D_PARAM n)
|
||||
* and rearranges all the elements according to their hash values.
|
||||
*
|
||||
* If $(D_PARAM n) is greater than the current $(D_PSYMBOL capacity)
|
||||
* If $(D_PARAM n) is greater than the current $(D_PSYMBOL bucketCount)
|
||||
* and lower than or equal to $(D_PSYMBOL maxBucketCount), a rehash is
|
||||
* forced.
|
||||
*
|
||||
* If $(D_PARAM n) is greater than $(D_PSYMBOL maxBucketCount),
|
||||
* $(D_PSYMBOL maxBucketCount) is used instead as a new number of buckets.
|
||||
*
|
||||
* If $(D_PARAM n) is equal to the current $(D_PSYMBOL capacity), rehashing
|
||||
* is forced without resizing the container.
|
||||
*
|
||||
* If $(D_PARAM n) is lower than the current $(D_PSYMBOL capacity), the
|
||||
* function may have no effect.
|
||||
* If $(D_PARAM n) is less than or equal to the current
|
||||
* $(D_PSYMBOL bucketCount), the function may have no effect.
|
||||
*
|
||||
* Rehashing is automatically performed whenever the container needs space
|
||||
* to insert new elements.
|
||||
@ -697,6 +981,86 @@ if (is(typeof(hasher(Key.init)) == size_t))
|
||||
assert(hashTable[].front == hashTable.KeyValue("Iguanodon", 9));
|
||||
assert(hashTable[].back == hashTable.KeyValue("Iguanodon", 9));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bidirectional range that iterats over the keys of this
|
||||
* $(D_PSYMBOL HashTable).
|
||||
*
|
||||
* This function always returns a $(D_KEYWORD const) range, since changing
|
||||
* a key of a hash table would probably change its hash value and require
|
||||
* rehashing.
|
||||
*
|
||||
* Returns: $(D_KEYWORD const) bidirectional range that iterates over the
|
||||
* keys of the container.
|
||||
*
|
||||
* See_Also: $(D_PSYMBOL byValue).
|
||||
*/
|
||||
ByKey byKey() const
|
||||
{
|
||||
return typeof(return)(this.data.array[]);
|
||||
}
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
HashTable!(string, int) hashTable;
|
||||
hashTable["one"] = 1;
|
||||
hashTable["two"] = 2;
|
||||
|
||||
auto byKey = hashTable.byKey();
|
||||
assert(!byKey.empty);
|
||||
|
||||
assert(byKey.front == "one" || byKey.front == "two");
|
||||
assert(byKey.back == "one" || byKey.back == "two");
|
||||
assert(byKey.front != byKey.back);
|
||||
|
||||
byKey.popFront();
|
||||
assert(byKey.front == byKey.back);
|
||||
|
||||
byKey.popBack();
|
||||
assert(byKey.empty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bidirectional range that iterats over the values of this
|
||||
* $(D_PSYMBOL HashTable).
|
||||
*
|
||||
* Returns: A bidirectional range that iterates over the values of the
|
||||
* container.
|
||||
*
|
||||
* See_Also: $(D_PSYMBOL byKey).
|
||||
*/
|
||||
ByValue byValue()
|
||||
{
|
||||
return typeof(return)(this.data.array[]);
|
||||
}
|
||||
|
||||
/// ditto
|
||||
ConstByValue byValue() const
|
||||
{
|
||||
return typeof(return)(this.data.array[]);
|
||||
}
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
HashTable!(string, int) hashTable;
|
||||
hashTable["one"] = 1;
|
||||
hashTable["two"] = 2;
|
||||
|
||||
auto byValue = hashTable.byValue();
|
||||
assert(!byValue.empty);
|
||||
|
||||
assert(byValue.front == 1 || byValue.front == 2);
|
||||
assert(byValue.back == 1 || byValue.back == 2);
|
||||
assert(byValue.front != byValue.back);
|
||||
|
||||
byValue.popFront();
|
||||
assert(byValue.front == byValue.back);
|
||||
|
||||
byValue.popBack();
|
||||
assert(byValue.empty);
|
||||
}
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
@ -727,6 +1091,9 @@ if (is(typeof(hasher(Key.init)) == size_t))
|
||||
static assert(is(HashTable!(string, int) a));
|
||||
static assert(is(const HashTable!(string, int)));
|
||||
static assert(isForwardRange!(HashTable!(string, int).Range));
|
||||
|
||||
static assert(is(HashTable!(int, int, (ref const int) => size_t.init)));
|
||||
static assert(is(HashTable!(int, int, (int) => size_t.init)));
|
||||
}
|
||||
|
||||
// Constructs by reference
|
||||
@ -773,3 +1140,60 @@ if (is(typeof(hasher(Key.init)) == size_t))
|
||||
}
|
||||
testFunc(hashTable);
|
||||
}
|
||||
|
||||
// Issue 53: https://github.com/caraus-ecms/tanya/issues/53
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
{
|
||||
HashTable!(uint, uint) hashTable;
|
||||
foreach (uint i; 0 .. 14)
|
||||
{
|
||||
hashTable[i + 1] = i;
|
||||
}
|
||||
assert(hashTable.length == 14);
|
||||
}
|
||||
{
|
||||
HashTable!(int, int) hashtable;
|
||||
|
||||
hashtable[1194250162] = 3;
|
||||
hashtable[-1131293824] = 6;
|
||||
hashtable[838100082] = 9;
|
||||
|
||||
hashtable.rehash(11);
|
||||
|
||||
assert(hashtable[-1131293824] == 6);
|
||||
}
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
static struct String
|
||||
{
|
||||
bool opEquals(string) const @nogc nothrow pure @safe
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool opEquals(ref const string) const @nogc nothrow pure @safe
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool opEquals(String) const @nogc nothrow pure @safe
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool opEquals(ref const String) const @nogc nothrow pure @safe
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t toHash() const @nogc nothrow pure @safe
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
static assert(is(typeof("asdf" in HashTable!(String, int)())));
|
||||
static assert(is(typeof(HashTable!(String, int)()["asdf"])));
|
||||
}
|
||||
|
@ -15,7 +15,6 @@
|
||||
*/
|
||||
module tanya.container.list;
|
||||
|
||||
import std.algorithm.comparison : equal;
|
||||
import std.algorithm.searching;
|
||||
import tanya.algorithm.comparison;
|
||||
import tanya.algorithm.mutation;
|
||||
@ -178,7 +177,6 @@ struct SList(T)
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l = SList!int(2, 3);
|
||||
assert(l.length == 2);
|
||||
assert(l.front == 3);
|
||||
}
|
||||
|
||||
@ -192,7 +190,6 @@ struct SList(T)
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l = SList!int(2);
|
||||
assert(l.length == 2);
|
||||
assert(l.front == 0);
|
||||
}
|
||||
|
||||
@ -432,7 +429,6 @@ struct SList(T)
|
||||
assert(l2.front == 25);
|
||||
|
||||
l2.insertFront(l1[]);
|
||||
assert(l2.length == 5);
|
||||
assert(l2.front == 9);
|
||||
}
|
||||
|
||||
@ -566,12 +562,6 @@ struct SList(T)
|
||||
assert(l1 == l2);
|
||||
}
|
||||
|
||||
deprecated
|
||||
@property size_t length() const
|
||||
{
|
||||
return count(this[]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparison for equality.
|
||||
*
|
||||
@ -583,7 +573,7 @@ struct SList(T)
|
||||
*/
|
||||
bool opEquals()(auto ref typeof(this) that) inout
|
||||
{
|
||||
return equal(this[], that[]);
|
||||
return equal(opIndex(), that[]);
|
||||
}
|
||||
|
||||
///
|
||||
@ -1154,7 +1144,6 @@ struct DList(T)
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l = DList!int(2, 3);
|
||||
assert(l.length == 2);
|
||||
assert(l.front == 3);
|
||||
assert(l.back == 3);
|
||||
}
|
||||
@ -1169,7 +1158,6 @@ struct DList(T)
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto l = DList!int(2);
|
||||
assert(l.length == 2);
|
||||
assert(l.front == 0);
|
||||
}
|
||||
|
||||
@ -1480,7 +1468,6 @@ struct DList(T)
|
||||
assert(l2.back == 15);
|
||||
|
||||
l2.insertFront(l1[]);
|
||||
assert(l2.length == 5);
|
||||
assert(l2.front == 9);
|
||||
assert(l2.back == 15);
|
||||
}
|
||||
@ -1600,7 +1587,6 @@ struct DList(T)
|
||||
assert(l2.back == 15);
|
||||
|
||||
l2.insertBack(l1[]);
|
||||
assert(l2.length == 5);
|
||||
assert(l2.back == 9);
|
||||
}
|
||||
|
||||
@ -1855,12 +1841,6 @@ struct DList(T)
|
||||
return insertAfter!(T[])(r, el[]);
|
||||
}
|
||||
|
||||
deprecated
|
||||
@property size_t length() const
|
||||
{
|
||||
return count(this[]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparison for equality.
|
||||
*
|
||||
@ -2354,7 +2334,6 @@ struct DList(T)
|
||||
l.insertAfter(l[], 234);
|
||||
assert(l.front == 234);
|
||||
assert(l.back == 234);
|
||||
assert(l.length == 1);
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
|
@ -20,26 +20,3 @@ public import tanya.container.hashtable;
|
||||
public import tanya.container.list;
|
||||
public import tanya.container.set;
|
||||
public import tanya.container.string;
|
||||
|
||||
/**
|
||||
* Thrown if $(D_PSYMBOL Set) cannot insert a new element because the container
|
||||
* is full.
|
||||
*/
|
||||
deprecated
|
||||
class HashContainerFullException : 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 nothrow pure @safe
|
||||
{
|
||||
super(msg, file, line, next);
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@
|
||||
*/
|
||||
module tanya.container.set;
|
||||
|
||||
import tanya.algorithm.mutation;
|
||||
import tanya.container.array;
|
||||
import tanya.container.entry;
|
||||
import tanya.hash.lookup;
|
||||
@ -432,6 +431,23 @@ if (is(typeof(hasher(T.init)) == size_t))
|
||||
assert(set.empty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current bucket count in the container.
|
||||
*
|
||||
* Bucket count equals to the number of the elements can be saved in the
|
||||
* container in the best case scenario for key distribution, i.d. every key
|
||||
* has a unique hash value. In a worse case the bucket count can be less
|
||||
* than the number of elements stored in the container.
|
||||
*
|
||||
* Returns: Current bucket count.
|
||||
*
|
||||
* See_Also: $(D_PSYMBOL rehash).
|
||||
*/
|
||||
@property size_t bucketCount() const
|
||||
{
|
||||
return this.data.bucketCount;
|
||||
}
|
||||
|
||||
/// The maximum number of buckets the container can have.
|
||||
enum size_t maxBucketCount = primes[$ - 1];
|
||||
|
||||
@ -443,6 +459,17 @@ if (is(typeof(hasher(T.init)) == size_t))
|
||||
*
|
||||
* Returns: Amount of new elements inserted.
|
||||
*/
|
||||
size_t insert(ref T value)
|
||||
{
|
||||
auto e = ((ref v) @trusted => &this.data.insert(v))(value);
|
||||
if (e.status != BucketStatus.used)
|
||||
{
|
||||
e.moveKey(value);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t insert(T value)
|
||||
{
|
||||
auto e = ((ref v) @trusted => &this.data.insert(v))(value);
|
||||
@ -534,12 +561,14 @@ if (is(typeof(hasher(T.init)) == size_t))
|
||||
* $(D_KEYWORD in) operator.
|
||||
*
|
||||
* Params:
|
||||
* U = Type comparable with the element type, used for the lookup.
|
||||
* value = Element to be searched for.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if the given element exists in the container,
|
||||
* $(D_KEYWORD false) otherwise.
|
||||
*/
|
||||
bool opBinaryRight(string op : "in")(auto ref inout(T) value) inout
|
||||
bool opBinaryRight(string op : "in", U)(auto ref const U value) const
|
||||
if (ifTestable!(U, a => T.init == a))
|
||||
{
|
||||
return value in this.data;
|
||||
}
|
||||
@ -559,18 +588,15 @@ if (is(typeof(hasher(T.init)) == size_t))
|
||||
* Sets the number of buckets in the container to at least $(D_PARAM n)
|
||||
* and rearranges all the elements according to their hash values.
|
||||
*
|
||||
* If $(D_PARAM n) is greater than the current $(D_PSYMBOL capacity)
|
||||
* If $(D_PARAM n) is greater than the current $(D_PSYMBOL bucketCount)
|
||||
* and lower than or equal to $(D_PSYMBOL maxBucketCount), a rehash is
|
||||
* forced.
|
||||
*
|
||||
* If $(D_PARAM n) is greater than $(D_PSYMBOL maxBucketCount),
|
||||
* $(D_PSYMBOL maxBucketCount) is used instead as a new number of buckets.
|
||||
*
|
||||
* If $(D_PARAM n) is equal to the current $(D_PSYMBOL capacity), rehashing
|
||||
* is forced without resizing the container.
|
||||
*
|
||||
* If $(D_PARAM n) is lower than the current $(D_PSYMBOL capacity), the
|
||||
* function may have no effect.
|
||||
* If $(D_PARAM n) is less than or equal to the current
|
||||
* $(D_PSYMBOL bucketCount), the function may have no effect.
|
||||
*
|
||||
* Rehashing is automatically performed whenever the container needs space
|
||||
* to insert new elements.
|
||||
|
@ -26,11 +26,12 @@
|
||||
*/
|
||||
module tanya.container.string;
|
||||
|
||||
import std.algorithm.comparison : cmp, equal;
|
||||
import std.algorithm.comparison : cmp;
|
||||
import std.algorithm.mutation : bringToFront, copy;
|
||||
import std.algorithm.searching;
|
||||
import tanya.algorithm.comparison;
|
||||
import tanya.algorithm.mutation;
|
||||
import tanya.hash.lookup;
|
||||
import tanya.memory;
|
||||
import tanya.meta.trait;
|
||||
import tanya.meta.transform;
|
||||
@ -499,7 +500,7 @@ struct String
|
||||
}
|
||||
}
|
||||
|
||||
@nogc pure @safe unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto s = String(0, 'K');
|
||||
assert(s.length == 0);
|
||||
@ -579,16 +580,10 @@ struct String
|
||||
* Params:
|
||||
* chr = The character should be inserted.
|
||||
*
|
||||
* Returns: The number of bytes inserted.
|
||||
*
|
||||
* Throws: $(D_PSYMBOL UTFException).
|
||||
* Returns: The number of bytes inserted (1).
|
||||
*/
|
||||
size_t insertBack(const char chr) @nogc pure @trusted
|
||||
size_t insertBack(char chr) @nogc nothrow pure @trusted
|
||||
{
|
||||
if ((chr & 0x80) != 0)
|
||||
{
|
||||
throw defaultAllocator.make!UTFException("Invalid UTF-8 character");
|
||||
}
|
||||
reserve(length + 1);
|
||||
|
||||
*(data + length) = chr;
|
||||
@ -652,8 +647,6 @@ struct String
|
||||
* str = String should be inserted.
|
||||
*
|
||||
* Returns: The number of bytes inserted.
|
||||
*
|
||||
* Throws: $(D_PSYMBOL UTFException).
|
||||
*/
|
||||
size_t insertBack(R)(R str) @trusted
|
||||
if (!isInfinite!R
|
||||
@ -673,46 +666,18 @@ struct String
|
||||
this.length_ = size;
|
||||
return str.length;
|
||||
}
|
||||
else static if (isInstanceOf!(ByCodeUnit, R))
|
||||
{
|
||||
str.get.copy(this.data[length .. size]);
|
||||
this.length_ = size;
|
||||
return str.length;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t insertedLength;
|
||||
while (!str.empty)
|
||||
foreach (c; str)
|
||||
{
|
||||
ubyte expectedLength;
|
||||
if ((str.front & 0x80) == 0x00)
|
||||
{
|
||||
expectedLength = 1;
|
||||
}
|
||||
else if ((str.front & 0xe0) == 0xc0)
|
||||
{
|
||||
expectedLength = 2;
|
||||
}
|
||||
else if ((str.front & 0xf0) == 0xe0)
|
||||
{
|
||||
expectedLength = 3;
|
||||
}
|
||||
else if ((str.front & 0xf8) == 0xf0)
|
||||
{
|
||||
expectedLength = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw defaultAllocator.make!UTFException("Invalid UTF-8 sequeunce");
|
||||
}
|
||||
size = length + expectedLength;
|
||||
reserve(size);
|
||||
|
||||
for (; expectedLength > 0; --expectedLength)
|
||||
{
|
||||
if (str.empty)
|
||||
{
|
||||
throw defaultAllocator.make!UTFException("Invalid UTF-8 sequeunce");
|
||||
}
|
||||
*(data + length) = str.front;
|
||||
str.popFront();
|
||||
}
|
||||
insertedLength += expectedLength;
|
||||
this.length_ = size;
|
||||
insertedLength += insertBack(c);
|
||||
}
|
||||
return insertedLength;
|
||||
}
|
||||
@ -828,7 +793,7 @@ struct String
|
||||
}
|
||||
|
||||
///
|
||||
@nogc pure @safe unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
String s;
|
||||
assert(s.capacity == 0);
|
||||
@ -869,7 +834,7 @@ struct String
|
||||
}
|
||||
|
||||
///
|
||||
@nogc pure @safe unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto s = String("Die Alten lasen laut.");
|
||||
assert(s.capacity == 21);
|
||||
@ -894,7 +859,7 @@ struct String
|
||||
}
|
||||
|
||||
///
|
||||
@nogc pure @safe unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto s = String("In allem Schreiben ist Schamlosigkeit.");
|
||||
assert(s.capacity == 38);
|
||||
@ -991,7 +956,7 @@ struct String
|
||||
*
|
||||
* Returns: Null-terminated string.
|
||||
*/
|
||||
const(char)* toStringz() @nogc nothrow pure
|
||||
const(char)* toStringz() @nogc nothrow pure @system
|
||||
{
|
||||
reserve(length + 1);
|
||||
this.data[length] = '\0';
|
||||
@ -999,7 +964,7 @@ struct String
|
||||
}
|
||||
|
||||
///
|
||||
@nogc pure unittest
|
||||
@nogc nothrow pure @system unittest
|
||||
{
|
||||
auto s = String("C string.");
|
||||
assert(s.toStringz()[0] == 'C');
|
||||
@ -1018,7 +983,7 @@ struct String
|
||||
alias opDollar = length;
|
||||
|
||||
///
|
||||
@nogc pure @safe unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto s = String("Piscis primuin a capite foetat.");
|
||||
assert(s.length == 31);
|
||||
@ -1044,7 +1009,7 @@ struct String
|
||||
}
|
||||
|
||||
///
|
||||
@nogc pure @safe unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto s = String("Alea iacta est.");
|
||||
assert(s[0] == 'A');
|
||||
@ -1067,7 +1032,7 @@ struct String
|
||||
}
|
||||
|
||||
///
|
||||
@nogc pure @safe unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto s = String("Plutarchus");
|
||||
auto r = s[];
|
||||
@ -1086,7 +1051,7 @@ struct String
|
||||
}
|
||||
|
||||
///
|
||||
@nogc pure @safe unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto s = const String("Was ich vermag, soll gern geschehen. Goethe");
|
||||
auto r1 = s[];
|
||||
@ -1162,7 +1127,7 @@ struct String
|
||||
}
|
||||
|
||||
///
|
||||
@nogc pure @safe unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
String s;
|
||||
assert(s.empty);
|
||||
@ -1207,7 +1172,7 @@ struct String
|
||||
}
|
||||
|
||||
///
|
||||
@nogc pure @safe unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto s = String("Vladimir Soloviev");
|
||||
auto r = s[9 .. $];
|
||||
@ -1271,7 +1236,7 @@ struct String
|
||||
}
|
||||
|
||||
///
|
||||
@nogc pure @safe unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto s = String("Черная, потом пропахшая выть!");
|
||||
s = String("Как мне тебя не ласкать, не любить?");
|
||||
@ -1299,10 +1264,11 @@ struct String
|
||||
}
|
||||
|
||||
///
|
||||
@nogc pure @safe unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto s = String("Оловом светится лужная голь...");
|
||||
s = "Грустная песня, ты - русская боль.";
|
||||
assert(s == "Грустная песня, ты - русская боль.");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1344,7 +1310,7 @@ struct String
|
||||
}
|
||||
|
||||
///
|
||||
@nogc pure @safe unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
assert(String("Голубая кофта.") < String("Синие глаза."));
|
||||
assert(String("Никакой я правды") < String("милой не сказал")[]);
|
||||
@ -1397,7 +1363,7 @@ struct String
|
||||
}
|
||||
|
||||
///
|
||||
@nogc pure @safe unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
assert(String("Милая спросила:") != String("Крутит ли метель?"));
|
||||
assert(String("Затопить бы печку,") != String("постелить постель.")[]);
|
||||
@ -1430,7 +1396,7 @@ struct String
|
||||
}
|
||||
|
||||
///
|
||||
@nogc pure @safe unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto s = String("alea iacta est.");
|
||||
|
||||
@ -1455,7 +1421,7 @@ struct String
|
||||
return opSliceAssign(value, 0, length);
|
||||
}
|
||||
|
||||
@nogc pure @safe unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto s1 = String("Buttercup");
|
||||
auto s2 = String("Cap");
|
||||
@ -1469,7 +1435,7 @@ struct String
|
||||
return opSliceAssign(value, 0, length);
|
||||
}
|
||||
|
||||
@nogc pure @safe unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto s1 = String("Wow");
|
||||
s1[] = 'a';
|
||||
@ -1482,7 +1448,7 @@ struct String
|
||||
return opSliceAssign(value, 0, length);
|
||||
}
|
||||
|
||||
@nogc pure @safe unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto s1 = String("ö");
|
||||
s1[] = "oe";
|
||||
@ -1574,7 +1540,7 @@ struct String
|
||||
}
|
||||
|
||||
///
|
||||
@nogc pure @safe unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto s = String("Казнить нельзя помиловать.");
|
||||
s.insertAfter(s[0 .. 27], ",");
|
||||
@ -1603,7 +1569,7 @@ struct String
|
||||
}
|
||||
|
||||
///
|
||||
@nogc pure @safe unittest
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto s = String("Казнить нельзя помиловать.");
|
||||
s.insertBefore(s[27 .. $], ",");
|
||||
@ -1614,11 +1580,21 @@ struct String
|
||||
assert(s == "Казнить, нельзя помиловать.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the hash value for the string.
|
||||
*
|
||||
* Returns: Hash value for the string.
|
||||
*/
|
||||
size_t toHash() const @nogc nothrow pure @safe
|
||||
{
|
||||
return hash(get);
|
||||
}
|
||||
|
||||
mixin DefaultAllocator;
|
||||
}
|
||||
|
||||
// Postblit works.
|
||||
@nogc pure @safe unittest
|
||||
// Postblit works
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
void internFunc(String arg)
|
||||
{
|
||||
@ -1637,7 +1613,7 @@ struct String
|
||||
topFunc(String("asdf"));
|
||||
}
|
||||
|
||||
// Const range produces mutable ranges.
|
||||
// Const range produces mutable ranges
|
||||
@nogc pure @safe unittest
|
||||
{
|
||||
auto s = const String("И снизу лед, и сверху - маюсь между.");
|
||||
@ -1663,7 +1639,7 @@ struct String
|
||||
}
|
||||
}
|
||||
|
||||
// Can pop multibyte characters.
|
||||
// Can pop multibyte characters
|
||||
@nogc pure @safe unittest
|
||||
{
|
||||
auto s = String("\U00024B62\U00002260");
|
||||
@ -1680,3 +1656,12 @@ struct String
|
||||
s[$ - 3] = 0xf0;
|
||||
assertThrown!UTFException(&(range.popFront));
|
||||
}
|
||||
|
||||
// Inserts own char range correctly
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto s1 = String(`ü`);
|
||||
String s2;
|
||||
s2.insertBack(s1[]);
|
||||
assert(s1 == s2);
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
/**
|
||||
* This module provides functions for converting between different types.
|
||||
*
|
||||
* 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)
|
||||
@ -20,6 +20,8 @@ import tanya.memory;
|
||||
import tanya.memory.op;
|
||||
import tanya.meta.trait;
|
||||
import tanya.meta.transform;
|
||||
import tanya.range.array;
|
||||
import tanya.range.primitive;
|
||||
|
||||
version (unittest)
|
||||
{
|
||||
@ -264,28 +266,169 @@ final class ConvException : Exception
|
||||
}
|
||||
}
|
||||
|
||||
package bool stringToInt(R)(R range, ref ushort n)
|
||||
/*
|
||||
* Converts a string $(D_PARAM range) into an integral value of type
|
||||
* $(D_PARAM T) in $(D_PARAM base).
|
||||
*
|
||||
* The convertion stops when $(D_PARAM range) is empty of if the next character
|
||||
* cannot be converted because it is not a digit (with respect to the
|
||||
* $(D_PARAM base)) or if the reading the next character would cause integer
|
||||
* overflow. The function returns the value converted so far then. The front
|
||||
* element of the $(D_PARAM range) points to the first character cannot be
|
||||
* converted or $(D_PARAM range) is empty if the whole string could be
|
||||
* converted.
|
||||
*
|
||||
* Base must be between 2 and 36 inclursive. Default base is 10.
|
||||
*
|
||||
* The function doesn't handle the sign (+ or -) or number prefixes (like 0x).
|
||||
*/
|
||||
package T readIntegral(T, R)(ref R range, const ubyte base = 10)
|
||||
if (isInputRange!R
|
||||
&& isSomeChar!(ElementType!R)
|
||||
&& isIntegral!T
|
||||
&& isUnsigned!T)
|
||||
in
|
||||
{
|
||||
import tanya.encoding.ascii;
|
||||
import tanya.range.array;
|
||||
|
||||
size_t i = 1;
|
||||
uint lPort;
|
||||
|
||||
for (; !range.empty && range.front.isDigit() && i <= 6; ++i, range.popFront())
|
||||
assert(base >= 2);
|
||||
assert(base <= 36);
|
||||
}
|
||||
do
|
||||
{
|
||||
T boundary = cast(T) (T.max / base);
|
||||
if (range.empty)
|
||||
{
|
||||
lPort = lPort * 10 + (range.front - '0');
|
||||
return T.init;
|
||||
}
|
||||
if (i != 1 && (range.empty || range.front == '/'))
|
||||
|
||||
T n;
|
||||
int digit;
|
||||
do
|
||||
{
|
||||
if (lPort > ushort.max)
|
||||
if (range.front >= 'a')
|
||||
{
|
||||
return false;
|
||||
digit = range.front - 'W';
|
||||
}
|
||||
else if (range.front >= 'A')
|
||||
{
|
||||
digit = range.front - '7';
|
||||
}
|
||||
else if (range.front >= '0')
|
||||
{
|
||||
digit = range.front - '0';
|
||||
}
|
||||
else
|
||||
{
|
||||
return n;
|
||||
}
|
||||
if (digit >= base)
|
||||
{
|
||||
return n;
|
||||
}
|
||||
n = cast(T) (n * base + digit);
|
||||
range.popFront();
|
||||
|
||||
if (range.empty)
|
||||
{
|
||||
return n;
|
||||
}
|
||||
n = cast(ushort) lPort;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
while (n < boundary);
|
||||
|
||||
if (range.front >= 'a')
|
||||
{
|
||||
digit = range.front - 'W';
|
||||
}
|
||||
else if (range.front >= 'A')
|
||||
{
|
||||
digit = range.front - '7';
|
||||
}
|
||||
else if (range.front >= '0')
|
||||
{
|
||||
digit = range.front - '0';
|
||||
}
|
||||
else
|
||||
{
|
||||
return n;
|
||||
}
|
||||
if (n > cast(T) ((T.max - digit) / base))
|
||||
{
|
||||
return n;
|
||||
}
|
||||
n = cast(T) (n * base + digit);
|
||||
range.popFront();
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
// reads ubyte.max
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
string number = "255";
|
||||
assert(readIntegral!ubyte(number) == 255);
|
||||
assert(number.empty);
|
||||
}
|
||||
|
||||
// detects integer overflow
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
string number = "500";
|
||||
readIntegral!ubyte(number);
|
||||
assert(number.front == '0');
|
||||
assert(number.length == 1);
|
||||
}
|
||||
|
||||
// stops on a non-digit
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
string number = "10-";
|
||||
readIntegral!ubyte(number);
|
||||
assert(number.front == '-');
|
||||
}
|
||||
|
||||
// returns false if the number string is empty
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
string number = "";
|
||||
readIntegral!ubyte(number);
|
||||
assert(number.empty);
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
string number = "29";
|
||||
assert(readIntegral!ubyte(number) == 29);
|
||||
assert(number.empty);
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
string number = "25467";
|
||||
readIntegral!ubyte(number);
|
||||
assert(number.front == '6');
|
||||
}
|
||||
|
||||
// Converts lower case hexadecimals
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
string number = "a";
|
||||
assert(readIntegral!ubyte(number, 16) == 10);
|
||||
assert(number.empty);
|
||||
}
|
||||
|
||||
// Converts upper case hexadecimals
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
string number = "FF";
|
||||
assert(readIntegral!ubyte(number, 16) == 255);
|
||||
assert(number.empty);
|
||||
}
|
||||
|
||||
// Handles small overflows
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
string number = "256";
|
||||
assert(readIntegral!ubyte(number, 10) == 25);
|
||||
assert(number.front == '6');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -732,3 +875,135 @@ if (is(Unqual!To == String))
|
||||
static assert(is(typeof((const String("true")).to!bool)));
|
||||
static assert(is(typeof(false.to!(const String) == "false")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a stringish range to an integral value.
|
||||
*
|
||||
* 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) doesn't contain an
|
||||
* integral value.
|
||||
*/
|
||||
To to(To, From)(auto ref From from)
|
||||
if (isInputRange!From && isSomeChar!(ElementType!From) && isIntegral!To)
|
||||
{
|
||||
if (from.empty)
|
||||
{
|
||||
throw make!ConvException(defaultAllocator, "Input range is empty");
|
||||
}
|
||||
|
||||
static if (isSigned!To)
|
||||
{
|
||||
bool negative;
|
||||
}
|
||||
if (from.front == '-')
|
||||
{
|
||||
static if (isUnsigned!To)
|
||||
{
|
||||
throw make!ConvException(defaultAllocator,
|
||||
"Negative integer overflow");
|
||||
}
|
||||
else
|
||||
{
|
||||
negative = true;
|
||||
from.popFront();
|
||||
}
|
||||
}
|
||||
|
||||
if (from.empty)
|
||||
{
|
||||
throw make!ConvException(defaultAllocator, "Input range is empty");
|
||||
}
|
||||
|
||||
ubyte base = 10;
|
||||
if (from.front == '0')
|
||||
{
|
||||
from.popFront();
|
||||
if (from.empty)
|
||||
{
|
||||
return To.init;
|
||||
}
|
||||
else if (from.front == 'x' || from.front == 'X')
|
||||
{
|
||||
base = 16;
|
||||
from.popFront();
|
||||
}
|
||||
else if (from.front == 'b' || from.front == 'B')
|
||||
{
|
||||
base = 2;
|
||||
from.popFront();
|
||||
}
|
||||
else
|
||||
{
|
||||
base = 8;
|
||||
}
|
||||
}
|
||||
|
||||
auto unsigned = readIntegral!(Unsigned!To, From)(from, base);
|
||||
if (!from.empty)
|
||||
{
|
||||
throw make!ConvException(defaultAllocator, "Integer overflow");
|
||||
}
|
||||
|
||||
static if (isSigned!To)
|
||||
{
|
||||
if (negative)
|
||||
{
|
||||
auto predecessor = cast(Unsigned!To) (unsigned - 1);
|
||||
if (predecessor > cast(Unsigned!To) To.max)
|
||||
{
|
||||
throw make!ConvException(defaultAllocator,
|
||||
"Negative integer overflow");
|
||||
}
|
||||
return cast(To) (-(cast(Largest!(To, ptrdiff_t)) predecessor) - 1);
|
||||
}
|
||||
else if (unsigned > cast(Unsigned!To) To.max)
|
||||
{
|
||||
throw make!ConvException(defaultAllocator, "Integer overflow");
|
||||
}
|
||||
else
|
||||
{
|
||||
return unsigned;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return unsigned;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
@nogc pure @safe unittest
|
||||
{
|
||||
assert("1234".to!uint() == 1234);
|
||||
assert("1234".to!int() == 1234);
|
||||
assert("1234".to!int() == 1234);
|
||||
|
||||
assert("0".to!int() == 0);
|
||||
assert("-0".to!int() == 0);
|
||||
|
||||
assert("0x10".to!int() == 16);
|
||||
assert("0X10".to!int() == 16);
|
||||
assert("-0x10".to!int() == -16);
|
||||
|
||||
assert("0b10".to!int() == 2);
|
||||
assert("0B10".to!int() == 2);
|
||||
assert("-0b10".to!int() == -2);
|
||||
|
||||
assert("010".to!int() == 8);
|
||||
assert("-010".to!int() == -8);
|
||||
|
||||
|
||||
assert("-128".to!byte == cast(byte) -128);
|
||||
|
||||
assertThrown!ConvException(() => "".to!int);
|
||||
assertThrown!ConvException(() => "-".to!int);
|
||||
assertThrown!ConvException(() => "-5".to!uint);
|
||||
assertThrown!ConvException(() => "-129".to!byte);
|
||||
assertThrown!ConvException(() => "256".to!ubyte);
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
/**
|
||||
* Common exceptions and errors.
|
||||
*
|
||||
* 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)
|
||||
|
File diff suppressed because it is too large
Load Diff
71
source/tanya/functional.d
Normal file
71
source/tanya/functional.d
Normal file
@ -0,0 +1,71 @@
|
||||
/* 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/. */
|
||||
|
||||
/**
|
||||
* Functions that manipulate other functions and their argument lists.
|
||||
*
|
||||
* Copyright: Eugene Wissner 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)
|
||||
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/functional.d,
|
||||
* tanya/functional.d)
|
||||
*/
|
||||
module tanya.functional;
|
||||
|
||||
import tanya.algorithm.mutation;
|
||||
import tanya.meta.metafunction;
|
||||
|
||||
private template forwardOne(alias arg)
|
||||
{
|
||||
static if (__traits(isRef, arg) || __traits(isOut, arg))
|
||||
{
|
||||
alias forwardOne = arg;
|
||||
}
|
||||
else
|
||||
{
|
||||
@property auto forwardOne()
|
||||
{
|
||||
return move(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwards its argument list preserving $(D_KEYWORD ref) and $(D_KEYWORD out)
|
||||
* storage classes.
|
||||
*
|
||||
* $(D_PSYMBOL forward) accepts a list of variables or literals. It returns an
|
||||
* argument list of the same length that can be for example passed to a
|
||||
* function accepting the arguments of this type.
|
||||
*
|
||||
* Params:
|
||||
* args = Argument list.
|
||||
*
|
||||
* Returns: $(D_PARAM args) with their original storage classes.
|
||||
*/
|
||||
template forward(args...)
|
||||
{
|
||||
static if (args.length == 1)
|
||||
{
|
||||
alias forward = forwardOne!(args[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
alias forward = Map!(forwardOne, args);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
static assert(is(typeof((int i) { int v = forward!i; })));
|
||||
static assert(is(typeof((ref int i) { int v = forward!i; })));
|
||||
static assert(is(typeof({
|
||||
void f(int i, ref int j, out int k)
|
||||
{
|
||||
f(forward!(i, j, k));
|
||||
}
|
||||
})));
|
||||
}
|
@ -14,7 +14,7 @@
|
||||
*/
|
||||
module tanya.math.mp;
|
||||
|
||||
import std.algorithm.comparison : cmp, equal;
|
||||
import std.algorithm.comparison : cmp;
|
||||
import std.algorithm.mutation : copy, fill, reverse;
|
||||
import std.range;
|
||||
import tanya.algorithm.comparison;
|
||||
|
@ -38,7 +38,7 @@ enum IEEEPrecision : ubyte
|
||||
/**
|
||||
* Tests the precision of floating-point type $(D_PARAM F).
|
||||
*
|
||||
* For $(D_KEYWORD float), $(D_PSYMBOL ieeePrecision) always evaluates to
|
||||
* For $(D_KEYWORD float) $(D_PSYMBOL ieeePrecision) always evaluates to
|
||||
* $(D_INLINECODE IEEEPrecision.single); for $(D_KEYWORD double) - to
|
||||
* $(D_INLINECODE IEEEPrecision.double). It returns different values only
|
||||
* for $(D_KEYWORD real), since $(D_KEYWORD real) is a platform-dependent type.
|
||||
@ -87,7 +87,7 @@ if (isFloatingPoint!F)
|
||||
static assert(ieeePrecision!double == IEEEPrecision.double_);
|
||||
}
|
||||
|
||||
private union FloatBits(F)
|
||||
package(tanya) union FloatBits(F)
|
||||
{
|
||||
Unqual!F floating;
|
||||
static if (ieeePrecision!F == IEEEPrecision.single)
|
||||
@ -396,7 +396,7 @@ if (isFloatingPoint!F)
|
||||
|
||||
/**
|
||||
* Determines whether $(D_PARAM x) is a denormilized number or not.
|
||||
|
||||
*
|
||||
* Denormalized number is a number between `0` and `1` that cannot be
|
||||
* represented as
|
||||
*
|
||||
@ -459,7 +459,7 @@ if (isFloatingPoint!F)
|
||||
|
||||
/**
|
||||
* Determines whether $(D_PARAM x) is a normilized number or not.
|
||||
|
||||
*
|
||||
* Normalized number is a number that can be represented as
|
||||
*
|
||||
* <pre>
|
||||
@ -739,101 +739,3 @@ bool isPseudoprime(ulong x) @nogc nothrow pure @safe
|
||||
assert(899809363.isPseudoprime);
|
||||
assert(982451653.isPseudoprime);
|
||||
}
|
||||
|
||||
deprecated("Use tanya.algorithm.comparison.min instead")
|
||||
T min(T)(T x, T y)
|
||||
if (isIntegral!T)
|
||||
{
|
||||
return x < y ? x : y;
|
||||
}
|
||||
|
||||
deprecated("Use tanya.algorithm.comparison.min instead")
|
||||
T min(T)(T x, T y)
|
||||
if (isFloatingPoint!T)
|
||||
{
|
||||
if (isNaN(x))
|
||||
{
|
||||
return y;
|
||||
}
|
||||
if (isNaN(y))
|
||||
{
|
||||
return x;
|
||||
}
|
||||
return x < y ? x : y;
|
||||
}
|
||||
|
||||
deprecated("Use tanya.algorithm.comparison.min instead")
|
||||
ref T min(T)(ref T x, ref T y)
|
||||
if (is(Unqual!T == Integer))
|
||||
{
|
||||
return x < y ? x : y;
|
||||
}
|
||||
|
||||
deprecated("Use tanya.algorithm.comparison.min instead")
|
||||
T min(T)(T x, T y)
|
||||
if (is(T == Integer))
|
||||
{
|
||||
return x < y ? move(x) : move(y);
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
assert(min(Integer(5), Integer(3)) == 3);
|
||||
}
|
||||
|
||||
deprecated("Use tanya.algorithm.comparison.max instead")
|
||||
T max(T)(T x, T y)
|
||||
if (isIntegral!T)
|
||||
{
|
||||
return x > y ? x : y;
|
||||
}
|
||||
|
||||
deprecated("Use tanya.algorithm.comparison.max instead")
|
||||
T max(T)(T x, T y)
|
||||
if (isFloatingPoint!T)
|
||||
{
|
||||
if (isNaN(x))
|
||||
{
|
||||
return y;
|
||||
}
|
||||
if (isNaN(y))
|
||||
{
|
||||
return x;
|
||||
}
|
||||
return x > y ? x : y;
|
||||
}
|
||||
|
||||
deprecated("Use tanya.algorithm.comparison.max instead")
|
||||
ref T max(T)(ref T x, ref T y)
|
||||
if (is(Unqual!T == Integer))
|
||||
{
|
||||
return x > y ? x : y;
|
||||
}
|
||||
|
||||
deprecated("Use tanya.algorithm.comparison.max instead")
|
||||
T max(T)(T x, T y)
|
||||
if (is(T == Integer))
|
||||
{
|
||||
return x > y ? move(x) : move(y);
|
||||
}
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
assert(max(Integer(5), Integer(3)) == 5);
|
||||
}
|
||||
|
||||
// min/max accept const and mutable references.
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
{
|
||||
Integer i1 = 5, i2 = 3;
|
||||
assert(min(i1, i2) == 3);
|
||||
assert(max(i1, i2) == 5);
|
||||
}
|
||||
{
|
||||
const Integer i1 = 5, i2 = 3;
|
||||
assert(min(i1, i2) == 3);
|
||||
assert(max(i1, i2) == 5);
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,11 @@ version (TanyaNative)
|
||||
fillMemory(buffer[1 .. $], 0);
|
||||
assert(buffer[0] == 1 && buffer[1] == 0);
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
assert(cmp(null, null) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
private enum alignMask = size_t.sizeof - 1;
|
||||
|
@ -70,26 +70,6 @@ enum bool isWideString(T) = is(T : const dchar[]) && !isStaticArray!T;
|
||||
static assert(!isWideString!(dchar[10]));
|
||||
}
|
||||
|
||||
deprecated("Use tanya.meta.transform.Smallest instead")
|
||||
template Smallest(Args...)
|
||||
if (Args.length >= 1)
|
||||
{
|
||||
static assert(is(Args[0]), T.stringof ~ " doesn't have .sizeof property");
|
||||
|
||||
static if (Args.length == 1)
|
||||
{
|
||||
alias Smallest = Args[0];
|
||||
}
|
||||
else static if (Smallest!(Args[1 .. $]).sizeof < Args[0].sizeof)
|
||||
{
|
||||
alias Smallest = Smallest!(Args[1 .. $]);
|
||||
}
|
||||
else
|
||||
{
|
||||
alias Smallest = Args[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether $(D_PARAM T) is a complex type.
|
||||
*
|
||||
@ -178,7 +158,7 @@ enum bool isPolymorphicType(T) = is(T == class) || is(T == interface);
|
||||
*/
|
||||
template hasStaticMember(T, string member)
|
||||
{
|
||||
static if (__traits(hasMember, T, member))
|
||||
static if (hasMember!(T, member))
|
||||
{
|
||||
alias Member = Alias!(__traits(getMember, T, member));
|
||||
|
||||
@ -928,26 +908,6 @@ template mostNegative(T)
|
||||
static assert(mostNegative!cfloat == -cfloat.max);
|
||||
}
|
||||
|
||||
deprecated("Use tanya.meta.transform.Largest instead")
|
||||
template Largest(Args...)
|
||||
if (Args.length >= 1)
|
||||
{
|
||||
static assert(is(Args[0]), T.stringof ~ " doesn't have .sizeof property");
|
||||
|
||||
static if (Args.length == 1)
|
||||
{
|
||||
alias Largest = Args[0];
|
||||
}
|
||||
else static if (Largest!(Args[1 .. $]).sizeof > Args[0].sizeof)
|
||||
{
|
||||
alias Largest = Largest!(Args[1 .. $]);
|
||||
}
|
||||
else
|
||||
{
|
||||
alias Largest = Args[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the type $(D_PARAM T) is copyable.
|
||||
*
|
||||
@ -1540,7 +1500,6 @@ if (F.length == 1)
|
||||
* Returns: $(D_KEYWORD true) if $(D_PARAM T) defines a symbol
|
||||
* $(D_PARAM member), $(D_KEYWORD false) otherwise.
|
||||
*/
|
||||
deprecated("Use __traits(hasMember) instead")
|
||||
enum bool hasMember(T, string member) = __traits(hasMember, T, member);
|
||||
|
||||
///
|
||||
|
@ -305,7 +305,14 @@ struct URL
|
||||
*/
|
||||
private bool parsePort(const(char)[] port) @nogc nothrow pure @safe
|
||||
{
|
||||
return stringToInt(port[1 .. $], this.port);
|
||||
auto unparsed = port[1 .. $];
|
||||
auto parsed = readIntegral!ushort(unparsed);
|
||||
if (unparsed.length == 0 || unparsed[0] == '/')
|
||||
{
|
||||
this.port = parsed;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
/**
|
||||
* Network programming.
|
||||
*
|
||||
* Copyright: Eugene Wissner 2016-2017.
|
||||
* Copyright: Eugene Wissner 2016-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)
|
||||
|
@ -5,7 +5,43 @@
|
||||
/**
|
||||
* Low-level socket programming.
|
||||
*
|
||||
* Copyright: Eugene Wissner 2016-2017.
|
||||
* Current API supports only server-side TCP communication.
|
||||
*
|
||||
* Here is an example of a cross-platform blocking server:
|
||||
*
|
||||
* ---
|
||||
* import std.stdio;
|
||||
* import tanya.memory;
|
||||
* import tanya.network;
|
||||
*
|
||||
* void main()
|
||||
* {
|
||||
* auto socket = defaultAllocator.make!StreamSocket(AddressFamily.inet);
|
||||
* auto address = defaultAllocator.make!InternetAddress("127.0.0.1",
|
||||
* cast(ushort) 8192);
|
||||
*
|
||||
* socket.setOption(SocketOptionLevel.SOCKET, SocketOption.REUSEADDR, true);
|
||||
* socket.blocking = true;
|
||||
* socket.bind(address);
|
||||
* socket.listen(5);
|
||||
*
|
||||
* auto client = socket.accept();
|
||||
* client.send(cast(const(ubyte)[]) "Test\n");
|
||||
*
|
||||
* ubyte[100] buf;
|
||||
* auto response = client.receive(buf[]);
|
||||
*
|
||||
* writeln(cast(const(char)[]) buf[0 .. response]);
|
||||
*
|
||||
* defaultAllocator.dispose(client);
|
||||
* defaultAllocator.dispose(socket);
|
||||
* }
|
||||
* ---
|
||||
*
|
||||
* For an example of an asynchronous server refer to the documentation of the
|
||||
* $(D_PSYMBOL tanya.async.loop) module.
|
||||
*
|
||||
* Copyright: Eugene Wissner 2016-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)
|
||||
@ -76,17 +112,10 @@ else version (Windows)
|
||||
socket,
|
||||
socklen_t,
|
||||
SOL_SOCKET,
|
||||
WSAEWOULDBLOCK,
|
||||
WSAGetLastError;
|
||||
import tanya.async.iocp;
|
||||
import tanya.sys.windows.def;
|
||||
import tanya.sys.windows.error : ECONNABORTED = WSAECONNABORTED,
|
||||
ENOBUFS = WSAENOBUFS,
|
||||
EOPNOTSUPP = WSAEOPNOTSUPP,
|
||||
EPROTONOSUPPORT = WSAEPROTONOSUPPORT,
|
||||
EPROTOTYPE = WSAEPROTOTYPE,
|
||||
ESOCKTNOSUPPORT = WSAESOCKTNOSUPPORT,
|
||||
ETIMEDOUT = WSAETIMEDOUT,
|
||||
EWOULDBLOCK = WSAEWOULDBLOCK;
|
||||
public import tanya.sys.windows.winbase;
|
||||
public import tanya.sys.windows.winsock2;
|
||||
|
||||
@ -580,37 +609,6 @@ enum AddressFamily : int
|
||||
inet6 = 10, /// IP version 6.
|
||||
}
|
||||
|
||||
deprecated("Use tanya.os.error.ErrorCode.ErrorNo instead")
|
||||
enum SocketError : int
|
||||
{
|
||||
/// Unknown error.
|
||||
unknown = 0,
|
||||
/// Firewall rules forbid connection.
|
||||
accessDenied = EPERM,
|
||||
/// A socket operation was attempted on a non-socket.
|
||||
notSocket = EBADF,
|
||||
/// The network is not available.
|
||||
networkDown = ECONNABORTED,
|
||||
/// An invalid pointer address was detected by the underlying socket provider.
|
||||
fault = EFAULT,
|
||||
/// An invalid argument was supplied to a $(D_PSYMBOL Socket) member.
|
||||
invalidArgument = EINVAL,
|
||||
/// The limit on the number of open sockets has been reached.
|
||||
tooManyOpenSockets = ENFILE,
|
||||
/// No free buffer space is available for a Socket operation.
|
||||
noBufferSpaceAvailable = ENOBUFS,
|
||||
/// The address family is not supported by the protocol family.
|
||||
operationNotSupported = EOPNOTSUPP,
|
||||
/// The protocol is not implemented or has not been configured.
|
||||
protocolNotSupported = EPROTONOSUPPORT,
|
||||
/// Protocol error.
|
||||
protocolError = EPROTOTYPE,
|
||||
/// The connection attempt timed out, or the connected host has failed to respond.
|
||||
timedOut = ETIMEDOUT,
|
||||
/// The support for the specified socket type does not exist in this address family.
|
||||
socketNotSupported = ESOCKTNOSUPPORT,
|
||||
}
|
||||
|
||||
/**
|
||||
* $(D_PSYMBOL SocketException) should be thrown only if one of the socket functions
|
||||
* $(D_PSYMBOL socketError) and sets $(D_PSYMBOL errno), because
|
||||
@ -1440,7 +1438,7 @@ bool wouldHaveBlocked() nothrow @trusted @nogc
|
||||
else version (Windows)
|
||||
{
|
||||
return WSAGetLastError() == ERROR_IO_PENDING
|
||||
|| WSAGetLastError() == EWOULDBLOCK
|
||||
|| WSAGetLastError() == WSAEWOULDBLOCK
|
||||
|| WSAGetLastError() == ERROR_IO_INCOMPLETE;
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
* This package provides platform-independent interfaces to operating system
|
||||
* functionality.
|
||||
*
|
||||
* 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)
|
||||
|
375
source/tanya/range/adapter.d
Normal file
375
source/tanya/range/adapter.d
Normal file
@ -0,0 +1,375 @@
|
||||
/* 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/. */
|
||||
|
||||
/**
|
||||
* Range adapters.
|
||||
*
|
||||
* A range adapter wraps another range and modifies the way, how the original
|
||||
* range is iterated, or the order in which its elements are accessed.
|
||||
*
|
||||
* All adapters are lazy algorithms, they request the next element of the
|
||||
* adapted range on demand.
|
||||
*
|
||||
* Copyright: Eugene Wissner 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)
|
||||
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/range/adapter.d,
|
||||
* tanya/range/adapter.d)
|
||||
*/
|
||||
module tanya.range.adapter;
|
||||
|
||||
import tanya.algorithm.mutation;
|
||||
import tanya.math;
|
||||
import tanya.range.primitive;
|
||||
|
||||
private mixin template Take(R, bool exactly)
|
||||
{
|
||||
private R source;
|
||||
size_t length_;
|
||||
|
||||
@disable this();
|
||||
|
||||
private this(R source, size_t length)
|
||||
{
|
||||
this.source = source;
|
||||
static if (!exactly && hasLength!R)
|
||||
{
|
||||
this.length_ = min(source.length, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.length_ = length;
|
||||
}
|
||||
}
|
||||
|
||||
@property auto ref front()
|
||||
in
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
do
|
||||
{
|
||||
return this.source.front;
|
||||
}
|
||||
|
||||
void popFront()
|
||||
in
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
do
|
||||
{
|
||||
this.source.popFront();
|
||||
--this.length_;
|
||||
}
|
||||
|
||||
@property bool empty()
|
||||
{
|
||||
static if (exactly || isInfinite!R)
|
||||
{
|
||||
return length == 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return length == 0 || this.source.empty;
|
||||
}
|
||||
}
|
||||
|
||||
@property size_t length()
|
||||
{
|
||||
return this.length_;
|
||||
}
|
||||
|
||||
static if (hasAssignableElements!R)
|
||||
{
|
||||
@property void front(ref ElementType!R value)
|
||||
in
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
do
|
||||
{
|
||||
this.source.front = value;
|
||||
}
|
||||
|
||||
@property void front(ElementType!R value)
|
||||
in
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
do
|
||||
{
|
||||
this.source.front = move(value);
|
||||
}
|
||||
}
|
||||
|
||||
static if (isForwardRange!R)
|
||||
{
|
||||
typeof(this) save()
|
||||
{
|
||||
return typeof(this)(this.source.save(), length);
|
||||
}
|
||||
}
|
||||
static if (isRandomAccessRange!R)
|
||||
{
|
||||
@property auto ref back()
|
||||
in
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
do
|
||||
{
|
||||
return this.source[this.length - 1];
|
||||
}
|
||||
|
||||
void popBack()
|
||||
in
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
do
|
||||
{
|
||||
--this.length_;
|
||||
}
|
||||
|
||||
auto ref opIndex(size_t i)
|
||||
in
|
||||
{
|
||||
assert(i < length);
|
||||
}
|
||||
do
|
||||
{
|
||||
return this.source[i];
|
||||
}
|
||||
|
||||
static if (hasAssignableElements!R)
|
||||
{
|
||||
@property void back(ref ElementType!R value)
|
||||
in
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
do
|
||||
{
|
||||
this.source[length - 1] = value;
|
||||
}
|
||||
|
||||
@property void back(ElementType!R value)
|
||||
in
|
||||
{
|
||||
assert(!empty);
|
||||
}
|
||||
do
|
||||
{
|
||||
this.source[length - 1] = move(value);
|
||||
}
|
||||
|
||||
void opIndexAssign(ref ElementType!R value, size_t i)
|
||||
in
|
||||
{
|
||||
assert(i < length);
|
||||
}
|
||||
do
|
||||
{
|
||||
this.source[i] = value;
|
||||
}
|
||||
|
||||
void opIndexAssign(ElementType!R value, size_t i)
|
||||
in
|
||||
{
|
||||
assert(i < length);
|
||||
}
|
||||
do
|
||||
{
|
||||
this.source[i] = move(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
static if (hasSlicing!R)
|
||||
{
|
||||
auto opSlice(size_t i, size_t j)
|
||||
in
|
||||
{
|
||||
assert(i <= j);
|
||||
assert(j <= length);
|
||||
}
|
||||
do
|
||||
{
|
||||
return take(this.source[i .. j], length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes $(D_PARAM n) elements from $(D_PARAM range).
|
||||
*
|
||||
* If $(D_PARAM range) doesn't have $(D_PARAM n) elements, the resulting range
|
||||
* spans all elements of $(D_PARAM range).
|
||||
*
|
||||
* $(D_PSYMBOL take) is particulary useful with infinite ranges. You can take
|
||||
` $(B n) elements from such range and pass the result to an algorithm which
|
||||
* expects a finit range.
|
||||
*
|
||||
* Params:
|
||||
* R = Type of the adapted range.
|
||||
* range = The range to take the elements from.
|
||||
* n = The number of elements to take.
|
||||
*
|
||||
* Returns: A range containing maximum $(D_PARAM n) first elements of
|
||||
* $(D_PARAM range).
|
||||
*
|
||||
* See_Also: $(D_PSYMBOL takeExactly).
|
||||
*/
|
||||
auto take(R)(R range, size_t n)
|
||||
if (isInputRange!R)
|
||||
{
|
||||
struct Take
|
||||
{
|
||||
mixin .Take!(R, false);
|
||||
}
|
||||
return Take(range, n);
|
||||
}
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
static struct InfiniteRange
|
||||
{
|
||||
private size_t front_ = 1;
|
||||
|
||||
enum bool empty = false;
|
||||
|
||||
@property size_t front() @nogc nothrow pure @safe
|
||||
{
|
||||
return this.front_;
|
||||
}
|
||||
|
||||
@property void front(size_t i) @nogc nothrow pure @safe
|
||||
{
|
||||
this.front_ = i;
|
||||
}
|
||||
|
||||
void popFront() @nogc nothrow pure @safe
|
||||
{
|
||||
++this.front_;
|
||||
}
|
||||
|
||||
size_t opIndex(size_t i) @nogc nothrow pure @safe
|
||||
{
|
||||
return this.front_ + i;
|
||||
}
|
||||
|
||||
void opIndexAssign(size_t value, size_t i) @nogc nothrow pure @safe
|
||||
{
|
||||
this.front = i + value;
|
||||
}
|
||||
|
||||
InfiniteRange save() @nogc nothrow pure @safe
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
auto t = InfiniteRange().take(3);
|
||||
assert(t.length == 3);
|
||||
assert(t.front == 1);
|
||||
assert(t.back == 3);
|
||||
|
||||
t.popFront();
|
||||
assert(t.front == 2);
|
||||
assert(t.back == 3);
|
||||
|
||||
t.popBack();
|
||||
assert(t.front == 2);
|
||||
assert(t.back == 2);
|
||||
|
||||
t.popFront();
|
||||
assert(t.empty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes exactly $(D_PARAM n) elements from $(D_PARAM range).
|
||||
*
|
||||
* $(D_PARAM range) must have at least $(D_PARAM n) elements.
|
||||
*
|
||||
* $(D_PSYMBOL takeExactly) is particulary useful with infinite ranges. You can
|
||||
` take $(B n) elements from such range and pass the result to an algorithm
|
||||
* which expects a finit range.
|
||||
*
|
||||
* Params:
|
||||
* R = Type of the adapted range.
|
||||
* range = The range to take the elements from.
|
||||
* n = The number of elements to take.
|
||||
*
|
||||
* Returns: A range containing $(D_PARAM n) first elements of $(D_PARAM range).
|
||||
*
|
||||
* See_Also: $(D_PSYMBOL take).
|
||||
*/
|
||||
auto takeExactly(R)(R range, size_t n)
|
||||
if (isInputRange!R)
|
||||
{
|
||||
struct TakeExactly
|
||||
{
|
||||
mixin Take!(R, true);
|
||||
}
|
||||
return TakeExactly(range, n);
|
||||
}
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
static struct InfiniteRange
|
||||
{
|
||||
private size_t front_ = 1;
|
||||
|
||||
enum bool empty = false;
|
||||
|
||||
@property size_t front() @nogc nothrow pure @safe
|
||||
{
|
||||
return this.front_;
|
||||
}
|
||||
|
||||
@property void front(size_t i) @nogc nothrow pure @safe
|
||||
{
|
||||
this.front_ = i;
|
||||
}
|
||||
|
||||
void popFront() @nogc nothrow pure @safe
|
||||
{
|
||||
++this.front_;
|
||||
}
|
||||
|
||||
size_t opIndex(size_t i) @nogc nothrow pure @safe
|
||||
{
|
||||
return this.front_ + i;
|
||||
}
|
||||
|
||||
void opIndexAssign(size_t value, size_t i) @nogc nothrow pure @safe
|
||||
{
|
||||
this.front = i + value;
|
||||
}
|
||||
|
||||
InfiniteRange save() @nogc nothrow pure @safe
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
auto t = InfiniteRange().takeExactly(3);
|
||||
assert(t.length == 3);
|
||||
assert(t.front == 1);
|
||||
assert(t.back == 3);
|
||||
|
||||
t.popFront();
|
||||
assert(t.front == 2);
|
||||
assert(t.back == 3);
|
||||
|
||||
t.popBack();
|
||||
assert(t.front == 2);
|
||||
assert(t.back == 2);
|
||||
|
||||
t.popFront();
|
||||
assert(t.empty);
|
||||
}
|
@ -15,5 +15,6 @@
|
||||
*/
|
||||
module tanya.range;
|
||||
|
||||
public import tanya.range.adapter;
|
||||
public import tanya.range.array;
|
||||
public import tanya.range.primitive;
|
||||
|
@ -14,8 +14,8 @@
|
||||
*/
|
||||
module tanya.range.primitive;
|
||||
|
||||
import tanya.algorithm.comparison;
|
||||
import tanya.algorithm.mutation;
|
||||
import tanya.math;
|
||||
import tanya.meta.trait;
|
||||
import tanya.meta.transform;
|
||||
import tanya.range.array;
|
||||
|
@ -16,7 +16,7 @@
|
||||
* defined here.
|
||||
* Also aliases for specific types like $(D_PSYMBOL SOCKET) are defined here.
|
||||
*
|
||||
* 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)
|
||||
@ -58,4 +58,4 @@ align(1) struct GUID
|
||||
ushort Data2;
|
||||
ushort Data3;
|
||||
char[8] Data4;
|
||||
}
|
||||
}
|
||||
|
@ -1,115 +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/. */
|
||||
|
||||
/**
|
||||
* Copyright: Eugene Wissner 2017.
|
||||
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
||||
* Mozilla Public License, v. 2.0).
|
||||
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
|
||||
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/sys/windows/error.d,
|
||||
* tanya/sys/windows/error.d)
|
||||
*/
|
||||
deprecated("Use core.sys.windows.winerror instead")
|
||||
module tanya.sys.windows.error;
|
||||
|
||||
version (Windows):
|
||||
|
||||
private enum WSABASEERR = 10000;
|
||||
enum
|
||||
{
|
||||
WSAEINTR = WSABASEERR + 4,
|
||||
WSAEBADF = WSABASEERR + 9,
|
||||
WSAEACCES = WSABASEERR + 13,
|
||||
WSAEFAULT = WSABASEERR + 14,
|
||||
WSAEINVAL = WSABASEERR + 22,
|
||||
WSAEMFILE = WSABASEERR + 24,
|
||||
|
||||
WSAEWOULDBLOCK = WSABASEERR + 35,
|
||||
WSAEINPROGRESS = WSABASEERR + 36,
|
||||
WSAEALREADY = WSABASEERR + 37,
|
||||
WSAENOTSOCK = WSABASEERR + 38,
|
||||
WSAEDESTADDRREQ = WSABASEERR + 39,
|
||||
WSAEMSGSIZE = WSABASEERR + 40,
|
||||
WSAEPROTOTYPE = WSABASEERR + 41,
|
||||
WSAENOPROTOOPT = WSABASEERR + 42,
|
||||
WSAEPROTONOSUPPORT = WSABASEERR + 43,
|
||||
WSAESOCKTNOSUPPORT = WSABASEERR + 44,
|
||||
WSAEOPNOTSUPP = WSABASEERR + 45,
|
||||
WSAEPFNOSUPPORT = WSABASEERR + 46,
|
||||
WSAEAFNOSUPPORT = WSABASEERR + 47,
|
||||
WSAEADDRINUSE = WSABASEERR + 48,
|
||||
WSAEADDRNOTAVAIL = WSABASEERR + 49,
|
||||
WSAENETDOWN = WSABASEERR + 50,
|
||||
WSAENETUNREACH = WSABASEERR + 51,
|
||||
WSAENETRESET = WSABASEERR + 52,
|
||||
WSAECONNABORTED = WSABASEERR + 53,
|
||||
WSAECONNRESET = WSABASEERR + 54,
|
||||
WSAENOBUFS = WSABASEERR + 55,
|
||||
WSAEISCONN = WSABASEERR + 56,
|
||||
WSAENOTCONN = WSABASEERR + 57,
|
||||
WSAESHUTDOWN = WSABASEERR + 58,
|
||||
WSAETOOMANYREFS = WSABASEERR + 59,
|
||||
WSAETIMEDOUT = WSABASEERR + 60,
|
||||
WSAECONNREFUSED = WSABASEERR + 61,
|
||||
WSAELOOP = WSABASEERR + 62,
|
||||
WSAENAMETOOLONG = WSABASEERR + 63,
|
||||
WSAEHOSTDOWN = WSABASEERR + 64,
|
||||
WSAEHOSTUNREACH = WSABASEERR + 65,
|
||||
WSAENOTEMPTY = WSABASEERR + 66,
|
||||
WSAEPROCLIM = WSABASEERR + 67,
|
||||
WSAEUSERS = WSABASEERR + 68,
|
||||
WSAEDQUOT = WSABASEERR + 69,
|
||||
WSAESTALE = WSABASEERR + 70,
|
||||
WSAEREMOTE = WSABASEERR + 71,
|
||||
|
||||
WSASYSNOTREADY = WSABASEERR + 91,
|
||||
WSAVERNOTSUPPORTED = WSABASEERR + 92,
|
||||
WSANOTINITIALISED = WSABASEERR + 93,
|
||||
WSAEDISCON = WSABASEERR + 101,
|
||||
WSAENOMORE = WSABASEERR + 102,
|
||||
WSAECANCELLED = WSABASEERR + 103,
|
||||
WSAEINVALIDPROCTABLE = WSABASEERR + 104,
|
||||
WSAEINVALIDPROVIDER = WSABASEERR + 105,
|
||||
WSAEPROVIDERFAILEDINIT = WSABASEERR + 106,
|
||||
WSASYSCALLFAILURE = WSABASEERR + 107,
|
||||
WSASERVICE_NOT_FOUND = WSABASEERR + 108,
|
||||
WSATYPE_NOT_FOUND = WSABASEERR + 109,
|
||||
WSA_E_NO_MORE = WSABASEERR + 110,
|
||||
WSA_E_CANCELLED = WSABASEERR + 111,
|
||||
WSAEREFUSED = WSABASEERR + 112,
|
||||
|
||||
WSAHOST_NOT_FOUND = WSABASEERR + 1001,
|
||||
WSATRY_AGAIN = WSABASEERR + 1002,
|
||||
WSANO_RECOVERY = WSABASEERR + 1003,
|
||||
WSANO_DATA = WSABASEERR + 1004,
|
||||
|
||||
WSA_QOS_RECEIVERS = WSABASEERR + 1005,
|
||||
WSA_QOS_SENDERS = WSABASEERR + 1006,
|
||||
WSA_QOS_NO_SENDERS = WSABASEERR + 1007,
|
||||
WSA_QOS_NO_RECEIVERS = WSABASEERR + 1008,
|
||||
WSA_QOS_REQUEST_CONFIRMED = WSABASEERR + 1009,
|
||||
WSA_QOS_ADMISSION_FAILURE = WSABASEERR + 1010,
|
||||
WSA_QOS_POLICY_FAILURE = WSABASEERR + 1011,
|
||||
WSA_QOS_BAD_STYLE = WSABASEERR + 1012,
|
||||
WSA_QOS_BAD_OBJECT = WSABASEERR + 1013,
|
||||
|
||||
WSA_QOS_TRAFFIC_CTRL_ERROR = WSABASEERR + 1014,
|
||||
WSA_QOS_GENERIC_ERROR = WSABASEERR + 1015,
|
||||
WSA_QOS_ESERVICETYPE = WSABASEERR + 1016,
|
||||
WSA_QOS_EFLOWSPEC = WSABASEERR + 1017,
|
||||
WSA_QOS_EPROVSPECBUF = WSABASEERR + 1018,
|
||||
WSA_QOS_EFILTERSTYLE = WSABASEERR + 1019,
|
||||
WSA_QOS_EFILTERTYPE = WSABASEERR + 1020,
|
||||
WSA_QOS_EFILTERCOUNT = WSABASEERR + 1021,
|
||||
WSA_QOS_EOBJLENGTH = WSABASEERR + 1022,
|
||||
WSA_QOS_EFLOWCOUNT = WSABASEERR + 1023,
|
||||
WSA_QOS_EUNKOWNPSOBJ = WSABASEERR + 1024,
|
||||
WSA_QOS_EPOLICYOBJ = WSABASEERR + 1025,
|
||||
WSA_QOS_EFLOWDESC = WSABASEERR + 1026,
|
||||
WSA_QOS_EPSFLOWSPEC = WSABASEERR + 1027,
|
||||
WSA_QOS_EPSFILTERSPEC = WSABASEERR + 1028,
|
||||
WSA_QOS_ESDMODEOBJ = WSABASEERR + 1029,
|
||||
WSA_QOS_ESHAPERATEOBJ = WSABASEERR + 1030,
|
||||
WSA_QOS_RESERVED_PETYPE = WSABASEERR + 1031,
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* 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)
|
||||
@ -15,6 +15,5 @@ module tanya.sys.windows;
|
||||
version (Windows):
|
||||
|
||||
public import tanya.sys.windows.def;
|
||||
public import tanya.sys.windows.error;
|
||||
public import tanya.sys.windows.winbase;
|
||||
public import tanya.sys.windows.winsock2;
|
||||
public import tanya.sys.windows.winsock2;
|
||||
|
@ -5,7 +5,7 @@
|
||||
/**
|
||||
* Definitions from winbase.h.
|
||||
*
|
||||
* 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)
|
||||
@ -52,4 +52,4 @@ extern(Windows)
|
||||
BOOL GetOverlappedResult(HANDLE hFile,
|
||||
OVERLAPPED* lpOverlapped,
|
||||
DWORD* lpNumberOfBytesTransferred,
|
||||
BOOL bWait) nothrow @system @nogc;
|
||||
BOOL bWait) nothrow @system @nogc;
|
||||
|
@ -5,7 +5,7 @@
|
||||
/**
|
||||
* Definitions from winsock2.h, ws2def.h and MSWSock.h.
|
||||
*
|
||||
* 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)
|
||||
@ -216,4 +216,4 @@ enum
|
||||
SO_UPDATE_ACCEPT_CONTEXT = 0x700B,
|
||||
SO_CONNECT_TIME = 0x700C,
|
||||
SO_UPDATE_CONNECT_CONTEXT = 0x7010,
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
* The functions can cause segmentation fault if the module is compiled
|
||||
* in production mode and the condition fails.
|
||||
*
|
||||
* 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)
|
||||
|
@ -5,7 +5,7 @@
|
||||
/**
|
||||
* Test suite for $(D_KEYWORD unittest)-blocks.
|
||||
*
|
||||
* 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)
|
||||
|
@ -17,71 +17,16 @@
|
||||
*/
|
||||
module tanya.typecons;
|
||||
|
||||
import tanya.algorithm.mutation;
|
||||
import tanya.format;
|
||||
import tanya.meta.metafunction : AliasSeq, AliasTuple = Tuple, Map;
|
||||
|
||||
deprecated("Use tanya.typecons.Tuple instead")
|
||||
template Pair(Specs...)
|
||||
{
|
||||
template parseSpecs(size_t fieldCount, Specs...)
|
||||
{
|
||||
static if (Specs.length == 0)
|
||||
{
|
||||
alias parseSpecs = AliasSeq!();
|
||||
}
|
||||
else static if (is(Specs[0]) && fieldCount < 2)
|
||||
{
|
||||
static if (is(typeof(Specs[1]) == string))
|
||||
{
|
||||
alias parseSpecs
|
||||
= AliasSeq!(AliasTuple!(Specs[0], Specs[1]),
|
||||
parseSpecs!(fieldCount + 1, Specs[2 .. $]));
|
||||
}
|
||||
else
|
||||
{
|
||||
alias parseSpecs
|
||||
= AliasSeq!(AliasTuple!(Specs[0]),
|
||||
parseSpecs!(fieldCount + 1, Specs[1 .. $]));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
static assert(false, "Invalid argument: " ~ Specs[0].stringof);
|
||||
}
|
||||
}
|
||||
|
||||
alias ChooseType(alias T) = T.Seq[0];
|
||||
alias ParsedSpecs = parseSpecs!(0, Specs);
|
||||
|
||||
static assert(ParsedSpecs.length == 2, "Invalid argument count");
|
||||
|
||||
struct Pair
|
||||
{
|
||||
/// Field types.
|
||||
alias Types = Map!(ChooseType, ParsedSpecs);
|
||||
|
||||
// Create field aliases.
|
||||
static if (ParsedSpecs[0].length == 2)
|
||||
{
|
||||
mixin("alias " ~ ParsedSpecs[0][1] ~ " = expand[0];");
|
||||
}
|
||||
static if (ParsedSpecs[1].length == 2)
|
||||
{
|
||||
mixin("alias " ~ ParsedSpecs[1][1] ~ " = expand[1];");
|
||||
}
|
||||
|
||||
/// Represents the values of the $(D_PSYMBOL Pair) as a list of values.
|
||||
Types expand;
|
||||
|
||||
alias expand this;
|
||||
}
|
||||
}
|
||||
import tanya.meta.trait;
|
||||
|
||||
/**
|
||||
* $(D_PSYMBOL Tuple) can store two heterogeneous objects.
|
||||
* $(D_PSYMBOL Tuple) can store two or more heterogeneous objects.
|
||||
*
|
||||
* The objects can by accessed by index as $(D_INLINECODE obj[0]) and
|
||||
* $(D_INLINECODE obj[1]) or by optional names (e.g.
|
||||
* $(D_INLINECODE obj.first)).
|
||||
* The objects can by accessed by index as `obj[0]` and `obj[1]` or by optional
|
||||
* names (e.g. `obj.first`).
|
||||
*
|
||||
* $(D_PARAM Specs) contains a list of object types and names. First
|
||||
* comes the object type, then an optional string containing the name.
|
||||
@ -123,7 +68,26 @@ template Tuple(Specs...)
|
||||
alias ChooseType(alias T) = T.Seq[0];
|
||||
alias ParsedSpecs = parseSpecs!(0, Specs);
|
||||
|
||||
static assert(ParsedSpecs.length == 2, "Invalid argument count");
|
||||
static assert(ParsedSpecs.length > 1, "Invalid argument count");
|
||||
|
||||
private string formatAliases(size_t n, Specs...)()
|
||||
{
|
||||
static if (Specs.length == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
else
|
||||
{
|
||||
string fieldAlias;
|
||||
static if (Specs[0].length == 2)
|
||||
{
|
||||
char[21] buffer;
|
||||
fieldAlias = "alias " ~ Specs[0][1] ~ " = expand["
|
||||
~ integral2String(n, buffer).idup ~ "];";
|
||||
}
|
||||
return fieldAlias ~ formatAliases!(n + 1, Specs[1 .. $])();
|
||||
}
|
||||
}
|
||||
|
||||
struct Tuple
|
||||
{
|
||||
@ -131,14 +95,7 @@ template Tuple(Specs...)
|
||||
alias Types = Map!(ChooseType, ParsedSpecs);
|
||||
|
||||
// Create field aliases.
|
||||
static if (ParsedSpecs[0].length == 2)
|
||||
{
|
||||
mixin("alias " ~ ParsedSpecs[0][1] ~ " = expand[0];");
|
||||
}
|
||||
static if (ParsedSpecs[1].length == 2)
|
||||
{
|
||||
mixin("alias " ~ ParsedSpecs[1][1] ~ " = expand[1];");
|
||||
}
|
||||
mixin(formatAliases!(0, ParsedSpecs[0 .. $])());
|
||||
|
||||
/// Represents the values of the $(D_PSYMBOL Tuple) as a list of values.
|
||||
Types expand;
|
||||
@ -171,4 +128,327 @@ template Tuple(Specs...)
|
||||
static assert(!is(Tuple!(int, int, int)));
|
||||
|
||||
static assert(!is(Tuple!(int, "first")));
|
||||
|
||||
static assert(!is(Tuple!(int, double, char)));
|
||||
static assert(!is(Tuple!(int, "first", double, "second", char, "third")));
|
||||
}
|
||||
|
||||
/**
|
||||
* $(D_PSYMBOL Option) is a type that contains an optional value.
|
||||
*
|
||||
* Params:
|
||||
* T = Type of the encapsulated value.
|
||||
*/
|
||||
struct Option(T)
|
||||
{
|
||||
private bool isNothing_ = true;
|
||||
private T value = void;
|
||||
|
||||
/**
|
||||
* Constructs a new option with $(D_PARAM value).
|
||||
*
|
||||
* Params:
|
||||
* value = Encapsulated value.
|
||||
*/
|
||||
this()(ref T value)
|
||||
{
|
||||
this.value = value;
|
||||
this.isNothing_ = false;
|
||||
}
|
||||
|
||||
/// ditto
|
||||
this()(T value) @trusted
|
||||
{
|
||||
moveEmplace(value, this.value);
|
||||
this.isNothing_ = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the option is just a value or nothing.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if this $(D_PSYMBOL Option) contains a nothing,
|
||||
* $(D_KEYWORD false) if it contains a value.
|
||||
*/
|
||||
@property bool isNothing() const
|
||||
{
|
||||
return this.isNothing_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the encapsulated value.
|
||||
*
|
||||
* Returns: Value encapsulated in this $(D_PSYMBOL Option).
|
||||
*
|
||||
* See_Also: $(D_PSYMBOL or).
|
||||
*
|
||||
* Precondition: `!isNothing`.
|
||||
*/
|
||||
@property ref inout(T) get() inout
|
||||
in
|
||||
{
|
||||
assert(!isNothing, "Option is nothing");
|
||||
}
|
||||
do
|
||||
{
|
||||
return this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the encapsulated value if available or a default value
|
||||
* otherwise.
|
||||
*
|
||||
* Note that the contained value can be returned by reference only if the
|
||||
* default value is passed by reference as well.
|
||||
*
|
||||
* Params:
|
||||
* U = Type of the default value.
|
||||
* defaultValue = Default value.
|
||||
*
|
||||
* Returns: The value of this $(D_PSYMBOL Option) if available,
|
||||
* $(D_PARAM defaultValue) otherwise.
|
||||
*
|
||||
* See_Also: $(D_PSYMBOL isNothing), $(D_PSYMBOL get).
|
||||
*/
|
||||
@property U or(U)(U defaultValue) inout
|
||||
if (is(U == T) && isCopyable!T)
|
||||
{
|
||||
return isNothing ? defaultValue : this.value;
|
||||
}
|
||||
|
||||
/// ditto
|
||||
@property ref inout(T) or(ref inout(T) defaultValue) inout
|
||||
{
|
||||
return isNothing ? defaultValue : this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Casts this $(D_PSYMBOL Option) to $(D_KEYWORD bool).
|
||||
*
|
||||
* An $(D_PSYMBOL Option) is $(D_KEYWORD true) if it contains a value,
|
||||
* ($D_KEYWORD false) if it contains nothing.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if this $(D_PSYMBOL Option) contains a value,
|
||||
* ($D_KEYWORD false) if it contains nothing.
|
||||
*/
|
||||
bool opCast(U : bool)()
|
||||
{
|
||||
return !isNothing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this $(D_PSYMBOL Option) with $(D_PARAM that).
|
||||
*
|
||||
* If both objects are options of the same type and they don't contain a
|
||||
* value, they are considered equal. If only one of them contains a value,
|
||||
* they aren't equal. Otherwise, the encapsulated values are compared for
|
||||
* equality.
|
||||
*
|
||||
* If $(D_PARAM U) is a type comparable with the type encapsulated by this
|
||||
* $(D_PSYMBOL Option), the value of this $(D_PSYMBOL Option) is compared
|
||||
* with $(D_PARAM that), this $(D_PSYMBOL Option) must have a value then.
|
||||
*
|
||||
* Params:
|
||||
* U = Type of the object to compare with.
|
||||
* that = Object to compare with.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if this $(D_PSYMBOL Option) and
|
||||
* $(D_PARAM that) are equal, $(D_KEYWORD false) if not.
|
||||
*
|
||||
* Precondition: `!isNothing` if $(D_PARAM U) is equality comparable with
|
||||
* $(D_PARAM T).
|
||||
*/
|
||||
bool opEquals(U)(auto ref const U that) const
|
||||
if (is(U == Option))
|
||||
{
|
||||
if (!isNothing && !that.isNothing)
|
||||
{
|
||||
return this.value == that.value;
|
||||
}
|
||||
return isNothing == that.isNothing;
|
||||
}
|
||||
|
||||
/// ditto
|
||||
bool opEquals(U)(auto ref const U that) const
|
||||
if (ifTestable!(U, a => a == T.init) && !is(U == Option))
|
||||
in
|
||||
{
|
||||
assert(!isNothing);
|
||||
}
|
||||
do
|
||||
{
|
||||
return get == that;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets this $(D_PSYMBOL Option) and destroys the contained value.
|
||||
*
|
||||
* $(D_PSYMBOL reset) can be safely called on an $(D_PSYMBOL Option) that
|
||||
* doesn't contain any value.
|
||||
*/
|
||||
void reset()
|
||||
{
|
||||
static if (hasElaborateDestructor!T)
|
||||
{
|
||||
destroy(this.value);
|
||||
}
|
||||
this.isNothing_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a new value.
|
||||
*
|
||||
* Params:
|
||||
* U = Type of the new value.
|
||||
* that = New value.
|
||||
*
|
||||
* Returns: $(D_KEYWORD this).
|
||||
*/
|
||||
ref typeof(this) opAssign(U)(ref U that)
|
||||
if (is(U : T) && !is(U == Option))
|
||||
{
|
||||
this.value = that;
|
||||
this.isNothing_ = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// ditto
|
||||
ref typeof(this) opAssign(U)(U that)
|
||||
if (is(U == T))
|
||||
{
|
||||
move(that, this.value);
|
||||
this.isNothing_ = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// ditto
|
||||
ref typeof(this) opAssign(U)(ref U that)
|
||||
if (is(U == Option))
|
||||
{
|
||||
this.value = that;
|
||||
this.isNothing_ = that.isNothing;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// ditto
|
||||
ref typeof(this) opAssign(U)(U that)
|
||||
if (is(U == Option))
|
||||
{
|
||||
move(that.value, this.value);
|
||||
this.isNothing_ = that.isNothing_;
|
||||
return this;
|
||||
}
|
||||
|
||||
alias get this;
|
||||
}
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
Option!int option;
|
||||
assert(option.isNothing);
|
||||
assert(option.or(8) == 8);
|
||||
|
||||
option = 5;
|
||||
assert(!option.isNothing);
|
||||
assert(option.get == 5);
|
||||
assert(option.or(8) == 5);
|
||||
|
||||
option.reset();
|
||||
assert(option.isNothing);
|
||||
}
|
||||
|
||||
// Assigns a new value
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
{
|
||||
Option!int option = 5;
|
||||
option = 8;
|
||||
assert(!option.isNothing);
|
||||
assert(option == 8);
|
||||
}
|
||||
{
|
||||
Option!int option;
|
||||
const int newValue = 8;
|
||||
assert(option.isNothing);
|
||||
option = newValue;
|
||||
assert(!option.isNothing);
|
||||
assert(option == newValue);
|
||||
}
|
||||
{
|
||||
Option!int option1;
|
||||
Option!int option2 = 5;
|
||||
assert(option1.isNothing);
|
||||
option1 = option2;
|
||||
assert(!option1.isNothing);
|
||||
assert(option1.get == 5);
|
||||
}
|
||||
}
|
||||
|
||||
// Constructs with a value passed by reference
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
int i = 5;
|
||||
assert(Option!int(i).get == 5);
|
||||
}
|
||||
|
||||
// Moving
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
static struct NotCopyable
|
||||
{
|
||||
@disable this(this);
|
||||
}
|
||||
|
||||
static assert(is(typeof(Option!NotCopyable(NotCopyable()))));
|
||||
// The value cannot be returned by reference because the default value
|
||||
// isn't passed by reference
|
||||
static assert(!is(typeof(Option!DisabledPostblit().or(NotCopyable()))));
|
||||
{
|
||||
NotCopyable notCopyable;
|
||||
static assert(is(typeof(Option!NotCopyable().or(notCopyable))));
|
||||
}
|
||||
{
|
||||
Option!NotCopyable option;
|
||||
assert(option.isNothing);
|
||||
option = NotCopyable();
|
||||
assert(!option.isNothing);
|
||||
}
|
||||
{
|
||||
Option!NotCopyable option;
|
||||
assert(option.isNothing);
|
||||
option = Option!NotCopyable(NotCopyable());
|
||||
assert(!option.isNothing);
|
||||
}
|
||||
}
|
||||
|
||||
// Cast to bool is done before touching the encapsulated value
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
assert(Option!bool(false));
|
||||
}
|
||||
|
||||
// Option can be const
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
assert((const Option!int(5)).get == 5);
|
||||
assert((const Option!int()).or(5) == 5);
|
||||
}
|
||||
|
||||
// Equality
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
assert(Option!int() == Option!int());
|
||||
assert(Option!int(0) != Option!int());
|
||||
assert(Option!int(5) == Option!int(5));
|
||||
assert(Option!int(5) == 5);
|
||||
assert(Option!int(5) == cast(ubyte) 5);
|
||||
}
|
||||
|
||||
// Returns default value
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
{
|
||||
int i = 5;
|
||||
assert(((ref e) => e)(Option!int().or(i)) == 5);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user