12 Commits

Author SHA1 Message Date
c5eb2f27be Add algorithm.iteration 2018-09-28 05:40:33 +02:00
349e6dfede Create separate travis job for D-Scanner 2018-09-26 06:30:05 +02:00
fd133554f3 net.ip: Implement opCmp. Fix #63 2018-09-24 06:45:44 +02:00
9ac56c50f1 typecons: Add option constructor function 2018-09-23 06:59:41 +02:00
03b45ae441 Add typecons.tuple(), Tuple construction function 2018-09-22 07:32:30 +02:00
31d4f30a49 functional.forward: Fix template visibility bug
Because of the private template forwardOne, forward couldn't be used in
other modules. forwardOne cannot be a local template either since it
accepts an alias as its template parameter.
2018-09-21 06:23:59 +02:00
180c4d3956 typecons.Option: Implement toHash forwarder 2018-09-18 22:27:54 +02:00
b0dc7b59e5 Add predicate support for algorithm.comparison.equal 2018-09-17 19:17:39 +02:00
eb796e0ddf Add bitmanip.BitFlags 2018-09-16 19:07:55 +02:00
e5569e5fea meta.trait.EnumMembers: Fix one-member enums
Produce a tuple for an enum with only one member.
2018-09-15 06:06:17 +02:00
b831a05407 Introduce hash.lookup.isHashFunction trait
Fix #66.
2018-09-14 15:16:08 +02:00
b6d1766d58 Implement compare algorithm. Fix #50 2018-09-11 10:05:15 +02:00
18 changed files with 973 additions and 76 deletions

View File

@ -1,37 +1,47 @@
sudo: false sudo: false
os: os:
- linux - linux
- osx - osx
language: d language: d
d: d:
- dmd-2.082.0 - dmd-2.082.0
- dmd-2.081.2 - dmd-2.081.2
env: env:
matrix: matrix:
- ARCH=x86_64 - ARCH=x86_64
- ARCH=x86 - ARCH=x86
matrix:
include:
- name: "D-Scanner"
d: dmd-2.082.0
env: DSCANNER=0.5.10
os: linux
addons: addons:
apt: apt:
packages: packages:
- gcc-multilib - gcc-multilib
before_script: before_script:
- if [ "`$DC --version | head -n 1 | grep 'v2.082.0'`" ]; then - if [ "`$DC --version | head -n 1 | grep 'v2.082.0'`" ] &&
export UNITTEST="unittest-cov"; [ -z "$DSCANNER" ]; then
fi export UNITTEST="unittest-cov";
fi
script: script:
- dub test -b ${UNITTEST:-unittest} --arch=$ARCH --compiler=$DC - if [ -z "$DSCANNER" ]; then
- if [ "$UNITTEST" ] && [ "$ARCH" = "x86_64" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; dub test -b ${UNITTEST:-unittest} --arch=$ARCH --compiler=$DC;
then else
dub fetch dscanner --version=0.5.10; dub fetch dscanner --version=$DSCANNER;
dub run dscanner -- --styleCheck ./source/;
fi FILES=$(find source -type f);
dub run dscanner -- --styleCheck $FILES;
fi
after_success: after_success:
- test "$UNITTEST" && bash <(curl -s https://codecov.io/bash) - test "$UNITTEST" && bash <(curl -s https://codecov.io/bash)

View File

@ -25,6 +25,7 @@ Tanya consists of the following packages and (top-level) modules:
* `algorithm`: Collection of generic algorithms. * `algorithm`: Collection of generic algorithms.
* `async`: Event loop (epoll, kqueue and IOCP). * `async`: Event loop (epoll, kqueue and IOCP).
* `bitmanip`: Bit manipulation.
* `container`: Queue, Array, Singly and doubly linked lists, Buffers, UTF-8 * `container`: Queue, Array, Singly and doubly linked lists, Buffers, UTF-8
string, Set, Hash table. string, Set, Hash table.
* `conv`: This module provides functions for converting between different * `conv`: This module provides functions for converting between different

View File

@ -278,16 +278,19 @@ if (isForwardRange!Range && isOrderingComparable!(ElementType!Range))
* If the ranges have different lengths, they aren't equal. * If the ranges have different lengths, they aren't equal.
* *
* Params: * Params:
* R1 = First range type. * pred = Predicate used to compare individual element pairs.
* R2 = Second range type. * R1 = First range type.
* r1 = First range. * R2 = Second range type.
* r2 = Second range. * r1 = First range.
* r2 = Second range.
* *
* Returns: $(D_KEYWORD true) if both ranges are equal, $(D_KEYWORD false) * Returns: $(D_KEYWORD true) if both ranges are equal, $(D_KEYWORD false)
* otherwise. * otherwise.
*/ */
bool equal(R1, R2)(R1 r1, R2 r2) bool equal(alias pred = (auto ref a, auto ref b) => a == b, R1, R2)
if (allSatisfy!(isInputRange, R1, R2) && is(typeof(r1.front == r2.front))) (R1 r1, R2 r2)
if (allSatisfy!(isInputRange, R1, R2)
&& is(typeof(pred(r1.front, r2.front)) == bool))
{ {
static if (isDynamicArray!R1 static if (isDynamicArray!R1
&& is(R1 == R2) && is(R1 == R2)
@ -306,7 +309,7 @@ if (allSatisfy!(isInputRange, R1, R2) && is(typeof(r1.front == r2.front)))
} }
for (; !r1.empty && !r2.empty; r1.popFront(), r2.popFront()) for (; !r1.empty && !r2.empty; r1.popFront(), r2.popFront())
{ {
if (r1.front != r2.front) if (!pred(r1.front, r2.front))
{ {
return false; return false;
} }
@ -331,3 +334,130 @@ if (allSatisfy!(isInputRange, R1, R2) && is(typeof(r1.front == r2.front)))
int[3] range2 = [1, 2, 3]; int[3] range2 = [1, 2, 3];
assert(!equal(range1[], range2[])); assert(!equal(range1[], range2[]));
} }
/**
* Compares element-wise two ranges for ordering.
*
* $(D_PSYMBOL compare) returns a negative value if $(D_PARAM r1) is less than
* $(D_PARAM r2), a positive value if $(D_PARAM r2) is less than $(D_PARAM r1),
* or `0` if $(D_PARAM r1) and $(D_PARAM r2) equal.
*
* $(D_PSYMBOL compare) iterates both ranges in lockstep. Whichever of them
* contains an element that is greater than the respective element at the same
* position in the other range is the greater one of the two.
*
* If one of the ranges becomes empty when iterating, but all elements equal so
* far, the range with more elements is the greater one.
*
* If $(D_PARAM pred) is given, it is used for comparison. $(D_PARAM pred) is
* called as $(D_INLINECODE pred(r1.front, r2.front)) and
* $(D_INLINECODE pred(r2.front, r1.front)) to perform three-way comparison.
* $(D_PARAM pred) should return a $(D_KEYWORD bool).
*
* If $(D_PARAM pred) is not given, but the element type of $(D_PARAM R1)
* defines `opCmp()` for the element type of $(D_PARAM R2), `opCmp()` is used.
*
* Otherwise the comparison is perfomed using the basic comparison operators.
*
* Params:
* pred = Predicate used for comparison.
* R1 = First range type.
* R2 = Second range type.
* r1 = First range.
* r2 = Second range.
*
* Returns: A negative value if $(D_PARAM r1) is less than $(D_PARAM r2), a
* positive value if $D(_PARAM r2) is less than $(D_PARAM r1), `0`
* otherwise.
*/
int compare(alias pred, R1, R2)(R1 r1, R2 r2)
if (allSatisfy!(isInputRange, R1, R2)
&& is(typeof(pred(r1.front, r2.front)) == bool)
&& is(typeof(pred(r2.front, r1.front)) == bool))
{
alias predImpl = (ref r1, ref r2) {
return pred(r2.front, r1.front) - pred(r1.front, r2.front);
};
return compareImpl!(predImpl, R1, R2)(r1, r2);
}
/// ditto
int compare(R1, R2)(R1 r1, R2 r2)
if (allSatisfy!(isInputRange, R1, R2)
&& is(typeof(r1.front < r2.front || r2.front < r1.front)))
{
static if (is(typeof(r1.front.opCmp(r2.front)) == int))
{
alias pred = (ref r1, ref r2) => r1.front.opCmp(r2.front);
}
else
{
alias pred = (ref r1, ref r2) {
return (r2.front < r1.front) - (r1.front < r2.front);
};
}
return compareImpl!(pred, R1, R2)(r1, r2);
}
///
@nogc nothrow pure @safe unittest
{
assert(compare("abc", "abc") == 0);
assert(compare("abcd", "abc") > 0);
assert(compare("ab", "abc") < 0);
assert(compare("abc", "abcd") < 0);
assert(compare("abc", "ab") > 0);
assert(compare("aec", "abc") > 0);
assert(compare("aac", "abc") < 0);
assert(compare("abc", "aec") < 0);
assert(compare("abc", "aab") > 0);
assert(compare("aacd", "abc") < 0);
assert(compare("abc", "aacd") > 0);
assert(compare!((a, b) => a > b)("aec", "abc") < 0);
assert(compare!((a, b) => a > b)("aac", "abc") > 0);
}
private int compareImpl(alias pred, R1, R2)(ref R1 r1, ref R2 r2)
{
for (; !r1.empty || !r2.empty; r1.popFront(), r2.popFront())
{
if (r1.empty)
{
return -1;
}
else if (r2.empty)
{
return 1;
}
const comparison = pred(r1, r2);
if (comparison != 0)
{
return comparison;
}
}
return 0;
}
@nogc nothrow pure @safe unittest
{
static struct OpCmp(int value)
{
int opCmp(OpCmp) @nogc nothrow pure @safe
{
return value;
}
}
{
OpCmp!(-1)[1] range;
assert(compare(range[], range[]) < 0);
}
{
OpCmp!1[1] range;
assert(compare(range[], range[]) > 0);
}
{
OpCmp!0[1] range;
assert(compare(range[], range[]) == 0);
}
}

View File

@ -407,3 +407,172 @@ if (isInputRange!R)
assert(slice.back == 3); assert(slice.back == 3);
} }
} }
/**
* Iterates a bidirectional range backwards.
*
* If $(D_PARAM Range) is a random-access range as well, the resulting range
* is a random-access range too.
*
* Params:
* Range = Bidirectional range type.
* range = Bidirectional range.
*
* Returns: Bidirectional range with the elements order reversed.
*/
auto retro(Range)(Range range)
if (isBidirectionalRange!Range)
{
static struct Retro
{
Range source;
@disable this();
private this(Range source)
{
this.source = source;
}
Retro save()
{
return this;
}
@property auto ref front()
in (!empty)
{
return this.source.back;
}
void popFront()
in (!empty)
{
this.source.popBack();
}
@property auto ref back()
in (!empty)
{
return this.source.front;
}
void popBack()
in (!empty)
{
this.source.popFront();
}
@property bool empty()
{
return this.source.empty;
}
static if (hasLength!Range)
{
@property size_t length()
{
return this.source.length;
}
}
static if (isRandomAccessRange!Range && hasLength!Range)
{
auto ref opIndex(size_t i)
in (i < length)
{
return this.source[$ - ++i];
}
}
static if (hasAssignableElements!Range)
{
@property void front(ref ElementType!Range value)
in (!empty)
{
this.source.back = value;
}
@property void front(ElementType!Range value)
in (!empty)
{
this.source.back = move(value);
}
@property void back(ref ElementType!Range value)
in (!empty)
{
this.source.front = value;
}
@property void back(ElementType!Range value)
in (!empty)
{
this.source.front = move(value);
}
static if (isRandomAccessRange!Range && hasLength!Range)
{
void opIndexAssign(ref ElementType!Range value, size_t i)
in (i < length)
{
this.source[$ - ++i] = value;
}
void opIndexAssign(ElementType!Range value, size_t i)
in (i < length)
{
this.source[$ - ++i] = move(value);
}
}
}
}
return Retro(range);
}
///
@nogc nothrow pure @safe unittest
{
import tanya.algorithm.comparison : equal;
const int[3] given = [1, 2, 3];
const int[3] expected = [3, 2, 1];
auto actual = retro(given[]);
assert(actual.length == expected.length);
assert(!actual.empty);
assert(equal(actual, expected[]));
}
// Elements are accessible in reverse order
@nogc nothrow pure @safe unittest
{
const int[3] given = [1, 2, 3];
auto actual = retro(given[]);
assert(actual.back == given[].front);
assert(actual[0] == 3);
assert(actual[2] == 1);
actual.popBack();
assert(actual.back == 2);
assert(actual[1] == 2);
}
// Elements can be assigned
@nogc nothrow pure @safe unittest
{
int[4] given = [1, 2, 3, 4];
auto actual = retro(given[]);
actual.front = 5;
assert(given[].back == 5);
actual.back = 8;
assert(given[].front == 8);
actual[2] = 10;
assert(given[1] == 10);
}

View File

@ -318,7 +318,9 @@ final class IOCPLoop : Loop
connection.incoming.insertBack(transport); connection.incoming.insertBack(transport);
reify(transport, EventMask(Event.none), EventMask(Event.read, Event.write)); reify(transport,
EventMask(Event.none),
EventMask(Event.read | Event.write));
pendings.insertBack(connection); pendings.insertBack(connection);
listener.beginAccept(overlapped); listener.beginAccept(overlapped);

View File

@ -140,7 +140,7 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
{ {
closing = true; closing = true;
loop.reify(this, loop.reify(this,
EventMask(Event.read, Event.write), EventMask(Event.read | Event.write),
EventMask(Event.write)); EventMask(Event.write));
} }
@ -393,7 +393,9 @@ abstract class SelectorLoop : Loop
transport.socket = client; transport.socket = client;
} }
reify(transport, EventMask(Event.none), EventMask(Event.read, Event.write)); reify(transport,
EventMask(Event.none),
EventMask(Event.read | Event.write));
connection.incoming.insertBack(transport); connection.incoming.insertBack(transport);
} }

View File

@ -72,9 +72,9 @@
module tanya.async.loop; module tanya.async.loop;
import core.time; import core.time;
import std.typecons;
import tanya.async.transport; import tanya.async.transport;
import tanya.async.watcher; import tanya.async.watcher;
import tanya.bitmanip;
import tanya.container.buffer; import tanya.container.buffer;
import tanya.container.list; import tanya.container.list;
import tanya.memory; import tanya.memory;

359
source/tanya/bitmanip.d Normal file
View File

@ -0,0 +1,359 @@
/* 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/. */
/**
* Bit manipulation.
*
* 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/bitmanip.d,
* tanya/bitmanip.d)
*/
module tanya.bitmanip;
import tanya.meta.metafunction;
import tanya.meta.trait;
import tanya.meta.transform;
/**
* Determines whether $(D_PARAM E) is a $(D_KEYWORD enum), whose members can be
* used as bit flags.
*
* This is the case if all members of $(D_PARAM E) are integral numbers that
* are either 0 or positive integral powers of 2.
*
* Params:
* E = Some $(D_KEYWORD enum).
*
* Returns: $(D_KEYWORD true) if $(D_PARAM E) contains only bit flags,
* $(D_KEYWORD false) otherwise.
*/
template isBitFlagEnum(E)
{
enum bool isValid(OriginalType!E x) = x == 0
|| (x > 0 && ((x & (x - 1)) == 0));
static if (isIntegral!E)
{
enum bool isBitFlagEnum = allSatisfy!(isValid, EnumMembers!E);
}
else
{
enum bool isBitFlagEnum = false;
}
}
///
@nogc nothrow pure @safe unittest
{
enum Valid
{
none = 0,
one = 1 << 0,
two = 1 << 1,
}
static assert(isBitFlagEnum!Valid);
enum Invalid
{
one,
two,
three,
four,
}
static assert(!isBitFlagEnum!Invalid);
enum Negative
{
one = -1,
two = -2,
}
static assert(!isBitFlagEnum!Negative);
}
/**
* Validates that $(D_PARAM field) contains only bits from $(D_PARAM E).
*
* Params:
* E = Some $(D_KEYWORD enum).
* field = Bit field.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM field) is valid, $(D_KEYWORD false)
* otherwise.
*/
bool containsBitFlags(E)(E field)
if (isBitFlagEnum!E)
{
OriginalType!E fillField()
{
typeof(return) full;
static foreach (member; EnumMembers!E)
{
full |= member;
}
return full;
}
enum OriginalType!E full = fillField();
return (field & ~full) == OriginalType!E.init;
}
///
@nogc nothrow pure @safe unittest
{
enum E
{
one,
two,
three,
}
assert(containsBitFlags(E.one | E.two));
assert(!containsBitFlags(cast(E) 0x8));
}
/**
* Allows to use $(D_KEYWORD enum) values as a set of bit flags.
*
* $(D_PSYMBOL BitFlags) behaves the same as a bit field of type $(D_PARAM E),
* but does additional cheks to ensure that the bit field contains only valid
* values, this is only values from $(D_PARAM E).
*
* Params:
* E = Some $(D_KEYWORD enum).
*/
struct BitFlags(E)
if (isBitFlagEnum!E)
{
private OriginalType!E field;
/**
* Constructs $(D_PSYMBOL BitFlags) from $(D_PARAM field).
*
* Params:
* field = Bits to be set.
*/
this(E field)
{
this.field = field;
}
/**
* Converts $(D_PSYMBOL BitFlags) to a boolean.
*
* It is $(D_KEYWORD true) if any bit is set, $(D_KEYWORD false) otherwise.
*
* Returns: $(D_KEYWORD true) if this $(D_PSYMBOL BitFlags) contains any
* set bits, $(D_KEYWORD false) otherwise.
*/
bool opCast(T : bool)()
{
return this.field != 0;
}
/**
* Converts to the original type of $(D_PARAM E) ($(D_KEYWORD int) by
* default).
*
* Returns: $(D_KEYWORD this) as $(D_INLINECODE OriginalType!T).
*/
OriginalType!E opCast(T : OriginalType!E)() const
{
return this.field;
}
/**
* Tests (&), sets (|) or toggles (^) bits.
*
* Params:
* op = Operation.
* that = 0 or more bit flags.
*
* Returns: New $(D_PSYMBOL BitFlags) object.
*/
BitFlags opBinary(string op)(E that) const
if (op == "&" || op == "|" || op == "^")
{
BitFlags result = this;
mixin("return result " ~ op ~ "= that;");
}
/// ditto
BitFlags opBinary(string op)(BitFlags that) const
if (op == "&" || op == "|" || op == "^")
{
BitFlags result = this;
mixin("return result " ~ op ~ "= that;");
}
/// ditto
BitFlags opBinaryRight(string op)(E that) const
if (op == "&" || op == "|" || op == "^")
{
BitFlags result = this;
mixin("return result " ~ op ~ "= that;");
}
/**
* Tests (&), sets (|) or toggles (^) bits.
*
* Params:
* op = Operation.
* that = 0 or more bit flags.
*
* Returns: $(D_KEYWORD this).
*/
ref BitFlags opOpAssign(string op)(E that)
if (op == "&" || op == "|" || op == "^")
{
mixin("this.field " ~ op ~ "= that;");
return this;
}
/// ditto
ref BitFlags opOpAssign(string op)(BitFlags that)
if (op == "&" || op == "|" || op == "^")
{
mixin("this.field " ~ op ~ "= that.field;");
return this;
}
/**
* Inverts all bit flags.
*
* Returns: New $(D_PSYMBOL BitFlags) object with all bits inverted.
*/
BitFlags opUnary(string op : "~")() const
{
BitFlags result;
result.field = ~this.field;
return result;
}
/**
* Assigns a bit field.
*
* Params:
* that = Bit field of type $(D_PARAM E).
*
* Returns: $(D_KEYWORD this).
*/
ref BitFlags opAssign(E that)
{
this.field = that;
return this;
}
/**
* Compares this $(D_PSYMBOL BitFlags) object to another bit field.
*
* Params:
* that = $(D_PSYMBOL BitFlags) object or a bit field of type
* $(D_PARAM E).
*
* Returns: $(D_KEYWORD true) if $(D_KEYWORD this) and $(D_PARAM that)
* contain the same bits ,$(D_KEYWORD false) otherwise.
*/
bool opEquals(E that) const
{
return this.field == that;
}
/// ditto
bool opEquals(BitFlags that) const
{
return this.field == that.field;
}
/**
* Generates a hash value of this object.
*
* Returns: Hash value.
*/
size_t toHash() const
{
return cast(size_t) this.field;
}
}
@nogc nothrow pure @safe unittest
{
enum E : int
{
one = 1,
}
// Casts to a boolean
assert(BitFlags!E(E.one));
assert(!BitFlags!E());
// Assigns to and compares with a single value
{
BitFlags!E bitFlags;
bitFlags = E.one;
assert(bitFlags == E.one);
}
// Assigns to and compares with the same type
{
auto bitFlags1 = BitFlags!E(E.one);
BitFlags!E bitFlags2;
bitFlags2 = bitFlags1;
assert(bitFlags1 == bitFlags2);
}
assert((BitFlags!E() | E.one) == BitFlags!E(E.one));
assert((BitFlags!E() | BitFlags!E(E.one)) == BitFlags!E(E.one));
assert(!(BitFlags!E() & BitFlags!E(E.one)));
assert(!(BitFlags!E(E.one) ^ E.one));
assert(BitFlags!E() ^ BitFlags!E(E.one));
assert(~BitFlags!E());
assert(BitFlags!E().toHash() == 0);
assert(BitFlags!E(E.one).toHash() != 0);
// opBinaryRight is allowed
static assert(is(typeof({ E.one | BitFlags!E(); })));
}
/**
* Creates a $(D_PSYMBOL BitFlags) object initialized with $(D_PARAM field).
*
* Params:
* E = Some $(D_KEYWORD enum).
* field = Bits to be set.
*/
BitFlags!E bitFlags(E)(E field)
if (isBitFlagEnum!E)
{
return BitFlags!E(field);
}
///
@nogc nothrow pure @safe unittest
{
enum E
{
one = 1 << 0,
two = 1 << 1,
three = 1 << 2,
}
// Construct with E.one and E.two set
auto flags = bitFlags(E.one | E.two);
// Test wheter E.one is set
assert(flags & E.one);
// Toggle E.one
flags ^= E.one;
assert(!(flags & E.one));
// Set E.three
flags |= E.three;
assert(flags & E.three);
// Clear E.three
flags &= ~E.three;
assert(!(flags & E.three));
}

View File

@ -1554,14 +1554,14 @@ struct Array(T)
{ {
struct MutableEqualsStruct struct MutableEqualsStruct
{ {
int opEquals(typeof(this) that) @nogc nothrow pure @safe bool opEquals(typeof(this) that) @nogc nothrow pure @safe
{ {
return true; return true;
} }
} }
struct ConstEqualsStruct struct ConstEqualsStruct
{ {
int opEquals(const typeof(this) that) const @nogc nothrow pure @safe bool opEquals(const typeof(this) that) const @nogc nothrow pure @safe
{ {
return true; return true;
} }

View File

@ -386,7 +386,7 @@ struct ByValue(T)
* hasher = Hash function for $(D_PARAM Key). * hasher = Hash function for $(D_PARAM Key).
*/ */
struct HashTable(Key, Value, alias hasher = hash) struct HashTable(Key, Value, alias hasher = hash)
if (is(typeof(((Key k) => hasher(k))(Key.init)) == size_t)) if (isHashFunction!(hasher, Key))
{ {
private alias HashArray = .HashArray!(hasher, Key, Value); private alias HashArray = .HashArray!(hasher, Key, Value);
private alias Buckets = HashArray.Buckets; private alias Buckets = HashArray.Buckets;

View File

@ -154,7 +154,7 @@ struct Range(T)
* hasher = Hash function for $(D_PARAM T). * hasher = Hash function for $(D_PARAM T).
*/ */
struct Set(T, alias hasher = hash) struct Set(T, alias hasher = hash)
if (is(typeof(((T x) => hasher(x))(T.init)) == size_t)) if (isHashFunction!(hasher, T))
{ {
private alias HashArray = .HashArray!(hasher, T); private alias HashArray = .HashArray!(hasher, T);
private alias Buckets = HashArray.Buckets; private alias Buckets = HashArray.Buckets;
@ -768,8 +768,8 @@ if (is(typeof(((T x) => hasher(x))(T.init)) == size_t))
testFunc(set); testFunc(set);
} }
// Hasher can take argument by ref
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
// Using hasher that takes argument by ref. static assert(is(Set!(int, (const ref x) => cast(size_t) x)));
Set!(int, (const ref x) => cast(size_t)x) set;
} }

View File

@ -26,9 +26,8 @@
*/ */
module tanya.container.string; module tanya.container.string;
import std.algorithm.comparison : cmp;
import std.algorithm.mutation : bringToFront; import std.algorithm.mutation : bringToFront;
import std.algorithm.searching; import std.algorithm.searching : count;
import tanya.algorithm.comparison; import tanya.algorithm.comparison;
import tanya.algorithm.mutation; import tanya.algorithm.mutation;
import tanya.hash.lookup; import tanya.hash.lookup;
@ -1284,29 +1283,29 @@ struct String
int opCmp(S)(auto ref S that) const @trusted int opCmp(S)(auto ref S that) const @trusted
if (is(Unqual!S == String)) if (is(Unqual!S == String))
{ {
return cmp(this.data[0 .. length], that.data[0 .. that.length]); return compare(this.data[0 .. length], that.data[0 .. that.length]);
} }
/// ditto /// ditto
int opCmp(S)(ByCodeUnit!S that) const @trusted int opCmp(S)(ByCodeUnit!S that) const @trusted
if (is(Unqual!S == char)) if (is(Unqual!S == char))
{ {
return cmp(this.data[0 .. length], return compare(this.data[0 .. length],
that.begin[0 .. that.end - that.begin]); that.begin[0 .. that.end - that.begin]);
} }
/// ditto /// ditto
int opCmp(S)(ByCodePoint!S that) const @trusted int opCmp(S)(ByCodePoint!S that) const @trusted
if (is(Unqual!S == char)) if (is(Unqual!S == char))
{ {
return cmp(this.data[0 .. length], return compare(this.data[0 .. length],
that.begin[0 .. that.end - that.begin]); that.begin[0 .. that.end - that.begin]);
} }
/// ditto /// ditto
int opCmp()(const char[] that) const @trusted int opCmp()(const char[] that) const @trusted
{ {
return cmp(this.data[0 .. length], that); return compare(this.data[0 .. length], that);
} }
/// ///

View File

@ -17,21 +17,6 @@ module tanya.functional;
import tanya.algorithm.mutation; import tanya.algorithm.mutation;
import tanya.meta.metafunction; 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) * Forwards its argument list preserving $(D_KEYWORD ref) and $(D_KEYWORD out)
* storage classes. * storage classes.
@ -47,13 +32,35 @@ private template forwardOne(alias arg)
*/ */
template forward(args...) template forward(args...)
{ {
static if (args.length == 1) static if (args.length == 0)
{ {
alias forward = forwardOne!(args[0]); alias forward = AliasSeq!();
}
else static if (__traits(isRef, args[0]) || __traits(isOut, args[0]))
{
static if (args.length == 1)
{
alias forward = args[0];
}
else
{
alias forward = AliasSeq!(args[0], forward!(args[1 .. $]));
}
} }
else else
{ {
alias forward = Map!(forwardOne, args); @property auto forwardOne()
{
return move(args[0]);
}
static if (args.length == 1)
{
alias forward = forwardOne;
}
else
{
alias forward = AliasSeq!(forwardOne, forward!(args[1 .. $]));
}
} }
} }

View File

@ -636,3 +636,27 @@ static if (size_t.sizeof == 8) @nogc nothrow pure @safe unittest
assert(hash(r500!"~") == 0xc1af12bdfe16b5b5UL); assert(hash(r500!"~") == 0xc1af12bdfe16b5b5UL);
assert(hash(r500!"\x7f") == 0x39e9f18f2f85e221UL); assert(hash(r500!"\x7f") == 0x39e9f18f2f85e221UL);
} }
/**
* Determines whether $(D_PARAM hasher) is hash function for $(D_PARAM T), i.e.
* it is callable with a value of type $(D_PARAM T) and returns a
* $(D_PSYMBOL size_t) value.
*
* Params:
* hasher = Hash function candidate.
* T = Type to test the hash function with.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM hasher) is a hash function for
* $(D_PARAM T), $(D_KEYWORD false) otherwise.
*/
template isHashFunction(alias hasher, T)
{
private alias wrapper = (T x) => hasher(x);
enum bool isHashFunction = is(typeof(wrapper(T.init)) == size_t);
}
///
@nogc nothrow pure @safe unittest
{
static assert(isHashFunction!(hash, int));
}

View File

@ -14,16 +14,16 @@
*/ */
module tanya.math.mp; module tanya.math.mp;
import std.algorithm.comparison : cmp; import std.algorithm.mutation : fill;
import std.algorithm.mutation : fill, reverse;
import std.range;
import tanya.algorithm.comparison; import tanya.algorithm.comparison;
import tanya.algorithm.iteration;
import tanya.algorithm.mutation; import tanya.algorithm.mutation;
import tanya.container.array; import tanya.container.array;
import tanya.encoding.ascii; import tanya.encoding.ascii;
import tanya.memory; import tanya.memory;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform; import tanya.meta.transform;
import tanya.range;
/** /**
* Algebraic sign. * Algebraic sign.
@ -629,7 +629,7 @@ struct Integer
} }
return this.rep[0 .. this.size] return this.rep[0 .. this.size]
.retro .retro
.cmp(that.rep[0 .. that.size].retro); .compare(that.rep[0 .. that.size].retro);
} }
/** /**
@ -930,7 +930,7 @@ struct Integer
const shift = digitBitCount - bit; const shift = digitBitCount - bit;
digit carry; digit carry;
foreach (ref d; this.rep[0 .. this.size].retro) foreach_reverse (ref d; this.rep[0 .. this.size])
{ {
const newCarry = d & mask; const newCarry = d & mask;
d = (d >> bit) | (carry << shift); d = (d >> bit) | (carry << shift);
@ -1506,14 +1506,11 @@ struct Integer
tmp = this; tmp = this;
} }
do array.length = length;
for (size_t i = array.length - 1; tmp != 0; tmp >>= 8, --i)
{ {
array.insertBack(cast(ubyte) (tmp.rep[0] & 0xff)); array[i] = (cast(ubyte) (tmp.rep[0] & 0xff));
tmp >>= 8;
} }
while (tmp != 0);
array[].reverse();
return array; return array;
} }

View File

@ -1601,7 +1601,7 @@ if (is(T == class) || is(T == struct) || is(T == union))
} }
/// ///
pure nothrow @safe unittest @nogc pure nothrow @safe unittest
{ {
static struct S static struct S
{ {
@ -2613,14 +2613,23 @@ if (is(T == enum))
} }
else else
{ {
alias getEnumMembers = AliasSeq!(__traits(getMember, T, Args[0]), getEnumMembers!(Args[1 .. $])); alias getEnumMembers = AliasSeq!(__traits(getMember, T, Args[0]),
getEnumMembers!(Args[1 .. $]));
} }
} }
alias EnumMembers = getEnumMembers!(__traits(allMembers, T)); private alias allMembers = AliasSeq!(__traits(allMembers, T));
static if (allMembers.length == 1)
{
alias EnumMembers = AliasSeq!(__traits(getMember, T, allMembers));
}
else
{
alias EnumMembers = getEnumMembers!allMembers;
}
} }
/// ///
pure nothrow @nogc @safe unittest @nogc nothrow pure @safe unittest
{ {
enum E : int enum E : int
{ {
@ -2628,7 +2637,17 @@ pure nothrow @nogc @safe unittest
two, two,
three, three,
} }
static assert([E.one, E.two, E.three] == [ EnumMembers!E ]); static assert([EnumMembers!E] == [E.one, E.two, E.three]);
}
// Produces a tuple for an enum with only one member
@nogc nothrow pure @safe unittest
{
enum E : int
{
one = 0,
}
static assert(EnumMembers!E == AliasSeq!0);
} }
/** /**

View File

@ -14,6 +14,7 @@
*/ */
module tanya.net.ip; module tanya.net.ip;
import tanya.algorithm.comparison;
import tanya.algorithm.mutation; import tanya.algorithm.mutation;
import tanya.container.string; import tanya.container.string;
import tanya.conv; import tanya.conv;
@ -66,6 +67,37 @@ struct Address4
assert(Address4(0x00202000U).toUInt() == 0x00202000U); assert(Address4(0x00202000U).toUInt() == 0x00202000U);
} }
/**
* Compares two $(D_PARAM Address4) objects.
*
* Params:
* that = Another address.
*
* Returns: Positive number if $(D_KEYWORD this) is larger than
* $(D_PARAM that), negative - if it is smaller, or 0 if they
* equal.
*/
int opCmp(ref const Address4 that) const @nogc nothrow pure @safe
{
const lhs = toUInt();
const rhs = that.toUInt();
return (rhs < lhs) - (lhs < rhs);
}
/// ditto
int opCmp(const Address4 that) const @nogc nothrow pure @safe
{
return opCmp(that);
}
///
@nogc nothrow pure @safe unittest
{
assert(address4("127.0.0.1") > address4("126.0.0.0"));
assert(address4("127.0.0.1") < address4("127.0.0.2"));
assert(address4("127.0.0.1") == address4("127.0.0.1"));
}
/** /**
* Returns object that represents 127.0.0.1. * Returns object that represents 127.0.0.1.
* *
@ -423,6 +455,45 @@ struct Address6
assert(actual.scopeID == 1); assert(actual.scopeID == 1);
} }
/**
* Compares two $(D_PARAM Address6) objects.
*
* If $(D_KEYWORD this) and $(D_PARAM that) contain the same address, scope
* IDs are compared.
*
* Params:
* that = Another address.
*
* Returns: Positive number if $(D_KEYWORD this) is larger than
* $(D_PARAM that), negative - if it is smaller, or 0 if they
* equal.
*/
int opCmp(ref const Address6 that) const @nogc nothrow pure @safe
{
const diff = compare(this.address[], that.address[]);
if (diff == 0)
{
return (that.scopeID < this.scopeID) - (this.scopeID < that.scopeID);
}
return diff;
}
/// ditto
int opCmp(const Address6 that) const @nogc nothrow pure @safe
{
return opCmp(that);
}
///
@nogc nothrow @safe unittest
{
assert(address6("::14") > address6("::1"));
assert(address6("::1") < address6("::14"));
assert(address6("::1") == address6("::1"));
assert(address6("::1%1") < address6("::1%2"));
assert(address6("::1%2") > address6("::1%1"));
}
/** /**
* Returns object that represents ::. * Returns object that represents ::.
* *

View File

@ -19,6 +19,7 @@ module tanya.typecons;
import tanya.algorithm.mutation; import tanya.algorithm.mutation;
import tanya.format; import tanya.format;
import tanya.functional;
import tanya.meta.metafunction; import tanya.meta.metafunction;
import tanya.meta.trait; import tanya.meta.trait;
@ -35,6 +36,8 @@ import tanya.meta.trait;
* *
* Params: * Params:
* Specs = Field types and names. * Specs = Field types and names.
*
* See_Also: $(D_PSYMBOL tuple).
*/ */
template Tuple(Specs...) template Tuple(Specs...)
{ {
@ -133,11 +136,50 @@ template Tuple(Specs...)
static assert(!is(Tuple!(int, "first", double, "second", char, "third"))); static assert(!is(Tuple!(int, "first", double, "second", char, "third")));
} }
/**
* Creates a new $(D_PSYMBOL Tuple).
*
* Params:
* Names = Field names.
*
* See_Also: $(D_PSYMBOL Tuple).
*/
template tuple(Names...)
{
/**
* Creates a new $(D_PSYMBOL Tuple).
*
* Params:
* Args = Field types.
* args = Field values.
*
* Returns: Newly created $(D_PSYMBOL Tuple).
*/
auto tuple(Args...)(auto ref Args args)
if (Args.length >= Names.length && isTypeTuple!Args)
{
alias Zipped = ZipWith!(AliasSeq, Pack!Args, Pack!Names);
alias Nameless = Args[Names.length .. $];
return Tuple!(Zipped, Nameless)(forward!args);
}
}
///
@nogc nothrow pure @safe unittest
{
auto t = tuple!("one", "two")(20, 5);
assert(t.one == 20);
assert(t.two == 5);
}
/** /**
* $(D_PSYMBOL Option) is a type that contains an optional value. * $(D_PSYMBOL Option) is a type that contains an optional value.
* *
* Params: * Params:
* T = Type of the encapsulated value. * T = Type of the encapsulated value.
*
* See_Also: $(D_PSYMBOL option).
*/ */
struct Option(T) struct Option(T)
{ {
@ -338,6 +380,24 @@ struct Option(T)
return this; return this;
} }
version (D_Ddoc)
{
/**
* If $(D_PARAM T) has a `toHash()` method, $(D_PSYMBOL Option) defines
* `toHash()` which returns `T.toHash()` if it is set or 0 otherwise.
*
* Returns: Hash value.
*/
size_t toHash() const;
}
else static if (is(typeof(T.init.toHash()) == size_t))
{
size_t toHash() const
{
return isNothing ? 0U : this.value.toHash();
}
}
alias get this; alias get this;
} }
@ -452,3 +512,50 @@ struct Option(T)
assert(((ref e) => e)(Option!int().or(i)) == 5); assert(((ref e) => e)(Option!int().or(i)) == 5);
} }
} }
// Implements toHash() for nothing
@nogc nothrow pure @safe unittest
{
static struct ToHash
{
size_t toHash() const @nogc nothrow pure @safe
{
return 1U;
}
}
{
Option!ToHash toHash;
assert(toHash.toHash() == 0U);
}
{
auto toHash = Option!ToHash(ToHash());
assert(toHash.toHash() == 1U);
}
}
/**
* Creates a new $(D_PSYMBOL Option).
*
* Params:
* T = Option type.
* value = Initial value.
*
* See_Also: $(D_PSYMBOL Option).
*/
Option!T option(T)(auto ref T value)
{
return Option!T(forward!value);
}
/// ditto
Option!T option(T)()
{
return Option!T();
}
///
@nogc nothrow pure @safe unittest
{
assert(option!int().isNothing);
assert(option(5) == 5);
}