1 module gfm.core.queue; 2 3 import std.range; 4 5 import core.sync.mutex, 6 core.sync.semaphore; 7 8 import gfm.core.memory; 9 10 // what to do when capacity is exceeded? 11 private enum OverflowPolicy 12 { 13 GROW, 14 CRASH, 15 DROP 16 } 17 18 /** 19 20 Doubly-indexed queue, can be used as a FIFO or stack. 21 22 Bugs: 23 Doesn't call struct destructors, don't scan memory. 24 You should probably only put POD types in them. 25 */ 26 deprecated("Use emsi_containers instead") final class QueueImpl(T, OverflowPolicy overflowPolicy) 27 { 28 public 29 { 30 /// Create a QueueImpl with specified initial capacity. 31 this(size_t initialCapacity) nothrow 32 { 33 _data.length = initialCapacity; 34 clear(); 35 } 36 37 /// Returns: true if the queue is full. 38 @property bool isFull() pure const nothrow 39 { 40 return _count == capacity; 41 } 42 43 /// Returns: capacity of the queue. 44 @property size_t capacity() pure const nothrow 45 { 46 return _data.length; 47 } 48 49 /// Adds an item on the back of the queue. 50 void pushBack(T x) nothrow 51 { 52 checkOverflow!popFront(); 53 _data[(_first + _count) % _data.length] = x; 54 ++_count; 55 } 56 57 /// Adds an item on the front of the queue. 58 void pushFront(T x) nothrow 59 { 60 checkOverflow!popBack(); 61 ++_count; 62 _first = (_first - 1 + _data.length) % _data.length; 63 _data[_first] = x; 64 } 65 66 /// Removes an item from the front of the queue. 67 /// Returns: the removed item. 68 T popFront() nothrow 69 { 70 crashIfEmpty(); 71 T res = _data[_first]; 72 _first = (_first + 1) % _data.length; 73 --_count; 74 return res; 75 } 76 77 /// Removes an item from the back of the queue. 78 /// Returns: the removed item. 79 T popBack() nothrow 80 { 81 crashIfEmpty(); 82 --_count; 83 return _data[(_first + _count) % _data.length]; 84 } 85 86 /// Removes all items from the queue. 87 void clear() nothrow 88 { 89 _first = 0; 90 _count = 0; 91 } 92 93 /// Returns: number of items in the queue. 94 size_t length() pure const nothrow 95 { 96 return _count; 97 } 98 99 /// Returns: item at the front of the queue. 100 T front() pure 101 { 102 crashIfEmpty(); 103 return _data[_first]; 104 } 105 106 /// Returns: item on the back of the queue. 107 T back() pure 108 { 109 crashIfEmpty(); 110 return _data[(_first + _count + _data.length - 1) % _data.length]; 111 } 112 113 /// Returns: item index from the queue. 114 T opIndex(size_t index) 115 { 116 // crash if index out-of-bounds (not recoverable) 117 if (index > _count) 118 assert(0); 119 120 return _data[(_first + index) % _data.length]; 121 } 122 123 /// Returns: random-access range over the whole queue. 124 Range opSlice() nothrow 125 { 126 return Range(this); 127 } 128 129 /// Returns: random-access range over a queue sub-range. 130 Range opSlice(size_t i, size_t j) nothrow 131 { 132 // verify that all elements are in bound 133 if (i != j && i >= _count) 134 assert(false); 135 136 if (j > _count) 137 assert(false); 138 139 if (j < i) 140 assert(false); 141 142 return Range(this); 143 } 144 145 // range type, random access 146 static struct Range 147 { 148 nothrow: 149 public 150 { 151 this(QueueImpl queue) pure 152 { 153 this(queue, 0, queue._count); 154 _first = queue._first; 155 _count = queue._count; 156 } 157 158 this(QueueImpl queue, size_t index, size_t count) pure 159 { 160 _index = index; 161 _data = queue._data; 162 _first = (queue._first + index) % _data.length; 163 _count = _count; 164 } 165 166 @property bool empty() pure const 167 { 168 return _index >= _count; 169 } 170 171 void popFront() 172 { 173 _index++; 174 } 175 176 @property T front() pure 177 { 178 return _data[(_first + _index) % _data.length]; 179 } 180 181 void popBack() 182 { 183 _count--; 184 } 185 186 @property T back() pure 187 { 188 return _data[(_first + _count - 1) % _data.length]; 189 } 190 191 @property Range save() 192 { 193 return this; 194 } 195 196 T opIndex(size_t i) 197 { 198 // crash if index out-of-bounds of the range (not recoverable) 199 if (i > _count) 200 assert(0); 201 202 return _data[(_first + _index + i) % _data.length]; 203 } 204 205 @property size_t length() pure 206 { 207 return _count; 208 } 209 210 alias length opDollar; 211 } 212 213 private 214 { 215 size_t _index; 216 T[] _data; 217 size_t _first; 218 size_t _count; 219 } 220 } 221 } 222 223 private 224 { 225 void crashIfEmpty() 226 { 227 // popping if empty is not a recoverable error 228 if (_count == 0) 229 assert(false); 230 } 231 232 // element lie from _first to _first + _count - 1 index, modulo the allocated size 233 T[] _data; 234 size_t _first; 235 size_t _count; 236 237 void checkOverflow(alias popMethod)() nothrow 238 { 239 if (isFull()) 240 { 241 static if (overflowPolicy == OverflowPolicy.GROW) 242 extend(); 243 244 static if (overflowPolicy == OverflowPolicy.CRASH) 245 assert(false); // not recoverable to overflow such a queue 246 247 static if (overflowPolicy == OverflowPolicy.DROP) 248 popMethod(); 249 } 250 } 251 252 void extend() nothrow 253 { 254 size_t newCapacity = capacity * 2; 255 if (newCapacity < 8) 256 newCapacity = 8; 257 258 assert(newCapacity >= _count + 1); 259 260 T[] newData = new T[newCapacity]; 261 262 auto r = this[]; 263 size_t i = 0; 264 while (!r.empty()) 265 { 266 newData[i] = r.front(); 267 r.popFront(); 268 ++i; 269 } 270 _data = newData; 271 _first = 0; 272 } 273 } 274 } 275 276 static assert (isRandomAccessRange!(Queue!int.Range)); 277 278 279 /** 280 281 A queue that can only grow. 282 283 284 See_also: $(LINK2 #QueueImpl, QueueImpl) 285 286 */ 287 template Queue(T) 288 { 289 alias QueueImpl!(T, OverflowPolicy.GROW) Queue; 290 } 291 292 /** 293 294 A fixed-sized queue that will crash on overflow. 295 296 See_also: $(LINK2 #QueueImpl, QueueImpl) 297 298 299 */ 300 template FixedSizeQueue(T) 301 { 302 alias QueueImpl!(T, OverflowPolicy.CRASH) FixedSizeQueue; 303 } 304 305 /** 306 307 Ring buffer, it drops if its capacity is exceeded. 308 309 See_also: $(LINK2 #QueueImpl, QueueImpl) 310 311 */ 312 template RingBuffer(T) 313 { 314 alias QueueImpl!(T, OverflowPolicy.DROP) RingBuffer; 315 } 316