10 Commits

Author SHA1 Message Date
f51e9405c9 Update socket documentation 2018-06-20 07:59:37 +02:00
de15281ccb Tuple with more than two fields
Fix #41.
2018-06-19 05:44:15 +02:00
a86b6690f0 Implement auto-decoding free equal comparison
Fix #39.
2018-06-12 20:19:06 +02:00
15f7994187 Add takeExactly
Fix #43.
2018-06-10 19:03:26 +02:00
37b0afe290 take: Remove moveFront, moveBack, moveAt 2018-06-10 14:46:40 +02:00
cd9960db2a Add take range adapter 2018-06-10 14:46:40 +02:00
7357503c5a Update 2.080 series to 2.080.1 2018-06-09 05:05:30 +02:00
173ae115ee readIntegral: Support base between 2 and 36 2018-06-08 21:05:35 +02:00
7561b964d3 Make intToString -> readString more generic
Make readString work with any char range and unsigned integral type.
2018-06-07 07:23:39 +02:00
c663703221 container.list: Remove deprecated list length property 2018-06-01 14:13:27 +02:00
30 changed files with 793 additions and 187 deletions

View File

@ -7,7 +7,7 @@ os:
language: d language: d
d: d:
- dmd-2.080.0 - dmd-2.080.1
- dmd-2.079.1 - dmd-2.079.1
- dmd-2.078.3 - dmd-2.078.3
- dmd-2.077.1 - dmd-2.077.1
@ -23,7 +23,7 @@ addons:
- gcc-multilib - gcc-multilib
before_script: before_script:
- if [ "`$DC --version | head -n 1 | grep 'v2.080.0'`" ]; then - if [ "`$DC --version | head -n 1 | grep 'v2.080.1'`" ]; then
export UNITTEST="unittest-cov"; export UNITTEST="unittest-cov";
fi fi

View File

@ -172,7 +172,7 @@ parameter is used)
| DMD | GCC | | DMD | GCC |
|:-------:|:---------:| |:-------:|:---------:|
| 2.080.0 | *master* | | 2.080.1 | *master* |
| 2.079.1 | | | 2.079.1 | |
| 2.078.3 | | | 2.078.3 | |
| 2.077.1 | | | 2.077.1 | |

View File

@ -4,10 +4,10 @@ os: Visual Studio 2015
environment: environment:
matrix: matrix:
- DC: dmd - DC: dmd
DVersion: 2.080.0 DVersion: 2.080.1
arch: x64 arch: x64
- DC: dmd - DC: dmd
DVersion: 2.080.0 DVersion: 2.080.1
arch: x86 arch: x86
- DC: dmd - DC: dmd
DVersion: 2.079.1 DVersion: 2.079.1

View File

@ -47,6 +47,7 @@ _D5tanya6memory2op9cmpMemoryFNaNbNixAvxAvZi:
aligned_1: // Compare the remaining bytes aligned_1: // Compare the remaining bytes
mov %rdx, %rcx mov %rdx, %rcx
cmp $0x0, %rcx
repe cmpsb repe cmpsb
jl less jl less

View File

@ -16,6 +16,7 @@ module tanya.algorithm.comparison;
import tanya.algorithm.mutation; import tanya.algorithm.mutation;
import tanya.math : isNaN; import tanya.math : isNaN;
import tanya.memory.op;
import tanya.meta.metafunction; import tanya.meta.metafunction;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform; import tanya.meta.transform;
@ -270,3 +271,63 @@ if (isForwardRange!Range && isOrderingComparable!(ElementType!Range))
assert(max(s2, s3).s == 3); 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[]));
}

View File

@ -13,50 +13,52 @@
* *
* class EchoProtocol : TransmissionControlProtocol * class EchoProtocol : TransmissionControlProtocol
* { * {
* private DuplexTransport transport; * private DuplexTransport transport;
* *
* void received(in ubyte[] data) @nogc * void received(in ubyte[] data) @nogc
* { * {
* transport.write(data); * ubyte[512] buffer;
* } * buffer[0 .. data.length] = data;
* transport.write(buffer[]);
* }
* *
* void connected(DuplexTransport transport) @nogc * void connected(DuplexTransport transport) @nogc
* { * {
* this.transport = transport; * this.transport = transport;
* } * }
* *
* void disconnected(SocketException e) @nogc * void disconnected(SocketException e) @nogc
* { * {
* } * }
* } * }
* *
* void main() * 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) * version (Windows)
* { * {
* auto sock = defaultAllocator.make!OverlappedStreamSocket(AddressFamily.inet); * auto sock = defaultAllocator.make!OverlappedStreamSocket(AddressFamily.inet);
* } * }
* else * else
* { * {
* auto sock = defaultAllocator.make!StreamSocket(AddressFamily.inet); * auto sock = defaultAllocator.make!StreamSocket(AddressFamily.inet);
* sock.blocking = false; * sock.blocking = false;
* } * }
* *
* sock.bind(address); * sock.bind(address);
* sock.listen(5); * sock.listen(5);
* *
* auto io = defaultAllocator.make!ConnectionWatcher(sock); * auto io = defaultAllocator.make!ConnectionWatcher(sock);
* io.setProtocol!EchoProtocol; * io.setProtocol!EchoProtocol;
* *
* defaultLoop.start(io); * defaultLoop.start(io);
* defaultLoop.run(); * defaultLoop.run();
* *
* sock.shutdown(); * sock.shutdown();
* defaultAllocator.dispose(io); * defaultAllocator.dispose(io);
* defaultAllocator.dispose(sock); * defaultAllocator.dispose(sock);
* defaultAllocator.dispose(address); * defaultAllocator.dispose(address);
* } * }
* --- * ---
* *

View File

@ -15,7 +15,6 @@
module tanya.container.array; module tanya.container.array;
import core.checkedint; import core.checkedint;
import std.algorithm.comparison : equal;
import std.algorithm.mutation : bringToFront, import std.algorithm.mutation : bringToFront,
copy, copy,
fill, fill,

View File

@ -51,7 +51,7 @@ version (unittest)
* T = Buffer type. * T = Buffer type.
*/ */
struct ReadBuffer(T = ubyte) struct ReadBuffer(T = ubyte)
if (isScalarType!T) if (isScalarType!T)
{ {
/// Internal buffer. /// Internal buffer.
private T[] buffer_; private T[] buffer_;
@ -66,16 +66,16 @@ struct ReadBuffer(T = ubyte)
private size_t ring; private size_t ring;
/// Available space. /// Available space.
private immutable size_t minAvailable = 1024; private size_t minAvailable = 1024;
/// Size by which the buffer will grow. /// Size by which the buffer will grow.
private immutable size_t blockSize = 8192; private size_t blockSize = 8192;
invariant invariant
{ {
assert(length_ <= buffer_.length); assert(this.length_ <= this.buffer_.length);
assert(blockSize > 0); assert(this.blockSize > 0);
assert(minAvailable > 0); assert(this.minAvailable > 0);
} }
/** /**
@ -89,14 +89,14 @@ struct ReadBuffer(T = ubyte)
* $(D_PSYMBOL free) < $(D_PARAM minAvailable)). * $(D_PSYMBOL free) < $(D_PARAM minAvailable)).
* allocator = Allocator. * allocator = Allocator.
*/ */
this(in size_t size, this(size_t size,
in size_t minAvailable = 1024, size_t minAvailable = 1024,
shared Allocator allocator = defaultAllocator) @trusted shared Allocator allocator = defaultAllocator) @trusted
{ {
this(allocator); this(allocator);
this.minAvailable = minAvailable; this.minAvailable = minAvailable;
this.blockSize = size; this.blockSize = size;
buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof); this.buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof);
} }
/// ditto /// ditto
@ -115,7 +115,7 @@ struct ReadBuffer(T = ubyte)
*/ */
~this() @trusted ~this() @trusted
{ {
allocator.deallocate(buffer_); allocator.deallocate(this.buffer_);
} }
/// ///
@ -131,7 +131,7 @@ struct ReadBuffer(T = ubyte)
*/ */
@property size_t capacity() const @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 @property size_t length() const
{ {
return length_ - start; return this.length_ - start;
} }
/// ditto /// ditto
@ -152,7 +152,7 @@ struct ReadBuffer(T = ubyte)
*/ */
void clear() void clear()
{ {
start = length_ = ring; start = this.length_ = ring;
} }
/** /**
@ -187,10 +187,10 @@ struct ReadBuffer(T = ubyte)
* *
* Returns: $(D_KEYWORD this). * Returns: $(D_KEYWORD this).
*/ */
ref ReadBuffer opOpAssign(string op)(in size_t length) ref ReadBuffer opOpAssign(string op)(size_t length)
if (op == "+") if (op == "+")
{ {
length_ += length; this.length_ += length;
ring = start; ring = start;
return this; return this;
} }
@ -234,9 +234,9 @@ struct ReadBuffer(T = ubyte)
* *
* Returns: Array between $(D_PARAM start) and $(D_PARAM end). * 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) if (start > 0)
{ {
auto ret = buffer_[0 .. start]; auto ret = this.buffer_[0 .. start];
ring = 0; ring = 0;
return ret; return ret;
} }
else else
{ {
if (capacity - length < minAvailable) if (capacity - length < this.minAvailable)
{ {
void[] buf = buffer_; void[] buf = this.buffer_;
immutable cap = capacity; const cap = capacity;
() @trusted { () @trusted {
allocator.reallocate(buf, (cap + blockSize) * T.sizeof); allocator.reallocate(buf,
buffer_ = cast(T[]) buf; (cap + this.blockSize) * T.sizeof);
this.buffer_ = cast(T[]) buf;
}(); }();
} }
ring = length_; ring = this.length_;
return buffer_[length_ .. $]; return this.buffer_[this.length_ .. $];
} }
} }
@ -310,7 +311,7 @@ struct ReadBuffer(T = ubyte)
* T = Buffer type. * T = Buffer type.
*/ */
struct WriteBuffer(T = ubyte) struct WriteBuffer(T = ubyte)
if (isScalarType!T) if (isScalarType!T)
{ {
/// Internal buffer. /// Internal buffer.
private T[] buffer_; private T[] buffer_;
@ -322,16 +323,16 @@ struct WriteBuffer(T = ubyte)
private size_t ring; private size_t ring;
/// Size by which the buffer will grow. /// 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. /// The position of the free area in the buffer.
private size_t position; private size_t position;
invariant invariant
{ {
assert(blockSize > 0); assert(this.blockSize > 0);
// Position can refer to an element outside the buffer if the buffer is full. // 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) * 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 in
{ {
assert(size > 0); assert(size > 0);
@ -350,10 +351,10 @@ struct WriteBuffer(T = ubyte)
} }
do do
{ {
blockSize = size; this.blockSize = size;
ring = size - 1; ring = size - 1;
allocator_ = allocator; allocator_ = allocator;
buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof); this.buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof);
} }
@disable this(); @disable this();
@ -363,7 +364,7 @@ struct WriteBuffer(T = ubyte)
*/ */
~this() ~this()
{ {
allocator.deallocate(buffer_); allocator.deallocate(this.buffer_);
} }
/** /**
@ -371,7 +372,7 @@ struct WriteBuffer(T = ubyte)
*/ */
@property size_t capacity() const @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 @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; return ring - start + 1;
} }
else else
{ {
return position - start; return this.position - start;
} }
} }
@ -433,61 +434,62 @@ struct WriteBuffer(T = ubyte)
* Params: * Params:
* buffer = Buffer chunk got with $(D_PSYMBOL opIndex). * 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 == "~") if (op == "~")
{ {
size_t end, start; size_t end, start;
if (position >= this.start && position <= ring) if (this.position >= this.start && this.position <= ring)
{ {
auto afterRing = ring + 1; auto afterRing = ring + 1;
end = position + buffer.length; end = this.position + buffer.length;
if (end > afterRing) if (end > afterRing)
{ {
end = afterRing; end = afterRing;
} }
start = end - position; start = end - this.position;
buffer_[position .. end] = buffer[0 .. start]; this.buffer_[this.position .. end] = buffer[0 .. start];
if (end == afterRing) if (end == afterRing)
{ {
position = this.start == 0 ? afterRing : 0; this.position = this.start == 0 ? afterRing : 0;
} }
else else
{ {
position = end; this.position = end;
} }
} }
// Check if we have some free space at the beginning // 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) if (end > this.start)
{ {
end = this.start; end = this.start;
} }
auto areaEnd = end - position + start; auto areaEnd = end - this.position + start;
buffer_[position .. end] = buffer[start .. areaEnd]; this.buffer_[this.position .. end] = buffer[start .. areaEnd];
position = end == this.start ? ring + 1 : end - position; this.position = end == this.start ? ring + 1 : end - this.position;
start = areaEnd; start = areaEnd;
} }
// And if we still haven't found any place, save the rest in the overflow area // And if we still haven't found any place, save the rest in the overflow area
if (start < buffer.length) if (start < buffer.length)
{ {
end = position + buffer.length - start; end = this.position + buffer.length - start;
if (end > capacity) if (end > capacity)
{ {
auto newSize = (end / blockSize * blockSize + blockSize) * T.sizeof; const newSize = end / this.blockSize * this.blockSize
+ this.blockSize;
() @trusted { () @trusted {
void[] buf = buffer_; void[] buf = this.buffer_;
allocator.reallocate(buf, newSize); allocator.reallocate(buf, newSize * T.sizeof);
buffer_ = cast(T[]) buf; this.buffer_ = cast(T[]) buf;
}(); }();
} }
buffer_[position .. end] = buffer[start .. $]; this.buffer_[this.position .. end] = buffer[start .. $];
position = end; this.position = end;
if (this.start == 0) if (this.start == 0)
{ {
ring = capacity - 1; ring = capacity - 1;
@ -506,7 +508,7 @@ struct WriteBuffer(T = ubyte)
* *
* Returns: $(D_KEYWORD this). * Returns: $(D_KEYWORD this).
*/ */
ref WriteBuffer opOpAssign(string op)(in size_t length) ref WriteBuffer opOpAssign(string op)(size_t length)
if (op == "+") if (op == "+")
in in
{ {
@ -521,42 +523,42 @@ struct WriteBuffer(T = ubyte)
{ {
return this; return this;
} }
else if (position <= afterRing) else if (this.position <= afterRing)
{ {
start += length; start += length;
if (start > 0 && position == afterRing) if (start > 0 && this.position == afterRing)
{ {
position = oldStart; this.position = oldStart;
} }
} }
else else
{ {
auto overflow = position - afterRing; auto overflow = this.position - afterRing;
if (overflow > length) if (overflow > length)
{ {
immutable afterLength = afterRing + length; const afterLength = afterRing + length;
buffer_[start .. start + length] = buffer_[afterRing .. afterLength]; this.buffer_[start .. start + length] = this.buffer_[afterRing .. afterLength];
buffer_[afterRing .. afterLength] = buffer_[afterLength .. position]; this.buffer_[afterRing .. afterLength] = this.buffer_[afterLength .. this.position];
position -= length; this.position -= length;
} }
else if (overflow == length) else if (overflow == length)
{ {
buffer_[start .. start + overflow] = buffer_[afterRing .. position]; this.buffer_[start .. start + overflow] = this.buffer_[afterRing .. this.position];
position -= overflow; this.position -= overflow;
} }
else else
{ {
buffer_[start .. start + overflow] = buffer_[afterRing .. position]; this.buffer_[start .. start + overflow] = this.buffer_[afterRing .. this.position];
position = overflow; this.position = overflow;
} }
start += length; start += length;
if (start == position) if (start == this.position)
{ {
if (position != afterRing) if (this.position != afterRing)
{ {
position = 0; this.position = 0;
} }
start = 0; start = 0;
ring = capacity - 1; ring = capacity - 1;
@ -596,15 +598,15 @@ struct WriteBuffer(T = ubyte)
* *
* Returns: A chunk of data buffer. * 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 else
{ {
return buffer_[this.start .. this.start + end]; return this.buffer_[this.start .. this.start + end];
} }
} }

View File

@ -15,7 +15,6 @@
*/ */
module tanya.container.list; module tanya.container.list;
import std.algorithm.comparison : equal;
import std.algorithm.searching; import std.algorithm.searching;
import tanya.algorithm.comparison; import tanya.algorithm.comparison;
import tanya.algorithm.mutation; import tanya.algorithm.mutation;
@ -178,7 +177,6 @@ struct SList(T)
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
auto l = SList!int(2, 3); auto l = SList!int(2, 3);
assert(l.length == 2);
assert(l.front == 3); assert(l.front == 3);
} }
@ -192,7 +190,6 @@ struct SList(T)
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
auto l = SList!int(2); auto l = SList!int(2);
assert(l.length == 2);
assert(l.front == 0); assert(l.front == 0);
} }
@ -432,7 +429,6 @@ struct SList(T)
assert(l2.front == 25); assert(l2.front == 25);
l2.insertFront(l1[]); l2.insertFront(l1[]);
assert(l2.length == 5);
assert(l2.front == 9); assert(l2.front == 9);
} }
@ -566,12 +562,6 @@ struct SList(T)
assert(l1 == l2); assert(l1 == l2);
} }
deprecated
@property size_t length() const
{
return count(this[]);
}
/** /**
* Comparison for equality. * Comparison for equality.
* *
@ -583,7 +573,7 @@ struct SList(T)
*/ */
bool opEquals()(auto ref typeof(this) that) inout 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 @nogc nothrow pure @safe unittest
{ {
auto l = DList!int(2, 3); auto l = DList!int(2, 3);
assert(l.length == 2);
assert(l.front == 3); assert(l.front == 3);
assert(l.back == 3); assert(l.back == 3);
} }
@ -1169,7 +1158,6 @@ struct DList(T)
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
auto l = DList!int(2); auto l = DList!int(2);
assert(l.length == 2);
assert(l.front == 0); assert(l.front == 0);
} }
@ -1480,7 +1468,6 @@ struct DList(T)
assert(l2.back == 15); assert(l2.back == 15);
l2.insertFront(l1[]); l2.insertFront(l1[]);
assert(l2.length == 5);
assert(l2.front == 9); assert(l2.front == 9);
assert(l2.back == 15); assert(l2.back == 15);
} }
@ -1600,7 +1587,6 @@ struct DList(T)
assert(l2.back == 15); assert(l2.back == 15);
l2.insertBack(l1[]); l2.insertBack(l1[]);
assert(l2.length == 5);
assert(l2.back == 9); assert(l2.back == 9);
} }
@ -1855,12 +1841,6 @@ struct DList(T)
return insertAfter!(T[])(r, el[]); return insertAfter!(T[])(r, el[]);
} }
deprecated
@property size_t length() const
{
return count(this[]);
}
/** /**
* Comparison for equality. * Comparison for equality.
* *
@ -2354,7 +2334,6 @@ struct DList(T)
l.insertAfter(l[], 234); l.insertAfter(l[], 234);
assert(l.front == 234); assert(l.front == 234);
assert(l.back == 234); assert(l.back == 234);
assert(l.length == 1);
} }
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest

View File

@ -26,7 +26,7 @@
*/ */
module tanya.container.string; module tanya.container.string;
import std.algorithm.comparison : cmp, equal; import std.algorithm.comparison : cmp;
import std.algorithm.mutation : bringToFront, copy; import std.algorithm.mutation : bringToFront, copy;
import std.algorithm.searching; import std.algorithm.searching;
import tanya.algorithm.comparison; import tanya.algorithm.comparison;

View File

@ -5,7 +5,7 @@
/** /**
* This module provides functions for converting between different types. * 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/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -20,6 +20,8 @@ import tanya.memory;
import tanya.memory.op; import tanya.memory.op;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform; import tanya.meta.transform;
import tanya.range.array;
import tanya.range.primitive;
version (unittest) version (unittest)
{ {
@ -264,28 +266,148 @@ 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, ubyte base = 10)
if (isForwardRange!R
&& isSomeChar!(ElementType!R)
&& isIntegral!T
&& isUnsigned!T)
in
{ {
import tanya.encoding.ascii; assert(base >= 2);
import tanya.range.array; assert(base <= 36);
}
size_t i = 1; do
uint lPort; {
T boundary = cast(T) (T.max / base);
for (; !range.empty && range.front.isDigit() && i <= 6; ++i, range.popFront()) if (range.empty)
{ {
lPort = lPort * 10 + (range.front - '0'); return T.init;
} }
if (i != 1 && (range.empty || range.front == '/'))
T n;
while (n <= boundary)
{ {
if (lPort > ushort.max) int digit;
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; if (range.length > 1)
{
return n;
}
int digit = range.front - '0';
if (n > cast(T) ((T.max - digit) / base))
{
return n;
}
n = cast(T) (n * base + digit);
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);
} }
/** /**

View File

@ -5,7 +5,7 @@
/** /**
* Common exceptions and errors. * Common exceptions and errors.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)

View File

@ -3,9 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/** /**
* This package contains formatting and conversion functions. * This package doesn't yet contain public symbols. Refer to
* $(D_PSYMBOL tanya.conv) for basic formatting and conversion functionality.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)

View File

@ -14,7 +14,7 @@
*/ */
module tanya.math.mp; module tanya.math.mp;
import std.algorithm.comparison : cmp, equal; import std.algorithm.comparison : cmp;
import std.algorithm.mutation : copy, fill, reverse; import std.algorithm.mutation : copy, fill, reverse;
import std.range; import std.range;
import tanya.algorithm.comparison; import tanya.algorithm.comparison;

View File

@ -38,7 +38,7 @@ enum IEEEPrecision : ubyte
/** /**
* Tests the precision of floating-point type $(D_PARAM F). * 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.single); for $(D_KEYWORD double) - to
* $(D_INLINECODE IEEEPrecision.double). It returns different values only * $(D_INLINECODE IEEEPrecision.double). It returns different values only
* for $(D_KEYWORD real), since $(D_KEYWORD real) is a platform-dependent type. * for $(D_KEYWORD real), since $(D_KEYWORD real) is a platform-dependent type.
@ -396,7 +396,7 @@ if (isFloatingPoint!F)
/** /**
* Determines whether $(D_PARAM x) is a denormilized number or not. * Determines whether $(D_PARAM x) is a denormilized number or not.
*
* Denormalized number is a number between `0` and `1` that cannot be * Denormalized number is a number between `0` and `1` that cannot be
* represented as * represented as
* *
@ -459,7 +459,7 @@ if (isFloatingPoint!F)
/** /**
* Determines whether $(D_PARAM x) is a normilized number or not. * Determines whether $(D_PARAM x) is a normilized number or not.
*
* Normalized number is a number that can be represented as * Normalized number is a number that can be represented as
* *
* <pre> * <pre>

View File

@ -40,6 +40,11 @@ version (TanyaNative)
fillMemory(buffer[1 .. $], 0); fillMemory(buffer[1 .. $], 0);
assert(buffer[0] == 1 && 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; private enum alignMask = size_t.sizeof - 1;

View File

@ -305,7 +305,14 @@ struct URL
*/ */
private bool parsePort(const(char)[] port) @nogc nothrow pure @safe 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;
} }
} }

View File

@ -5,7 +5,7 @@
/** /**
* Network programming. * Network programming.
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)

View File

@ -5,7 +5,43 @@
/** /**
* Low-level socket programming. * 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/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)

View File

@ -6,7 +6,7 @@
* This package provides platform-independent interfaces to operating system * This package provides platform-independent interfaces to operating system
* functionality. * functionality.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)

View 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);
}

View File

@ -15,5 +15,6 @@
*/ */
module tanya.range; module tanya.range;
public import tanya.range.adapter;
public import tanya.range.array; public import tanya.range.array;
public import tanya.range.primitive; public import tanya.range.primitive;

View File

@ -16,7 +16,7 @@
* defined here. * defined here.
* Also aliases for specific types like $(D_PSYMBOL SOCKET) are 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/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -58,4 +58,4 @@ align(1) struct GUID
ushort Data2; ushort Data2;
ushort Data3; ushort Data3;
char[8] Data4; char[8] Data4;
} }

View File

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * 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/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)

View File

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * 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/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -17,4 +17,4 @@ version (Windows):
public import tanya.sys.windows.def; public import tanya.sys.windows.def;
public import tanya.sys.windows.error; public import tanya.sys.windows.error;
public import tanya.sys.windows.winbase; public import tanya.sys.windows.winbase;
public import tanya.sys.windows.winsock2; public import tanya.sys.windows.winsock2;

View File

@ -5,7 +5,7 @@
/** /**
* Definitions from winbase.h. * Definitions from winbase.h.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -52,4 +52,4 @@ extern(Windows)
BOOL GetOverlappedResult(HANDLE hFile, BOOL GetOverlappedResult(HANDLE hFile,
OVERLAPPED* lpOverlapped, OVERLAPPED* lpOverlapped,
DWORD* lpNumberOfBytesTransferred, DWORD* lpNumberOfBytesTransferred,
BOOL bWait) nothrow @system @nogc; BOOL bWait) nothrow @system @nogc;

View File

@ -5,7 +5,7 @@
/** /**
* Definitions from winsock2.h, ws2def.h and MSWSock.h. * 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/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -216,4 +216,4 @@ enum
SO_UPDATE_ACCEPT_CONTEXT = 0x700B, SO_UPDATE_ACCEPT_CONTEXT = 0x700B,
SO_CONNECT_TIME = 0x700C, SO_CONNECT_TIME = 0x700C,
SO_UPDATE_CONNECT_CONTEXT = 0x7010, SO_UPDATE_CONNECT_CONTEXT = 0x7010,
} }

View File

@ -13,7 +13,7 @@
* The functions can cause segmentation fault if the module is compiled * The functions can cause segmentation fault if the module is compiled
* in production mode and the condition fails. * 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/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)

View File

@ -5,7 +5,7 @@
/** /**
* Test suite for $(D_KEYWORD unittest)-blocks. * 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/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)

View File

@ -17,6 +17,7 @@
*/ */
module tanya.typecons; module tanya.typecons;
import tanya.format;
import tanya.meta.metafunction : AliasSeq, AliasTuple = Tuple, Map; import tanya.meta.metafunction : AliasSeq, AliasTuple = Tuple, Map;
deprecated("Use tanya.typecons.Tuple instead") deprecated("Use tanya.typecons.Tuple instead")
@ -77,11 +78,10 @@ template Pair(Specs...)
} }
/** /**
* $(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 * The objects can by accessed by index as `obj[0]` and `obj[1]` or by optional
* $(D_INLINECODE obj[1]) or by optional names (e.g. * names (e.g. `obj.first`).
* $(D_INLINECODE obj.first)).
* *
* $(D_PARAM Specs) contains a list of object types and names. First * $(D_PARAM Specs) contains a list of object types and names. First
* comes the object type, then an optional string containing the name. * comes the object type, then an optional string containing the name.
@ -123,7 +123,26 @@ template Tuple(Specs...)
alias ChooseType(alias T) = T.Seq[0]; alias ChooseType(alias T) = T.Seq[0];
alias ParsedSpecs = parseSpecs!(0, Specs); 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 struct Tuple
{ {
@ -131,14 +150,7 @@ template Tuple(Specs...)
alias Types = Map!(ChooseType, ParsedSpecs); alias Types = Map!(ChooseType, ParsedSpecs);
// Create field aliases. // Create field aliases.
static if (ParsedSpecs[0].length == 2) mixin(formatAliases!(0, ParsedSpecs[0 .. $])());
{
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 Tuple) as a list of values. /// Represents the values of the $(D_PSYMBOL Tuple) as a list of values.
Types expand; Types expand;
@ -171,4 +183,7 @@ template Tuple(Specs...)
static assert(!is(Tuple!(int, int, int))); static assert(!is(Tuple!(int, int, int)));
static assert(!is(Tuple!(int, "first"))); static assert(!is(Tuple!(int, "first")));
static assert(!is(Tuple!(int, double, char)));
static assert(!is(Tuple!(int, "first", double, "second", char, "third")));
} }