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:
- dmd-2.085.0
- dmd-2.084.1
- dmd-2.083.1
- dmd-2.082.1
env:

View File

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

View File

@ -5,7 +5,7 @@
/**
* Algorithms for comparing values.
*
* Copyright: Eugene Wissner 2018.
* Copyright: Eugene Wissner 2018-2019.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -99,22 +99,6 @@ if (Args.length >= 2
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
Range min(Range)(Range range)
if (isForwardRange!Range && isOrderingComparable!(ElementType!Range))
@ -150,11 +134,6 @@ if (isForwardRange!Range && isOrderingComparable!(ElementType!Range))
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.
*
@ -192,22 +171,6 @@ if (Args.length >= 2
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
Range max(Range)(Range range)
if (isForwardRange!Range && isOrderingComparable!(ElementType!Range))
@ -243,35 +206,6 @@ if (isForwardRange!Range && isOrderingComparable!(ElementType!Range))
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.
*
@ -438,26 +372,3 @@ private int compareImpl(alias pred, R1, R2)(ref R1 r1, ref R2 r2)
}
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
* original range on demand.
*
* Copyright: Eugene Wissner 2018.
* Copyright: Eugene Wissner 2018-2019.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -26,7 +26,6 @@ import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range;
import tanya.typecons;
version (unittest) import tanya.test.stub;
private struct Take(R, bool exactly)
{
@ -49,21 +48,13 @@ private struct Take(R, bool exactly)
}
@property auto ref front()
in
{
assert(!empty);
}
do
in (!empty)
{
return this.source.front;
}
void popFront()
in
{
assert(!empty);
}
do
in (!empty)
{
this.source.popFront();
--this.length_;
@ -92,21 +83,13 @@ private struct Take(R, bool exactly)
static if (hasAssignableElements!R)
{
@property void front(ref ElementType!R value)
in
{
assert(!empty);
}
do
in (!empty)
{
this.source.front = value;
}
@property void front(ElementType!R value)
in
{
assert(!empty);
}
do
in (!empty)
{
this.source.front = move(value);
}
@ -122,31 +105,19 @@ private struct Take(R, bool exactly)
static if (isRandomAccessRange!R)
{
@property auto ref back()
in
{
assert(!empty);
}
do
in (!empty)
{
return this.source[this.length - 1];
}
void popBack()
in
{
assert(!empty);
}
do
in (!empty)
{
--this.length_;
}
auto ref opIndex(size_t i)
in
{
assert(i < length);
}
do
in (i < length)
{
return this.source[i];
}
@ -154,41 +125,25 @@ private struct Take(R, bool exactly)
static if (hasAssignableElements!R)
{
@property void back(ref ElementType!R value)
in
{
assert(!empty);
}
do
in (!empty)
{
this.source[length - 1] = value;
}
@property void back(ElementType!R value)
in
{
assert(!empty);
}
do
in (!empty)
{
this.source[length - 1] = move(value);
}
void opIndexAssign(ref ElementType!R value, size_t i)
in
{
assert(i < length);
}
do
in (i < length)
{
this.source[i] = value;
}
void opIndexAssign(ElementType!R value, size_t i)
in
{
assert(i < length);
}
do
in (i < length)
{
this.source[i] = move(value);
}
@ -198,12 +153,8 @@ private struct Take(R, bool exactly)
static if (!exactly && hasSlicing!R)
{
auto opSlice(size_t i, size_t j)
in
{
assert(i <= j);
assert(j <= length);
}
do
in (i <= j)
in (j <= length)
{
return typeof(this)(this.source[i .. j], length);
}
@ -322,18 +273,6 @@ if (isInputRange!R)
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).
*
@ -428,32 +367,6 @@ if (isInputRange!R)
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`.
private struct Retro(Range)
{
@ -522,12 +435,8 @@ private struct Retro(Range)
alias opDollar = length;
auto opSlice(size_t i, size_t j)
in
{
assert(i <= j);
assert(j <= length);
}
do
in (i <= j)
in (j <= length)
{
return typeof(this)(this.source[$-j .. $-i]);
}
@ -615,41 +524,6 @@ if (isBidirectionalRange!Range)
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 Option!E element;
@ -807,49 +681,3 @@ auto singleton(E)(return ref E element)
singleChar.popFront();
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.transform;
import tanya.range;
version (unittest) import tanya.test.stub;
deprecated("Use tanya.memory.lifetime.swap instead")
alias swap = tanya.memory.lifetime.swap;
@ -96,49 +95,6 @@ do
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).
*
@ -176,42 +132,6 @@ if (isInputRange!Range && isAssignable!(ElementType!Range, Value))
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
* $(D_PARAM range) aren't initialized.
@ -294,12 +214,6 @@ if (isInputRange!Range && hasLvalueElements!Range)
assert(equal(actual[], expected[]));
}
@nogc nothrow pure @safe unittest
{
NonCopyable[] nonCopyable;
initializeAll(nonCopyable);
}
/**
* Destroys all elements in the $(D_PARAM range).
*
@ -390,38 +304,3 @@ if (isForwardRange!Range && hasSwappableElements!Range)
rotate(actual[0 .. 2], actual[4 .. 6]);
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.
*
* Copyright: Eugene Wissner 2017-2018.
* Copyright: Eugene Wissner 2017-2019.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -17,3 +17,4 @@ module tanya.algorithm;
public import tanya.algorithm.comparison;
public import tanya.algorithm.iteration;
public import tanya.algorithm.mutation;
public import tanya.algorithm.searching;

View File

@ -5,7 +5,7 @@
/**
* Searching algorithms.
*
* Copyright: Eugene Wissner 2018.
* Copyright: Eugene Wissner 2018-2019.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -51,28 +51,3 @@ if (isInputRange!R)
int[3] array;
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 (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.
@ -160,7 +136,7 @@ alias EventMask = BitFlags!Event;
*/
abstract class Loop
{
private bool done = true;
protected bool done = true;
/// Pending watchers.
protected DList!Watcher pendings;
@ -175,14 +151,6 @@ abstract class Loop
return 128U;
}
@nogc @system unittest
{
auto loop = defaultAllocator.make!TestLoop;
assert(loop.maxEvents == 64);
defaultAllocator.dispose(loop);
}
/**
* Initializes the loop.
*/
@ -228,31 +196,6 @@ abstract class Loop
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.
*
@ -329,17 +272,6 @@ abstract class Loop
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.
*/
@ -418,16 +350,3 @@ do
}
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;
}
version (unittest)
{
final class DummyWatcher : Watcher
{
bool invoked;
override void invoke() @nogc
{
this.invoked = true;
}
}
}
/**
* Socket watcher.
*/

View File

@ -21,7 +21,6 @@ import tanya.memory;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range;
version (unittest) import tanya.test.stub;
/**
* Random-access range for the $(D_PSYMBOL Array).
@ -35,22 +34,15 @@ struct Range(A)
private E* begin, end;
private A* container;
invariant
{
assert(this.begin <= this.end);
assert(this.container !is null);
assert(this.begin >= this.container.data);
assert(this.end <= this.container.data + this.container.length);
}
invariant (this.begin <= this.end);
invariant (this.container !is null);
invariant (this.begin >= this.container.data);
invariant (this.end <= this.container.data + this.container.length);
private this(ref A container, E* begin, E* end) @trusted
in
{
assert(begin <= end);
assert(begin >= container.data);
assert(end <= container.data + container.length);
}
do
in (begin <= end)
in (begin >= container.data)
in (end <= container.data + container.length)
{
this.container = &container;
this.begin = begin;
@ -77,51 +69,31 @@ struct Range(A)
alias opDollar = length;
@property ref inout(E) front() inout
in
{
assert(!empty);
}
do
in (!empty)
{
return *this.begin;
}
@property ref inout(E) back() inout @trusted
in
{
assert(!empty);
}
do
in (!empty)
{
return *(this.end - 1);
}
void popFront() @trusted
in
{
assert(!empty);
}
do
in (!empty)
{
++this.begin;
}
void popBack() @trusted
in
{
assert(!empty);
}
do
in (!empty)
{
--this.end;
}
ref inout(E) opIndex(size_t i) inout @trusted
in
{
assert(i < length);
}
do
in (i < length)
{
return *(this.begin + i);
}
@ -137,23 +109,15 @@ struct Range(A)
}
Range opSlice(size_t i, size_t j) @trusted
in
{
assert(i <= j);
assert(j <= length);
}
do
in (i <= j)
in (j <= length)
{
return typeof(return)(*this.container, this.begin + i, this.begin + j);
}
A.ConstRange opSlice(size_t i, size_t j) const @trusted
in
{
assert(i <= j);
assert(j <= length);
}
do
in (i <= j)
in (j <= length)
{
return typeof(return)(*this.container, this.begin + i, this.begin + j);
}
@ -182,11 +146,8 @@ struct Array(T)
private T* data;
private size_t capacity_;
invariant
{
assert(this.length_ <= this.capacity_);
assert(this.capacity_ == 0 || this.data !is null);
}
invariant (this.length_ <= this.capacity_);
invariant (this.capacity_ == 0 || this.data !is null);
/**
* Creates a new $(D_PSYMBOL Array) with the elements from a static array.
@ -312,11 +273,7 @@ struct Array(T)
/// ditto
this(shared Allocator allocator)
in
{
assert(allocator !is null);
}
do
in (allocator !is null)
{
allocator_ = allocator;
}
@ -567,11 +524,7 @@ struct Array(T)
* Precondition: $(D_INLINECODE !empty).
*/
void removeBack()
in
{
assert(!empty);
}
do
in (!empty)
{
length = length - 1;
}
@ -589,11 +542,7 @@ struct Array(T)
* Returns: The number of elements removed
*/
size_t removeBack(size_t howMany)
out (removed)
{
assert(removed <= howMany);
}
do
out (removed; removed <= howMany)
{
const toRemove = min(howMany, length);
@ -614,11 +563,7 @@ struct Array(T)
}
private inout(T)[] slice(size_t length) inout @trusted
in
{
assert(length <= capacity);
}
do
in (length <= capacity)
{
return this.data[0 .. length];
}
@ -640,13 +585,9 @@ struct Array(T)
* Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this).
*/
Range remove(Range r)
in
{
assert(r.container is &this);
assert(r.begin >= this.data);
assert(r.end <= end);
}
do
in (r.container is &this)
in (r.begin >= this.data)
in (r.end <= end)
{
auto target = r.begin;
auto source = r.end;
@ -801,13 +742,9 @@ struct Array(T)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
in
{
assert(r.container is &this);
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
do
in (r.container is &this)
in (r.begin >= this.data)
in (r.end <= this.data + length)
{
const oldLength = length;
const after = r.end - this.data;
@ -819,13 +756,9 @@ struct Array(T)
/// ditto
size_t insertAfter(size_t R)(Range r, T[R] el)
in
{
assert(r.container is &this);
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
do
in (r.container is &this)
in (r.begin >= this.data)
in (r.end <= this.data + length)
{
return insertAfter!(T[])(r, el[]);
}
@ -833,13 +766,9 @@ struct Array(T)
/// ditto
size_t insertAfter(R)(Range r, auto ref R el)
if (isImplicitlyConvertible!(R, T))
in
{
assert(r.container is &this);
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
do
in (r.container is &this)
in (r.begin >= this.data)
in (r.end <= this.data + length)
{
const oldLen = length;
const offset = r.end - this.data;
@ -862,26 +791,18 @@ struct Array(T)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
in
{
assert(r.container is &this);
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
do
in (r.container is &this)
in (r.begin >= this.data)
in (r.end <= this.data + length)
{
return insertAfter(Range(this, this.data, r.begin), el);
}
/// ditto
size_t insertBefore(size_t R)(Range r, T[R] el)
in
{
assert(r.container is &this);
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
do
in (r.container is &this)
in (r.begin >= this.data)
in (r.end <= this.data + length)
{
return insertBefore!(T[])(r, el[]);
}
@ -889,13 +810,9 @@ struct Array(T)
/// ditto
size_t insertBefore(R)(Range r, auto ref R el)
if (isImplicitlyConvertible!(R, T))
in
{
assert(r.container is &this);
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
do
in (r.container is &this)
in (r.begin >= this.data)
in (r.end <= this.data + length)
{
const oldLen = length;
const offset = r.begin - this.data;
@ -1063,11 +980,7 @@ struct Array(T)
* Precondition: $(D_INLINECODE length > pos).
*/
ref inout(T) opIndex(size_t pos) inout @trusted
in
{
assert(length > pos);
}
do
in (length > pos)
{
return *(this.data + pos);
}
@ -1168,11 +1081,7 @@ struct Array(T)
* Precondition: $(D_INLINECODE !empty).
*/
@property ref inout(T) front() inout
in
{
assert(!empty);
}
do
in (!empty)
{
return *this.data;
}
@ -1195,11 +1104,7 @@ struct Array(T)
* Precondition: $(D_INLINECODE !empty).
*/
@property ref inout(T) back() inout @trusted
in
{
assert(!empty);
}
do
in (!empty)
{
return *(this.data + length - 1);
}
@ -1227,24 +1132,16 @@ struct Array(T)
* Precondition: $(D_INLINECODE i <= j && j <= length).
*/
Range opSlice(size_t i, size_t j) @trusted
in
{
assert(i <= j);
assert(j <= length);
}
do
in (i <= j)
in (j <= length)
{
return typeof(return)(this, this.data + i, this.data + j);
}
/// ditto
ConstRange opSlice(size_t i, size_t j) const @trusted
in
{
assert(i <= j);
assert(j <= length);
}
do
in (i <= j)
in (j <= length)
{
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)
@trusted
in
{
assert(i <= j);
assert(j <= length);
}
do
in (i <= j)
in (j <= length)
{
copy(value[], this.data[i .. j]);
return opSlice(i, j);
@ -1315,12 +1208,8 @@ struct Array(T)
/// ditto
Range opSliceAssign(R : T)(auto ref R value, size_t i, size_t j)
@trusted
in
{
assert(i <= j);
assert(j <= length);
}
do
in (i <= j)
in (j <= length)
{
fill(this.data[i .. j], value);
return opSlice(i, j);
@ -1328,13 +1217,9 @@ struct Array(T)
/// ditto
Range opSliceAssign()(Range value, size_t i, size_t j) @trusted
in
{
assert(i <= j);
assert(j <= length);
assert(j - i == value.length);
}
do
in (i <= j)
in (j <= length)
in (j - i == value.length)
{
copy(value, this.data[i .. j]);
return opSlice(i, j);
@ -1491,198 +1376,3 @@ struct Array(T)
assert(r.front == 7);
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,
int start = 0,
int end = 10) @nogc pure nothrow
in
{
assert(start < end);
}
do
in (start < end)
{
auto numberRead = end - start;
for (ubyte i; i < numberRead; ++i)
@ -71,12 +67,9 @@ if (isScalarType!T)
/// Size by which the buffer will grow.
private size_t blockSize = 8192;
invariant
{
assert(this.length_ <= this.buffer_.length);
assert(this.blockSize > 0);
assert(this.minAvailable > 0);
}
invariant (this.length_ <= this.buffer_.length);
invariant (this.blockSize > 0);
invariant (this.minAvailable > 0);
/**
* Creates a new read buffer.
@ -101,11 +94,7 @@ if (isScalarType!T)
/// ditto
this(shared Allocator allocator)
in
{
assert(allocator_ is null);
}
do
in (allocator_ is null)
{
allocator_ = allocator;
}
@ -293,11 +282,6 @@ if (isScalarType!T)
mixin DefaultAllocator;
}
@nogc nothrow pure @safe unittest
{
static assert(is(ReadBuffer!int));
}
/**
* Circular, self-expanding buffer with overflow support. Can be used with
* functions returning the number of the transferred bytes.
@ -328,12 +312,9 @@ if (isScalarType!T)
/// The position of the free area in the buffer.
private size_t position;
invariant
{
assert(this.blockSize > 0);
invariant (this.blockSize > 0);
// Position can refer to an element outside the buffer if the buffer is full.
assert(this.position <= this.buffer_.length);
}
invariant (this.position <= this.buffer_.length);
/**
* Params:
@ -344,12 +325,8 @@ if (isScalarType!T)
* Precondition: $(D_INLINECODE size > 0 && allocator !is null)
*/
this(size_t size, shared Allocator allocator = defaultAllocator) @trusted
in
{
assert(size > 0);
assert(allocator !is null);
}
do
in (size > 0)
in (allocator !is null)
{
this.blockSize = size;
ring = size - 1;
@ -510,11 +487,7 @@ if (isScalarType!T)
*/
ref WriteBuffer opOpAssign(string op)(size_t length)
if (op == "+")
in
{
assert(length <= this.length);
}
do
in (length <= this.length)
{
auto afterRing = ring + 1;
auto oldStart = start;
@ -649,11 +622,6 @@ if (isScalarType!T)
mixin DefaultAllocator;
}
@nogc nothrow pure @safe unittest
{
static assert(is(typeof(WriteBuffer!int(5))));
}
@nogc nothrow pure @system unittest
{
auto b = WriteBuffer!ubyte(4);

View File

@ -20,7 +20,6 @@ import tanya.memory.lifetime;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.typecons;
version (unittest) import tanya.test.stub;
package struct SEntry(T)
{
@ -129,22 +128,14 @@ package struct HashArray(alias hasher, K, V = void)
size_t length;
this(shared Allocator allocator)
in
{
assert(allocator !is null);
}
do
in (allocator !is null)
{
this.array = Buckets(allocator);
}
this(T)(ref T data, shared Allocator allocator)
if (is(Unqual!T == HashArray))
in
{
assert(allocator !is null);
}
do
in (allocator !is null)
{
this.array = Buckets(data.array, allocator);
this.lengthIndex = data.lengthIndex;
@ -153,11 +144,7 @@ package struct HashArray(alias hasher, K, V = void)
// Move constructor
void move(ref HashArray data, shared Allocator allocator)
in
{
assert(allocator !is null);
}
do
in (allocator !is null)
{
this.array = Buckets(.move(data.array), allocator);
this.lengthIndex = data.lengthIndex;
@ -238,11 +225,7 @@ package struct HashArray(alias hasher, K, V = void)
// Takes an index in the primes array.
void rehashToSize(const size_t n)
in
{
assert(n < primes.length);
}
do
in (n < primes.length)
{
auto storage = typeof(this.array)(primes[n], this.array.allocator);
DataLoop: foreach (ref e1; this.array[])
@ -327,13 +310,3 @@ package struct HashArray(alias hasher, K, V = void)
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.transform;
import tanya.range.primitive;
version (unittest) import tanya.test.stub;
/**
* Bidirectional range whose element type is a tuple of a key and the
@ -70,16 +69,9 @@ struct Range(T)
}
void popFront()
in
{
assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
out
{
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
in (!empty)
in (this.dataRange.front.status == BucketStatus.used)
out (; empty || this.dataRange.back.status == BucketStatus.used)
{
do
{
@ -89,16 +81,9 @@ struct Range(T)
}
void popBack()
in
{
assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
out
{
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
in (!empty)
in (this.dataRange.back.status == BucketStatus.used)
out (; empty || this.dataRange.back.status == BucketStatus.used)
{
do
{
@ -108,23 +93,15 @@ struct Range(T)
}
@property ref inout(KV) front() inout
in
{
assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
do
in (!empty)
in (this.dataRange.front.status == BucketStatus.used)
{
return this.dataRange.front.kv;
}
@property ref inout(KV) back() inout
in
{
assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
do
in (!empty)
in (this.dataRange.back.status == BucketStatus.used)
{
return this.dataRange.back.kv;
}
@ -185,16 +162,9 @@ struct ByKey(T)
}
@property void popFront()
in
{
assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
out
{
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
in (!empty)
in (this.dataRange.front.status == BucketStatus.used)
out (; empty || this.dataRange.back.status == BucketStatus.used)
{
do
{
@ -204,16 +174,9 @@ struct ByKey(T)
}
@property void popBack()
in
{
assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
out
{
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
in (!empty)
in (this.dataRange.back.status == BucketStatus.used)
out (; empty || this.dataRange.back.status == BucketStatus.used)
{
do
{
@ -223,23 +186,15 @@ struct ByKey(T)
}
@property ref inout(Key) front() inout
in
{
assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
do
in (!empty)
in (this.dataRange.front.status == BucketStatus.used)
{
return this.dataRange.front.key;
}
@property ref inout(Key) back() inout
in
{
assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
do
in (!empty)
in (this.dataRange.back.status == BucketStatus.used)
{
return this.dataRange.back.key;
}
@ -300,16 +255,9 @@ struct ByValue(T)
}
@property void popFront()
in
{
assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
out
{
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
in (!empty)
in (this.dataRange.front.status == BucketStatus.used)
out (; empty || this.dataRange.back.status == BucketStatus.used)
{
do
{
@ -319,16 +267,9 @@ struct ByValue(T)
}
@property void popBack()
in
{
assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
out
{
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
in (!empty)
in (this.dataRange.back.status == BucketStatus.used)
out (; empty || this.dataRange.back.status == BucketStatus.used)
{
do
{
@ -338,23 +279,15 @@ struct ByValue(T)
}
@property ref inout(Value) front() inout
in
{
assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
do
in (!empty)
in (this.dataRange.front.status == BucketStatus.used)
{
return this.dataRange.front.kv.value;
}
@property ref inout(Value) back() inout
in
{
assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
do
in (!empty)
in (this.dataRange.back.status == BucketStatus.used)
{
return this.dataRange.back.kv.value;
}
@ -412,10 +345,7 @@ if (isHashFunction!(hasher, Key))
/// ditto
alias ConstByValue = .ByValue!(const HashArray);
invariant
{
assert(this.data.lengthIndex < primes.length);
}
invariant (this.data.lengthIndex < primes.length);
/**
* Constructor.
@ -427,11 +357,7 @@ if (isHashFunction!(hasher, Key))
* Precondition: $(D_INLINECODE allocator !is null).
*/
this(size_t n, shared Allocator allocator = defaultAllocator)
in
{
assert(allocator !is null);
}
do
in (allocator !is null)
{
this(allocator);
this.data.rehash(n);
@ -446,11 +372,7 @@ if (isHashFunction!(hasher, Key))
/// ditto
this(shared Allocator allocator)
in
{
assert(allocator !is null);
}
do
in (allocator !is null)
{
this.data = HashArray(allocator);
}
@ -470,11 +392,7 @@ if (isHashFunction!(hasher, Key))
*/
this(S)(ref S init, shared Allocator allocator = defaultAllocator)
if (is(Unqual!S == HashTable))
in
{
assert(allocator !is null);
}
do
in (allocator !is null)
{
this.data = HashArray(init.data, allocator);
}
@ -482,11 +400,7 @@ if (isHashFunction!(hasher, Key))
/// ditto
this(S)(S init, shared Allocator allocator = defaultAllocator)
if (is(S == HashTable))
in
{
assert(allocator !is null);
}
do
in (allocator !is null)
{
this.data.move(init.data, allocator);
}
@ -503,11 +417,7 @@ if (isHashFunction!(hasher, Key))
*/
this(R)(R range, shared Allocator allocator = defaultAllocator)
if (isForwardRange!R && is(ElementType!R == KeyValue))
in
{
assert(allocator !is null);
}
do
in (allocator !is null)
{
this(allocator);
insert(range);
@ -537,11 +447,7 @@ if (isHashFunction!(hasher, Key))
*/
this(size_t n)(KeyValue[n] array,
shared Allocator allocator = defaultAllocator)
in
{
assert(allocator !is null);
}
do
in (allocator !is null)
{
insert(array[]);
}
@ -589,11 +495,7 @@ if (isHashFunction!(hasher, Key))
* Postcondition: $(D_INLINECODE allocator !is null)
*/
@property shared(Allocator) allocator() const
out (allocator)
{
assert(allocator !is null);
}
do
out (allocator; allocator !is null)
{
return this.data.array.allocator;
}
@ -1085,129 +987,3 @@ if (isHashFunction!(hasher, Key))
dinos.clear();
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.range.array;
import tanya.range.primitive;
version (unittest) import tanya.test.stub;
/**
* Forward range for the $(D_PSYMBOL SList).
@ -37,10 +36,7 @@ struct SRange(L)
private EntryPointer* head;
invariant
{
assert(this.head !is null);
}
invariant (this.head !is null);
private this(ref EntryPointer head) @trusted
{
@ -60,21 +56,13 @@ struct SRange(L)
}
@property ref inout(E) front() inout
in
{
assert(!empty);
}
do
in (!empty)
{
return (*this.head).content;
}
void popFront() @trusted
in
{
assert(!empty);
}
do
in (!empty)
{
this.head = &(*this.head).next;
}
@ -206,11 +194,7 @@ struct SList(T)
/// ditto
this(shared Allocator allocator)
in
{
assert(allocator !is null);
}
do
in (allocator !is null)
{
this.allocator_ = allocator;
}
@ -332,11 +316,7 @@ struct SList(T)
* Precondition: $(D_INLINECODE !empty).
*/
@property ref inout(T) front() inout
in
{
assert(!empty);
}
do
in (!empty)
{
return this.head.content;
}
@ -477,11 +457,7 @@ struct SList(T)
*/
size_t insertBefore(R)(Range r, R el)
if (isImplicitlyConvertible!(R, T))
in
{
assert(checkRangeBelonging(r));
}
do
in (checkRangeBelonging(r))
{
return moveEntry(*r.head, el);
}
@ -500,11 +476,7 @@ struct SList(T)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
in
{
assert(checkRangeBelonging(r));
}
do
in (checkRangeBelonging(r))
{
size_t inserted;
foreach (e; el)
@ -529,11 +501,7 @@ struct SList(T)
/// ditto
size_t insertBefore()(Range r, ref T el) @trusted
in
{
assert(checkRangeBelonging(r));
}
do
in (checkRangeBelonging(r))
{
*r.head = allocator.make!Entry(el, *r.head);
return 1;
@ -629,11 +597,7 @@ struct SList(T)
* Precondition: $(D_INLINECODE !empty)
*/
void removeFront()
in
{
assert(!empty);
}
do
in (!empty)
{
auto n = this.head.next;
@ -668,11 +632,7 @@ struct SList(T)
* Returns: The number of elements removed.
*/
size_t removeFront(size_t howMany)
out (removed)
{
assert(removed <= howMany);
}
do
out (removed; removed <= howMany)
{
size_t i;
for (; i < howMany && !empty; ++i)
@ -704,11 +664,7 @@ struct SList(T)
* Precondition: $(D_PARAM r) is extracted from this list.
*/
Range remove(Range r)
in
{
assert(checkRangeBelonging(r));
}
do
in (checkRangeBelonging(r))
{
auto outOfScopeList = typeof(this)(allocator);
outOfScopeList.head = *r.head;
@ -742,12 +698,8 @@ struct SList(T)
* $(D_PARAM range) is extracted from this list.
*/
Range popFirstOf(Range range)
in
{
assert(!range.empty);
assert(checkRangeBelonging(range));
}
do
in (!range.empty)
in (checkRangeBelonging(range))
{
auto next = (*range.head).next;
@ -923,46 +875,6 @@ struct SList(T)
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).
*
@ -977,11 +889,8 @@ struct DRange(L)
private EntryPointer* head;
private EntryPointer* tail;
invariant
{
assert(this.head !is null);
assert(this.tail !is null);
}
invariant (this.head !is null);
invariant (this.tail !is null);
private this(ref EntryPointer head, ref EntryPointer tail) @trusted
{
@ -1002,41 +911,25 @@ struct DRange(L)
}
@property ref inout(E) front() inout
in
{
assert(!empty);
}
do
in (!empty)
{
return (*this.head).content;
}
@property ref inout(E) back() inout
in
{
assert(!empty);
}
do
in (!empty)
{
return (*this.tail).content;
}
void popFront() @trusted
in
{
assert(!empty);
}
do
in (!empty)
{
this.head = &(*this.head).next;
}
void popBack() @trusted
in
{
assert(!empty);
}
do
in (!empty)
{
this.tail = &(*this.tail).prev;
}
@ -1082,13 +975,10 @@ struct DList(T)
// 0th and the last elements of the list.
private Entry* head, tail;
invariant
{
assert((this.tail is null && this.head is null)
invariant ((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);
assert(this.head is null || this.head.prev is null);
}
invariant (this.tail is null || this.tail.next 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.
@ -1192,11 +1082,7 @@ struct DList(T)
/// ditto
this(shared Allocator allocator)
in
{
assert(allocator !is null);
}
do
in (allocator !is null)
{
this.allocator_ = allocator;
}
@ -1322,11 +1208,7 @@ struct DList(T)
* Precondition: $(D_INLINECODE !empty).
*/
@property ref inout(T) front() inout
in
{
assert(!empty);
}
do
in (!empty)
{
return this.head.content;
}
@ -1337,11 +1219,7 @@ struct DList(T)
* Precondition: $(D_INLINECODE !empty).
*/
@property ref inout(T) back() inout
in
{
assert(!empty);
}
do
in (!empty)
{
return this.tail.content;
}
@ -1656,11 +1534,7 @@ struct DList(T)
*/
size_t insertBefore(R)(Range r, R el)
if (isImplicitlyConvertible!(R, T))
in
{
assert(checkRangeBelonging(r));
}
do
in (checkRangeBelonging(r))
{
return moveFront(*r.head, el);
}
@ -1676,11 +1550,7 @@ struct DList(T)
/// ditto
size_t insertBefore()(Range r, ref T el) @trusted
in
{
assert(checkRangeBelonging(r));
}
do
in (checkRangeBelonging(r))
{
auto temp = allocator.make!Entry(el, *r.head);
@ -1714,11 +1584,7 @@ struct DList(T)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
in
{
assert(checkRangeBelonging(r));
}
do
in (checkRangeBelonging(r))
{
size_t inserted;
foreach (e; el)
@ -1771,11 +1637,7 @@ struct DList(T)
*/
size_t insertAfter(R)(Range r, R el) @trusted
if (isImplicitlyConvertible!(R, T))
in
{
assert(checkRangeBelonging(r));
}
do
in (checkRangeBelonging(r))
{
return moveBack(*r.tail, el);
}
@ -1793,11 +1655,7 @@ struct DList(T)
/// ditto
size_t insertAfter()(Range r, ref T el) @trusted
in
{
assert(checkRangeBelonging(r));
}
do
in (checkRangeBelonging(r))
{
auto temp = allocator.make!Entry(el, null, *r.tail);
@ -1831,11 +1689,7 @@ struct DList(T)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
in
{
assert(checkRangeBelonging(r));
}
do
in (checkRangeBelonging(r))
{
size_t inserted;
foreach (e; el)
@ -1926,11 +1780,7 @@ struct DList(T)
* Precondition: $(D_INLINECODE !empty)
*/
void removeFront()
in
{
assert(!empty);
}
do
in (!empty)
{
auto n = this.head.next;
@ -1962,11 +1812,7 @@ struct DList(T)
/// ditto
void removeBack()
in
{
assert(!empty);
}
do
in (!empty)
{
auto n = this.tail.prev;
@ -2008,11 +1854,7 @@ struct DList(T)
* Returns: The number of elements removed.
*/
size_t removeFront(size_t howMany)
out (removed)
{
assert(removed <= howMany);
}
do
out (removed; removed <= howMany)
{
size_t i;
for (; i < howMany && !empty; ++i)
@ -2035,11 +1877,7 @@ struct DList(T)
/// ditto
size_t removeBack(size_t howMany)
out (removed)
{
assert(removed <= howMany);
}
do
out (removed; removed <= howMany)
{
size_t i;
for (; i < howMany && !empty; ++i)
@ -2071,11 +1909,7 @@ struct DList(T)
* Precondition: $(D_PARAM r) is extracted from this list.
*/
Range remove(Range r)
in
{
assert(checkRangeBelonging(r));
}
do
in (checkRangeBelonging(r))
{
// Save references to the elements before and after the range.
Entry* headPrev;
@ -2147,11 +1981,7 @@ struct DList(T)
* $(D_PARAM range) is extracted from this list.
*/
Range popFirstOf(Range range)
in
{
assert(!range.empty);
}
do
in (!range.empty)
{
remove(Range(*range.head, *range.head));
return range;
@ -2159,11 +1989,7 @@ struct DList(T)
/// ditto
Range popLastOf(Range range)
in
{
assert(!range.empty);
}
do
in (!range.empty)
{
remove(Range(*range.tail, *range.tail));
return range;
@ -2317,82 +2143,3 @@ struct DList(T)
}
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.transform;
import tanya.range.primitive;
version (unittest) import tanya.test.stub;
/**
* Bidirectional range that iterates over the $(D_PSYMBOL Set)'s values.
@ -69,16 +68,9 @@ struct Range(T)
}
void popFront()
in
{
assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
out
{
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
in (!empty)
in (this.dataRange.front.status == BucketStatus.used)
out (; empty || this.dataRange.back.status == BucketStatus.used)
{
do
{
@ -88,16 +80,9 @@ struct Range(T)
}
void popBack()
in
{
assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
out
{
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
in (!empty)
in (this.dataRange.back.status == BucketStatus.used)
out (; empty || this.dataRange.back.status == BucketStatus.used)
{
do
{
@ -107,23 +92,15 @@ struct Range(T)
}
@property ref inout(E) front() inout
in
{
assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
do
in (!empty)
in (this.dataRange.front.status == BucketStatus.used)
{
return this.dataRange.front.key;
}
@property ref inout(E) back() inout
in
{
assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
do
in (!empty)
in (this.dataRange.back.status == BucketStatus.used)
{
return this.dataRange.back.key;
}
@ -168,12 +145,9 @@ if (isHashFunction!(hasher, T))
/// ditto
alias ConstRange = .Range!(const HashArray);
invariant
{
assert(this.data.lengthIndex < primes.length);
assert(this.data.array.length == 0
invariant (this.data.lengthIndex < primes.length);
invariant (this.data.array.length == 0
|| this.data.array.length == primes[this.data.lengthIndex]);
}
/**
* Constructor.
@ -185,11 +159,7 @@ if (isHashFunction!(hasher, T))
* Precondition: $(D_INLINECODE allocator !is null).
*/
this(size_t n, shared Allocator allocator = defaultAllocator)
in
{
assert(allocator !is null);
}
do
in (allocator !is null)
{
this(allocator);
this.data.rehash(n);
@ -204,11 +174,7 @@ if (isHashFunction!(hasher, T))
/// ditto
this(shared Allocator allocator)
in
{
assert(allocator !is null);
}
do
in (allocator !is null)
{
this.data = HashArray(allocator);
}
@ -228,11 +194,7 @@ if (isHashFunction!(hasher, T))
*/
this(S)(ref S init, shared Allocator allocator = defaultAllocator)
if (is(Unqual!S == Set))
in
{
assert(allocator !is null);
}
do
in (allocator !is null)
{
this.data = HashArray(init.data, allocator);
}
@ -240,11 +202,7 @@ if (isHashFunction!(hasher, T))
/// ditto
this(S)(S init, shared Allocator allocator = defaultAllocator)
if (is(S == Set))
in
{
assert(allocator !is null);
}
do
in (allocator !is null)
{
this.data.move(init.data, allocator);
}
@ -261,11 +219,7 @@ if (isHashFunction!(hasher, T))
*/
this(R)(R range, shared Allocator allocator = defaultAllocator)
if (isForwardRange!R && isImplicitlyConvertible!(ElementType!R, T))
in
{
assert(allocator !is null);
}
do
in (allocator !is null)
{
insert(range);
}
@ -291,11 +245,7 @@ if (isHashFunction!(hasher, T))
* Precondition: $(D_INLINECODE allocator !is null).
*/
this(size_t n)(T[n] array, shared Allocator allocator = defaultAllocator)
in
{
assert(allocator !is null);
}
do
in (allocator !is null)
{
insert(array[]);
}
@ -342,11 +292,7 @@ if (isHashFunction!(hasher, T))
* Postcondition: $(D_INLINECODE allocator !is null)
*/
@property shared(Allocator) allocator() const
out (allocator)
{
assert(allocator !is null);
}
do
out (allocator; allocator !is null)
{
return this.data.array.allocator;
}
@ -638,150 +584,3 @@ if (isHashFunction!(hasher, T))
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.primitive;
version (unittest)
{
import tanya.test.assertion;
}
/**
* Thrown on encoding errors.
*/
@ -74,22 +69,15 @@ if (is(Unqual!E == char))
private alias ContainerType = CopyConstness!(E, String);
private ContainerType* container;
invariant
{
assert(this.begin <= this.end);
assert(this.container !is null);
assert(this.begin >= this.container.data);
assert(this.end <= this.container.data + this.container.length);
}
invariant (this.begin <= this.end);
invariant (this.container !is null);
invariant (this.begin >= this.container.data);
invariant (this.end <= this.container.data + this.container.length);
private this(ref ContainerType container, E* begin, E* end) @trusted
in
{
assert(begin <= end);
assert(begin >= container.data);
assert(end <= container.data + container.length);
}
do
in (begin <= end)
in (begin >= container.data)
in (end <= container.data + container.length)
{
this.container = &container;
this.begin = begin;
@ -116,51 +104,31 @@ if (is(Unqual!E == char))
alias opDollar = length;
@property ref inout(E) front() inout
in
{
assert(!empty);
}
do
in (!empty)
{
return *this.begin;
}
@property ref inout(E) back() inout @trusted
in
{
assert(!empty);
}
do
in (!empty)
{
return *(this.end - 1);
}
void popFront() @trusted
in
{
assert(!empty);
}
do
in (!empty)
{
++this.begin;
}
void popBack() @trusted
in
{
assert(!empty);
}
do
in (!empty)
{
--this.end;
}
ref inout(E) opIndex(const size_t i) inout @trusted
in
{
assert(i < length);
}
do
in (i < length)
{
return *(this.begin + i);
}
@ -176,23 +144,15 @@ if (is(Unqual!E == char))
}
ByCodeUnit opSlice(const size_t i, const size_t j) @trusted
in
{
assert(i <= j);
assert(j <= length);
}
do
in (i <= j)
in (j <= length)
{
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
in
{
assert(i <= j);
assert(j <= length);
}
do
in (i <= j)
in (j <= length)
{
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 ContainerType* container;
invariant
{
assert(this.begin <= this.end);
assert(this.container !is null);
assert(this.begin >= this.container.data);
assert(this.end <= this.container.data + this.container.length);
}
invariant (this.begin <= this.end);
invariant (this.container !is null);
invariant (this.begin >= this.container.data);
invariant (this.end <= this.container.data + this.container.length);
private this(ref ContainerType container, E* begin, E* end) @trusted
in
{
assert(begin <= end);
assert(begin >= container.data);
assert(end <= container.data + container.length);
}
do
in (begin <= end)
in (begin >= container.data)
in (end <= container.data + container.length)
{
this.container = &container;
this.begin = begin;
@ -251,15 +204,8 @@ if (is(Unqual!E == char))
}
@property dchar front() const @trusted
in
{
assert(!empty);
}
out (chr)
{
assert(chr < 0xd800 || chr > 0xdfff);
}
do
in (!empty)
out (chr; chr < 0xd800 || chr > 0xdfff)
{
dchar chr;
ubyte units;
@ -289,11 +235,7 @@ if (is(Unqual!E == char))
}
void popFront() @trusted
in
{
assert(!empty);
}
do
in (!empty)
{
ubyte units;
if ((*begin & 0xf0) == 0xf0)
@ -339,10 +281,7 @@ struct String
private char* data;
private size_t capacity_;
pure nothrow @safe @nogc invariant
{
assert(this.length_ <= this.capacity_);
}
@nogc nothrow pure @safe invariant (this.length_ <= this.capacity_);
/**
* Constructs the string from a stringish range.
@ -432,11 +371,7 @@ struct String
/// ditto
this(shared Allocator allocator) @nogc nothrow pure @safe
in
{
assert(allocator !is null);
}
do
in (allocator !is null)
{
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
{
auto buf = this.data[0 .. this.length_];
@ -521,12 +450,8 @@ struct String
private void write4Bytes(ref const dchar src)
@nogc nothrow pure @trusted
in
{
assert(capacity - length >= 4);
assert(src - 0x10000 < 0x100000);
}
do
in (capacity - length >= 4)
in (src - 0x10000 < 0x100000)
{
auto dst = this.data + length;
@ -540,11 +465,7 @@ struct String
private size_t insertWideChar(C)(auto ref const C chr) @trusted
if (is(C == wchar) || is(C == dchar))
in
{
assert(capacity - length >= 3);
}
do
in (capacity - length >= 3)
{
auto dst = this.data + length;
if (chr < 0x80)
@ -602,13 +523,6 @@ struct String
return ret;
}
// Allocates enough space for 3-byte character.
@nogc pure @safe unittest
{
String s;
s.insertBack('\u8100');
}
/// ditto
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.
*
@ -880,13 +788,9 @@ struct String
const size_t i,
const size_t j)
if (is(Unqual!R == char))
in
{
assert(i <= j);
assert(j <= length);
assert(j - i == value.length);
}
do
in (i <= j)
in (j <= length)
in (j - i == value.length)
{
auto target = opSlice(i, j);
copy(value, target);
@ -898,12 +802,8 @@ struct String
const size_t i,
const size_t j)
@nogc nothrow pure @trusted
in
{
assert(i <= j);
assert(j <= length);
}
do
in (i <= j)
in (j <= length)
{
copy(value[], this.data[i .. j]);
return opSlice(i, j);
@ -914,12 +814,8 @@ struct String
const size_t i,
const size_t j)
@nogc nothrow pure @trusted
in
{
assert(i <= j);
assert(j <= length);
}
do
in (i <= j)
in (j <= length)
{
for (auto p = this.data + i; p < this.data + j; ++p)
{
@ -996,11 +892,7 @@ struct String
* Precondition: $(D_INLINECODE length > pos).
*/
ref inout(char) opIndex(const size_t pos) inout @nogc nothrow pure @trusted
in
{
assert(length > pos);
}
do
in (length > pos)
{
return *(this.data + pos);
}
@ -1145,12 +1037,8 @@ struct String
*/
ByCodeUnit!char opSlice(const size_t i, const size_t j)
@nogc nothrow pure @trusted
in
{
assert(i <= j);
assert(j <= length);
}
do
in (i <= j)
in (j <= length)
{
return typeof(return)(this, this.data + i, this.data + j);
}
@ -1158,12 +1046,8 @@ struct String
/// ditto
ByCodeUnit!(const char) opSlice(const size_t i, const size_t j)
const @nogc nothrow pure @trusted
in
{
assert(i <= j);
assert(j <= length);
}
do
in (i <= j)
in (j <= length)
{
return typeof(return)(this, this.data + i, this.data + j);
}
@ -1418,40 +1302,18 @@ struct String
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
ByCodeUnit!char opIndexAssign(const char value) @nogc nothrow pure @safe
{
return opSliceAssign(value, 0, length);
}
@nogc nothrow pure @safe unittest
{
auto s1 = String("Wow");
s1[] = 'a';
assert(s1 == "aaa");
}
/// ditto
ByCodeUnit!char opIndexAssign(const char[] value) @nogc nothrow pure @safe
{
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).
*
@ -1466,13 +1328,9 @@ struct String
*/
R remove(R)(R r) @trusted
if (is(R == ByCodeUnit!char) || is(R == ByCodePoint!char))
in
{
assert(r.container is &this);
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
do
in (r.container is &this)
in (r.begin >= this.data)
in (r.end <= this.data + length)
{
auto end = this.data + this.length;
copy(ByCodeUnit!char(this, r.end, end), ByCodeUnit!char(this, r.begin, end));
@ -1521,13 +1379,9 @@ struct String
&& isInputRange!T
&& isSomeChar!(ElementType!T)))
&& (is(R == ByCodeUnit!char) || is(R == ByCodePoint!char)))
in
{
assert(r.container is &this);
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
do
in (r.container is &this)
in (r.begin >= this.data)
in (r.end <= this.data + length)
{
const oldLength = length;
const after = r.end - this.data;
@ -1555,13 +1409,9 @@ struct String
&& isInputRange!T
&& isSomeChar!(ElementType!T)))
&& (is(R == ByCodeUnit!char) || is(R == ByCodePoint!char)))
in
{
assert(r.container is &this);
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
do
in (r.container is &this)
in (r.begin >= this.data)
in (r.end <= this.data + length)
{
return insertAfter(R(this, this.data, r.begin), el);
}
@ -1590,76 +1440,3 @@ struct String
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);
}