1 /*******************************************************************************
2 
3     Fixed size memory-based ring queue with a fixed element size.
4 
5     TODO:
6         Update the classes in this module to be able to use a custom allocater
7         like the constructors of FlexibleByteRingQueue allow.
8 
9     Copyright:
10         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
11         All rights reserved.
12 
13     License:
14         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
15         Alternatively, this file may be distributed under the terms of the Tango
16         3-Clause BSD License (see LICENSE_BSD.txt for details).
17 
18 *******************************************************************************/
19 
20 module ocean.util.container.queue.FixedRingQueue;
21 
22 
23 
24 
25 import ocean.util.container.queue.model.IRingQueue;
26 
27 import ocean.util.container.queue.model.IQueue;
28 
29 import ocean.util.container.queue.model.IByteQueue;
30 
31 import ocean.util.container.mem.MemManager;
32 
33 version (unittest) import ocean.core.Test;
34 
35 import ocean.meta.types.Qualifiers;
36 
37 
38 /*******************************************************************************
39 
40     Ring queue for elements of type T, T must be a value type.
41 
42 *******************************************************************************/
43 
44 class FixedRingQueue ( T ) : FixedRingQueueBase!(IQueue!(T))
45 {
46     import ocean.core.Verify;
47 
48     /***************************************************************************
49 
50         Constructor
51 
52         Params:
53             max_items = maximum number of elements in queue
54 
55     ***************************************************************************/
56 
57     public this ( size_t max_items )
58     {
59         super(max_items, T.sizeof);
60     }
61 
62     /***************************************************************************
63 
64         Pushes an element into the queue.
65 
66         Params:
67             element = element to push (will be left unchanged)
68 
69         Returns:
70             true on success or false if the queue is full.
71 
72     ***************************************************************************/
73 
74     public bool push ( T element )
75     {
76         T* element_in_queue = this.push();
77 
78         if (element_in_queue)
79         {
80             *element_in_queue = element;
81             return true;
82         }
83         else
84         {
85             return false;
86         }
87     }
88 
89     /***************************************************************************
90 
91         Pushes an element into the queue and returns a pointer to that element.
92         The value of the element must then be copied to the location pointed to
93         before calling push() or pop() the next time.
94 
95         Returns:
96             pointer to the element pushed into the queue or null if the queue is
97             full.
98 
99     ***************************************************************************/
100 
101     public T* push ( )
102     {
103         return cast (T*) super.push_().ptr;
104     }
105 
106     /***************************************************************************
107 
108         Pops an element from the queue.
109 
110         Params:
111             element = destination for popped element, will be changed only if
112                       the return value is true.
113 
114         Returns:
115             true on success or false if the queue is empty.
116 
117     ***************************************************************************/
118 
119     public bool pop ( ref T element )
120     {
121         T* element_in_queue = this.pop();
122 
123         if (element_in_queue)
124         {
125             element = *element_in_queue;
126             return true;
127         }
128         else
129         {
130             return false;
131         }
132     }
133 
134 
135     /***************************************************************************
136 
137         Pops an element from the queue and returns a pointer to that element.
138         The value of the element must then be copied from the location pointed
139         to before calling push() or pop() the next time.
140 
141         Returns:
142             pointer to the element popped from the queue or null if the queue is
143             empty.
144 
145     ***************************************************************************/
146 
147     public T* pop ( )
148     {
149         return cast (T*) super.pop_().ptr;
150     }
151 }
152 
153 /*******************************************************************************
154 
155     Ring queue for raw element data.
156 
157 *******************************************************************************/
158 
159 class FixedByteRingQueue : FixedRingQueueBase!(IByteQueue)
160 {
161     import ocean.core.Verify;
162 
163     /***************************************************************************
164 
165         Constructor
166 
167         Params:
168             element_size = element size in bytes
169             max_items        = maximum number of elements in queue
170 
171     ***************************************************************************/
172 
173     this ( size_t element_size, size_t max_items )
174     {
175         super(max_items, element_size);
176     }
177 
178     /***************************************************************************
179 
180         Pushes an element into the queue.
181 
182         Params:
183             element = element to push (will be left unchanged)
184 
185         Returns:
186             true on success or false if the queue is full.
187 
188     ***************************************************************************/
189 
190     bool push ( in void[] element )
191     {
192         verify (element.length == super.element_size, "element size mismatch");
193 
194         auto element_in_queue = super.push_();
195 
196         if (element_in_queue)
197         {
198             const(void)[] element_ = element;
199             element_in_queue[] = element_[];
200             return true;
201         }
202         else
203         {
204             return false;
205         }
206     }
207 
208     /***************************************************************************
209 
210         Pushes an element into the queue and returns a slice to that element.
211         The value of the element must then be copied to the sliced location
212         before the next push() or pop() is called.
213 
214         Returns:
215             slice to the element pushed into the queue or null if the queue is
216             full.
217 
218     ***************************************************************************/
219 
220     void[] push ( size_t ignored = 0 )
221     {
222         return super.push_();
223     }
224 
225     /***************************************************************************
226 
227         Pops an element from the queue and returns a slice to that element.
228         The value of the element must then be copied from the sliced location
229         before the next push() or pop() is called.
230 
231         Returns:
232             pointer to the element popped from the queue or null if the queue is
233             empty.
234 
235      ***************************************************************************/
236 
237     void[] pop ( )
238     {
239         return super.pop_();
240     }
241 
242     void[] peek ( )
243     {
244         return super.peek_();
245     }
246 
247     /***************************************************************************
248 
249         Pops an element from the queue and copies the value to element.
250 
251         Params:
252             element = destination buffer, the length must be the element size.
253                       Will be changed only if the return value is true.
254 
255         Returns:
256             true on success or false if the queue is empty.
257 
258     ***************************************************************************/
259 
260     bool pop ( void[] element )
261     {
262         verify (element.length == super.element_size, "element size mismatch");
263 
264         auto element_in_queue = super.pop_();
265 
266         if (element_in_queue)
267         {
268             element[] = element_in_queue[];
269             return true;
270         }
271         else
272         {
273             return false;
274         }
275     }
276 }
277 
278 /*******************************************************************************
279 
280     Ring queue base class.
281 
282 *******************************************************************************/
283 
284 abstract class FixedRingQueueBase ( IBaseQueue ) : IRingQueue!(IBaseQueue)
285 {
286     import ocean.core.Verify;
287 
288     /***************************************************************************
289 
290         Maximum number of elements in queue
291 
292     ***************************************************************************/
293 
294     protected size_t max_items;
295 
296     /***************************************************************************
297 
298         Element size in bytes
299 
300     ***************************************************************************/
301 
302     protected size_t element_size;
303 
304     /***************************************************************************
305 
306         Consistency check
307 
308     ***************************************************************************/
309 
310     invariant ( )
311     {
312         assert (super.items <= this.max_items);
313 
314         // Both read and write position must be integer multiples of the element
315         // size.
316 
317         assert (!(super.write_to  % this.element_size));
318         assert (!(super.read_from % this.element_size));
319 
320         // If the queue is empty or full, the read position must equal the write
321         // position. If the read position equals the write position, the queue
322         // must be empty or full.
323 
324         assert ((0 < super.items && super.items < this.max_items) ^ (super.read_from == super.write_to));
325 
326         assert (super.write_to  < super.data.length);
327         assert (super.read_from < super.data.length);
328 
329         size_t used_space = super.items * this.element_size;
330 
331         if (super.write_to < super.read_from)
332         {
333             assert (used_space == super.data.length - (super.read_from - super.write_to));
334         }
335         else if (super.write_to > super.read_from)
336         {
337             assert (used_space == super.write_to - super.read_from);
338         }
339         else if (super.items)
340         {
341             assert (super.items == this.max_items);
342             assert (used_space == super.data.length);
343         }
344         else
345         {
346             assert (!used_space);
347         }
348     }
349 
350     /***************************************************************************
351 
352         Constructor
353 
354         Params:
355             element_size = element size in bytes
356             max_items        = maximum number of elements in queue
357 
358     ***************************************************************************/
359 
360     this ( size_t max_items, size_t element_size )
361     {
362         verify (element_size > 0);
363         verify (max_items > 0);
364 
365         super((this.element_size = element_size) * (this.max_items = max_items));
366     }
367 
368     /***************************************************************************
369 
370         Returns:
371             number of bytes stored in queue
372 
373     ***************************************************************************/
374 
375     override ulong used_space ( )
376     {
377         return super.items * this.element_size;
378     }
379 
380     /***************************************************************************
381 
382         Returns:
383             maximum number of elements that could be held in queue.
384 
385     ***************************************************************************/
386 
387     size_t maxItems ( )
388     {
389         return this.max_items;
390     }
391 
392 
393     /***************************************************************************
394 
395         Finds out whether another item will fit
396 
397         Params:
398             bytes = size of item to check, ignored as sizes is fixed
399 
400         Returns:
401             true if the item fits, else false
402 
403     ***************************************************************************/
404 
405     public bool willFit ( size_t bytes = 0)
406     {
407         return super.items < this.max_items;
408     }
409 
410 
411     /***************************************************************************
412 
413         Pushes an element into the queue and returns a slice to that element.
414         The value of the element must then be copied to the sliced location
415         before the next push_() or pop_() is called.
416 
417         Returns:
418             slice to the element pushed into the queue or null if the queue is
419             full.
420 
421     ***************************************************************************/
422 
423     protected void[] push_ ( )
424     out (element)
425     {
426         assert (!element || element.length == this.element_size);
427     }
428     do
429     {
430         if (super.items < this.max_items)
431         {
432             super.items++;
433 
434             return this.getElement(super.write_to);
435         }
436         else
437         {
438             return null;
439         }
440     }
441 
442     /***************************************************************************
443 
444         Pops an element from the queue and returns a slice to that element.
445         The value of the element must then be copied from the sliced location
446         before the next push() or pop() is called.
447 
448         Returns:
449             slice of the element popped from the queue or null if the queue is
450             empty.
451 
452     ***************************************************************************/
453 
454     protected void[] pop_ ( )
455     out (element)
456     {
457         assert (!element || element.length == this.element_size);
458     }
459     do
460     {
461         if (super.items)
462         {
463             super.items--;
464 
465             return this.getElement(super.read_from);
466         }
467         else
468         {
469             return null;
470         }
471     }
472 
473 
474     protected void[] peek_ ( )
475     out (element)
476     {
477         assert (!element || element.length == this.element_size);
478     }
479     do
480     {
481         if (super.items)
482         {
483             auto read_pos = super.read_from;
484 
485             return this.getElement(read_pos);
486         }
487         else
488         {
489             return null;
490         }
491     }
492 
493     /***************************************************************************
494 
495         Slices the element at position pos in super.data and increments pos by
496         the element size. If pos reaches the end of data, it is reset (wrapped
497         around) to 0.
498 
499         Params:
500             pos = position in super.data
501 
502         Returns:
503             slice to the element at the requested position in super.data.
504 
505     ***************************************************************************/
506 
507     private void[] getElement ( ref size_t pos )
508     {
509         size_t end = pos + this.element_size;
510 
511         auto chunk = super.data[pos .. end];
512 
513         pos = end;
514 
515         if (pos == super.data.length)
516         {
517             pos = 0;
518         }
519 
520         return chunk;
521     }
522 }
523 
524 unittest
525 {
526     static size_t pos ( size_t n )
527     {
528         return n * int.sizeof;
529     }
530 
531     scope queue = new FixedRingQueue!(int)(3);
532 
533     int n;
534 
535     test (queue.is_empty);
536     test (!queue.pop());
537     test (queue.is_empty);
538 
539     test (queue.push(1));
540     test (queue.get_write_to  == pos(1));
541     test (queue.get_read_from == pos(0));
542     test (!queue.is_empty);
543 
544     test (queue.pop(n));
545     test (n == 1);
546 
547     test (queue.is_empty);
548     test (!queue.pop());
549     test (queue.get_write_to == queue.get_read_from);
550 
551     test (queue.push(2));
552     test (!queue.is_empty);
553     test (queue.push(3));
554     test (queue.push(4));
555     test (!queue.push(5));
556     test (queue.get_write_to == queue.get_read_from);
557 
558     test (queue.pop(n));
559     test (n == 2);
560 
561     test (queue.pop(n));
562     test (n == 3);
563 
564     test (queue.pop(n));
565     test (n == 4);
566 
567     test (queue.is_empty);
568     test (!queue.pop());
569     test (queue.get_write_to == queue.get_read_from);
570 
571     test (queue.push(5));
572 
573     test (queue.pop(n));
574     test (n == 5);
575 
576     test (queue.is_empty);
577     test (!queue.pop());
578     test (queue.get_write_to == queue.get_read_from);
579 }