1 module dkh.container.stack; 2 3 import dkh.container.stackpayload; 4 5 /// Stack 6 struct Stack(T) { 7 import core.exception : RangeError; 8 9 alias Payload = StackPayload!T; 10 private Payload* p; 11 12 import std.traits : isImplicitlyConvertible; 13 import std.range : ElementType, isInputRange, hasLength; 14 /// Stack(1, 2, 3) 15 this(U)(U[] values...) if (isImplicitlyConvertible!(U, T)) { 16 p = new Payload(); 17 p.reserve(values.length); 18 foreach (v; values) this ~= v; 19 } 20 /// Stack(iota(3)) 21 this(Range)(Range r) 22 if (isInputRange!Range && 23 isImplicitlyConvertible!(ElementType!Range, T) && 24 !is(Range == T[])) { 25 p = new Payload(); 26 static if (hasLength!Range) p.reserve(r.length); 27 foreach (v; r) this ~= v; 28 } 29 30 @property bool empty() const { return (!p || p.empty); } /// 31 @property size_t length() const { return (p ? p.length : 0); } /// 32 alias opDollar = length; /// ditto 33 @property inout(T)[] data() inout { return (!p) ? [] : p.data; } /// 34 35 ref inout(T) opIndex(size_t i) inout { 36 assert(!empty, "Stack.opIndex: Stack is empty"); 37 return (*p)[i]; 38 } /// 39 ref inout(T) front() inout { return this[0]; } /// 40 ref inout(T) back() inout { return this[$-1]; } /// 41 42 void clear() { if (p) p.clear(); } /// 43 44 void insertBack(T item) { 45 if (!p) p = new Payload(); 46 p.insertBack(item); 47 } /// 48 alias opOpAssign(string op : "~") = insertBack; /// ditto 49 alias stableInsertBack = insertBack; /// ditto 50 void removeBack() { 51 assert(!empty, "Stack.removeBack: Stack is empty"); 52 p.removeBack(); 53 } /// 54 alias stableRemoveBack = removeBack; /// ditto 55 56 /// Random-access range 57 alias Range = RangeT!(StackPayload!T); 58 alias ConstRange = RangeT!(const StackPayload!T); /// ditto 59 alias ImmutableRange = RangeT!(immutable StackPayload!T); /// ditto 60 61 size_t[2] opSlice(size_t dim : 0)(size_t start, size_t end) const { 62 assert(start <= end && end <= length); 63 return [start, end]; 64 } /// 65 Range opIndex(size_t[2] rng) { return Range(p, rng[0], rng[1]); } /// Get slice 66 ConstRange opIndex(size_t[2] rng) const { return ConstRange(p, rng[0], rng[1]); } /// ditto 67 ImmutableRange opIndex(size_t[2] rng) immutable { return ImmutableRange(p, rng[0], rng[1]); } /// ditto 68 auto opIndex() inout { return this[0..$]; } /// ditto 69 70 static struct RangeT(QualifiedPayload) { 71 alias A = QualifiedPayload; 72 import std.traits : CopyTypeQualifiers; 73 alias E = CopyTypeQualifiers!(A, T); 74 A *p; 75 size_t l, r; 76 77 @property bool empty() const { return r <= l; } 78 @property size_t length() const { return r - l; } 79 alias opDollar = length; 80 81 @property auto save() { return this; } 82 83 ref inout(E) opIndex(size_t i) inout { 84 version(assert) if (empty) throw new RangeError(); 85 return (*p)[l+i]; 86 } 87 @property ref inout(E) front() inout { return this[0]; } 88 @property ref inout(E) back() inout { return this[$-1]; } 89 90 void popFront() { 91 version(assert) if (empty) throw new RangeError(); 92 l++; 93 } 94 void popBack() { 95 version(assert) if (empty) throw new RangeError(); 96 r--; 97 } 98 99 size_t[2] opSlice(size_t dim : 0)(size_t start, size_t end) const { 100 assert(start <= end && end <= length); 101 return [start, end]; 102 } 103 auto opIndex(size_t[2] rng) { return RangeT(p, l+rng[0], l+rng[1]); } 104 auto opIndex(size_t[2] rng) const { return RangeT!(const A)(p, l+rng[0], l+rng[1]); } 105 auto opIndex(size_t[2] rng) immutable { return RangeT!(immutable A)(p, l+rng[0], l+rng[1]); } 106 auto opIndex() inout { return this[0..$]; } 107 } 108 } 109 110 /// 111 unittest { 112 import std.algorithm : equal; 113 import std.range.primitives : isRandomAccessRange; 114 import std.container.util : make; 115 auto q = Stack!int(); 116 117 assert(equal(q[], new int[0])); 118 q.insertBack(1); 119 assert(equal(q[], [1])); 120 q.insertBack(2); 121 assert(equal(q[], [1, 2])); 122 q.insertBack(3); 123 assert(equal(q[], [1, 2, 3])); 124 q.removeBack(); 125 assert(equal(q[], [1, 2])); 126 q.insertBack(4); 127 assert(equal(q[], [1, 2, 4])); 128 q.removeBack(); 129 assert(equal(q[], [1, 2])); 130 } 131 132 unittest { 133 import std.range; 134 auto q1 = Stack!int(1, 2, 3); 135 auto q2 = Stack!int(iota(3)); 136 } 137 138 unittest { 139 import std.algorithm : equal; 140 auto q = Stack!int(1, 2, 3, 4, 5); 141 assert(equal(q[1..4], [2, 3, 4])); 142 assert(q[1..4][1] == 3); 143 const auto rng = q[1..4]; 144 assert(rng.front == 2 && rng.back == 4); 145 assert(equal(rng[0..3], [2, 3, 4])); 146 assert(equal(rng[], [2, 3, 4])); 147 } 148 149 unittest { 150 import std.range : isRandomAccessRange; 151 static assert(isRandomAccessRange!(Stack!int.Range)); 152 static assert(isRandomAccessRange!(Stack!int.ConstRange)); 153 static assert(isRandomAccessRange!(Stack!int.ImmutableRange)); 154 }