algorithm: import searching publically

This commit is contained in:
Eugen Wissner 2019-03-20 07:30:47 +01:00
parent 20c7e47ff7
commit 0fe7308a22
28 changed files with 1468 additions and 2020 deletions

View File

@ -9,7 +9,6 @@ language: d
d: d:
- dmd-2.085.0 - dmd-2.085.0
- dmd-2.084.1 - dmd-2.084.1
- dmd-2.083.1
- dmd-2.082.1 - dmd-2.082.1
env: env:

View File

@ -15,12 +15,6 @@ environment:
- DC: dmd - DC: dmd
DVersion: 2.084.1 DVersion: 2.084.1
arch: x86 arch: x86
- DC: dmd
DVersion: 2.083.1
arch: x64
- DC: dmd
DVersion: 2.083.1
arch: x86
- DC: dmd - DC: dmd
DVersion: 2.082.1 DVersion: 2.082.1
arch: x64 arch: x64

View File

@ -5,7 +5,7 @@
/** /**
* Algorithms for comparing values. * Algorithms for comparing values.
* *
* Copyright: Eugene Wissner 2018. * Copyright: Eugene Wissner 2018-2019.
* 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)
@ -99,22 +99,6 @@ if (Args.length >= 2
return minMax!((ref a, ref b) => a < b)(args); return minMax!((ref a, ref b) => a < b)(args);
} }
@nogc nothrow pure @safe unittest
{
static assert(!is(typeof(min(1, 1UL))));
}
@nogc nothrow pure @safe unittest
{
assert(min(5, 3) == 3);
assert(min(4, 4) == 4);
assert(min(5.2, 3.0) == 3.0);
assert(min(5.2, double.nan) == 5.2);
assert(min(double.nan, 3.0) == 3.0);
assert(isNaN(min(double.nan, double.nan)));
}
/// ditto /// ditto
Range min(Range)(Range range) Range min(Range)(Range range)
if (isForwardRange!Range && isOrderingComparable!(ElementType!Range)) if (isForwardRange!Range && isOrderingComparable!(ElementType!Range))
@ -150,11 +134,6 @@ if (isForwardRange!Range && isOrderingComparable!(ElementType!Range))
assert(minElement.length == 3); assert(minElement.length == 3);
} }
@nogc nothrow pure @safe unittest
{
assert(min(cast(ubyte[]) []).empty);
}
/** /**
* Finds the largest element in the argument list or a range. * Finds the largest element in the argument list or a range.
* *
@ -192,22 +171,6 @@ if (Args.length >= 2
return minMax!((ref a, ref b) => a > b)(args); return minMax!((ref a, ref b) => a > b)(args);
} }
@nogc nothrow pure @safe unittest
{
static assert(!is(typeof(max(1, 1UL))));
}
@nogc nothrow pure @safe unittest
{
assert(max(5, 3) == 5);
assert(max(4, 4) == 4);
assert(max(5.2, 3.0) == 5.2);
assert(max(5.2, double.nan) == 5.2);
assert(max(double.nan, 3.0) == 3.0);
assert(isNaN(max(double.nan, double.nan)));
}
/// ditto /// ditto
Range max(Range)(Range range) Range max(Range)(Range range)
if (isForwardRange!Range && isOrderingComparable!(ElementType!Range)) if (isForwardRange!Range && isOrderingComparable!(ElementType!Range))
@ -243,35 +206,6 @@ if (isForwardRange!Range && isOrderingComparable!(ElementType!Range))
assert(maxElement.length == 3); assert(maxElement.length == 3);
} }
@nogc nothrow pure @safe unittest
{
assert(max(cast(ubyte[]) []).empty);
}
// min/max compare const and mutable structs.
@nogc nothrow pure @safe unittest
{
static struct S
{
int s;
int opCmp(typeof(this) that) const @nogc nothrow pure @safe
{
return this.s - that.s;
}
}
{
const s1 = S(1);
assert(min(s1, S(2)).s == 1);
assert(max(s1, S(2)).s == 2);
}
{
auto s2 = S(2), s3 = S(3);
assert(min(s2, s3).s == 2);
assert(max(s2, s3).s == 3);
}
}
/** /**
* Compares element-wise two ranges for equality. * Compares element-wise two ranges for equality.
* *
@ -438,26 +372,3 @@ private int compareImpl(alias pred, R1, R2)(ref R1 r1, ref R2 r2)
} }
return 0; 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

@ -11,7 +11,7 @@
* All algorithms in this module are lazy, they request the next element of the * All algorithms in this module are lazy, they request the next element of the
* original range on demand. * original range on demand.
* *
* Copyright: Eugene Wissner 2018. * Copyright: Eugene Wissner 2018-2019.
* 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)
@ -26,7 +26,6 @@ import tanya.meta.trait;
import tanya.meta.transform; import tanya.meta.transform;
import tanya.range; import tanya.range;
import tanya.typecons; import tanya.typecons;
version (unittest) import tanya.test.stub;
private struct Take(R, bool exactly) private struct Take(R, bool exactly)
{ {
@ -49,21 +48,13 @@ private struct Take(R, bool exactly)
} }
@property auto ref front() @property auto ref front()
in in (!empty)
{
assert(!empty);
}
do
{ {
return this.source.front; return this.source.front;
} }
void popFront() void popFront()
in in (!empty)
{
assert(!empty);
}
do
{ {
this.source.popFront(); this.source.popFront();
--this.length_; --this.length_;
@ -92,21 +83,13 @@ private struct Take(R, bool exactly)
static if (hasAssignableElements!R) static if (hasAssignableElements!R)
{ {
@property void front(ref ElementType!R value) @property void front(ref ElementType!R value)
in in (!empty)
{
assert(!empty);
}
do
{ {
this.source.front = value; this.source.front = value;
} }
@property void front(ElementType!R value) @property void front(ElementType!R value)
in in (!empty)
{
assert(!empty);
}
do
{ {
this.source.front = move(value); this.source.front = move(value);
} }
@ -122,31 +105,19 @@ private struct Take(R, bool exactly)
static if (isRandomAccessRange!R) static if (isRandomAccessRange!R)
{ {
@property auto ref back() @property auto ref back()
in in (!empty)
{
assert(!empty);
}
do
{ {
return this.source[this.length - 1]; return this.source[this.length - 1];
} }
void popBack() void popBack()
in in (!empty)
{
assert(!empty);
}
do
{ {
--this.length_; --this.length_;
} }
auto ref opIndex(size_t i) auto ref opIndex(size_t i)
in in (i < length)
{
assert(i < length);
}
do
{ {
return this.source[i]; return this.source[i];
} }
@ -154,41 +125,25 @@ private struct Take(R, bool exactly)
static if (hasAssignableElements!R) static if (hasAssignableElements!R)
{ {
@property void back(ref ElementType!R value) @property void back(ref ElementType!R value)
in in (!empty)
{
assert(!empty);
}
do
{ {
this.source[length - 1] = value; this.source[length - 1] = value;
} }
@property void back(ElementType!R value) @property void back(ElementType!R value)
in in (!empty)
{
assert(!empty);
}
do
{ {
this.source[length - 1] = move(value); this.source[length - 1] = move(value);
} }
void opIndexAssign(ref ElementType!R value, size_t i) void opIndexAssign(ref ElementType!R value, size_t i)
in in (i < length)
{
assert(i < length);
}
do
{ {
this.source[i] = value; this.source[i] = value;
} }
void opIndexAssign(ElementType!R value, size_t i) void opIndexAssign(ElementType!R value, size_t i)
in in (i < length)
{
assert(i < length);
}
do
{ {
this.source[i] = move(value); this.source[i] = move(value);
} }
@ -198,12 +153,8 @@ private struct Take(R, bool exactly)
static if (!exactly && hasSlicing!R) static if (!exactly && hasSlicing!R)
{ {
auto opSlice(size_t i, size_t j) auto opSlice(size_t i, size_t j)
in in (i <= j)
{ in (j <= length)
assert(i <= j);
assert(j <= length);
}
do
{ {
return typeof(this)(this.source[i .. j], length); return typeof(this)(this.source[i .. j], length);
} }
@ -322,18 +273,6 @@ if (isInputRange!R)
assert(t.empty); assert(t.empty);
} }
// length is unknown when taking from a range without length
@nogc nothrow pure @safe unittest
{
static struct R
{
mixin InputRangeStub;
}
auto actual = take(R(), 100);
static assert(!hasLength!(typeof(actual)));
}
/** /**
* Takes exactly $(D_PARAM n) elements from $(D_PARAM range). * Takes exactly $(D_PARAM n) elements from $(D_PARAM range).
* *
@ -428,32 +367,6 @@ if (isInputRange!R)
assert(t.empty); assert(t.empty);
} }
// Takes minimum length if the range length > n
@nogc nothrow pure @safe unittest
{
auto range = take(cast(int[]) null, 8);
assert(range.length == 0);
}
@nogc nothrow pure @safe unittest
{
const int[9] range = [1, 2, 3, 4, 5, 6, 7, 8, 9];
{
auto slice = take(range[], 8)[1 .. 3];
assert(slice.length == 2);
assert(slice.front == 2);
assert(slice.back == 3);
}
{
auto slice = takeExactly(range[], 8)[1 .. 3];
assert(slice.length == 2);
assert(slice.front == 2);
assert(slice.back == 3);
}
}
// Reverse-access-order range returned by `retro`. // Reverse-access-order range returned by `retro`.
private struct Retro(Range) private struct Retro(Range)
{ {
@ -522,12 +435,8 @@ private struct Retro(Range)
alias opDollar = length; alias opDollar = length;
auto opSlice(size_t i, size_t j) auto opSlice(size_t i, size_t j)
in in (i <= j)
{ in (j <= length)
assert(i <= j);
assert(j <= length);
}
do
{ {
return typeof(this)(this.source[$-j .. $-i]); return typeof(this)(this.source[$-j .. $-i]);
} }
@ -615,41 +524,6 @@ if (isBidirectionalRange!Range)
assert(equal(actual, expected[])); 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);
// Check slicing.
auto slice = retro(given[])[1 .. $];
assert(slice.length == 2 && slice.front == 2 && slice.back == 1);
}
// 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);
}
private struct SingletonByValue(E) private struct SingletonByValue(E)
{ {
private Option!E element; private Option!E element;
@ -807,49 +681,3 @@ auto singleton(E)(return ref E element)
singleChar.popFront(); singleChar.popFront();
assert(singleChar.empty); assert(singleChar.empty);
} }
// Singleton range is bidirectional and random-access
@nogc nothrow pure @safe unittest
{
static assert(isBidirectionalRange!(typeof(singleton('a'))));
static assert(isRandomAccessRange!(typeof(singleton('a'))));
assert({ char a; return isBidirectionalRange!(typeof(singleton(a))); });
assert({ char a; return isRandomAccessRange!(typeof(singleton(a))); });
}
@nogc nothrow pure @safe unittest
{
char a = 'a';
auto single = singleton(a);
assert(single.front == 'a');
assert(single.back == 'a');
assert(single[0] == 'a');
assert(single.length == 1);
assert(!single.empty);
}
// popFront makes SingletonByRef empty
@nogc nothrow pure @safe unittest
{
char a = 'a';
auto single = singleton(a);
single.popFront();
assert(single.empty);
assert(single.length == 0);
assert(single.empty);
}
// popBack makes SingletonByRef empty
@nogc nothrow pure @safe unittest
{
char a = 'b';
auto single = singleton(a);
single.popBack();
assert(single.empty);
assert(single.length == 0);
assert(single.empty);
}

View File

@ -19,7 +19,6 @@ static import tanya.memory.op;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform; import tanya.meta.transform;
import tanya.range; import tanya.range;
version (unittest) import tanya.test.stub;
deprecated("Use tanya.memory.lifetime.swap instead") deprecated("Use tanya.memory.lifetime.swap instead")
alias swap = tanya.memory.lifetime.swap; alias swap = tanya.memory.lifetime.swap;
@ -96,49 +95,6 @@ do
assert(equal(source[], target[])); assert(equal(source[], target[]));
} }
// Returns advanced target
@nogc nothrow pure @safe unittest
{
int[5] input = [1, 2, 3, 4, 5];
assert(copy(input[3 .. 5], input[]).front == 3);
}
// Copies overlapping arrays
@nogc nothrow pure @safe unittest
{
import tanya.algorithm.comparison : equal;
int[6] actual = [1, 2, 3, 4, 5, 6];
const int[6] expected = [1, 2, 1, 2, 3, 4];
copy(actual[0 .. 4], actual[2 .. 6]);
assert(equal(actual[], expected[]));
}
@nogc nothrow pure @safe unittest
{
static assert(is(typeof(copy((ubyte[]).init, (ushort[]).init))));
static assert(!is(typeof(copy((ushort[]).init, (ubyte[]).init))));
}
@nogc nothrow pure @safe unittest
{
static struct OutPutRange
{
int value;
void opCall(int value) @nogc nothrow pure @safe
in (this.value == 0)
{
this.value = value;
}
}
int[1] source = [5];
OutPutRange target;
assert(copy(source[], target).value == 5);
}
/** /**
* Fills $(D_PARAM range) with $(D_PARAM value). * Fills $(D_PARAM range) with $(D_PARAM value).
* *
@ -176,42 +132,6 @@ if (isInputRange!Range && isAssignable!(ElementType!Range, Value))
assert(equal(actual[], expected[])); assert(equal(actual[], expected[]));
} }
// [] is called where possible
@nogc nothrow pure @system unittest
{
static struct Slice
{
bool* slicingCalled;
int front() @nogc nothrow pure @safe
{
return 0;
}
void front(int) @nogc nothrow pure @safe
{
}
void popFront() @nogc nothrow pure @safe
{
}
bool empty() @nogc nothrow pure @safe
{
return true;
}
void opIndexAssign(int) @nogc nothrow pure @safe
{
*this.slicingCalled = true;
}
}
bool slicingCalled;
auto range = Slice(&slicingCalled);
fill(range, 0);
assert(slicingCalled);
}
/** /**
* Fills $(D_PARAM range) with $(D_PARAM value) assuming the elements of the * Fills $(D_PARAM range) with $(D_PARAM value) assuming the elements of the
* $(D_PARAM range) aren't initialized. * $(D_PARAM range) aren't initialized.
@ -294,12 +214,6 @@ if (isInputRange!Range && hasLvalueElements!Range)
assert(equal(actual[], expected[])); assert(equal(actual[], expected[]));
} }
@nogc nothrow pure @safe unittest
{
NonCopyable[] nonCopyable;
initializeAll(nonCopyable);
}
/** /**
* Destroys all elements in the $(D_PARAM range). * Destroys all elements in the $(D_PARAM range).
* *
@ -390,38 +304,3 @@ if (isForwardRange!Range && hasSwappableElements!Range)
rotate(actual[0 .. 2], actual[4 .. 6]); rotate(actual[0 .. 2], actual[4 .. 6]);
assert(equal(actual[], expected[])); assert(equal(actual[], expected[]));
} }
@nogc nothrow pure @safe unittest
{
import tanya.algorithm.comparison : equal;
const int[5] expected = [1, 2, 3, 4, 5];
int[5] actual = [4, 5, 1, 2, 3];
rotate(actual[0 .. 2], actual[2 .. $]);
assert(equal(actual[], expected[]));
}
// Doesn't cause an infinite loop if back is shorter than the front
@nogc nothrow pure @safe unittest
{
import tanya.algorithm.comparison : equal;
const int[5] expected = [1, 2, 3, 4, 5];
int[5] actual = [3, 4, 5, 1, 2];
rotate(actual[0 .. 3], actual[3 .. $]);
assert(equal(actual[], expected[]));
}
// Doesn't call .front on an empty front
@nogc nothrow pure @safe unittest
{
import tanya.algorithm.comparison : equal;
const int[2] expected = [2, 8];
int[2] actual = expected;
rotate(actual[0 .. 0], actual[]);
assert(equal(actual[], expected[]));
}

View File

@ -5,7 +5,7 @@
/** /**
* Collection of generic algorithms. * Collection of generic algorithms.
* *
* Copyright: Eugene Wissner 2017-2018. * Copyright: Eugene Wissner 2017-2019.
* 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,3 +17,4 @@ module tanya.algorithm;
public import tanya.algorithm.comparison; public import tanya.algorithm.comparison;
public import tanya.algorithm.iteration; public import tanya.algorithm.iteration;
public import tanya.algorithm.mutation; public import tanya.algorithm.mutation;
public import tanya.algorithm.searching;

View File

@ -5,7 +5,7 @@
/** /**
* Searching algorithms. * Searching algorithms.
* *
* Copyright: Eugene Wissner 2018. * Copyright: Eugene Wissner 2018-2019.
* 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)
@ -51,28 +51,3 @@ if (isInputRange!R)
int[3] array; int[3] array;
assert(count(array) == 3); assert(count(array) == 3);
} }
@nogc nothrow pure @safe unittest
{
static struct Range
{
private int counter = 3;
int front() const @nogc nothrow pure @safe
{
return this.counter;
}
void popFront() @nogc nothrow pure @safe
{
--this.counter;
}
bool empty() const @nogc nothrow pure @safe
{
return this.counter == 0;
}
}
Range range;
assert(count(range) == 3);
}

View File

@ -116,30 +116,6 @@ else version (DragonFlyBSD)
{ {
version = Kqueue; version = Kqueue;
} }
version (unittest)
{
final class TestLoop : Loop
{
override protected bool reify(SocketWatcher watcher,
EventMask oldEvents,
EventMask events) @nogc
{
return true;
}
override protected void poll() @nogc
{
assert(!this.done);
unloop();
}
override protected @property uint maxEvents()
const pure nothrow @safe @nogc
{
return 64U;
}
}
}
/** /**
* Events. * Events.
@ -160,7 +136,7 @@ alias EventMask = BitFlags!Event;
*/ */
abstract class Loop abstract class Loop
{ {
private bool done = true; protected bool done = true;
/// Pending watchers. /// Pending watchers.
protected DList!Watcher pendings; protected DList!Watcher pendings;
@ -175,14 +151,6 @@ abstract class Loop
return 128U; return 128U;
} }
@nogc @system unittest
{
auto loop = defaultAllocator.make!TestLoop;
assert(loop.maxEvents == 64);
defaultAllocator.dispose(loop);
}
/** /**
* Initializes the loop. * Initializes the loop.
*/ */
@ -228,31 +196,6 @@ abstract class Loop
this.done = true; this.done = true;
} }
@nogc @system unittest
{
auto loop = defaultAllocator.make!TestLoop;
assert(loop.done);
loop.run();
assert(loop.done);
defaultAllocator.dispose(loop);
}
@nogc @system unittest
{
auto loop = defaultAllocator.make!TestLoop;
auto watcher = defaultAllocator.make!DummyWatcher;
loop.pendings.insertBack(watcher);
assert(!watcher.invoked);
loop.run();
assert(watcher.invoked);
defaultAllocator.dispose(loop);
defaultAllocator.dispose(watcher);
}
/** /**
* Start watching. * Start watching.
* *
@ -329,17 +272,6 @@ abstract class Loop
blockTime_ = blockTime; blockTime_ = blockTime;
} }
@nogc @system unittest
{
auto loop = defaultAllocator.make!TestLoop;
assert(loop.blockTime == 1.dur!"minutes");
loop.blockTime = 2.dur!"minutes";
assert(loop.blockTime == 2.dur!"minutes");
defaultAllocator.dispose(loop);
}
/** /**
* Does the actual polling. * Does the actual polling.
*/ */
@ -418,16 +350,3 @@ do
} }
private Loop defaultLoop_; private Loop defaultLoop_;
@nogc @system unittest
{
auto oldLoop = defaultLoop_;
auto loop = defaultAllocator.make!TestLoop;
defaultLoop = loop;
assert(defaultLoop_ is loop);
assert(defaultLoop is loop);
defaultLoop_ = oldLoop;
defaultAllocator.dispose(loop);
}

View File

@ -37,19 +37,6 @@ abstract class Watcher
void invoke() @nogc; void invoke() @nogc;
} }
version (unittest)
{
final class DummyWatcher : Watcher
{
bool invoked;
override void invoke() @nogc
{
this.invoked = true;
}
}
}
/** /**
* Socket watcher. * Socket watcher.
*/ */

View File

@ -21,7 +21,6 @@ import tanya.memory;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform; import tanya.meta.transform;
import tanya.range; import tanya.range;
version (unittest) import tanya.test.stub;
/** /**
* Random-access range for the $(D_PSYMBOL Array). * Random-access range for the $(D_PSYMBOL Array).
@ -35,22 +34,15 @@ struct Range(A)
private E* begin, end; private E* begin, end;
private A* container; private A* container;
invariant invariant (this.begin <= this.end);
{ invariant (this.container !is null);
assert(this.begin <= this.end); invariant (this.begin >= this.container.data);
assert(this.container !is null); invariant (this.end <= this.container.data + this.container.length);
assert(this.begin >= this.container.data);
assert(this.end <= this.container.data + this.container.length);
}
private this(ref A container, E* begin, E* end) @trusted private this(ref A container, E* begin, E* end) @trusted
in in (begin <= end)
{ in (begin >= container.data)
assert(begin <= end); in (end <= container.data + container.length)
assert(begin >= container.data);
assert(end <= container.data + container.length);
}
do
{ {
this.container = &container; this.container = &container;
this.begin = begin; this.begin = begin;
@ -77,51 +69,31 @@ struct Range(A)
alias opDollar = length; alias opDollar = length;
@property ref inout(E) front() inout @property ref inout(E) front() inout
in in (!empty)
{
assert(!empty);
}
do
{ {
return *this.begin; return *this.begin;
} }
@property ref inout(E) back() inout @trusted @property ref inout(E) back() inout @trusted
in in (!empty)
{
assert(!empty);
}
do
{ {
return *(this.end - 1); return *(this.end - 1);
} }
void popFront() @trusted void popFront() @trusted
in in (!empty)
{
assert(!empty);
}
do
{ {
++this.begin; ++this.begin;
} }
void popBack() @trusted void popBack() @trusted
in in (!empty)
{
assert(!empty);
}
do
{ {
--this.end; --this.end;
} }
ref inout(E) opIndex(size_t i) inout @trusted ref inout(E) opIndex(size_t i) inout @trusted
in in (i < length)
{
assert(i < length);
}
do
{ {
return *(this.begin + i); return *(this.begin + i);
} }
@ -137,23 +109,15 @@ struct Range(A)
} }
Range opSlice(size_t i, size_t j) @trusted Range opSlice(size_t i, size_t j) @trusted
in in (i <= j)
{ in (j <= length)
assert(i <= j);
assert(j <= length);
}
do
{ {
return typeof(return)(*this.container, this.begin + i, this.begin + j); return typeof(return)(*this.container, this.begin + i, this.begin + j);
} }
A.ConstRange opSlice(size_t i, size_t j) const @trusted A.ConstRange opSlice(size_t i, size_t j) const @trusted
in in (i <= j)
{ in (j <= length)
assert(i <= j);
assert(j <= length);
}
do
{ {
return typeof(return)(*this.container, this.begin + i, this.begin + j); return typeof(return)(*this.container, this.begin + i, this.begin + j);
} }
@ -182,11 +146,8 @@ struct Array(T)
private T* data; private T* data;
private size_t capacity_; private size_t capacity_;
invariant invariant (this.length_ <= this.capacity_);
{ invariant (this.capacity_ == 0 || this.data !is null);
assert(this.length_ <= this.capacity_);
assert(this.capacity_ == 0 || this.data !is null);
}
/** /**
* Creates a new $(D_PSYMBOL Array) with the elements from a static array. * Creates a new $(D_PSYMBOL Array) with the elements from a static array.
@ -312,11 +273,7 @@ struct Array(T)
/// ditto /// ditto
this(shared Allocator allocator) this(shared Allocator allocator)
in in (allocator !is null)
{
assert(allocator !is null);
}
do
{ {
allocator_ = allocator; allocator_ = allocator;
} }
@ -567,11 +524,7 @@ struct Array(T)
* Precondition: $(D_INLINECODE !empty). * Precondition: $(D_INLINECODE !empty).
*/ */
void removeBack() void removeBack()
in in (!empty)
{
assert(!empty);
}
do
{ {
length = length - 1; length = length - 1;
} }
@ -589,11 +542,7 @@ struct Array(T)
* Returns: The number of elements removed * Returns: The number of elements removed
*/ */
size_t removeBack(size_t howMany) size_t removeBack(size_t howMany)
out (removed) out (removed; removed <= howMany)
{
assert(removed <= howMany);
}
do
{ {
const toRemove = min(howMany, length); const toRemove = min(howMany, length);
@ -614,11 +563,7 @@ struct Array(T)
} }
private inout(T)[] slice(size_t length) inout @trusted private inout(T)[] slice(size_t length) inout @trusted
in in (length <= capacity)
{
assert(length <= capacity);
}
do
{ {
return this.data[0 .. length]; return this.data[0 .. length];
} }
@ -640,13 +585,9 @@ struct Array(T)
* Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this). * Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this).
*/ */
Range remove(Range r) Range remove(Range r)
in in (r.container is &this)
{ in (r.begin >= this.data)
assert(r.container is &this); in (r.end <= end)
assert(r.begin >= this.data);
assert(r.end <= end);
}
do
{ {
auto target = r.begin; auto target = r.begin;
auto source = r.end; auto source = r.end;
@ -801,13 +742,9 @@ struct Array(T)
if (!isInfinite!R if (!isInfinite!R
&& isInputRange!R && isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T)) && isImplicitlyConvertible!(ElementType!R, T))
in in (r.container is &this)
{ in (r.begin >= this.data)
assert(r.container is &this); in (r.end <= this.data + length)
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
do
{ {
const oldLength = length; const oldLength = length;
const after = r.end - this.data; const after = r.end - this.data;
@ -819,13 +756,9 @@ struct Array(T)
/// ditto /// ditto
size_t insertAfter(size_t R)(Range r, T[R] el) size_t insertAfter(size_t R)(Range r, T[R] el)
in in (r.container is &this)
{ in (r.begin >= this.data)
assert(r.container is &this); in (r.end <= this.data + length)
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
do
{ {
return insertAfter!(T[])(r, el[]); return insertAfter!(T[])(r, el[]);
} }
@ -833,13 +766,9 @@ struct Array(T)
/// ditto /// ditto
size_t insertAfter(R)(Range r, auto ref R el) size_t insertAfter(R)(Range r, auto ref R el)
if (isImplicitlyConvertible!(R, T)) if (isImplicitlyConvertible!(R, T))
in in (r.container is &this)
{ in (r.begin >= this.data)
assert(r.container is &this); in (r.end <= this.data + length)
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
do
{ {
const oldLen = length; const oldLen = length;
const offset = r.end - this.data; const offset = r.end - this.data;
@ -862,26 +791,18 @@ struct Array(T)
if (!isInfinite!R if (!isInfinite!R
&& isInputRange!R && isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T)) && isImplicitlyConvertible!(ElementType!R, T))
in in (r.container is &this)
{ in (r.begin >= this.data)
assert(r.container is &this); in (r.end <= this.data + length)
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
do
{ {
return insertAfter(Range(this, this.data, r.begin), el); return insertAfter(Range(this, this.data, r.begin), el);
} }
/// ditto /// ditto
size_t insertBefore(size_t R)(Range r, T[R] el) size_t insertBefore(size_t R)(Range r, T[R] el)
in in (r.container is &this)
{ in (r.begin >= this.data)
assert(r.container is &this); in (r.end <= this.data + length)
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
do
{ {
return insertBefore!(T[])(r, el[]); return insertBefore!(T[])(r, el[]);
} }
@ -889,13 +810,9 @@ struct Array(T)
/// ditto /// ditto
size_t insertBefore(R)(Range r, auto ref R el) size_t insertBefore(R)(Range r, auto ref R el)
if (isImplicitlyConvertible!(R, T)) if (isImplicitlyConvertible!(R, T))
in in (r.container is &this)
{ in (r.begin >= this.data)
assert(r.container is &this); in (r.end <= this.data + length)
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
do
{ {
const oldLen = length; const oldLen = length;
const offset = r.begin - this.data; const offset = r.begin - this.data;
@ -1063,11 +980,7 @@ struct Array(T)
* Precondition: $(D_INLINECODE length > pos). * Precondition: $(D_INLINECODE length > pos).
*/ */
ref inout(T) opIndex(size_t pos) inout @trusted ref inout(T) opIndex(size_t pos) inout @trusted
in in (length > pos)
{
assert(length > pos);
}
do
{ {
return *(this.data + pos); return *(this.data + pos);
} }
@ -1168,11 +1081,7 @@ struct Array(T)
* Precondition: $(D_INLINECODE !empty). * Precondition: $(D_INLINECODE !empty).
*/ */
@property ref inout(T) front() inout @property ref inout(T) front() inout
in in (!empty)
{
assert(!empty);
}
do
{ {
return *this.data; return *this.data;
} }
@ -1195,11 +1104,7 @@ struct Array(T)
* Precondition: $(D_INLINECODE !empty). * Precondition: $(D_INLINECODE !empty).
*/ */
@property ref inout(T) back() inout @trusted @property ref inout(T) back() inout @trusted
in in (!empty)
{
assert(!empty);
}
do
{ {
return *(this.data + length - 1); return *(this.data + length - 1);
} }
@ -1227,24 +1132,16 @@ struct Array(T)
* Precondition: $(D_INLINECODE i <= j && j <= length). * Precondition: $(D_INLINECODE i <= j && j <= length).
*/ */
Range opSlice(size_t i, size_t j) @trusted Range opSlice(size_t i, size_t j) @trusted
in in (i <= j)
{ in (j <= length)
assert(i <= j);
assert(j <= length);
}
do
{ {
return typeof(return)(this, this.data + i, this.data + j); return typeof(return)(this, this.data + i, this.data + j);
} }
/// ditto /// ditto
ConstRange opSlice(size_t i, size_t j) const @trusted ConstRange opSlice(size_t i, size_t j) const @trusted
in in (i <= j)
{ in (j <= length)
assert(i <= j);
assert(j <= length);
}
do
{ {
return typeof(return)(this, this.data + i, this.data + j); return typeof(return)(this, this.data + i, this.data + j);
} }
@ -1301,12 +1198,8 @@ struct Array(T)
*/ */
Range opSliceAssign(size_t R)(T[R] value, size_t i, size_t j) Range opSliceAssign(size_t R)(T[R] value, size_t i, size_t j)
@trusted @trusted
in in (i <= j)
{ in (j <= length)
assert(i <= j);
assert(j <= length);
}
do
{ {
copy(value[], this.data[i .. j]); copy(value[], this.data[i .. j]);
return opSlice(i, j); return opSlice(i, j);
@ -1315,12 +1208,8 @@ struct Array(T)
/// ditto /// ditto
Range opSliceAssign(R : T)(auto ref R value, size_t i, size_t j) Range opSliceAssign(R : T)(auto ref R value, size_t i, size_t j)
@trusted @trusted
in in (i <= j)
{ in (j <= length)
assert(i <= j);
assert(j <= length);
}
do
{ {
fill(this.data[i .. j], value); fill(this.data[i .. j], value);
return opSlice(i, j); return opSlice(i, j);
@ -1328,13 +1217,9 @@ struct Array(T)
/// ditto /// ditto
Range opSliceAssign()(Range value, size_t i, size_t j) @trusted Range opSliceAssign()(Range value, size_t i, size_t j) @trusted
in in (i <= j)
{ in (j <= length)
assert(i <= j); in (j - i == value.length)
assert(j <= length);
assert(j - i == value.length);
}
do
{ {
copy(value, this.data[i .. j]); copy(value, this.data[i .. j]);
return opSlice(i, j); return opSlice(i, j);
@ -1491,198 +1376,3 @@ struct Array(T)
assert(r.front == 7); assert(r.front == 7);
assert(r.front == v.front); assert(r.front == v.front);
} }
@nogc nothrow pure @safe unittest
{
const v1 = Array!int();
const Array!int v2;
const v3 = Array!int([1, 5, 8]);
static assert(is(PointerTarget!(typeof(v3.data)) == const(int)));
}
@nogc nothrow pure @safe unittest
{
// Test that const arrays return usable ranges.
auto v = const Array!int([1, 2, 4]);
auto r1 = v[];
assert(r1.back == 4);
r1.popBack();
assert(r1.back == 2);
r1.popBack();
assert(r1.back == 1);
r1.popBack();
assert(r1.length == 0);
static assert(!is(typeof(r1[0] = 5)));
static assert(!is(typeof(v[0] = 5)));
const r2 = r1[];
static assert(is(typeof(r2[])));
}
@nogc nothrow pure @safe unittest
{
Array!int v1;
const Array!int v2;
auto r1 = v1[];
auto r2 = v1[];
assert(r1.length == 0);
assert(r2.empty);
assert(r1 == r2);
v1.insertBack([1, 2, 4]);
assert(v1[] == v1);
assert(v2[] == v2);
assert(v2[] != v1);
assert(v1[] != v2);
assert(v1[].equal(v1[]));
assert(v2[].equal(v2[]));
assert(!v1[].equal(v2[]));
}
@nogc nothrow pure @safe unittest
{
struct MutableEqualsStruct
{
bool opEquals(typeof(this) that) @nogc nothrow pure @safe
{
return true;
}
}
struct ConstEqualsStruct
{
bool opEquals(const typeof(this) that) const @nogc nothrow pure @safe
{
return true;
}
}
auto v1 = Array!ConstEqualsStruct();
auto v2 = Array!ConstEqualsStruct();
assert(v1 == v2);
assert(v1[] == v2);
assert(v1 == v2[]);
assert(v1[].equal(v2[]));
auto v3 = const Array!ConstEqualsStruct();
auto v4 = const Array!ConstEqualsStruct();
assert(v3 == v4);
assert(v3[] == v4);
assert(v3 == v4[]);
assert(v3[].equal(v4[]));
auto v7 = Array!MutableEqualsStruct(1, MutableEqualsStruct());
auto v8 = Array!MutableEqualsStruct(1, MutableEqualsStruct());
assert(v7 == v8);
assert(v7[] == v8);
assert(v7 == v8[]);
assert(v7[].equal(v8[]));
}
// Destructor can destroy empty arrays
@nogc nothrow pure @safe unittest
{
auto v = Array!WithDtor();
}
@nogc nothrow pure @safe unittest
{
class A
{
}
A a1, a2;
auto v1 = Array!A([a1, a2]);
static assert(is(Array!(A*)));
}
@nogc nothrow pure @safe unittest
{
auto v = Array!int([5, 15, 8]);
{
size_t i;
foreach (e; v)
{
assert(i != 0 || e == 5);
assert(i != 1 || e == 15);
assert(i != 2 || e == 8);
++i;
}
assert(i == 3);
}
{
size_t i = 3;
foreach_reverse (e; v)
{
--i;
assert(i != 2 || e == 8);
assert(i != 1 || e == 15);
assert(i != 0 || e == 5);
}
assert(i == 0);
}
}
// const constructor tests
@nogc nothrow pure @safe unittest
{
auto v1 = const Array!int([1, 2, 3]);
auto v2 = Array!int(v1);
assert(v1.data !is v2.data);
assert(v1 == v2);
auto v3 = const Array!int(Array!int([1, 2, 3]));
assert(v1 == v3);
assert(v3.length == 3);
assert(v3.capacity == 3);
}
@nogc nothrow pure @safe unittest
{
auto v1 = Array!int(defaultAllocator);
}
@nogc nothrow pure @safe unittest
{
Array!int v;
auto r = v[];
assert(r.length == 0);
assert(r.empty);
}
@nogc nothrow pure @safe unittest
{
auto v1 = const Array!int([5, 15, 8]);
Array!int v2;
v2 = v1[0 .. 2];
assert(equal(v1[0 .. 2], v2[]));
}
// Move assignment
@nogc nothrow pure @safe unittest
{
Array!int v1;
v1 = Array!int([5, 15, 8]);
}
// Postblit is safe
@nogc nothrow pure @safe unittest
{
auto array = Array!int(3);
void func(Array!int arg)
{
assert(arg.capacity == 3);
}
func(array);
}
// Can have non-copyable elements
@nogc nothrow pure @safe unittest
{
static assert(is(Array!NonCopyable));
static assert(is(typeof({ Array!NonCopyable.init[0] = NonCopyable(); })));
}

View File

@ -22,11 +22,7 @@ version (unittest)
private int fillBuffer(ubyte[] buffer, private int fillBuffer(ubyte[] buffer,
int start = 0, int start = 0,
int end = 10) @nogc pure nothrow int end = 10) @nogc pure nothrow
in in (start < end)
{
assert(start < end);
}
do
{ {
auto numberRead = end - start; auto numberRead = end - start;
for (ubyte i; i < numberRead; ++i) for (ubyte i; i < numberRead; ++i)
@ -71,12 +67,9 @@ if (isScalarType!T)
/// Size by which the buffer will grow. /// Size by which the buffer will grow.
private size_t blockSize = 8192; private size_t blockSize = 8192;
invariant invariant (this.length_ <= this.buffer_.length);
{ invariant (this.blockSize > 0);
assert(this.length_ <= this.buffer_.length); invariant (this.minAvailable > 0);
assert(this.blockSize > 0);
assert(this.minAvailable > 0);
}
/** /**
* Creates a new read buffer. * Creates a new read buffer.
@ -101,11 +94,7 @@ if (isScalarType!T)
/// ditto /// ditto
this(shared Allocator allocator) this(shared Allocator allocator)
in in (allocator_ is null)
{
assert(allocator_ is null);
}
do
{ {
allocator_ = allocator; allocator_ = allocator;
} }
@ -188,7 +177,7 @@ if (isScalarType!T)
* Returns: $(D_KEYWORD this). * Returns: $(D_KEYWORD this).
*/ */
ref ReadBuffer opOpAssign(string op)(size_t length) ref ReadBuffer opOpAssign(string op)(size_t length)
if (op == "+") if (op == "+")
{ {
this.length_ += length; this.length_ += length;
ring = start; ring = start;
@ -293,11 +282,6 @@ if (isScalarType!T)
mixin DefaultAllocator; mixin DefaultAllocator;
} }
@nogc nothrow pure @safe unittest
{
static assert(is(ReadBuffer!int));
}
/** /**
* Circular, self-expanding buffer with overflow support. Can be used with * Circular, self-expanding buffer with overflow support. Can be used with
* functions returning the number of the transferred bytes. * functions returning the number of the transferred bytes.
@ -328,12 +312,9 @@ if (isScalarType!T)
/// 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 (this.blockSize > 0);
{ // Position can refer to an element outside the buffer if the buffer is full.
assert(this.blockSize > 0); invariant (this.position <= this.buffer_.length);
// Position can refer to an element outside the buffer if the buffer is full.
assert(this.position <= this.buffer_.length);
}
/** /**
* Params: * Params:
@ -344,12 +325,8 @@ if (isScalarType!T)
* Precondition: $(D_INLINECODE size > 0 && allocator !is null) * Precondition: $(D_INLINECODE size > 0 && allocator !is null)
*/ */
this(size_t size, shared Allocator allocator = defaultAllocator) @trusted this(size_t size, shared Allocator allocator = defaultAllocator) @trusted
in in (size > 0)
{ in (allocator !is null)
assert(size > 0);
assert(allocator !is null);
}
do
{ {
this.blockSize = size; this.blockSize = size;
ring = size - 1; ring = size - 1;
@ -435,7 +412,7 @@ if (isScalarType!T)
* buffer = Buffer chunk got with $(D_PSYMBOL opIndex). * buffer = Buffer chunk got with $(D_PSYMBOL opIndex).
*/ */
ref WriteBuffer opOpAssign(string op)(const T[] buffer) ref WriteBuffer opOpAssign(string op)(const T[] buffer)
if (op == "~") if (op == "~")
{ {
size_t end, start; size_t end, start;
@ -509,12 +486,8 @@ if (isScalarType!T)
* Returns: $(D_KEYWORD this). * Returns: $(D_KEYWORD this).
*/ */
ref WriteBuffer opOpAssign(string op)(size_t length) ref WriteBuffer opOpAssign(string op)(size_t length)
if (op == "+") if (op == "+")
in in (length <= this.length)
{
assert(length <= this.length);
}
do
{ {
auto afterRing = ring + 1; auto afterRing = ring + 1;
auto oldStart = start; auto oldStart = start;
@ -649,11 +622,6 @@ if (isScalarType!T)
mixin DefaultAllocator; mixin DefaultAllocator;
} }
@nogc nothrow pure @safe unittest
{
static assert(is(typeof(WriteBuffer!int(5))));
}
@nogc nothrow pure @system unittest @nogc nothrow pure @system unittest
{ {
auto b = WriteBuffer!ubyte(4); auto b = WriteBuffer!ubyte(4);

View File

@ -20,7 +20,6 @@ import tanya.memory.lifetime;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform; import tanya.meta.transform;
import tanya.typecons; import tanya.typecons;
version (unittest) import tanya.test.stub;
package struct SEntry(T) package struct SEntry(T)
{ {
@ -129,22 +128,14 @@ package struct HashArray(alias hasher, K, V = void)
size_t length; size_t length;
this(shared Allocator allocator) this(shared Allocator allocator)
in in (allocator !is null)
{
assert(allocator !is null);
}
do
{ {
this.array = Buckets(allocator); this.array = Buckets(allocator);
} }
this(T)(ref T data, shared Allocator allocator) this(T)(ref T data, shared Allocator allocator)
if (is(Unqual!T == HashArray)) if (is(Unqual!T == HashArray))
in in (allocator !is null)
{
assert(allocator !is null);
}
do
{ {
this.array = Buckets(data.array, allocator); this.array = Buckets(data.array, allocator);
this.lengthIndex = data.lengthIndex; this.lengthIndex = data.lengthIndex;
@ -153,11 +144,7 @@ package struct HashArray(alias hasher, K, V = void)
// Move constructor // Move constructor
void move(ref HashArray data, shared Allocator allocator) void move(ref HashArray data, shared Allocator allocator)
in in (allocator !is null)
{
assert(allocator !is null);
}
do
{ {
this.array = Buckets(.move(data.array), allocator); this.array = Buckets(.move(data.array), allocator);
this.lengthIndex = data.lengthIndex; this.lengthIndex = data.lengthIndex;
@ -238,11 +225,7 @@ package struct HashArray(alias hasher, K, V = void)
// Takes an index in the primes array. // Takes an index in the primes array.
void rehashToSize(const size_t n) void rehashToSize(const size_t n)
in in (n < primes.length)
{
assert(n < primes.length);
}
do
{ {
auto storage = typeof(this.array)(primes[n], this.array.allocator); auto storage = typeof(this.array)(primes[n], this.array.allocator);
DataLoop: foreach (ref e1; this.array[]) DataLoop: foreach (ref e1; this.array[])
@ -327,13 +310,3 @@ package struct HashArray(alias hasher, K, V = void)
return false; return false;
} }
} }
// Can be constructed with non-copyable key/values
@nogc nothrow pure @safe unittest
{
static assert(is(Bucket!NonCopyable));
static assert(is(Bucket!(NonCopyable, NonCopyable)));
static assert(is(HashArray!((ref NonCopyable) => 0U, NonCopyable)));
static assert(is(HashArray!((ref NonCopyable) => 0U, NonCopyable, NonCopyable)));
}

View File

@ -22,7 +22,6 @@ import tanya.memory;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform; import tanya.meta.transform;
import tanya.range.primitive; import tanya.range.primitive;
version (unittest) import tanya.test.stub;
/** /**
* Bidirectional range whose element type is a tuple of a key and the * Bidirectional range whose element type is a tuple of a key and the
@ -70,16 +69,9 @@ struct Range(T)
} }
void popFront() void popFront()
in in (!empty)
{ in (this.dataRange.front.status == BucketStatus.used)
assert(!empty); out (; empty || this.dataRange.back.status == BucketStatus.used)
assert(this.dataRange.front.status == BucketStatus.used);
}
out
{
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
{ {
do do
{ {
@ -89,16 +81,9 @@ struct Range(T)
} }
void popBack() void popBack()
in in (!empty)
{ in (this.dataRange.back.status == BucketStatus.used)
assert(!empty); out (; empty || this.dataRange.back.status == BucketStatus.used)
assert(this.dataRange.back.status == BucketStatus.used);
}
out
{
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
{ {
do do
{ {
@ -108,23 +93,15 @@ struct Range(T)
} }
@property ref inout(KV) front() inout @property ref inout(KV) front() inout
in in (!empty)
{ in (this.dataRange.front.status == BucketStatus.used)
assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
do
{ {
return this.dataRange.front.kv; return this.dataRange.front.kv;
} }
@property ref inout(KV) back() inout @property ref inout(KV) back() inout
in in (!empty)
{ in (this.dataRange.back.status == BucketStatus.used)
assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
do
{ {
return this.dataRange.back.kv; return this.dataRange.back.kv;
} }
@ -185,16 +162,9 @@ struct ByKey(T)
} }
@property void popFront() @property void popFront()
in in (!empty)
{ in (this.dataRange.front.status == BucketStatus.used)
assert(!empty); out (; empty || this.dataRange.back.status == BucketStatus.used)
assert(this.dataRange.front.status == BucketStatus.used);
}
out
{
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
{ {
do do
{ {
@ -204,16 +174,9 @@ struct ByKey(T)
} }
@property void popBack() @property void popBack()
in in (!empty)
{ in (this.dataRange.back.status == BucketStatus.used)
assert(!empty); out (; empty || this.dataRange.back.status == BucketStatus.used)
assert(this.dataRange.back.status == BucketStatus.used);
}
out
{
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
{ {
do do
{ {
@ -223,23 +186,15 @@ struct ByKey(T)
} }
@property ref inout(Key) front() inout @property ref inout(Key) front() inout
in in (!empty)
{ in (this.dataRange.front.status == BucketStatus.used)
assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
do
{ {
return this.dataRange.front.key; return this.dataRange.front.key;
} }
@property ref inout(Key) back() inout @property ref inout(Key) back() inout
in in (!empty)
{ in (this.dataRange.back.status == BucketStatus.used)
assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
do
{ {
return this.dataRange.back.key; return this.dataRange.back.key;
} }
@ -300,16 +255,9 @@ struct ByValue(T)
} }
@property void popFront() @property void popFront()
in in (!empty)
{ in (this.dataRange.front.status == BucketStatus.used)
assert(!empty); out (; empty || this.dataRange.back.status == BucketStatus.used)
assert(this.dataRange.front.status == BucketStatus.used);
}
out
{
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
{ {
do do
{ {
@ -319,16 +267,9 @@ struct ByValue(T)
} }
@property void popBack() @property void popBack()
in in (!empty)
{ in (this.dataRange.back.status == BucketStatus.used)
assert(!empty); out (; empty || this.dataRange.back.status == BucketStatus.used)
assert(this.dataRange.back.status == BucketStatus.used);
}
out
{
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
{ {
do do
{ {
@ -338,23 +279,15 @@ struct ByValue(T)
} }
@property ref inout(Value) front() inout @property ref inout(Value) front() inout
in in (!empty)
{ in (this.dataRange.front.status == BucketStatus.used)
assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
do
{ {
return this.dataRange.front.kv.value; return this.dataRange.front.kv.value;
} }
@property ref inout(Value) back() inout @property ref inout(Value) back() inout
in in (!empty)
{ in (this.dataRange.back.status == BucketStatus.used)
assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
do
{ {
return this.dataRange.back.kv.value; return this.dataRange.back.kv.value;
} }
@ -412,10 +345,7 @@ if (isHashFunction!(hasher, Key))
/// ditto /// ditto
alias ConstByValue = .ByValue!(const HashArray); alias ConstByValue = .ByValue!(const HashArray);
invariant invariant (this.data.lengthIndex < primes.length);
{
assert(this.data.lengthIndex < primes.length);
}
/** /**
* Constructor. * Constructor.
@ -427,11 +357,7 @@ if (isHashFunction!(hasher, Key))
* Precondition: $(D_INLINECODE allocator !is null). * Precondition: $(D_INLINECODE allocator !is null).
*/ */
this(size_t n, shared Allocator allocator = defaultAllocator) this(size_t n, shared Allocator allocator = defaultAllocator)
in in (allocator !is null)
{
assert(allocator !is null);
}
do
{ {
this(allocator); this(allocator);
this.data.rehash(n); this.data.rehash(n);
@ -446,11 +372,7 @@ if (isHashFunction!(hasher, Key))
/// ditto /// ditto
this(shared Allocator allocator) this(shared Allocator allocator)
in in (allocator !is null)
{
assert(allocator !is null);
}
do
{ {
this.data = HashArray(allocator); this.data = HashArray(allocator);
} }
@ -470,11 +392,7 @@ if (isHashFunction!(hasher, Key))
*/ */
this(S)(ref S init, shared Allocator allocator = defaultAllocator) this(S)(ref S init, shared Allocator allocator = defaultAllocator)
if (is(Unqual!S == HashTable)) if (is(Unqual!S == HashTable))
in in (allocator !is null)
{
assert(allocator !is null);
}
do
{ {
this.data = HashArray(init.data, allocator); this.data = HashArray(init.data, allocator);
} }
@ -482,11 +400,7 @@ if (isHashFunction!(hasher, Key))
/// ditto /// ditto
this(S)(S init, shared Allocator allocator = defaultAllocator) this(S)(S init, shared Allocator allocator = defaultAllocator)
if (is(S == HashTable)) if (is(S == HashTable))
in in (allocator !is null)
{
assert(allocator !is null);
}
do
{ {
this.data.move(init.data, allocator); this.data.move(init.data, allocator);
} }
@ -503,11 +417,7 @@ if (isHashFunction!(hasher, Key))
*/ */
this(R)(R range, shared Allocator allocator = defaultAllocator) this(R)(R range, shared Allocator allocator = defaultAllocator)
if (isForwardRange!R && is(ElementType!R == KeyValue)) if (isForwardRange!R && is(ElementType!R == KeyValue))
in in (allocator !is null)
{
assert(allocator !is null);
}
do
{ {
this(allocator); this(allocator);
insert(range); insert(range);
@ -537,11 +447,7 @@ if (isHashFunction!(hasher, Key))
*/ */
this(size_t n)(KeyValue[n] array, this(size_t n)(KeyValue[n] array,
shared Allocator allocator = defaultAllocator) shared Allocator allocator = defaultAllocator)
in in (allocator !is null)
{
assert(allocator !is null);
}
do
{ {
insert(array[]); insert(array[]);
} }
@ -589,11 +495,7 @@ if (isHashFunction!(hasher, Key))
* Postcondition: $(D_INLINECODE allocator !is null) * Postcondition: $(D_INLINECODE allocator !is null)
*/ */
@property shared(Allocator) allocator() const @property shared(Allocator) allocator() const
out (allocator) out (allocator; allocator !is null)
{
assert(allocator !is null);
}
do
{ {
return this.data.array.allocator; return this.data.array.allocator;
} }
@ -1085,129 +987,3 @@ if (isHashFunction!(hasher, Key))
dinos.clear(); dinos.clear();
assert(dinos.empty); assert(dinos.empty);
} }
@nogc nothrow pure @safe unittest
{
import tanya.range.primitive : isForwardRange;
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
@nogc nothrow pure @safe unittest
{
auto hashTable1 = HashTable!(string, int)(7);
auto hashTable2 = HashTable!(string, int)(hashTable1);
assert(hashTable1.length == hashTable2.length);
assert(hashTable1.capacity == hashTable2.capacity);
}
// Constructs by value
@nogc nothrow pure @safe unittest
{
auto hashTable = HashTable!(string, int)(HashTable!(string, int)(7));
assert(hashTable.capacity == 7);
}
// Assigns by reference
@nogc nothrow pure @safe unittest
{
auto hashTable1 = HashTable!(string, int)(7);
HashTable!(string, int) hashTable2;
hashTable1 = hashTable2;
assert(hashTable1.length == hashTable2.length);
assert(hashTable1.capacity == hashTable2.capacity);
}
// Assigns by value
@nogc nothrow pure @safe unittest
{
HashTable!(string, int) hashTable;
hashTable = HashTable!(string, int)(7);
assert(hashTable.capacity == 7);
}
// Postblit copies
@nogc nothrow pure @safe unittest
{
auto hashTable = HashTable!(string, int)(7);
void testFunc(HashTable!(string, int) hashTable)
{
assert(hashTable.capacity == 7);
}
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"])));
}
// Can have non-copyable keys and elements
@nogc nothrow pure @safe unittest
{
@NonCopyable @Hashable
static struct S
{
mixin StructStub;
}
static assert(is(HashTable!(S, int)));
static assert(is(HashTable!(int, S)));
static assert(is(HashTable!(S, S)));
}

View File

@ -22,7 +22,6 @@ import tanya.meta.trait;
import tanya.meta.transform; import tanya.meta.transform;
import tanya.range.array; import tanya.range.array;
import tanya.range.primitive; import tanya.range.primitive;
version (unittest) import tanya.test.stub;
/** /**
* Forward range for the $(D_PSYMBOL SList). * Forward range for the $(D_PSYMBOL SList).
@ -37,10 +36,7 @@ struct SRange(L)
private EntryPointer* head; private EntryPointer* head;
invariant invariant (this.head !is null);
{
assert(this.head !is null);
}
private this(ref EntryPointer head) @trusted private this(ref EntryPointer head) @trusted
{ {
@ -60,21 +56,13 @@ struct SRange(L)
} }
@property ref inout(E) front() inout @property ref inout(E) front() inout
in in (!empty)
{
assert(!empty);
}
do
{ {
return (*this.head).content; return (*this.head).content;
} }
void popFront() @trusted void popFront() @trusted
in in (!empty)
{
assert(!empty);
}
do
{ {
this.head = &(*this.head).next; this.head = &(*this.head).next;
} }
@ -206,11 +194,7 @@ struct SList(T)
/// ditto /// ditto
this(shared Allocator allocator) this(shared Allocator allocator)
in in (allocator !is null)
{
assert(allocator !is null);
}
do
{ {
this.allocator_ = allocator; this.allocator_ = allocator;
} }
@ -332,11 +316,7 @@ struct SList(T)
* Precondition: $(D_INLINECODE !empty). * Precondition: $(D_INLINECODE !empty).
*/ */
@property ref inout(T) front() inout @property ref inout(T) front() inout
in in (!empty)
{
assert(!empty);
}
do
{ {
return this.head.content; return this.head.content;
} }
@ -477,11 +457,7 @@ struct SList(T)
*/ */
size_t insertBefore(R)(Range r, R el) size_t insertBefore(R)(Range r, R el)
if (isImplicitlyConvertible!(R, T)) if (isImplicitlyConvertible!(R, T))
in in (checkRangeBelonging(r))
{
assert(checkRangeBelonging(r));
}
do
{ {
return moveEntry(*r.head, el); return moveEntry(*r.head, el);
} }
@ -500,11 +476,7 @@ struct SList(T)
if (!isInfinite!R if (!isInfinite!R
&& isInputRange!R && isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T)) && isImplicitlyConvertible!(ElementType!R, T))
in in (checkRangeBelonging(r))
{
assert(checkRangeBelonging(r));
}
do
{ {
size_t inserted; size_t inserted;
foreach (e; el) foreach (e; el)
@ -529,11 +501,7 @@ struct SList(T)
/// ditto /// ditto
size_t insertBefore()(Range r, ref T el) @trusted size_t insertBefore()(Range r, ref T el) @trusted
in in (checkRangeBelonging(r))
{
assert(checkRangeBelonging(r));
}
do
{ {
*r.head = allocator.make!Entry(el, *r.head); *r.head = allocator.make!Entry(el, *r.head);
return 1; return 1;
@ -629,11 +597,7 @@ struct SList(T)
* Precondition: $(D_INLINECODE !empty) * Precondition: $(D_INLINECODE !empty)
*/ */
void removeFront() void removeFront()
in in (!empty)
{
assert(!empty);
}
do
{ {
auto n = this.head.next; auto n = this.head.next;
@ -668,11 +632,7 @@ struct SList(T)
* Returns: The number of elements removed. * Returns: The number of elements removed.
*/ */
size_t removeFront(size_t howMany) size_t removeFront(size_t howMany)
out (removed) out (removed; removed <= howMany)
{
assert(removed <= howMany);
}
do
{ {
size_t i; size_t i;
for (; i < howMany && !empty; ++i) for (; i < howMany && !empty; ++i)
@ -704,11 +664,7 @@ struct SList(T)
* Precondition: $(D_PARAM r) is extracted from this list. * Precondition: $(D_PARAM r) is extracted from this list.
*/ */
Range remove(Range r) Range remove(Range r)
in in (checkRangeBelonging(r))
{
assert(checkRangeBelonging(r));
}
do
{ {
auto outOfScopeList = typeof(this)(allocator); auto outOfScopeList = typeof(this)(allocator);
outOfScopeList.head = *r.head; outOfScopeList.head = *r.head;
@ -742,12 +698,8 @@ struct SList(T)
* $(D_PARAM range) is extracted from this list. * $(D_PARAM range) is extracted from this list.
*/ */
Range popFirstOf(Range range) Range popFirstOf(Range range)
in in (!range.empty)
{ in (checkRangeBelonging(range))
assert(!range.empty);
assert(checkRangeBelonging(range));
}
do
{ {
auto next = (*range.head).next; auto next = (*range.head).next;
@ -923,46 +875,6 @@ struct SList(T)
assert(i == 3); assert(i == 3);
} }
@nogc nothrow pure @safe unittest
{
interface Stuff
{
}
static assert(is(SList!Stuff));
}
@nogc nothrow pure @safe unittest
{
auto l = SList!int(0, 0);
assert(l.empty);
}
// foreach called using opIndex().
@nogc nothrow pure @safe unittest
{
SList!int l;
size_t i;
l.insertFront(5);
l.insertFront(4);
l.insertFront(9);
foreach (e; l)
{
assert(i != 0 || e == 9);
assert(i != 1 || e == 4);
assert(i != 2 || e == 5);
++i;
}
}
@nogc nothrow pure @safe unittest
{
auto l1 = SList!int();
auto l2 = SList!int([9, 4]);
l1 = l2[];
assert(l1 == l2);
}
/** /**
* Bidirectional range for the $(D_PSYMBOL DList). * Bidirectional range for the $(D_PSYMBOL DList).
* *
@ -977,11 +889,8 @@ struct DRange(L)
private EntryPointer* head; private EntryPointer* head;
private EntryPointer* tail; private EntryPointer* tail;
invariant invariant (this.head !is null);
{ invariant (this.tail !is null);
assert(this.head !is null);
assert(this.tail !is null);
}
private this(ref EntryPointer head, ref EntryPointer tail) @trusted private this(ref EntryPointer head, ref EntryPointer tail) @trusted
{ {
@ -1002,41 +911,25 @@ struct DRange(L)
} }
@property ref inout(E) front() inout @property ref inout(E) front() inout
in in (!empty)
{
assert(!empty);
}
do
{ {
return (*this.head).content; return (*this.head).content;
} }
@property ref inout(E) back() inout @property ref inout(E) back() inout
in in (!empty)
{
assert(!empty);
}
do
{ {
return (*this.tail).content; return (*this.tail).content;
} }
void popFront() @trusted void popFront() @trusted
in in (!empty)
{
assert(!empty);
}
do
{ {
this.head = &(*this.head).next; this.head = &(*this.head).next;
} }
void popBack() @trusted void popBack() @trusted
in in (!empty)
{
assert(!empty);
}
do
{ {
this.tail = &(*this.tail).prev; this.tail = &(*this.tail).prev;
} }
@ -1082,13 +975,10 @@ struct DList(T)
// 0th and the last elements of the list. // 0th and the last elements of the list.
private Entry* head, tail; private Entry* head, tail;
invariant invariant ((this.tail is null && this.head is null)
{
assert((this.tail is null && this.head is null)
|| (this.tail !is null && this.head !is null)); || (this.tail !is null && this.head !is null));
assert(this.tail is null || this.tail.next is null); invariant (this.tail is null || this.tail.next is null);
assert(this.head is null || this.head.prev is null); invariant (this.head is null || this.head.prev is null);
}
/** /**
* Creates a new $(D_PSYMBOL DList) with the elements from a static array. * Creates a new $(D_PSYMBOL DList) with the elements from a static array.
@ -1192,11 +1082,7 @@ struct DList(T)
/// ditto /// ditto
this(shared Allocator allocator) this(shared Allocator allocator)
in in (allocator !is null)
{
assert(allocator !is null);
}
do
{ {
this.allocator_ = allocator; this.allocator_ = allocator;
} }
@ -1322,11 +1208,7 @@ struct DList(T)
* Precondition: $(D_INLINECODE !empty). * Precondition: $(D_INLINECODE !empty).
*/ */
@property ref inout(T) front() inout @property ref inout(T) front() inout
in in (!empty)
{
assert(!empty);
}
do
{ {
return this.head.content; return this.head.content;
} }
@ -1337,11 +1219,7 @@ struct DList(T)
* Precondition: $(D_INLINECODE !empty). * Precondition: $(D_INLINECODE !empty).
*/ */
@property ref inout(T) back() inout @property ref inout(T) back() inout
in in (!empty)
{
assert(!empty);
}
do
{ {
return this.tail.content; return this.tail.content;
} }
@ -1656,11 +1534,7 @@ struct DList(T)
*/ */
size_t insertBefore(R)(Range r, R el) size_t insertBefore(R)(Range r, R el)
if (isImplicitlyConvertible!(R, T)) if (isImplicitlyConvertible!(R, T))
in in (checkRangeBelonging(r))
{
assert(checkRangeBelonging(r));
}
do
{ {
return moveFront(*r.head, el); return moveFront(*r.head, el);
} }
@ -1676,11 +1550,7 @@ struct DList(T)
/// ditto /// ditto
size_t insertBefore()(Range r, ref T el) @trusted size_t insertBefore()(Range r, ref T el) @trusted
in in (checkRangeBelonging(r))
{
assert(checkRangeBelonging(r));
}
do
{ {
auto temp = allocator.make!Entry(el, *r.head); auto temp = allocator.make!Entry(el, *r.head);
@ -1714,11 +1584,7 @@ struct DList(T)
if (!isInfinite!R if (!isInfinite!R
&& isInputRange!R && isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T)) && isImplicitlyConvertible!(ElementType!R, T))
in in (checkRangeBelonging(r))
{
assert(checkRangeBelonging(r));
}
do
{ {
size_t inserted; size_t inserted;
foreach (e; el) foreach (e; el)
@ -1771,11 +1637,7 @@ struct DList(T)
*/ */
size_t insertAfter(R)(Range r, R el) @trusted size_t insertAfter(R)(Range r, R el) @trusted
if (isImplicitlyConvertible!(R, T)) if (isImplicitlyConvertible!(R, T))
in in (checkRangeBelonging(r))
{
assert(checkRangeBelonging(r));
}
do
{ {
return moveBack(*r.tail, el); return moveBack(*r.tail, el);
} }
@ -1793,11 +1655,7 @@ struct DList(T)
/// ditto /// ditto
size_t insertAfter()(Range r, ref T el) @trusted size_t insertAfter()(Range r, ref T el) @trusted
in in (checkRangeBelonging(r))
{
assert(checkRangeBelonging(r));
}
do
{ {
auto temp = allocator.make!Entry(el, null, *r.tail); auto temp = allocator.make!Entry(el, null, *r.tail);
@ -1831,11 +1689,7 @@ struct DList(T)
if (!isInfinite!R if (!isInfinite!R
&& isInputRange!R && isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T)) && isImplicitlyConvertible!(ElementType!R, T))
in in (checkRangeBelonging(r))
{
assert(checkRangeBelonging(r));
}
do
{ {
size_t inserted; size_t inserted;
foreach (e; el) foreach (e; el)
@ -1926,11 +1780,7 @@ struct DList(T)
* Precondition: $(D_INLINECODE !empty) * Precondition: $(D_INLINECODE !empty)
*/ */
void removeFront() void removeFront()
in in (!empty)
{
assert(!empty);
}
do
{ {
auto n = this.head.next; auto n = this.head.next;
@ -1962,11 +1812,7 @@ struct DList(T)
/// ditto /// ditto
void removeBack() void removeBack()
in in (!empty)
{
assert(!empty);
}
do
{ {
auto n = this.tail.prev; auto n = this.tail.prev;
@ -2008,11 +1854,7 @@ struct DList(T)
* Returns: The number of elements removed. * Returns: The number of elements removed.
*/ */
size_t removeFront(size_t howMany) size_t removeFront(size_t howMany)
out (removed) out (removed; removed <= howMany)
{
assert(removed <= howMany);
}
do
{ {
size_t i; size_t i;
for (; i < howMany && !empty; ++i) for (; i < howMany && !empty; ++i)
@ -2035,11 +1877,7 @@ struct DList(T)
/// ditto /// ditto
size_t removeBack(size_t howMany) size_t removeBack(size_t howMany)
out (removed) out (removed; removed <= howMany)
{
assert(removed <= howMany);
}
do
{ {
size_t i; size_t i;
for (; i < howMany && !empty; ++i) for (; i < howMany && !empty; ++i)
@ -2071,11 +1909,7 @@ struct DList(T)
* Precondition: $(D_PARAM r) is extracted from this list. * Precondition: $(D_PARAM r) is extracted from this list.
*/ */
Range remove(Range r) Range remove(Range r)
in in (checkRangeBelonging(r))
{
assert(checkRangeBelonging(r));
}
do
{ {
// Save references to the elements before and after the range. // Save references to the elements before and after the range.
Entry* headPrev; Entry* headPrev;
@ -2147,11 +1981,7 @@ struct DList(T)
* $(D_PARAM range) is extracted from this list. * $(D_PARAM range) is extracted from this list.
*/ */
Range popFirstOf(Range range) Range popFirstOf(Range range)
in in (!range.empty)
{
assert(!range.empty);
}
do
{ {
remove(Range(*range.head, *range.head)); remove(Range(*range.head, *range.head));
return range; return range;
@ -2159,11 +1989,7 @@ struct DList(T)
/// ditto /// ditto
Range popLastOf(Range range) Range popLastOf(Range range)
in in (!range.empty)
{
assert(!range.empty);
}
do
{ {
remove(Range(*range.tail, *range.tail)); remove(Range(*range.tail, *range.tail));
return range; return range;
@ -2317,82 +2143,3 @@ struct DList(T)
} }
assert(i == 3); assert(i == 3);
} }
@nogc nothrow pure @safe unittest
{
class A
{
}
static assert(is(SList!(A*)));
static assert(is(DList!(A*)));
}
// Removes all elements
@nogc nothrow pure @safe unittest
{
auto l = DList!int([5]);
assert(l.remove(l[]).empty);
}
@nogc nothrow pure @safe unittest
{
auto l1 = DList!int([5, 234, 30, 1]);
auto l2 = DList!int([5, 1]);
auto r = l1[];
r.popFront();
r.popBack();
assert(r.front == 234);
assert(r.back == 30);
assert(!l1.remove(r).empty);
assert(l1 == l2);
}
@nogc nothrow pure @safe unittest
{
auto l = DList!int(0, 0);
assert(l.empty);
}
@nogc nothrow pure @safe unittest
{
auto l1 = DList!int([5, 234]);
assert(l1.head is l1.head.next.prev);
}
@nogc nothrow pure @safe unittest
{
DList!int l;
l.insertAfter(l[], 234);
assert(l.front == 234);
assert(l.back == 234);
}
@nogc nothrow pure @safe unittest
{
auto l1 = DList!int();
auto l2 = DList!int([9, 4]);
l1 = l2[];
assert(l1 == l2);
}
// Sets the new head
@nogc nothrow pure @safe unittest
{
auto l1 = DList!int([5, 234, 30, 1]);
auto l2 = DList!int([1]);
auto r = l1[];
r.popBack();
assert(!l1.remove(r).empty);
assert(l1 == l2);
}
// Can have non-copyable elements
@nogc nothrow pure @safe unittest
{
static assert(is(SList!NonCopyable));
static assert(is(DList!NonCopyable));
}

View File

@ -22,7 +22,6 @@ import tanya.memory;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform; import tanya.meta.transform;
import tanya.range.primitive; import tanya.range.primitive;
version (unittest) import tanya.test.stub;
/** /**
* Bidirectional range that iterates over the $(D_PSYMBOL Set)'s values. * Bidirectional range that iterates over the $(D_PSYMBOL Set)'s values.
@ -69,16 +68,9 @@ struct Range(T)
} }
void popFront() void popFront()
in in (!empty)
{ in (this.dataRange.front.status == BucketStatus.used)
assert(!empty); out (; empty || this.dataRange.back.status == BucketStatus.used)
assert(this.dataRange.front.status == BucketStatus.used);
}
out
{
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
{ {
do do
{ {
@ -88,16 +80,9 @@ struct Range(T)
} }
void popBack() void popBack()
in in (!empty)
{ in (this.dataRange.back.status == BucketStatus.used)
assert(!empty); out (; empty || this.dataRange.back.status == BucketStatus.used)
assert(this.dataRange.back.status == BucketStatus.used);
}
out
{
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
{ {
do do
{ {
@ -107,23 +92,15 @@ struct Range(T)
} }
@property ref inout(E) front() inout @property ref inout(E) front() inout
in in (!empty)
{ in (this.dataRange.front.status == BucketStatus.used)
assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
do
{ {
return this.dataRange.front.key; return this.dataRange.front.key;
} }
@property ref inout(E) back() inout @property ref inout(E) back() inout
in in (!empty)
{ in (this.dataRange.back.status == BucketStatus.used)
assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
do
{ {
return this.dataRange.back.key; return this.dataRange.back.key;
} }
@ -168,12 +145,9 @@ if (isHashFunction!(hasher, T))
/// ditto /// ditto
alias ConstRange = .Range!(const HashArray); alias ConstRange = .Range!(const HashArray);
invariant invariant (this.data.lengthIndex < primes.length);
{ invariant (this.data.array.length == 0
assert(this.data.lengthIndex < primes.length);
assert(this.data.array.length == 0
|| this.data.array.length == primes[this.data.lengthIndex]); || this.data.array.length == primes[this.data.lengthIndex]);
}
/** /**
* Constructor. * Constructor.
@ -185,11 +159,7 @@ if (isHashFunction!(hasher, T))
* Precondition: $(D_INLINECODE allocator !is null). * Precondition: $(D_INLINECODE allocator !is null).
*/ */
this(size_t n, shared Allocator allocator = defaultAllocator) this(size_t n, shared Allocator allocator = defaultAllocator)
in in (allocator !is null)
{
assert(allocator !is null);
}
do
{ {
this(allocator); this(allocator);
this.data.rehash(n); this.data.rehash(n);
@ -204,11 +174,7 @@ if (isHashFunction!(hasher, T))
/// ditto /// ditto
this(shared Allocator allocator) this(shared Allocator allocator)
in in (allocator !is null)
{
assert(allocator !is null);
}
do
{ {
this.data = HashArray(allocator); this.data = HashArray(allocator);
} }
@ -228,11 +194,7 @@ if (isHashFunction!(hasher, T))
*/ */
this(S)(ref S init, shared Allocator allocator = defaultAllocator) this(S)(ref S init, shared Allocator allocator = defaultAllocator)
if (is(Unqual!S == Set)) if (is(Unqual!S == Set))
in in (allocator !is null)
{
assert(allocator !is null);
}
do
{ {
this.data = HashArray(init.data, allocator); this.data = HashArray(init.data, allocator);
} }
@ -240,11 +202,7 @@ if (isHashFunction!(hasher, T))
/// ditto /// ditto
this(S)(S init, shared Allocator allocator = defaultAllocator) this(S)(S init, shared Allocator allocator = defaultAllocator)
if (is(S == Set)) if (is(S == Set))
in in (allocator !is null)
{
assert(allocator !is null);
}
do
{ {
this.data.move(init.data, allocator); this.data.move(init.data, allocator);
} }
@ -261,11 +219,7 @@ if (isHashFunction!(hasher, T))
*/ */
this(R)(R range, shared Allocator allocator = defaultAllocator) this(R)(R range, shared Allocator allocator = defaultAllocator)
if (isForwardRange!R && isImplicitlyConvertible!(ElementType!R, T)) if (isForwardRange!R && isImplicitlyConvertible!(ElementType!R, T))
in in (allocator !is null)
{
assert(allocator !is null);
}
do
{ {
insert(range); insert(range);
} }
@ -291,11 +245,7 @@ if (isHashFunction!(hasher, T))
* Precondition: $(D_INLINECODE allocator !is null). * Precondition: $(D_INLINECODE allocator !is null).
*/ */
this(size_t n)(T[n] array, shared Allocator allocator = defaultAllocator) this(size_t n)(T[n] array, shared Allocator allocator = defaultAllocator)
in in (allocator !is null)
{
assert(allocator !is null);
}
do
{ {
insert(array[]); insert(array[]);
} }
@ -342,11 +292,7 @@ if (isHashFunction!(hasher, T))
* Postcondition: $(D_INLINECODE allocator !is null) * Postcondition: $(D_INLINECODE allocator !is null)
*/ */
@property shared(Allocator) allocator() const @property shared(Allocator) allocator() const
out (allocator) out (allocator; allocator !is null)
{
assert(allocator !is null);
}
do
{ {
return this.data.array.allocator; return this.data.array.allocator;
} }
@ -638,150 +584,3 @@ if (isHashFunction!(hasher, T))
assert(set[].back == 8); assert(set[].back == 8);
} }
} }
// Basic insertion logic.
@nogc nothrow pure @safe unittest
{
Set!int set;
assert(set.insert(5) == 1);
assert(5 in set);
assert(set.data.array.length == 3);
assert(set.insert(5) == 0);
assert(5 in set);
assert(set.data.array.length == 3);
assert(set.insert(9) == 1);
assert(9 in set);
assert(5 in set);
assert(set.data.array.length == 3);
assert(set.insert(7) == 1);
assert(set.insert(8) == 1);
assert(8 in set);
assert(5 in set);
assert(9 in set);
assert(7 in set);
assert(set.data.array.length == 7);
assert(set.insert(16) == 1);
assert(16 in set);
assert(set.data.array.length == 7);
}
// Static checks.
@nogc nothrow pure @safe unittest
{
import tanya.range.primitive;
static assert(isBidirectionalRange!(Set!int.ConstRange));
static assert(isBidirectionalRange!(Set!int.Range));
static assert(!isInfinite!(Set!int.Range));
static assert(!hasLength!(Set!int.Range));
static assert(is(Set!uint));
static assert(is(Set!long));
static assert(is(Set!ulong));
static assert(is(Set!short));
static assert(is(Set!ushort));
static assert(is(Set!bool));
}
@nogc nothrow pure @safe unittest
{
const Set!int set;
assert(set[].empty);
}
@nogc nothrow pure @safe unittest
{
Set!int set;
set.insert(8);
auto r1 = set[];
auto r2 = r1.save();
r1.popFront();
assert(r1.empty);
r2.popBack();
assert(r2.empty);
}
// Initial capacity is 0.
@nogc nothrow pure @safe unittest
{
auto set = Set!int(defaultAllocator);
assert(set.capacity == 0);
}
// Capacity is set to a prime.
@nogc nothrow pure @safe unittest
{
auto set = Set!int(8);
assert(set.capacity == 13);
}
// Constructs by reference
@nogc nothrow pure @safe unittest
{
auto set1 = Set!int(7);
auto set2 = Set!int(set1);
assert(set1.length == set2.length);
assert(set1.capacity == set2.capacity);
}
// Constructs by value
@nogc nothrow pure @safe unittest
{
auto set = Set!int(Set!int(7));
assert(set.capacity == 7);
}
// Assigns by reference
@nogc nothrow pure @safe unittest
{
auto set1 = Set!int(7);
Set!int set2;
set1 = set2;
assert(set1.length == set2.length);
assert(set1.capacity == set2.capacity);
}
// Assigns by value
@nogc nothrow pure @safe unittest
{
Set!int set;
set = Set!int(7);
assert(set.capacity == 7);
}
// Postblit copies
@nogc nothrow pure @safe unittest
{
auto set = Set!int(7);
void testFunc(Set!int set)
{
assert(set.capacity == 7);
}
testFunc(set);
}
// Hasher can take argument by ref
@nogc nothrow pure @safe unittest
{
static assert(is(Set!(int, (const ref x) => cast(size_t) x)));
}
// Can have non-copyable elements
@nogc nothrow pure @safe unittest
{
@NonCopyable @Hashable
static struct S
{
mixin StructStub;
}
static assert(is(Set!S));
}

View File

@ -35,11 +35,6 @@ import tanya.meta.transform;
import tanya.range.array; import tanya.range.array;
import tanya.range.primitive; import tanya.range.primitive;
version (unittest)
{
import tanya.test.assertion;
}
/** /**
* Thrown on encoding errors. * Thrown on encoding errors.
*/ */
@ -74,22 +69,15 @@ if (is(Unqual!E == char))
private alias ContainerType = CopyConstness!(E, String); private alias ContainerType = CopyConstness!(E, String);
private ContainerType* container; private ContainerType* container;
invariant invariant (this.begin <= this.end);
{ invariant (this.container !is null);
assert(this.begin <= this.end); invariant (this.begin >= this.container.data);
assert(this.container !is null); invariant (this.end <= this.container.data + this.container.length);
assert(this.begin >= this.container.data);
assert(this.end <= this.container.data + this.container.length);
}
private this(ref ContainerType container, E* begin, E* end) @trusted private this(ref ContainerType container, E* begin, E* end) @trusted
in in (begin <= end)
{ in (begin >= container.data)
assert(begin <= end); in (end <= container.data + container.length)
assert(begin >= container.data);
assert(end <= container.data + container.length);
}
do
{ {
this.container = &container; this.container = &container;
this.begin = begin; this.begin = begin;
@ -116,51 +104,31 @@ if (is(Unqual!E == char))
alias opDollar = length; alias opDollar = length;
@property ref inout(E) front() inout @property ref inout(E) front() inout
in in (!empty)
{
assert(!empty);
}
do
{ {
return *this.begin; return *this.begin;
} }
@property ref inout(E) back() inout @trusted @property ref inout(E) back() inout @trusted
in in (!empty)
{
assert(!empty);
}
do
{ {
return *(this.end - 1); return *(this.end - 1);
} }
void popFront() @trusted void popFront() @trusted
in in (!empty)
{
assert(!empty);
}
do
{ {
++this.begin; ++this.begin;
} }
void popBack() @trusted void popBack() @trusted
in in (!empty)
{
assert(!empty);
}
do
{ {
--this.end; --this.end;
} }
ref inout(E) opIndex(const size_t i) inout @trusted ref inout(E) opIndex(const size_t i) inout @trusted
in in (i < length)
{
assert(i < length);
}
do
{ {
return *(this.begin + i); return *(this.begin + i);
} }
@ -176,23 +144,15 @@ if (is(Unqual!E == char))
} }
ByCodeUnit opSlice(const size_t i, const size_t j) @trusted ByCodeUnit opSlice(const size_t i, const size_t j) @trusted
in in (i <= j)
{ in (j <= length)
assert(i <= j);
assert(j <= length);
}
do
{ {
return typeof(return)(*this.container, this.begin + i, this.begin + j); return typeof(return)(*this.container, this.begin + i, this.begin + j);
} }
ByCodeUnit!(const E) opSlice(const size_t i, const size_t j) const @trusted ByCodeUnit!(const E) opSlice(const size_t i, const size_t j) const @trusted
in in (i <= j)
{ in (j <= length)
assert(i <= j);
assert(j <= length);
}
do
{ {
return typeof(return)(*this.container, this.begin + i, this.begin + j); return typeof(return)(*this.container, this.begin + i, this.begin + j);
} }
@ -216,22 +176,15 @@ if (is(Unqual!E == char))
private alias ContainerType = CopyConstness!(E, String); private alias ContainerType = CopyConstness!(E, String);
private ContainerType* container; private ContainerType* container;
invariant invariant (this.begin <= this.end);
{ invariant (this.container !is null);
assert(this.begin <= this.end); invariant (this.begin >= this.container.data);
assert(this.container !is null); invariant (this.end <= this.container.data + this.container.length);
assert(this.begin >= this.container.data);
assert(this.end <= this.container.data + this.container.length);
}
private this(ref ContainerType container, E* begin, E* end) @trusted private this(ref ContainerType container, E* begin, E* end) @trusted
in in (begin <= end)
{ in (begin >= container.data)
assert(begin <= end); in (end <= container.data + container.length)
assert(begin >= container.data);
assert(end <= container.data + container.length);
}
do
{ {
this.container = &container; this.container = &container;
this.begin = begin; this.begin = begin;
@ -251,15 +204,8 @@ if (is(Unqual!E == char))
} }
@property dchar front() const @trusted @property dchar front() const @trusted
in in (!empty)
{ out (chr; chr < 0xd800 || chr > 0xdfff)
assert(!empty);
}
out (chr)
{
assert(chr < 0xd800 || chr > 0xdfff);
}
do
{ {
dchar chr; dchar chr;
ubyte units; ubyte units;
@ -289,11 +235,7 @@ if (is(Unqual!E == char))
} }
void popFront() @trusted void popFront() @trusted
in in (!empty)
{
assert(!empty);
}
do
{ {
ubyte units; ubyte units;
if ((*begin & 0xf0) == 0xf0) if ((*begin & 0xf0) == 0xf0)
@ -339,10 +281,7 @@ struct String
private char* data; private char* data;
private size_t capacity_; private size_t capacity_;
pure nothrow @safe @nogc invariant @nogc nothrow pure @safe invariant (this.length_ <= this.capacity_);
{
assert(this.length_ <= this.capacity_);
}
/** /**
* Constructs the string from a stringish range. * Constructs the string from a stringish range.
@ -432,11 +371,7 @@ struct String
/// ditto /// ditto
this(shared Allocator allocator) @nogc nothrow pure @safe this(shared Allocator allocator) @nogc nothrow pure @safe
in in (allocator !is null)
{
assert(allocator !is null);
}
do
{ {
this.allocator_ = allocator; this.allocator_ = allocator;
} }
@ -497,12 +432,6 @@ struct String
} }
} }
@nogc nothrow pure @safe unittest
{
auto s = String(0, 'K');
assert(s.length == 0);
}
this(this) @nogc nothrow pure @trusted this(this) @nogc nothrow pure @trusted
{ {
auto buf = this.data[0 .. this.length_]; auto buf = this.data[0 .. this.length_];
@ -521,12 +450,8 @@ struct String
private void write4Bytes(ref const dchar src) private void write4Bytes(ref const dchar src)
@nogc nothrow pure @trusted @nogc nothrow pure @trusted
in in (capacity - length >= 4)
{ in (src - 0x10000 < 0x100000)
assert(capacity - length >= 4);
assert(src - 0x10000 < 0x100000);
}
do
{ {
auto dst = this.data + length; auto dst = this.data + length;
@ -540,11 +465,7 @@ struct String
private size_t insertWideChar(C)(auto ref const C chr) @trusted private size_t insertWideChar(C)(auto ref const C chr) @trusted
if (is(C == wchar) || is(C == dchar)) if (is(C == wchar) || is(C == dchar))
in in (capacity - length >= 3)
{
assert(capacity - length >= 3);
}
do
{ {
auto dst = this.data + length; auto dst = this.data + length;
if (chr < 0x80) if (chr < 0x80)
@ -602,13 +523,6 @@ struct String
return ret; return ret;
} }
// Allocates enough space for 3-byte character.
@nogc pure @safe unittest
{
String s;
s.insertBack('\u8100');
}
/// ditto /// ditto
size_t insertBack(const dchar chr) @nogc pure @trusted size_t insertBack(const dchar chr) @nogc pure @trusted
{ {
@ -630,12 +544,6 @@ struct String
} }
} }
@nogc pure @safe unittest
{
assertThrown!UTFException(() => String(1, cast(dchar) 0xd900));
assertThrown!UTFException(() => String(1, cast(wchar) 0xd900));
}
/** /**
* Inserts a stringish range at the end of the string. * Inserts a stringish range at the end of the string.
* *
@ -880,13 +788,9 @@ struct String
const size_t i, const size_t i,
const size_t j) const size_t j)
if (is(Unqual!R == char)) if (is(Unqual!R == char))
in in (i <= j)
{ in (j <= length)
assert(i <= j); in (j - i == value.length)
assert(j <= length);
assert(j - i == value.length);
}
do
{ {
auto target = opSlice(i, j); auto target = opSlice(i, j);
copy(value, target); copy(value, target);
@ -898,12 +802,8 @@ struct String
const size_t i, const size_t i,
const size_t j) const size_t j)
@nogc nothrow pure @trusted @nogc nothrow pure @trusted
in in (i <= j)
{ in (j <= length)
assert(i <= j);
assert(j <= length);
}
do
{ {
copy(value[], this.data[i .. j]); copy(value[], this.data[i .. j]);
return opSlice(i, j); return opSlice(i, j);
@ -914,12 +814,8 @@ struct String
const size_t i, const size_t i,
const size_t j) const size_t j)
@nogc nothrow pure @trusted @nogc nothrow pure @trusted
in in (i <= j)
{ in (j <= length)
assert(i <= j);
assert(j <= length);
}
do
{ {
for (auto p = this.data + i; p < this.data + j; ++p) for (auto p = this.data + i; p < this.data + j; ++p)
{ {
@ -996,11 +892,7 @@ struct String
* Precondition: $(D_INLINECODE length > pos). * Precondition: $(D_INLINECODE length > pos).
*/ */
ref inout(char) opIndex(const size_t pos) inout @nogc nothrow pure @trusted ref inout(char) opIndex(const size_t pos) inout @nogc nothrow pure @trusted
in in (length > pos)
{
assert(length > pos);
}
do
{ {
return *(this.data + pos); return *(this.data + pos);
} }
@ -1145,12 +1037,8 @@ struct String
*/ */
ByCodeUnit!char opSlice(const size_t i, const size_t j) ByCodeUnit!char opSlice(const size_t i, const size_t j)
@nogc nothrow pure @trusted @nogc nothrow pure @trusted
in in (i <= j)
{ in (j <= length)
assert(i <= j);
assert(j <= length);
}
do
{ {
return typeof(return)(this, this.data + i, this.data + j); return typeof(return)(this, this.data + i, this.data + j);
} }
@ -1158,12 +1046,8 @@ struct String
/// ditto /// ditto
ByCodeUnit!(const char) opSlice(const size_t i, const size_t j) ByCodeUnit!(const char) opSlice(const size_t i, const size_t j)
const @nogc nothrow pure @trusted const @nogc nothrow pure @trusted
in in (i <= j)
{ in (j <= length)
assert(i <= j);
assert(j <= length);
}
do
{ {
return typeof(return)(this, this.data + i, this.data + j); return typeof(return)(this, this.data + i, this.data + j);
} }
@ -1418,40 +1302,18 @@ struct String
return opSliceAssign(value, 0, length); return opSliceAssign(value, 0, length);
} }
@nogc nothrow pure @safe unittest
{
auto s1 = String("Buttercup");
auto s2 = String("Cap");
s2[] = s1[6 .. $];
assert(s2 == "cup");
}
/// ditto /// ditto
ByCodeUnit!char opIndexAssign(const char value) @nogc nothrow pure @safe ByCodeUnit!char opIndexAssign(const char value) @nogc nothrow pure @safe
{ {
return opSliceAssign(value, 0, length); return opSliceAssign(value, 0, length);
} }
@nogc nothrow pure @safe unittest
{
auto s1 = String("Wow");
s1[] = 'a';
assert(s1 == "aaa");
}
/// ditto /// ditto
ByCodeUnit!char opIndexAssign(const char[] value) @nogc nothrow pure @safe ByCodeUnit!char opIndexAssign(const char[] value) @nogc nothrow pure @safe
{ {
return opSliceAssign(value, 0, length); return opSliceAssign(value, 0, length);
} }
@nogc nothrow pure @safe unittest
{
auto s1 = String("ö");
s1[] = "oe";
assert(s1 == "oe");
}
/** /**
* Remove all characters beloning to $(D_PARAM r). * Remove all characters beloning to $(D_PARAM r).
* *
@ -1466,13 +1328,9 @@ struct String
*/ */
R remove(R)(R r) @trusted R remove(R)(R r) @trusted
if (is(R == ByCodeUnit!char) || is(R == ByCodePoint!char)) if (is(R == ByCodeUnit!char) || is(R == ByCodePoint!char))
in in (r.container is &this)
{ in (r.begin >= this.data)
assert(r.container is &this); in (r.end <= this.data + length)
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
do
{ {
auto end = this.data + this.length; auto end = this.data + this.length;
copy(ByCodeUnit!char(this, r.end, end), ByCodeUnit!char(this, r.begin, end)); copy(ByCodeUnit!char(this, r.end, end), ByCodeUnit!char(this, r.begin, end));
@ -1521,13 +1379,9 @@ struct String
&& isInputRange!T && isInputRange!T
&& isSomeChar!(ElementType!T))) && isSomeChar!(ElementType!T)))
&& (is(R == ByCodeUnit!char) || is(R == ByCodePoint!char))) && (is(R == ByCodeUnit!char) || is(R == ByCodePoint!char)))
in in (r.container is &this)
{ in (r.begin >= this.data)
assert(r.container is &this); in (r.end <= this.data + length)
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
do
{ {
const oldLength = length; const oldLength = length;
const after = r.end - this.data; const after = r.end - this.data;
@ -1555,13 +1409,9 @@ struct String
&& isInputRange!T && isInputRange!T
&& isSomeChar!(ElementType!T))) && isSomeChar!(ElementType!T)))
&& (is(R == ByCodeUnit!char) || is(R == ByCodePoint!char))) && (is(R == ByCodeUnit!char) || is(R == ByCodePoint!char)))
in in (r.container is &this)
{ in (r.begin >= this.data)
assert(r.container is &this); in (r.end <= this.data + length)
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
do
{ {
return insertAfter(R(this, this.data, r.begin), el); return insertAfter(R(this, this.data, r.begin), el);
} }
@ -1590,76 +1440,3 @@ struct String
mixin DefaultAllocator; mixin DefaultAllocator;
} }
// Postblit works
@nogc nothrow pure @safe unittest
{
void internFunc(String arg)
{
}
void middleFunc(S...)(S args)
{
foreach (arg; args)
{
internFunc(arg);
}
}
void topFunc(String args)
{
middleFunc(args);
}
topFunc(String("asdf"));
}
// Const range produces mutable ranges
@nogc pure @safe unittest
{
auto s = const String("И снизу лед, и сверху - маюсь между.");
{
const constRange = s[];
auto fromConstRange = constRange[];
fromConstRange.popFront();
assert(fromConstRange.front == s[1]);
fromConstRange = constRange[0 .. $];
fromConstRange.popFront();
assert(fromConstRange.front == s[1]);
assert(constRange.get() is s.get());
}
{
const constRange = s.byCodePoint();
auto fromConstRange = constRange[];
fromConstRange.popFront();
assert(fromConstRange.front == ' ');
}
}
// Can pop multibyte characters
@nogc pure @safe unittest
{
auto s = String("\U00024B62\U00002260");
auto range = s.byCodePoint();
range.popFront();
assert(!range.empty);
range.popFront();
assert(range.empty);
range = s.byCodePoint();
range.popFront();
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);
}

View File

@ -0,0 +1,97 @@
/* 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/. */
module tanya.algorithm.tests.comparison;
import tanya.algorithm.comparison;
import tanya.math;
import tanya.range;
@nogc nothrow pure @safe unittest
{
static assert(!is(typeof(min(1, 1UL))));
}
@nogc nothrow pure @safe unittest
{
assert(min(5, 3) == 3);
assert(min(4, 4) == 4);
assert(min(5.2, 3.0) == 3.0);
assert(min(5.2, double.nan) == 5.2);
assert(min(double.nan, 3.0) == 3.0);
assert(isNaN(min(double.nan, double.nan)));
}
@nogc nothrow pure @safe unittest
{
assert(min(cast(ubyte[]) []).empty);
}
@nogc nothrow pure @safe unittest
{
static assert(!is(typeof(max(1, 1UL))));
}
@nogc nothrow pure @safe unittest
{
assert(max(5, 3) == 5);
assert(max(4, 4) == 4);
assert(max(5.2, 3.0) == 5.2);
assert(max(5.2, double.nan) == 5.2);
assert(max(double.nan, 3.0) == 3.0);
assert(isNaN(max(double.nan, double.nan)));
}
@nogc nothrow pure @safe unittest
{
assert(max(cast(ubyte[]) []).empty);
}
// min/max compare const and mutable structs.
@nogc nothrow pure @safe unittest
{
static struct S
{
int s;
int opCmp(typeof(this) that) const @nogc nothrow pure @safe
{
return this.s - that.s;
}
}
{
const s1 = S(1);
assert(min(s1, S(2)).s == 1);
assert(max(s1, S(2)).s == 2);
}
{
auto s2 = S(2), s3 = S(3);
assert(min(s2, s3).s == 2);
assert(max(s2, s3).s == 3);
}
}
@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

@ -0,0 +1,127 @@
/* 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/. */
module tanya.algorithm.tests.iteration;
import tanya.algorithm.iteration;
import tanya.range;
import tanya.test.stub;
// length is unknown when taking from a range without length
@nogc nothrow pure @safe unittest
{
static struct R
{
mixin InputRangeStub;
}
auto actual = take(R(), 100);
static assert(!hasLength!(typeof(actual)));
}
// Takes minimum length if the range length > n
@nogc nothrow pure @safe unittest
{
auto range = take(cast(int[]) null, 8);
assert(range.length == 0);
}
@nogc nothrow pure @safe unittest
{
const int[9] range = [1, 2, 3, 4, 5, 6, 7, 8, 9];
{
auto slice = take(range[], 8)[1 .. 3];
assert(slice.length == 2);
assert(slice.front == 2);
assert(slice.back == 3);
}
{
auto slice = takeExactly(range[], 8)[1 .. 3];
assert(slice.length == 2);
assert(slice.front == 2);
assert(slice.back == 3);
}
}
// 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);
// Check slicing.
auto slice = retro(given[])[1 .. $];
assert(slice.length == 2 && slice.front == 2 && slice.back == 1);
}
// 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);
}
// Singleton range is bidirectional and random-access
@nogc nothrow pure @safe unittest
{
static assert(isBidirectionalRange!(typeof(singleton('a'))));
static assert(isRandomAccessRange!(typeof(singleton('a'))));
assert({ char a; return isBidirectionalRange!(typeof(singleton(a))); });
assert({ char a; return isRandomAccessRange!(typeof(singleton(a))); });
}
@nogc nothrow pure @safe unittest
{
char a = 'a';
auto single = singleton(a);
assert(single.front == 'a');
assert(single.back == 'a');
assert(single[0] == 'a');
assert(single.length == 1);
assert(!single.empty);
}
// popFront makes SingletonByRef empty
@nogc nothrow pure @safe unittest
{
char a = 'a';
auto single = singleton(a);
single.popFront();
assert(single.empty);
assert(single.length == 0);
assert(single.empty);
}
// popBack makes SingletonByRef empty
@nogc nothrow pure @safe unittest
{
char a = 'b';
auto single = singleton(a);
single.popBack();
assert(single.empty);
assert(single.length == 0);
assert(single.empty);
}

View File

@ -0,0 +1,128 @@
/* 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/. */
module tanya.algorithm.tests.mutation;
import tanya.algorithm.mutation;
import tanya.range;
import tanya.test.stub;
// Returns advanced target
@nogc nothrow pure @safe unittest
{
int[5] input = [1, 2, 3, 4, 5];
assert(copy(input[3 .. 5], input[]).front == 3);
}
// Copies overlapping arrays
@nogc nothrow pure @safe unittest
{
import tanya.algorithm.comparison : equal;
int[6] actual = [1, 2, 3, 4, 5, 6];
const int[6] expected = [1, 2, 1, 2, 3, 4];
copy(actual[0 .. 4], actual[2 .. 6]);
assert(equal(actual[], expected[]));
}
@nogc nothrow pure @safe unittest
{
static assert(is(typeof(copy((ubyte[]).init, (ushort[]).init))));
static assert(!is(typeof(copy((ushort[]).init, (ubyte[]).init))));
}
@nogc nothrow pure @safe unittest
{
static struct OutPutRange
{
int value;
void opCall(int value) @nogc nothrow pure @safe
in (this.value == 0)
{
this.value = value;
}
}
int[1] source = [5];
OutPutRange target;
assert(copy(source[], target).value == 5);
}
// [] is called where possible
@nogc nothrow pure @system unittest
{
static struct Slice
{
bool* slicingCalled;
int front() @nogc nothrow pure @safe
{
return 0;
}
void front(int) @nogc nothrow pure @safe
{
}
void popFront() @nogc nothrow pure @safe
{
}
bool empty() @nogc nothrow pure @safe
{
return true;
}
void opIndexAssign(int) @nogc nothrow pure @safe
{
*this.slicingCalled = true;
}
}
bool slicingCalled;
auto range = Slice(&slicingCalled);
fill(range, 0);
assert(slicingCalled);
}
@nogc nothrow pure @safe unittest
{
NonCopyable[] nonCopyable;
initializeAll(nonCopyable);
}
@nogc nothrow pure @safe unittest
{
import tanya.algorithm.comparison : equal;
const int[5] expected = [1, 2, 3, 4, 5];
int[5] actual = [4, 5, 1, 2, 3];
rotate(actual[0 .. 2], actual[2 .. $]);
assert(equal(actual[], expected[]));
}
// Doesn't cause an infinite loop if back is shorter than the front
@nogc nothrow pure @safe unittest
{
import tanya.algorithm.comparison : equal;
const int[5] expected = [1, 2, 3, 4, 5];
int[5] actual = [3, 4, 5, 1, 2];
rotate(actual[0 .. 3], actual[3 .. $]);
assert(equal(actual[], expected[]));
}
// Doesn't call .front on an empty front
@nogc nothrow pure @safe unittest
{
import tanya.algorithm.comparison : equal;
const int[2] expected = [2, 8];
int[2] actual = expected;
rotate(actual[0 .. 0], actual[]);
assert(equal(actual[], expected[]));
}

View File

@ -0,0 +1,17 @@
/* 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/. */
module tanya.algorithm.tests.searching;
import tanya.algorithm.searching;
import tanya.test.stub;
@nogc nothrow pure @safe unittest
{
@Count(3)
static struct Range
{
mixin InputRangeStub!int;
}
assert(count(Range()) == 3);
}

View File

@ -0,0 +1,97 @@
/* 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/. */
module tanya.async.tests.loop;
import core.time;
import tanya.async.loop;
import tanya.async.watcher;
import tanya.memory;
private final class DummyWatcher : Watcher
{
bool invoked;
override void invoke() @nogc
{
this.invoked = true;
}
}
private final class TestLoop : Loop
{
override protected bool reify(SocketWatcher watcher,
EventMask oldEvents,
EventMask events) @nogc
{
return true;
}
override protected void poll() @nogc
{
assert(!this.done);
unloop();
}
override protected @property uint maxEvents()
const pure nothrow @safe @nogc
{
return 64U;
}
@nogc @system unittest
{
auto loop = defaultAllocator.make!TestLoop;
assert(loop.blockTime == 1.dur!"minutes");
loop.blockTime = 2.dur!"minutes";
assert(loop.blockTime == 2.dur!"minutes");
defaultAllocator.dispose(loop);
}
@nogc @system unittest
{
auto loop = defaultAllocator.make!TestLoop;
assert(loop.done);
loop.run();
assert(loop.done);
defaultAllocator.dispose(loop);
}
@nogc @system unittest
{
auto loop = defaultAllocator.make!TestLoop;
auto watcher = defaultAllocator.make!DummyWatcher;
loop.pendings.insertBack(watcher);
assert(!watcher.invoked);
loop.run();
assert(watcher.invoked);
defaultAllocator.dispose(loop);
defaultAllocator.dispose(watcher);
}
}
@nogc @system unittest
{
auto loop = defaultAllocator.make!TestLoop;
assert(loop.maxEvents == 64);
defaultAllocator.dispose(loop);
}
@nogc @system unittest
{
auto oldLoop = defaultLoop;
auto loop = defaultAllocator.make!TestLoop;
defaultLoop = loop;
assert(defaultLoop is loop);
defaultLoop = oldLoop;
defaultAllocator.dispose(loop);
}

View File

@ -0,0 +1,196 @@
/* 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/. */
module tanya.container.tests.array;
import tanya.algorithm.comparison;
import tanya.container.array;
import tanya.memory;
import tanya.test.stub;
// const arrays return usable ranges
@nogc nothrow pure @safe unittest
{
auto v = const Array!int([1, 2, 4]);
auto r1 = v[];
assert(r1.back == 4);
r1.popBack();
assert(r1.back == 2);
r1.popBack();
assert(r1.back == 1);
r1.popBack();
assert(r1.length == 0);
static assert(!is(typeof(r1[0] = 5)));
static assert(!is(typeof(v[0] = 5)));
const r2 = r1[];
static assert(is(typeof(r2[])));
}
@nogc nothrow pure @safe unittest
{
Array!int v1;
const Array!int v2;
auto r1 = v1[];
auto r2 = v1[];
assert(r1.length == 0);
assert(r2.empty);
assert(r1 == r2);
v1.insertBack([1, 2, 4]);
assert(v1[] == v1);
assert(v2[] == v2);
assert(v2[] != v1);
assert(v1[] != v2);
assert(v1[].equal(v1[]));
assert(v2[].equal(v2[]));
assert(!v1[].equal(v2[]));
}
@nogc nothrow pure @safe unittest
{
struct MutableEqualsStruct
{
bool opEquals(typeof(this) that) @nogc nothrow pure @safe
{
return true;
}
}
struct ConstEqualsStruct
{
bool opEquals(const typeof(this) that) const @nogc nothrow pure @safe
{
return true;
}
}
auto v1 = Array!ConstEqualsStruct();
auto v2 = Array!ConstEqualsStruct();
assert(v1 == v2);
assert(v1[] == v2);
assert(v1 == v2[]);
assert(v1[].equal(v2[]));
auto v3 = const Array!ConstEqualsStruct();
auto v4 = const Array!ConstEqualsStruct();
assert(v3 == v4);
assert(v3[] == v4);
assert(v3 == v4[]);
assert(v3[].equal(v4[]));
auto v7 = Array!MutableEqualsStruct(1, MutableEqualsStruct());
auto v8 = Array!MutableEqualsStruct(1, MutableEqualsStruct());
assert(v7 == v8);
assert(v7[] == v8);
assert(v7 == v8[]);
assert(v7[].equal(v8[]));
}
// Destructor can destroy empty arrays
@nogc nothrow pure @safe unittest
{
auto v = Array!WithDtor();
}
@nogc nothrow pure @safe unittest
{
class A
{
}
A a1, a2;
auto v1 = Array!A([a1, a2]);
static assert(is(Array!(A*)));
}
@nogc nothrow pure @safe unittest
{
auto v = Array!int([5, 15, 8]);
{
size_t i;
foreach (e; v)
{
assert(i != 0 || e == 5);
assert(i != 1 || e == 15);
assert(i != 2 || e == 8);
++i;
}
assert(i == 3);
}
{
size_t i = 3;
foreach_reverse (e; v)
{
--i;
assert(i != 2 || e == 8);
assert(i != 1 || e == 15);
assert(i != 0 || e == 5);
}
assert(i == 0);
}
}
// const constructor tests
@nogc nothrow pure @safe unittest
{
auto v1 = const Array!int([1, 2, 3]);
auto v2 = Array!int(v1);
assert(v1.get !is v2.get);
assert(v1 == v2);
auto v3 = const Array!int(Array!int([1, 2, 3]));
assert(v1 == v3);
assert(v3.length == 3);
assert(v3.capacity == 3);
}
@nogc nothrow pure @safe unittest
{
auto v1 = Array!int(defaultAllocator);
}
@nogc nothrow pure @safe unittest
{
Array!int v;
auto r = v[];
assert(r.length == 0);
assert(r.empty);
}
@nogc nothrow pure @safe unittest
{
auto v1 = const Array!int([5, 15, 8]);
Array!int v2;
v2 = v1[0 .. 2];
assert(equal(v1[0 .. 2], v2[]));
}
// Move assignment
@nogc nothrow pure @safe unittest
{
Array!int v1;
v1 = Array!int([5, 15, 8]);
}
// Postblit is safe
@nogc nothrow pure @safe unittest
{
auto array = Array!int(3);
void func(Array!int arg)
{
assert(arg.capacity == 3);
}
func(array);
}
// Can have non-copyable elements
@nogc nothrow pure @safe unittest
{
static assert(is(Array!NonCopyable));
static assert(is(typeof({ Array!NonCopyable.init[0] = NonCopyable(); })));
}

View File

@ -0,0 +1,17 @@
/* 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/. */
module tanya.container.tests.buffer;
import tanya.container.buffer;
@nogc nothrow pure @safe unittest
{
static assert(is(ReadBuffer!int));
}
@nogc nothrow pure @safe unittest
{
static assert(is(typeof(WriteBuffer!int(5))));
}

View File

@ -0,0 +1,17 @@
/* 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/. */
module tanya.container.tests.entry;
import tanya.container.entry;
import tanya.test.stub;
// Can be constructed with non-copyable key/values
@nogc nothrow pure @safe unittest
{
static assert(is(Bucket!NonCopyable));
static assert(is(Bucket!(NonCopyable, NonCopyable)));
static assert(is(HashArray!((ref NonCopyable) => 0U, NonCopyable)));
static assert(is(HashArray!((ref NonCopyable) => 0U, NonCopyable, NonCopyable)));
}

View File

@ -0,0 +1,133 @@
/* 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/. */
module tanya.container.tests.hashtable;
import tanya.container.hashtable;
import tanya.test.stub;
@nogc nothrow pure @safe unittest
{
import tanya.range.primitive : isForwardRange;
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
@nogc nothrow pure @safe unittest
{
auto hashTable1 = HashTable!(string, int)(7);
auto hashTable2 = HashTable!(string, int)(hashTable1);
assert(hashTable1.length == hashTable2.length);
assert(hashTable1.capacity == hashTable2.capacity);
}
// Constructs by value
@nogc nothrow pure @safe unittest
{
auto hashTable = HashTable!(string, int)(HashTable!(string, int)(7));
assert(hashTable.capacity == 7);
}
// Assigns by reference
@nogc nothrow pure @safe unittest
{
auto hashTable1 = HashTable!(string, int)(7);
HashTable!(string, int) hashTable2;
hashTable1 = hashTable2;
assert(hashTable1.length == hashTable2.length);
assert(hashTable1.capacity == hashTable2.capacity);
}
// Assigns by value
@nogc nothrow pure @safe unittest
{
HashTable!(string, int) hashTable;
hashTable = HashTable!(string, int)(7);
assert(hashTable.capacity == 7);
}
// Postblit copies
@nogc nothrow pure @safe unittest
{
auto hashTable = HashTable!(string, int)(7);
void testFunc(HashTable!(string, int) hashTable)
{
assert(hashTable.capacity == 7);
}
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"])));
}
// Can have non-copyable keys and elements
@nogc nothrow pure @safe unittest
{
@NonCopyable @Hashable
static struct S
{
mixin StructStub;
}
static assert(is(HashTable!(S, int)));
static assert(is(HashTable!(int, S)));
static assert(is(HashTable!(S, S)));
}

View File

@ -0,0 +1,120 @@
/* 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/. */
module tanya.container.tests.list;
import tanya.container.list;
import tanya.test.stub;
@nogc nothrow pure @safe unittest
{
interface Stuff
{
}
static assert(is(SList!Stuff));
}
@nogc nothrow pure @safe unittest
{
auto l = SList!int(0, 0);
assert(l.empty);
}
// foreach called using opIndex().
@nogc nothrow pure @safe unittest
{
SList!int l;
size_t i;
l.insertFront(5);
l.insertFront(4);
l.insertFront(9);
foreach (e; l)
{
assert(i != 0 || e == 9);
assert(i != 1 || e == 4);
assert(i != 2 || e == 5);
++i;
}
}
@nogc nothrow pure @safe unittest
{
auto l1 = SList!int();
auto l2 = SList!int([9, 4]);
l1 = l2[];
assert(l1 == l2);
}
@nogc nothrow pure @safe unittest
{
class A
{
}
static assert(is(SList!(A*)));
static assert(is(DList!(A*)));
}
// Removes all elements
@nogc nothrow pure @safe unittest
{
auto l = DList!int([5]);
assert(l.remove(l[]).empty);
}
@nogc nothrow pure @safe unittest
{
auto l1 = DList!int([5, 234, 30, 1]);
auto l2 = DList!int([5, 1]);
auto r = l1[];
r.popFront();
r.popBack();
assert(r.front == 234);
assert(r.back == 30);
assert(!l1.remove(r).empty);
assert(l1 == l2);
}
@nogc nothrow pure @safe unittest
{
auto l = DList!int(0, 0);
assert(l.empty);
}
@nogc nothrow pure @safe unittest
{
DList!int l;
l.insertAfter(l[], 234);
assert(l.front == 234);
assert(l.back == 234);
}
@nogc nothrow pure @safe unittest
{
auto l1 = DList!int();
auto l2 = DList!int([9, 4]);
l1 = l2[];
assert(l1 == l2);
}
// Sets the new head
@nogc nothrow pure @safe unittest
{
auto l1 = DList!int([5, 234, 30, 1]);
auto l2 = DList!int([1]);
auto r = l1[];
r.popBack();
assert(!l1.remove(r).empty);
assert(l1 == l2);
}
// Can have non-copyable elements
@nogc nothrow pure @safe unittest
{
static assert(is(SList!NonCopyable));
static assert(is(DList!NonCopyable));
}

View File

@ -0,0 +1,155 @@
/* 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/. */
module tanya.container.tests.set;
import tanya.container.set;
import tanya.memory;
import tanya.test.stub;
// Basic insertion logic.
@nogc nothrow pure @safe unittest
{
Set!int set;
assert(set.insert(5) == 1);
assert(5 in set);
assert(set.capacity == 3);
assert(set.insert(5) == 0);
assert(5 in set);
assert(set.capacity == 3);
assert(set.insert(9) == 1);
assert(9 in set);
assert(5 in set);
assert(set.capacity == 3);
assert(set.insert(7) == 1);
assert(set.insert(8) == 1);
assert(8 in set);
assert(5 in set);
assert(9 in set);
assert(7 in set);
assert(set.capacity == 7);
assert(set.insert(16) == 1);
assert(16 in set);
assert(set.capacity == 7);
}
// Static checks.
@nogc nothrow pure @safe unittest
{
import tanya.range.primitive;
static assert(isBidirectionalRange!(Set!int.ConstRange));
static assert(isBidirectionalRange!(Set!int.Range));
static assert(!isInfinite!(Set!int.Range));
static assert(!hasLength!(Set!int.Range));
static assert(is(Set!uint));
static assert(is(Set!long));
static assert(is(Set!ulong));
static assert(is(Set!short));
static assert(is(Set!ushort));
static assert(is(Set!bool));
}
@nogc nothrow pure @safe unittest
{
const Set!int set;
assert(set[].empty);
}
@nogc nothrow pure @safe unittest
{
Set!int set;
set.insert(8);
auto r1 = set[];
auto r2 = r1.save();
r1.popFront();
assert(r1.empty);
r2.popBack();
assert(r2.empty);
}
// Initial capacity is 0.
@nogc nothrow pure @safe unittest
{
auto set = Set!int(defaultAllocator);
assert(set.capacity == 0);
}
// Capacity is set to a prime.
@nogc nothrow pure @safe unittest
{
auto set = Set!int(8);
assert(set.capacity == 13);
}
// Constructs by reference
@nogc nothrow pure @safe unittest
{
auto set1 = Set!int(7);
auto set2 = Set!int(set1);
assert(set1.length == set2.length);
assert(set1.capacity == set2.capacity);
}
// Constructs by value
@nogc nothrow pure @safe unittest
{
auto set = Set!int(Set!int(7));
assert(set.capacity == 7);
}
// Assigns by reference
@nogc nothrow pure @safe unittest
{
auto set1 = Set!int(7);
Set!int set2;
set1 = set2;
assert(set1.length == set2.length);
assert(set1.capacity == set2.capacity);
}
// Assigns by value
@nogc nothrow pure @safe unittest
{
Set!int set;
set = Set!int(7);
assert(set.capacity == 7);
}
// Postblit copies
@nogc nothrow pure @safe unittest
{
auto set = Set!int(7);
void testFunc(Set!int set)
{
assert(set.capacity == 7);
}
testFunc(set);
}
// Hasher can take argument by ref
@nogc nothrow pure @safe unittest
{
static assert(is(Set!(int, (const ref x) => cast(size_t) x)));
}
// Can have non-copyable elements
@nogc nothrow pure @safe unittest
{
@NonCopyable @Hashable
static struct S
{
mixin StructStub;
}
static assert(is(Set!S));
}

View File

@ -0,0 +1,121 @@
/* 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/. */
module tanya.container.tests.string;
import tanya.container.string;
import tanya.test.assertion;
@nogc nothrow pure @safe unittest
{
auto s = String(0, 'K');
assert(s.length == 0);
}
// Allocates enough space for 3-byte character.
@nogc pure @safe unittest
{
String s;
s.insertBack('\u8100');
}
@nogc pure @safe unittest
{
assertThrown!UTFException(() => String(1, cast(dchar) 0xd900));
assertThrown!UTFException(() => String(1, cast(wchar) 0xd900));
}
@nogc nothrow pure @safe unittest
{
auto s1 = String("Buttercup");
auto s2 = String("Cap");
s2[] = s1[6 .. $];
assert(s2 == "cup");
}
@nogc nothrow pure @safe unittest
{
auto s1 = String("Wow");
s1[] = 'a';
assert(s1 == "aaa");
}
@nogc nothrow pure @safe unittest
{
auto s1 = String("ö");
s1[] = "oe";
assert(s1 == "oe");
}
// Postblit works
@nogc nothrow pure @safe unittest
{
void internFunc(String arg)
{
}
void middleFunc(S...)(S args)
{
foreach (arg; args)
{
internFunc(arg);
}
}
void topFunc(String args)
{
middleFunc(args);
}
topFunc(String("asdf"));
}
// Const range produces mutable ranges
@nogc pure @safe unittest
{
auto s = const String("И снизу лед, и сверху - маюсь между.");
{
const constRange = s[];
auto fromConstRange = constRange[];
fromConstRange.popFront();
assert(fromConstRange.front == s[1]);
fromConstRange = constRange[0 .. $];
fromConstRange.popFront();
assert(fromConstRange.front == s[1]);
assert(constRange.get() is s.get());
}
{
const constRange = s.byCodePoint();
auto fromConstRange = constRange[];
fromConstRange.popFront();
assert(fromConstRange.front == ' ');
}
}
// Can pop multibyte characters
@nogc pure @safe unittest
{
auto s = String("\U00024B62\U00002260");
auto range = s.byCodePoint();
range.popFront();
assert(!range.empty);
range.popFront();
assert(range.empty);
range = s.byCodePoint();
range.popFront();
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);
}