summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/tanya/container/array.d45
-rw-r--r--source/tanya/container/entry.d17
-rw-r--r--source/tanya/container/hashtable.d22
-rw-r--r--source/tanya/container/list.d90
-rw-r--r--source/tanya/container/set.d20
-rw-r--r--source/tanya/hash/lookup.d15
-rw-r--r--source/tanya/test/stub.d71
-rw-r--r--source/tanya/typecons.d24
8 files changed, 215 insertions, 89 deletions
diff --git a/source/tanya/container/array.d b/source/tanya/container/array.d
index c2e35e2..0c5027e 100644
--- a/source/tanya/container/array.d
+++ b/source/tanya/container/array.d
@@ -22,6 +22,7 @@ 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).
@@ -293,7 +294,9 @@ struct Array(T)
* init = Initial value to fill the array with.
* allocator = Allocator.
*/
- this(size_t len, T init, shared Allocator allocator = defaultAllocator)
+ this()(size_t len,
+ auto ref T init,
+ shared Allocator allocator = defaultAllocator)
{
this(allocator);
reserve(len);
@@ -348,15 +351,19 @@ struct Array(T)
(() @trusted => allocator.deallocate(slice(capacity)))();
}
- /**
- * Copies the array.
- */
- this(this)
+ static if (isCopyable!T)
{
- auto buf = slice(this.length);
- this.length_ = capacity_ = 0;
- this.data = null;
- insertBack(buf);
+ this(this)
+ {
+ auto buf = slice(this.length);
+ this.length_ = capacity_ = 0;
+ this.data = null;
+ insertBack(buf);
+ }
+ }
+ else
+ {
+ @disable this(this);
}
/**
@@ -1027,7 +1034,7 @@ struct Array(T)
}
/// ditto
- Range opIndexAssign(Range value)
+ Range opIndexAssign()(Range value)
{
return opSliceAssign(value, 0, length);
}
@@ -1321,7 +1328,7 @@ struct Array(T)
}
/// ditto
- Range opSliceAssign(Range value, size_t i, size_t j) @trusted
+ Range opSliceAssign()(Range value, size_t i, size_t j) @trusted
in
{
assert(i <= j);
@@ -1575,15 +1582,10 @@ struct Array(T)
assert(v7[].equal(v8[]));
}
+// Destructor can destroy empty arrays
@nogc nothrow pure @safe unittest
{
- static struct SWithDtor
- {
- ~this() @nogc nothrow pure @safe
- {
- }
- }
- auto v = Array!SWithDtor(); // Destructor can destroy empty arrays.
+ auto v = Array!WithDtor();
}
@nogc nothrow pure @safe unittest
@@ -1594,7 +1596,6 @@ struct Array(T)
A a1, a2;
auto v1 = Array!A([a1, a2]);
- // Issue 232: https://issues.caraus.io/issues/232.
static assert(is(Array!(A*)));
}
@@ -1679,3 +1680,9 @@ struct Array(T)
}
func(array);
}
+
+// Can have non-copyable elements
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(Array!NonCopyable));
+}
diff --git a/source/tanya/container/entry.d b/source/tanya/container/entry.d
index 214ba00..e976cc3 100644
--- a/source/tanya/container/entry.d
+++ b/source/tanya/container/entry.d
@@ -20,6 +20,7 @@ import tanya.memory.allocator;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.typecons;
+version (unittest) import tanya.test.stub;
package struct SEntry(T)
{
@@ -59,12 +60,12 @@ package struct Bucket(K, V = void)
}
BucketStatus status = BucketStatus.empty;
- this(ref K key)
+ this()(ref K key)
{
this.key = key;
}
- @property void key(ref K key)
+ @property void key()(ref K key)
{
this.key() = key;
this.status = BucketStatus.used;
@@ -170,7 +171,7 @@ package struct HashArray(alias hasher, K, V = void)
.swap(this.length, data.length);
}
- void opAssign(ref typeof(this) that)
+ void opAssign()(ref typeof(this) that)
{
this.array = that.array;
this.lengthIndex = that.lengthIndex;
@@ -326,3 +327,13 @@ 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)));
+}
diff --git a/source/tanya/container/hashtable.d b/source/tanya/container/hashtable.d
index 771e0fa..b80d220 100644
--- a/source/tanya/container/hashtable.d
+++ b/source/tanya/container/hashtable.d
@@ -22,6 +22,7 @@ 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
@@ -68,7 +69,7 @@ struct Range(T)
return this.dataRange.empty();
}
- @property void popFront()
+ void popFront()
in
{
assert(!empty);
@@ -87,7 +88,7 @@ struct Range(T)
while (!empty && dataRange.front.status != BucketStatus.used);
}
- @property void popBack()
+ void popBack()
in
{
assert(!empty);
@@ -759,7 +760,7 @@ if (isHashFunction!(hasher, Key))
*
* Returns: The number of the inserted elements with a unique key.
*/
- size_t insert(ref KeyValue keyValue)
+ size_t insert()(ref KeyValue keyValue)
{
auto e = ((ref v) @trusted => &this.data.insert(v))(keyValue.key);
size_t inserted;
@@ -773,7 +774,7 @@ if (isHashFunction!(hasher, Key))
}
/// ditto
- size_t insert(KeyValue keyValue)
+ size_t insert()(KeyValue keyValue)
{
auto e = ((ref v) @trusted => &this.data.insert(v))(keyValue.key);
size_t inserted;
@@ -1197,3 +1198,16 @@ if (isHashFunction!(hasher, Key))
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)));
+}
diff --git a/source/tanya/container/list.d b/source/tanya/container/list.d
index 38bc680..39edf06 100644
--- a/source/tanya/container/list.d
+++ b/source/tanya/container/list.d
@@ -23,6 +23,7 @@ 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).
@@ -155,8 +156,9 @@ struct SList(T)
* init = Initial value to fill the list with.
* allocator = Allocator.
*/
- this(size_t len, T init, shared Allocator allocator = defaultAllocator)
- @trusted
+ this()(size_t len,
+ auto ref T init,
+ shared Allocator allocator = defaultAllocator)
{
this(allocator);
if (len == 0)
@@ -182,7 +184,18 @@ struct SList(T)
/// ditto
this(size_t len, shared Allocator allocator = defaultAllocator)
{
- this(len, T.init, allocator);
+ this(allocator);
+ if (len == 0)
+ {
+ return;
+ }
+
+ Entry* next = this.head = allocator.make!Entry();
+ foreach (i; 1 .. len)
+ {
+ next.next = allocator.make!Entry();
+ next = next.next;
+ }
}
///
@@ -271,14 +284,18 @@ struct SList(T)
clear();
}
- /**
- * Copies the list.
- */
- this(this)
+ static if (isCopyable!T)
{
- auto list = typeof(this)(this[], this.allocator);
- this.head = list.head;
- list.head = null;
+ this(this)
+ {
+ auto list = typeof(this)(this[], this.allocator);
+ this.head = list.head;
+ list.head = null;
+ }
+ }
+ else
+ {
+ @disable this(this);
}
///
@@ -512,7 +529,7 @@ struct SList(T)
}
/// ditto
- size_t insertBefore(Range r, ref T el) @trusted
+ size_t insertBefore()(Range r, ref T el) @trusted
in
{
assert(checkRangeBelonging(r));
@@ -1120,8 +1137,9 @@ struct DList(T)
* init = Initial value to fill the list with.
* allocator = Allocator.
*/
- this(size_t len, T init, shared Allocator allocator = defaultAllocator)
- @trusted
+ this()(size_t len,
+ auto ref T init,
+ shared Allocator allocator = defaultAllocator)
{
this(allocator);
if (len == 0)
@@ -1150,7 +1168,20 @@ struct DList(T)
/// ditto
this(size_t len, shared Allocator allocator = defaultAllocator)
{
- this(len, T.init, allocator);
+ this(allocator);
+ if (len == 0)
+ {
+ return;
+ }
+
+ Entry* next = this.head = allocator.make!Entry();
+ foreach (i; 1 .. len)
+ {
+ next.next = allocator.make!Entry();
+ next.next.prev = next;
+ next = next.next;
+ }
+ this.tail = next;
}
///
@@ -1242,15 +1273,19 @@ struct DList(T)
clear();
}
- /**
- * Copies the list.
- */
- this(this)
+ static if (isCopyable!T)
+ {
+ this(this)
+ {
+ auto list = typeof(this)(this[], this.allocator);
+ this.head = list.head;
+ this.tail = list.tail;
+ list.head = list .tail = null;
+ }
+ }
+ else
{
- auto list = typeof(this)(this[], this.allocator);
- this.head = list.head;
- this.tail = list.tail;
- list.head = list .tail = null;
+ @disable this(this);
}
///
@@ -1641,7 +1676,7 @@ struct DList(T)
}
/// ditto
- size_t insertBefore(Range r, ref T el) @trusted
+ size_t insertBefore()(Range r, ref T el) @trusted
in
{
assert(checkRangeBelonging(r));
@@ -1758,7 +1793,7 @@ struct DList(T)
}
/// ditto
- size_t insertAfter(Range r, ref T el) @trusted
+ size_t insertAfter()(Range r, ref T el) @trusted
in
{
assert(checkRangeBelonging(r));
@@ -2355,3 +2390,10 @@ struct DList(T)
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));
+}
diff --git a/source/tanya/container/set.d b/source/tanya/container/set.d
index 0c8a3f3..b95950b 100644
--- a/source/tanya/container/set.d
+++ b/source/tanya/container/set.d
@@ -22,6 +22,7 @@ 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.
@@ -67,7 +68,7 @@ struct Range(T)
return this.dataRange.empty();
}
- @property void popFront()
+ void popFront()
in
{
assert(!empty);
@@ -86,7 +87,7 @@ struct Range(T)
while (!empty && dataRange.front.status != BucketStatus.used);
}
- @property void popBack()
+ void popBack()
in
{
assert(!empty);
@@ -459,7 +460,7 @@ if (isHashFunction!(hasher, T))
*
* Returns: Amount of new elements inserted.
*/
- size_t insert(ref T value)
+ size_t insert()(ref T value)
{
auto e = ((ref v) @trusted => &this.data.insert(v))(value);
if (e.status != BucketStatus.used)
@@ -470,7 +471,7 @@ if (isHashFunction!(hasher, T))
return 0;
}
- size_t insert(T value)
+ size_t insert()(T value)
{
auto e = ((ref v) @trusted => &this.data.insert(v))(value);
if (e.status != BucketStatus.used)
@@ -773,3 +774,14 @@ if (isHashFunction!(hasher, T))
{
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));
+}
diff --git a/source/tanya/hash/lookup.d b/source/tanya/hash/lookup.d
index a509aae..b64545c 100644
--- a/source/tanya/hash/lookup.d
+++ b/source/tanya/hash/lookup.d
@@ -16,6 +16,7 @@ module tanya.hash.lookup;
import tanya.meta.trait;
import tanya.range.primitive;
+version (unittest) import tanya.test.stub;
private struct FNV
{
@@ -146,14 +147,6 @@ version (unittest)
~ r10!x ~ r10!x ~ r10!x ~ r10!x ~ r10!x;
enum string r500(string x) = r100!x ~ r100!x ~ r100!x ~ r100!x ~ r100!x;
- private static struct ToHash
- {
- size_t toHash() const @nogc nothrow pure @safe
- {
- return 0;
- }
- }
-
private static struct HashRange
{
string fo = "fo";
@@ -178,9 +171,9 @@ version (unittest)
{
bool empty_;
- @property ToHash front() const @nogc nothrow pure @safe
+ @property Hashable front() const @nogc nothrow pure @safe
{
- return ToHash();
+ return Hashable();
}
void popFront() @nogc nothrow pure @safe
@@ -199,7 +192,7 @@ version (unittest)
@nogc nothrow pure @safe unittest
{
assert(hash(null) == 0);
- assert(hash(ToHash()) == 0U);
+ assert(hash(Hashable()) == 0U);
assert(hash('a') == 'a');
}
diff --git a/source/tanya/test/stub.d b/source/tanya/test/stub.d
index 234e409..e1f8dcb 100644
--- a/source/tanya/test/stub.d
+++ b/source/tanya/test/stub.d
@@ -91,7 +91,7 @@ struct WithLvalueElements
mixin template InputRangeStub(E = int)
{
import tanya.meta.metafunction : Alias;
- import tanya.meta.trait : getUDAs, hasUDA;
+ import tanya.meta.trait : evalUDA, getUDAs, hasUDA;
/*
* Aliases for the attribute lookups to access them faster
@@ -279,6 +279,9 @@ mixin template RandomAccessRangeStub(E = int)
/**
* Struct with a disabled postblit constructor.
+ *
+ * $(D_PSYMBOL NonCopyable) can be used as an attribute for
+ * $(D_PSYMBOL StructStub) or as a standalone type.
*/
struct NonCopyable
{
@@ -288,10 +291,14 @@ struct NonCopyable
/**
* Struct with an elaborate destructor.
*
- * The constructor of $(D_PSYMBOL WithDtor) accepts an additional `counter`
- * argument, which is incremented by the destructor. $(D_PSYMBOL WithDtor)
- * stores a pointer to the passed variable, so the variable can be
- * investigated after the struct isn't available anymore.
+ * $(D_PSYMBOL WithDtor) can be used as an attribute for
+ * $(D_PSYMBOL StructStub) or as a standalone type.
+ *
+ * When used as a standalone object the constructor of $(D_PSYMBOL WithDtor)
+ * accepts an additional `counter` argument, which is incremented by the
+ * destructor. $(D_PSYMBOL WithDtor) stores a pointer to the passed variable,
+ * so the variable can be investigated after the struct isn't available
+ * anymore.
*/
struct WithDtor
{
@@ -310,3 +317,57 @@ struct WithDtor
}
}
}
+
+/**
+ * Struct supporting hashing.
+ *
+ * $(D_PSYMBOL Hashable) can be used as an attribute for
+ * $(D_PSYMBOL StructStub) or as a standalone type.
+ *
+ * The constructor accepts an additional parameter, which is returned by the
+ * `toHash()`-function. `0U` is returned if no hash value is given.
+ */
+struct Hashable
+{
+ size_t hash;
+
+ size_t toHash() const @nogc nothrow pure @safe
+ {
+ return this.hash;
+ }
+}
+
+/**
+ * Generates a $(D_KEYWORD struct) with common functionality.
+ *
+ * To specify the needed functionality use user-defined attributes on the
+ * $(D_KEYWORD struct) $(D_PSYMBOL StructStub) is mixed in.
+ *
+ * Supported attributes are: $(D_PSYMBOL NonCopyable), $(D_PSYMBOL Hashable),
+ * $(D_PSYMBOL WithDtor).
+ */
+mixin template StructStub()
+{
+ import tanya.meta.trait : evalUDA, getUDAs, hasUDA;
+
+ static if (hasUDA!(typeof(this), NonCopyable))
+ {
+ @disable this(this);
+ }
+
+ private alias Hashable = getUDAs!(typeof(this), .Hashable);
+ static if (Hashable.length > 0)
+ {
+ size_t toHash() const @nogc nothrow pure @safe
+ {
+ return evalUDA!(Hashable[0]).hash;
+ }
+ }
+
+ static if (hasUDA!(typeof(this), WithDtor))
+ {
+ ~this() @nogc nothrow pure @safe
+ {
+ }
+ }
+}
diff --git a/source/tanya/typecons.d b/source/tanya/typecons.d
index becb1e3..5904a7a 100644
--- a/source/tanya/typecons.d
+++ b/source/tanya/typecons.d
@@ -503,30 +503,16 @@ struct Option(T)
// Returns default value
@nogc nothrow pure @safe unittest
{
- {
- int i = 5;
- assert(((ref e) => e)(Option!int().or(i)) == 5);
- }
+ int i = 5;
+ assert(((ref e) => e)(Option!int().or(i)) == 5);
}
// Implements toHash() for nothing
@nogc nothrow pure @safe unittest
{
- static struct ToHash
- {
- size_t toHash() const @nogc nothrow pure @safe
- {
- return 1U;
- }
- }
- {
- Option!ToHash toHash;
- assert(toHash.toHash() == 0U);
- }
- {
- auto toHash = Option!ToHash(ToHash());
- assert(toHash.toHash() == 1U);
- }
+ alias OptionT = Option!Hashable;
+ assert(OptionT().toHash() == 0U);
+ assert(OptionT(Hashable(1U)).toHash() == 1U);
}
/**