diff --git a/source/tanya/container/entry.d b/source/tanya/container/entry.d index e3b6ea8..0df388f 100644 --- a/source/tanya/container/entry.d +++ b/source/tanya/container/entry.d @@ -12,6 +12,7 @@ */ module tanya.container.entry; +import std.traits; import tanya.typecons; package struct SEntry(T) @@ -63,14 +64,17 @@ package struct Bucket(T) this.status = BucketStatus.used; } - @property ref T content() + @property ref inout(T) content() inout { return this.content_; } void remove() { - this.content = T.init; + static if (hasElaborateDestructor!T) + { + destroy(this.content); + } this.status = BucketStatus.deleted; } diff --git a/source/tanya/container/set.d b/source/tanya/container/set.d index f26e679..f459770 100644 --- a/source/tanya/container/set.d +++ b/source/tanya/container/set.d @@ -27,15 +27,112 @@ import tanya.memory; */ struct Range(E) { - private alias ContainerType = CopyConstness!(E, Array!(Bucket!(Unqual!E))); - private ContainerType* container; + static if (isMutable!E) + { + private alias DataRange = Array!(Bucket!(Unqual!E)).Range; + } + else + { + private alias DataRange = Array!(Bucket!(Unqual!E)).ConstRange; + } + private DataRange dataRange; @disable this(); + private this(DataRange dataRange) + { + while (!dataRange.empty && dataRange.front.status != BucketStatus.used) + { + dataRange.popFront(); + } + while (!dataRange.empty && dataRange.back.status != BucketStatus.used) + { + dataRange.popBack(); + } + this.dataRange = dataRange; + } + @property Range save() { return this; } + + @property bool empty() const + { + return this.dataRange.empty(); + } + + @property void popFront() + in + { + assert(!this.dataRange.empty); + assert(this.dataRange.front.status == BucketStatus.used); + } + out + { + assert(this.dataRange.empty + || this.dataRange.back.status == BucketStatus.used); + } + body + { + do + { + dataRange.popFront(); + } + while (!dataRange.empty && dataRange.front.status != BucketStatus.used); + } + + @property void popBack() + in + { + assert(!this.dataRange.empty); + assert(this.dataRange.back.status == BucketStatus.used); + } + out + { + assert(this.dataRange.empty + || this.dataRange.back.status == BucketStatus.used); + } + body + { + do + { + dataRange.popBack(); + } + while (!dataRange.empty && dataRange.back.status != BucketStatus.used); + } + + @property ref inout(E) front() inout + in + { + assert(!this.dataRange.empty); + assert(this.dataRange.front.status == BucketStatus.used); + } + body + { + return dataRange.front.content; + } + + @property ref inout(E) back() inout + in + { + assert(!this.dataRange.empty); + assert(this.dataRange.back.status == BucketStatus.used); + } + body + { + return dataRange.back.content; + } + + Range opIndex() + { + return typeof(return)(this.dataRange[]); + } + + Range!(const E) opIndex() const + { + return typeof(return)(this.dataRange[]); + } } /** @@ -95,6 +192,24 @@ struct Set(T) return this.data.length; } + /** + * Iterates over the $(D_PSYMBOL Set) and counts the elements. + * + * Returns: Count of elements within the $(D_PSYMBOL Set). + */ + @property size_t length() const + { + size_t count; + foreach (ref e; this.data[]) + { + if (e.status == BucketStatus.used) + { + ++count; + } + } + return count; + } + private static const size_t[41] primes = [ 3, 7, 13, 23, 29, 37, 53, 71, 97, 131, 163, 193, 239, 293, 389, 521, 769, 919, 1103, 1327, 1543, 2333, 3079, 4861, 6151, 12289, 24593, @@ -229,6 +344,21 @@ struct Set(T) private DataType data; private size_t lengthIndex; + /** + * Returns: A bidirectional range that iterates over the $(D_PSYMBOL Set)'s + * elements. + */ + Range opIndex() + { + return typeof(return)(data[]); + } + + /// Ditto. + ConstRange opIndex() const + { + return typeof(return)(data[]); + } + mixin DefaultAllocator; } @@ -269,3 +399,15 @@ private unittest assert(set.data[3].content == 16); assert(set.data[4].status == BucketStatus.empty); } + +// Static checks. +private unittest +{ + import std.range.primitives; + + static assert(isBidirectionalRange!(Set!int.ConstRange)); + static assert(isBidirectionalRange!(Set!int.Range)); + + static assert(!isInfinite!(Set!int.Range)); + static assert(!hasLength!(Set!int.Range)); +}