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