1 /******************************************************************************
2 
3     Copyright:
4         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
5         All rights reserved.
6 
7     License:
8         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
9         Alternatively, this file may be distributed under the terms of the Tango
10         3-Clause BSD License (see LICENSE_BSD.txt for details).
11 
12 *******************************************************************************/
13 
14 module ocean.util.container.queue.LinkedListQueue;
15 
16 import ocean.meta.types.Qualifiers;
17 
18 import core.memory;
19 import ocean.util.container.Container;
20 import ocean.core.Test;
21 import ocean.util.container.queue.model.ITypedQueue;
22 import ocean.core.Array;
23 public import ocean.util.container.queue.model.ITypedQueue: push, pop;
24 
25 
26 /******************************************************************************
27 
28     A typed-queue based on a linked list. (Internally, the queue is composed of
29     instances of a struct which contains a value (of type T) and a pointer to
30     next item.)
31 
32     The linked list implementation allows the following extensions to ITypedQueue:
33 
34         * The ability to find a specified value in the queue: contains().
35         * The ability to remove one or all instances of a specified value from
36           the queue: remove().
37 
38     The items in the linked list are allocated and deallocated by Malloc allocation
39     manager, to avoid the GC from not so efficiently going over the linked list
40     (see explanation at LinkedListQueue.heap below).
41 
42     Params:
43         T = Type of values stored in the linked list queue
44         gc_tracking_policy = defines the GC tracking policy for T (see
45             GCTrackingPolicy struct below)
46 
47 *******************************************************************************/
48 
49 public class LinkedListQueue ( T, alias gc_tracking_policy = GCTrackingPolicy.refTypesOnly ) :
50     ITypedQueue!( T )
51 {
52     /**************************************************************************
53 
54         Should the values in queue be added to GC as roots, thus
55         preventing the GC from collecting these values.
56 
57         Since the items in the queue are allocated by the Malloc allocation
58         manager, they are not scanned by the GC. This means that whatever the items
59         reference - the values, are not considered as being referenced by anything
60         (unless the values are referenced by some other "GC scanned" object).
61         This will result in GC collecting these values, which will have very bad
62         ramifications as we're still referencing them in the queue.
63 
64         So we add the values to GC as roots, thus preventing the GC from collecting
65         them, depending on the value of 'root_values'
66 
67     ***************************************************************************/
68 
69     protected static bool root_values;
70 
71     /**************************************************************************
72 
73         Static constructor.
74 
75         Sets 'root_values' according to the gc_tracking_policy.
76 
77     ***************************************************************************/
78 
79     public static this ( )
80     {
81         typeof(this).root_values = gc_tracking_policy!(T);
82     }
83 
84 
85     /**************************************************************************
86 
87         Type of items in queue - a struct of value and pointer to next item
88 
89     ***************************************************************************/
90 
91     protected struct QueueItem
92     {
93         /**********************************************************************
94 
95             Pointer to next item in queue
96 
97         ***********************************************************************/
98 
99         public QueueItem* next;
100 
101 
102         /**********************************************************************
103 
104             The value stored in this item
105 
106         ***********************************************************************/
107 
108         public T value;
109 
110 
111         /**********************************************************************
112 
113             Finds a QueueItem which contains the given value
114 
115             Params:
116                 find_value = value to find
117 
118             Returns:
119                 pointer to the first QueueItem which contains the value, null if
120                 not found
121 
122         ***********************************************************************/
123 
124         public QueueItem* find ( T find_value )
125         {
126             for ( auto p = &this; p; p = p.next )
127                  if ( find_value == p.value )
128                      return p;
129             return null;
130         }
131     }
132 
133 
134     /**************************************************************************
135 
136         Number of items in the queue
137 
138     ***************************************************************************/
139 
140     protected size_t count;
141 
142 
143     /**************************************************************************
144 
145         Pointer to first (and oldest) item in queue
146 
147     ***************************************************************************/
148 
149     protected QueueItem* head;
150 
151 
152     /**************************************************************************
153 
154         Pointer to last item in queue
155 
156     ***************************************************************************/
157 
158     protected QueueItem* tail;
159 
160 
161     /**************************************************************************
162 
163         Malloc allocation manager, to allocate and deallocate items in queue.
164         We use the malloc allocation manager as to keep the linked list away
165         from the reach of the GC, for efficiency reasons.
166         The reason for this is:
167         The GC collector scans the memory in order to identify the root objects,
168         and then collect them. The scanning process, roughly speaking, is done in
169         such a way that each time the GC detects a new address it runs another scan.
170         On the entire memory. After each scan the amount of detected addresses is
171         increased until there are mo more undetected addresses, and the scanning
172         stops.
173         The problem with linked list is that the GC will detect a single item in
174         each scan. So GC will run as many scans as the number of items in the
175         linked list. And again, each scan goes over the entire memory.
176 
177     ***************************************************************************/
178 
179     protected Container.Malloc!(QueueItem) heap;
180 
181 
182     /**************************************************************************
183 
184         Validate the following:
185 
186         1. If length is > 0 then head must not be null
187         2. If length is 1, head and tail must point to the same item
188         3. If length is > 1, head and tail must not point to the same item
189         4. If it's empty then head is null
190 
191     ***************************************************************************/
192 
193     invariant ( )
194     {
195         if ( this.count )
196         {
197             assert ( this.head, "LinkedListQueue length isn't 0, it's head should not be null!" );
198 
199             if ( this.count == 1 )
200                 assert ( this.head is this.tail, "LinkedListQueue length is 1, it's head and tail should point to the same item!" );
201 
202             if ( this.count > 1 )
203                 assert ( this.head !is this.tail, "LinkedListQueue length is > 1, it's head and tail should not point to the same item!" );
204         }
205         else
206         {
207             assert ( !this.head, "LinkedListQueue length is 0, it's head should be null!" );
208         }
209     }
210 
211 
212     /**************************************************************************
213 
214         Returns:
215             true if queue is empty, false otherwise
216 
217     ***************************************************************************/
218 
219     public override bool empty ( )
220     {
221         return this.count == 0;
222     }
223 
224 
225     /**************************************************************************
226 
227         Returns:
228             number of items in the queue
229 
230     ***************************************************************************/
231 
232     public override size_t length ( )
233     {
234         return this.count;
235     }
236 
237 
238     /**************************************************************************
239 
240         Removes all items from the queue in O(n)
241 
242     ***************************************************************************/
243 
244     public override void clear ( )
245     {
246         while ( this.count )
247         {
248             this.discardTop();
249         }
250     }
251 
252 
253     /**************************************************************************
254 
255         Pushes an item to the queue. The caller should set the returned item as
256         desired
257 
258         Returns:
259             Pointer to the newly pushed item
260 
261     ***************************************************************************/
262 
263     public override T* push ( )
264     {
265         auto new_element = this.newItem();
266 
267         // Just created first item in queue, both head and tail
268         // should point to it
269         if ( this.count == 0 )
270         {
271             this.head = new_element;
272             this.tail = this.head;
273         }
274         else // Update tail to point to new item
275         {
276             this.tail.next = new_element;
277             this.tail = this.tail.next;
278         }
279 
280         this.count++;
281 
282         return &new_element.value;
283     }
284 
285 
286     /**************************************************************************
287 
288         Discards the item at the top of the queue.
289 
290     ***************************************************************************/
291 
292     public override void discardTop ( )
293     {
294         if ( this.count == 0 )
295             return;
296 
297         QueueItem* head_to_be_removed = this.head;
298         this.head = this.head.next;
299         this.count--;
300 
301         this.deleteItem(head_to_be_removed);
302     }
303 
304 
305     /**************************************************************************
306 
307         Returns:
308             A pointer to the item at the top of the queue, null if the queue is
309             empty
310 
311     ***************************************************************************/
312 
313     public override T* top ( )
314     {
315         if ( this.count == 0 )
316             return null;
317 
318         return &this.head.value;
319     }
320 
321 
322     /**************************************************************************
323 
324         Returns:
325             A pointer to the item at the bottom of the queue, null if the queue
326             is empty
327 
328     ***************************************************************************/
329 
330     public T* bottom ( )
331     {
332         if ( this.count == 0 )
333             return null;
334 
335         return &this.tail.value;
336     }
337 
338 
339     /**************************************************************************
340 
341         Checks whether a value exists in queue in O(n).
342 
343         Params:
344             value = value to find
345 
346         Returns:
347             true if value exists, false otherwise
348 
349     ***************************************************************************/
350 
351     public bool contains ( T value )
352     {
353         if ( this.count == 0 )
354             return false;
355 
356         return this.head.find(value) !is null;
357     }
358 
359 
360     /**************************************************************************
361 
362         Removes a value from queue, in O(n)
363 
364         Params:
365             value = value to remove
366             all   = if true then remove all values equal to value, othetrwise only
367                     first matching value will be removed
368 
369         Returns:
370             number of removed values
371 
372     ***************************************************************************/
373 
374     public size_t remove ( T value, bool all = false )
375     {
376         // iterate over items in queue
377         QueueItem* iterator = this.head;
378 
379         // keep pointer to previous item
380         QueueItem* previous = iterator;
381 
382         // keep count of how many we removed
383         auto old_count = this.count;
384 
385         while( iterator )
386         {
387             auto next = iterator.next;
388 
389             if ( iterator.value == value )
390             {
391                 bool is_tail;
392                 bool is_head;
393 
394                 // head item is to be removed
395                 // update head to point to next item
396                 if ( iterator is this.head )
397                 {
398                     this.head = next;
399                     previous = next;
400                     is_head = true;
401                 }
402 
403                 // tail item is to be removed
404                 // update tail to point to previous item
405                 if ( iterator is this.tail )
406                 {
407                     this.tail = previous;
408                     is_tail = true;
409 
410                     if ( !is_head )
411                         this.tail.next = null;
412                 }
413 
414                 // "in the middle" item is to be removed
415                 // connect the items before and after the removed one
416                 if ( !is_tail && !is_head )
417                 {
418                     previous.next = next;
419                 }
420 
421                 // remove
422                 this.count--;
423                 this.deleteItem(iterator);
424 
425                 // should we look for more matched values? have we reached the end?
426                 if ( !all || this.count == 0 )
427                     break;
428             }
429             else
430             {
431                 previous = iterator;
432             }
433 
434             iterator = next;
435         }
436 
437         return old_count - this.count;
438     }
439 
440 
441     /***************************************************************************
442 
443         Walk through the linked list
444 
445         Params:
446             dg = delegate to be called for each value in the list
447 
448         Returns:
449             the return value of the last call to the delegate,
450             or zero if no call happened (no elements to walk over)
451 
452     ***************************************************************************/
453 
454     public int opApply ( scope int delegate ( ref T value ) dg )
455     {
456         int result;
457 
458         if (!this.empty())
459         {
460             auto current = this.head;
461 
462             do
463             {
464                 result = dg(current.value);
465 
466                 if (result)
467                     break;
468 
469                 current = current.next;
470             }
471             while (current !is null);
472         }
473 
474         return result;
475     }
476 
477     /**************************************************************************
478 
479         Returns:
480             true if the elements of this class are tracked by the GC, false
481             otherwise.
482 
483     ***************************************************************************/
484 
485     public static bool isRootedValues ()
486     {
487         return typeof(this).root_values;
488     }
489 
490     /**************************************************************************
491 
492         Deallocate an item. Called upon item removal from queue.
493 
494         Params:
495             to_delete = pointer to item in queue to delete
496 
497     ***************************************************************************/
498 
499     protected void deleteItem ( QueueItem* to_delete )
500     {
501          if ( root_values )
502             GC.removeRange(&to_delete.value);
503 
504         this.heap.collect(to_delete);
505     }
506 
507 
508     /**************************************************************************
509 
510         Allocate an item. Called upon item addition to queue.
511 
512         Returns:
513             pointer to newly allocated item
514 
515     ***************************************************************************/
516 
517     protected QueueItem* newItem ( )
518     {
519         auto new_element = this.heap.allocate();
520         new_element.next = null;
521 
522         // By adding the value to GC as root we're preventing GC from
523         // collecting it
524         if ( root_values )
525             GC.addRange(&new_element.value, T.sizeof);
526 
527         return new_element;
528     }
529 }
530 
531 /******************************************************************************
532 
533     A wrapper around common used policies for adding data allocated by the
534     LinkedListQueue to the GC scan range.
535 
536 *******************************************************************************/
537 
538 public struct GCTrackingPolicy
539 {
540     /**************************************************************************
541 
542         Checks T parameter and decides accordingly whether T items allocated
543         by the LinkedListQueue will be added to the GC scan range.
544 
545         Allocated T elements will be added to the GC scan range only if T is
546         (or contains) reference types (e.g a class, an array or a pointer).
547         Otherwise allocated T elements won't be added to the GC scan range.
548 
549         Template params:
550             T = the type to be used
551 
552         Returns:
553             true  T contains reference items, returns false otherwise.
554 
555     ***************************************************************************/
556 
557     static bool refTypesOnly (T) ()
558     {
559         return (typeid(T).flags() & 1) == 1;
560     }
561 
562     /**************************************************************************
563 
564         Disable addition of allocated items to the GC scan range regardless of
565         the used type.
566 
567         Template params:
568             T = the type to be used
569 
570         Returns:
571             false regardless of the used type
572 
573     ***************************************************************************/
574 
575     static bool never (T) ()
576     {
577         return false;
578     }
579 
580     /**************************************************************************
581 
582         Enable addition of allocated items to the GC scan range regardless of
583         the used type.
584 
585         Template params:
586             T = the type to be used
587 
588         Returns:
589             true regardless of the used type
590 
591     ***************************************************************************/
592 
593     static bool always (T) ()
594     {
595         return true;
596     }
597 }
598 
599 /******************************************************************************
600 
601     Test walking over the linked list
602 
603 *******************************************************************************/
604 
605 unittest
606 {
607     auto list = new LinkedListQueue!(int);
608 
609     bool iterated = false;
610     foreach (item; list) iterated = true;
611     test!("==")(iterated, false);
612 
613     *list.push = 0;
614     *list.push = 1;
615     *list.push = 2;
616 
617     size_t count;
618     size_t idx;
619     foreach (item; list)
620     {
621         test!("==")(item, idx++);
622         ++count;
623     }
624 
625     test!("==")(count, 3);
626 }
627 
628 
629 /******************************************************************************
630 
631     Test root_values is determined correctly
632 
633 *******************************************************************************/
634 
635 unittest
636 {
637     struct emptyStruct { }
638     struct someStruct { int[] array; }
639     class someClass { }
640 
641     // int
642     test((new LinkedListQueue!(int)).root_values == false, "'int' should not be added as GC root");
643     test((new LinkedListQueue!(int, GCTrackingPolicy.refTypesOnly)).root_values == false, "'int' should not be added as GC root (2)");
644 
645     // empty struct
646     test((new LinkedListQueue!(emptyStruct)).root_values == false, "An empty struct should not be added as GC root");
647     test((new LinkedListQueue!(emptyStruct, GCTrackingPolicy.refTypesOnly)).root_values == false, "An empty struct should not be added as GC root (2)");
648 
649     // struct that points to GC memory
650     test((new LinkedListQueue!(someStruct)).root_values, "A struct containing an array should be added as GC root!");
651     test((new LinkedListQueue!(someStruct, GCTrackingPolicy.refTypesOnly)).root_values, "A struct containing an array should be added as GC root (2)!");
652 
653     // pointer to struct
654     test((new LinkedListQueue!(emptyStruct*)).root_values, "A pointer to a struct should be added as GC root!");
655     test((new LinkedListQueue!(emptyStruct*, GCTrackingPolicy.refTypesOnly)).root_values, "A pointer to a struct should be added as GC root (2)!");
656 
657     // class
658     test((new LinkedListQueue!(someClass)).root_values, "A class should be added as GC root!");
659     test((new LinkedListQueue!(someClass, GCTrackingPolicy.refTypesOnly)).root_values, "A class should be added as GC root (2)!");
660 
661     // Test forcing a GC policy
662     test((new LinkedListQueue!(someClass, GCTrackingPolicy.never)).root_values == false, "GC scanning should be forcefully disabled for class!");
663     test((new LinkedListQueue!(int, GCTrackingPolicy.always)).root_values, "GC scanning should be forcefully enabled for ints!");
664     test((new LinkedListQueue!(emptyStruct, GCTrackingPolicy.always)).root_values, "GC scanning should be forcefully enabled for empty struct!");
665 }
666 
667 
668 /******************************************************************************
669 
670     Test ITypedQueue methods
671 
672 *******************************************************************************/
673 
674 unittest
675 {
676     class JustSomeClass
677     {
678         protected int just_some_int;
679 
680         public this ( int set_just_some_int ) { this.just_some_int = set_just_some_int; }
681 
682         override public equals_t opEquals ( Object _another )
683         {
684             auto another = cast(JustSomeClass) _another;
685             if (another is null)
686                 return false;
687             return this.just_some_int == another.just_some_int;
688         }
689     }
690 
691     // T = int
692     LinkedListQueue!(int) integersList = new LinkedListQueue!(int)();
693 
694     // T = JustSomeClass
695     LinkedListQueue!(JustSomeClass) classesList = new LinkedListQueue!(JustSomeClass)();
696 
697     static immutable int size = 100;
698     int[] int_array;
699     JustSomeClass[] class_array;
700 
701     for(int i = 0; i < size; i++)
702     {
703         int_array ~= i;
704         class_array ~= new JustSomeClass(i);
705     }
706 
707     testInterfaceMethods(integersList, int_array);
708     testInterfaceMethods(classesList, class_array);
709 }
710 
711 
712 /******************************************************************************
713 
714     Test LinkedListQueue methods
715 
716 *******************************************************************************/
717 
718 unittest
719 {
720     /**************************************************************************
721 
722         A class to test LinkedListQueue.
723 
724         The 'invariant' section validates the exact content of the
725         LinkedListQueue. And in case of validation failure the name of the
726         particular test is printed.
727 
728     ***************************************************************************/
729 
730     class TestQueue
731     {
732         /**********************************************************************
733 
734             The tested LinkedListQueue
735 
736         ***********************************************************************/
737 
738         protected LinkedListQueue!(int) int_queue;
739 
740 
741         /**********************************************************************
742 
743             The values expected to be in intQueue, and their expected order
744 
745         ***********************************************************************/
746 
747         protected int[] expected_values;
748 
749 
750         /**********************************************************************
751 
752             The name of the particular test, to be printed in case of test
753             failure
754 
755         ***********************************************************************/
756 
757         protected istring name;
758 
759 
760         /**********************************************************************
761 
762             Constructor
763 
764             Params:
765                 in_name = The name of the particular test
766 
767         ***********************************************************************/
768 
769         public this (istring in_name)
770         {
771             this.int_queue = new LinkedListQueue!(int)();
772             this.name = in_name;
773         }
774 
775 
776         /**********************************************************************
777 
778             Invarinat to validate the contents in int_queue and expected_values
779             are the same.
780 
781         ***********************************************************************/
782 
783         invariant ( )
784         {
785             auto _this = cast(TestQueue) this;
786 
787             test(
788                 _this.int_queue.length() == _this.expected_values.length,
789                 name ~ ": length should be the same"
790             );
791 
792             if ( _this.expected_values.length == 0 )
793             {
794                 test(_this.int_queue.empty(), name ~ ": queue should be mepty");
795                 test(_this.int_queue.top() == null,
796                     name ~ ": queue is empty. Top should have returned null");
797                 test(_this.int_queue.bottom() == null,
798                     name ~ ": queue is empty. Bottom should have returned null");
799             }
800 
801             LinkedListQueue!(int).QueueItem* iterator = _this.int_queue.head;
802 
803             // compare all values
804             foreach( value; _this.expected_values)
805             {
806                 test(iterator.value == value,
807                     name ~ ": value incorrect");
808                 iterator = iterator.next;
809             }
810         }
811 
812 
813         /**********************************************************************
814 
815             Push values into LinkedListQueue.
816 
817             Params:
818                 values = values to push
819 
820         ***********************************************************************/
821 
822         public void push ( int[] values )
823         {
824             foreach( value; values )
825             {
826                 .push(this.int_queue, value);
827                 this.expected_values ~= value;
828                 test!("==")(*this.int_queue.bottom, value);
829             }
830         }
831 
832 
833         /**********************************************************************
834 
835             Removes a value from LinkedListQueue.
836 
837             Params:
838                 value              = value to remove
839                 expected_to_remove = number of items expected to be removed
840                 all                = should all instances of value be removed
841 
842         ***********************************************************************/
843 
844         public void remove ( int value, int expected_to_remove = 1, bool all = true )
845         {
846             bool continue_remove = true;
847 
848             if ( expected_to_remove )
849                 test(this.int_queue.contains(value),
850                     name ~ ": value should have been found in LinkedListQueue");
851 
852             test(this.int_queue.remove(value, all) == expected_to_remove,
853                 name ~ ": Removed wrong number of items from LinkedListQueue");
854             test(!this.int_queue.contains(value),
855                 name ~ ": value should NOT have been found in LinkedListQueue");
856 
857             if ( all )
858             {
859                 int[] result, match;
860                 match ~= value;
861                 this.expected_values = ocean.core.array.Transformation.remove(
862                     this.expected_values, match, result);
863             }
864             else
865             {
866                 foreach ( i, item; this.expected_values )
867                 {
868                     if ( item == value )
869                     {
870                         removeShift(this.expected_values, i);
871                         break;
872                     }
873                 }
874             }
875         }
876     }
877 
878 
879     {
880         // [1]
881         TestQueue testQueue = new TestQueue("Test 'remove' from LinkedListQueue with a single item");
882         testQueue.push([1]);
883         testQueue.remove(1);
884     }
885     {
886         // [3] 4
887         TestQueue testQueue = new TestQueue("Test 'remove' first item from LinkedListQueue");
888         testQueue.push([3, 4]);
889         testQueue.remove(3);
890     }
891     {
892         // 5 [6]
893         TestQueue testQueue = new TestQueue("Test 'remove' last item from LinkedListQueue");
894         testQueue.push([5, 6]);
895         testQueue.remove(6);
896     }
897     {
898         // [7] [8]
899         TestQueue testQueue = new TestQueue("Test 'remove' all items from LinkedListQueue");
900         testQueue.push([7, 8]);
901         testQueue.remove(7);
902         testQueue.remove(8);
903     }
904     {
905         // 9 [10] 11
906         TestQueue testQueue = new TestQueue("Test 'remove' middle item from LinkedListQueue");
907         testQueue.push([9, 10, 11]);
908         testQueue.remove(10);
909     }
910     {
911         // 12 [13] [14] 15
912         TestQueue testQueue = new TestQueue("Test 'remove' several middle items from LinkedListQueue");
913         testQueue.push([12, 13, 14, 15]);
914         testQueue.remove(13);
915         testQueue.remove(14);
916     }
917     {
918         // [16] 17 18 [19]
919         TestQueue testQueue = new TestQueue("Test 'remove' first and last items from LinkedListQueue");
920         testQueue.push([16, 17, 18, 19]);
921         testQueue.remove(16);
922         testQueue.remove(19);
923     }
924     {
925         // [20] 21 21 [20]
926         TestQueue testQueue = new TestQueue("Test 'remove' repeating values from LinkedListQueue");
927         testQueue.push([20, 21, 21, 20]);
928         testQueue.remove(20, 2);
929         testQueue.remove(20, 0);
930     }
931     {
932         // remove from an empty queue
933         TestQueue testQueue = new TestQueue("Test 'remove' on an empty LinkedListQueue");
934         testQueue.remove(1, 0);
935     }
936 }