1 module dkh.container.deque; 2 3 struct DequePayload(T) { 4 import core.exception : RangeError; 5 6 private T* _data; 7 private uint start, len, cap; 8 9 @property bool empty() const { return len == 0; } 10 @property size_t length() const { return len; } 11 alias opDollar = length; 12 13 ref inout(T) opIndex(size_t i) inout { 14 version(assert) if (len <= i) throw new RangeError(); 15 if (start + i < cap) return _data[start + i]; 16 else return _data[start + i - cap]; 17 } 18 ref inout(T) front() inout { return this[0]; } 19 ref inout(T) back() inout { return this[$-1]; } 20 21 void reserve(size_t newCap) { 22 import core.memory : GC; 23 import std.algorithm : max; 24 import std.conv : to; 25 if (newCap <= cap) return; 26 T* newData = cast(T*)GC.malloc(newCap * T.sizeof); 27 foreach (i; 0..length) { 28 newData[i] = this[i]; 29 } 30 _data = newData; start = 0; cap = newCap.to!uint; 31 } 32 void clear() { 33 start = len = 0; 34 } 35 import std.algorithm : max; 36 void insertFront(T item) { 37 if (len == cap) reserve(max(cap * 2, 4)); 38 if (start == 0) start += cap; 39 start--; len++; 40 this[0] = item; 41 } 42 void insertBack(T item) { 43 if (len == cap) reserve(max(cap * 2, 4)); 44 len++; 45 this[len-1] = item; 46 } 47 void removeFront() { 48 assert(!empty, "Deque.removeFront: Deque is empty"); 49 start++; len--; 50 if (start == cap) start = 0; 51 } 52 void removeBack() { 53 assert(!empty, "Deque.removeBack: Deque is empty"); 54 len--; 55 } 56 } 57 58 /** 59 Deque on ring buffer 60 */ 61 struct Deque(T, bool mayNull = true) { 62 import core.exception : RangeError; 63 import std.range : ElementType, isInputRange; 64 import std.traits : isImplicitlyConvertible; 65 66 alias Payload = DequePayload!T; 67 Payload* _p; 68 69 static if (!mayNull) @disable this(); 70 71 /// Deque(1, 2, 3) 72 this(U)(U[] values...) if (isImplicitlyConvertible!(U, T)) { 73 _p = new Payload(); 74 foreach (v; values) { 75 insertBack(v); 76 } 77 } 78 /// Deque(iota(3)) 79 this(Range)(Range r) 80 if (isInputRange!Range && 81 isImplicitlyConvertible!(ElementType!Range, T) && 82 !is(Range == T[])) { 83 _p = new Payload(); 84 foreach (v; r) { 85 insertBack(v); 86 } 87 } 88 private this(Payload* p) { _p = p; } 89 static Deque make() { return Deque(new Payload()); } 90 91 private bool havePayload() const { return (!mayNull || _p); } 92 @property bool empty() const { return (!havePayload || _p.empty); } /// 93 @property size_t length() const { return (havePayload ? _p.length : 0); } /// 94 alias opDollar = length; /// ditto 95 96 ref inout(T) opIndex(size_t i) inout { 97 assert(!empty, "Deque.opIndex: Deque is empty"); 98 return (*_p)[i]; 99 } /// 100 ref inout(T) front() inout { return this[0]; } /// 101 ref inout(T) back() inout { return this[$-1]; } /// 102 103 void clear() { if (_p) _p.clear(); } /// 104 105 /// Warning: break range 106 void insertFront(T item) { 107 if (mayNull && !_p) _p = new Payload(); 108 _p.insertFront(item); 109 } 110 void insertBack(T item) { 111 if (mayNull && !_p) _p = new Payload(); 112 _p.insertBack(item); 113 } /// 114 alias opOpAssign(string op : "~") = insertBack; /// ditto 115 alias stableInsertBack = insertBack; /// ditto 116 117 /// Warning: break range 118 void removeFront() { 119 assert(!mayNull || _p, "Deque.removeFront: Deque is empty"); 120 _p.removeFront(); 121 } 122 void removeBack() { 123 assert(!mayNull || _p, "Deque.removeBack: Deque is empty"); 124 _p.removeBack(); 125 } /// 126 alias stableRemoveBack = removeBack; /// ditto 127 128 /// Random-access range 129 alias Range = RangeT!(DequePayload!T); 130 alias ConstRange = RangeT!(const DequePayload!T); /// ditto 131 alias ImmutableRange = RangeT!(immutable DequePayload!T); /// ditto 132 133 size_t[2] opSlice(size_t dim : 0)(size_t start, size_t end) const { 134 assert(start <= end && end <= length); 135 return [start, end]; 136 } /// 137 Range opIndex(size_t[2] rng) { return Range(_p, rng[0], rng[1]); } /// Get slice 138 ConstRange opIndex(size_t[2] rng) const { return ConstRange(_p, rng[0], rng[1]); } /// ditto 139 ImmutableRange opIndex(size_t[2] rng) immutable { return ImmutableRange(_p, rng[0], rng[1]); } /// ditto 140 auto opIndex() inout { return this[0..$]; } /// ditto 141 142 static struct RangeT(QualifiedPayload) { 143 alias A = QualifiedPayload; 144 import std.traits : CopyTypeQualifiers; 145 alias E = CopyTypeQualifiers!(A, T); 146 A *p; 147 size_t l, r; 148 149 @property bool empty() const { return r <= l; } 150 @property size_t length() const { return r - l; } 151 alias opDollar = length; 152 153 @property auto save() { return this; } 154 155 ref inout(E) opIndex(size_t i) inout { 156 version(assert) if (empty) throw new RangeError(); 157 return (*p)[l+i]; 158 } 159 @property ref inout(E) front() inout { return this[0]; } 160 @property ref inout(E) back() inout { return this[$-1]; } 161 162 void popFront() { 163 version(assert) if (empty) throw new RangeError(); 164 l++; 165 } 166 void popBack() { 167 version(assert) if (empty) throw new RangeError(); 168 r--; 169 } 170 171 size_t[2] opSlice(size_t dim : 0)(size_t start, size_t end) const { 172 assert(start <= end && end <= length); 173 return [start, end]; 174 } 175 auto opIndex(size_t[2] rng) { return RangeT(p, l+rng[0], l+rng[1]); } 176 auto opIndex(size_t[2] rng) const { return RangeT!(const A)(p, l+rng[0], l+rng[1]); } 177 auto opIndex(size_t[2] rng) immutable { return RangeT!(immutable A)(p, l+rng[0], l+rng[1]); } 178 auto opIndex() inout { return this[0..$]; } 179 } 180 } 181 182 /// 183 unittest { 184 import std.algorithm : equal; 185 auto q = Deque!int(); 186 187 assert(equal(q[], new int[0])); 188 q.insertBack(1); 189 assert(equal(q[], [1])); 190 q.insertBack(2); 191 assert(equal(q[], [1, 2])); 192 q.insertFront(3); 193 assert(equal(q[], [3, 1, 2])); 194 q.removeFront; 195 assert(equal(q[], [1, 2])); 196 q.insertBack(4); 197 assert(equal(q[], [1, 2, 4])); 198 q.insertFront(5); 199 assert(equal(q[], [5, 1, 2, 4])); 200 q.removeBack(); 201 assert(equal(q[], [5, 1, 2])); 202 } 203 204 unittest { 205 import std.algorithm : equal; 206 import std.range.primitives : isRandomAccessRange; 207 import std.container.util : make; 208 auto q = Deque!int(); 209 assert(isRandomAccessRange!(typeof(q[]))); 210 211 //insert,remove 212 assert(equal(q[], new int[](0))); 213 q.insertBack(1); 214 assert(equal(q[], [1])); 215 q.insertBack(2); 216 assert(equal(q[], [1, 2])); 217 q.insertFront(3); 218 assert(equal(q[], [3, 1, 2]) && q.front == 3); 219 q.removeFront; 220 assert(equal(q[], [1, 2]) && q.length == 2); 221 q.insertBack(4); 222 assert(equal(q[], [1, 2, 4]) && q.front == 1 && q.back == 4 && q[$-1] == 4); 223 q.insertFront(5); 224 assert(equal(q[], [5, 1, 2, 4])); 225 226 //range 227 assert(equal(q[][1..3], [1, 2])); 228 assert(equal(q[][][][], q[])); 229 //const range 230 const auto rng = q[]; 231 assert(rng.front == 5 && rng.back == 4); 232 233 //reference type 234 auto q2 = q; 235 q2.insertBack(6); 236 q2.insertFront(7); 237 assert(equal(q[], q2[]) && q.length == q2.length); 238 239 //construct with make 240 auto a = make!(Deque!int)(1, 2, 3); 241 auto b = make!(Deque!int)([1, 2, 3]); 242 assert(equal(a[], b[])); 243 } 244 245 unittest { 246 static assert( is(typeof(Deque!(int, true)()))); 247 static assert(!is(typeof(Deque!(int, false)()))); 248 } 249 250 unittest { 251 import std.algorithm : equal; 252 import std.range.primitives : isRandomAccessRange; 253 import std.container.util : make; 254 auto q = make!(Deque!int); 255 q.clear(); 256 assert(equal(q[], new int[0])); 257 foreach (i; 0..100) { 258 q.insertBack(1); 259 q.insertBack(2); 260 q.insertBack(3); 261 q.insertBack(4); 262 q.insertBack(5); 263 assert(equal(q[], [1,2,3,4,5])); 264 q.clear(); 265 assert(equal(q[], new int[0])); 266 } 267 } 268 269 unittest { 270 Deque!(int, false) q1 = Deque!(int, false).make(); 271 q1.insertBack(3); 272 assert(q1[0] == 3); 273 Deque!(int, false) q2 = Deque!(int, false)(4, 2); 274 assert(q2[0] == 4); 275 Deque!(int, false) q3 = Deque!(int, false)([6, 9]); 276 assert(q3[1] == 9); 277 } 278 279 unittest { 280 Deque!int a; 281 Deque!int b; 282 a.insertFront(2); 283 assert(b.length == 0); 284 } 285 286 unittest { 287 import std.algorithm : equal; 288 import std.range : iota; 289 Deque!int a; 290 foreach (i; 0..100) { 291 a.insertBack(i); 292 } 293 assert(equal(a[], iota(100))); 294 }