1 ///
2 module cachetools.containers.lists;
3 
4 private import core.memory;
5 
6 private import std.experimental.allocator;
7 private import std.experimental.allocator.mallocator : Mallocator;
8 private import std.experimental.allocator.gc_allocator;
9 private import std.experimental.logger;
10 private import std.format;
11 
12 private import cachetools.internal;
13 
14 ///
15 /// N-way multilist
16 struct MultiDList(T, int N, Allocator = Mallocator, bool GCRangesAllowed = true)
17 {
18     static assert(N>0);
19     alias allocator = Allocator.instance;
20     struct Node {
21         T payload;
22         private:
23         Link[N] links;
24         Node* next(size_t i) @safe @nogc
25         {
26             return links[i].next;
27         }
28         Node* prev(size_t i) @safe @nogc
29         {
30             return links[i].prev;
31         }
32         alias payload this;
33     }
34     private 
35     {
36         struct Link
37         {
38             Node* prev;
39             Node* next;
40         }
41         Node*[N]    _heads;
42         Node*[N]    _tails;
43         size_t      _length;
44         
45     }
46     ~this() @safe
47     {
48         clear();
49     }
50     size_t length() const pure nothrow @safe @nogc {
51         return _length;
52     }
53 
54     Node* insert_last(T v) @safe nothrow
55     out
56     {
57         assert(_length>0);
58     }
59     do
60     {
61         auto n = make!(Node)(allocator, v);
62         static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) )
63         {
64             () @trusted
65             {
66                 GC.addRange(n, Node.sizeof);
67             }();
68         }
69         static foreach(index;0..N) {
70             if ( _heads[index] is null ) {
71                 _heads[index] = n;
72             }
73             n.links[index].prev = _tails[index];
74             if ( _tails[index] !is null )
75             {
76                 _tails[index].links[index].next = n;
77             }
78             _tails[index] = n;
79         }
80         _length++;
81         return n;
82     }
83 
84     void move_to_tail(Node* n, size_t i) @safe @nogc
85     in
86     {
87         assert(i < N);
88         assert(_length>0);
89     }
90     out
91     {
92         assert(_heads[i] !is null && _tails[i] !is null);
93     }
94     do
95     {
96         if ( n == _tails[i] ) {
97             return;
98         }
99         // unlink
100         if ( n.links[i].prev is null )
101         {
102             _heads[i] = n.links[i].next;
103         }
104         else
105         {
106             n.links[i].prev.links[i].next = n.links[i].next;
107         }
108         n.links[i].next.links[i].prev = n.links[i].prev;
109 
110         // insert back
111         if ( _heads[i] is null ) {
112             _heads[i] = n;
113         }
114         n.links[i].prev = _tails[i];
115         if ( _tails[i] !is null )
116         {
117             _tails[i].links[i].next = n;
118         }
119         n.links[i].next = null;
120         _tails[i] = n;
121     }
122 
123     void remove(Node* n) nothrow @safe @nogc
124     {
125         if ( n is null || _length == 0 )
126         {
127             return;
128         }
129         static foreach(i;0..N) {
130             if ( n.links[i].prev !is null ) {
131                 n.links[i].prev.links[i].next = n.links[i].next;
132             }
133             if ( n.links[i].next !is null ) {
134                 n.links[i].next.links[i].prev = n.links[i].prev;
135             }
136             if ( n == _tails[i] ) {
137                 _tails[i] = n.links[i].prev;
138             }
139             if ( n == _heads[i] ) {
140                 _heads[i] = n.links[i].next;
141             }
142         }
143         () @trusted {
144             static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) )
145             {
146                 GC.removeRange(n);
147             }
148             dispose(allocator, n);
149         }();
150         _length--;
151     }
152     Node* tail(size_t i) pure nothrow @safe @nogc
153     {
154         return _tails[i];
155     }
156     Node* head(size_t i) pure nothrow @safe @nogc
157     {
158         return _heads[i];
159     }
160     void clear() nothrow @safe @nogc
161     {
162         while(_length>0)
163         {
164             auto n = _heads[0];
165             remove(n);
166         }
167     }
168 }
169 
170 @safe unittest {
171     import std.algorithm;
172     import std.stdio;
173     import std.range;
174     struct Person
175     {
176         string name;
177         int    age;
178     }
179     MultiDList!(Person*, 2) mdlist;
180     Person[3] persons = [{"Alice", 11}, {"Bob", 9}, {"Carl", 10}];
181     foreach(i; 0..persons.length)
182     {
183         mdlist.insert_last(&persons[i]);
184     }
185     enum NameIndex = 0;
186     enum AgeIndex  = 1;
187     assert(mdlist.head(NameIndex).payload.name == "Alice");
188     assert(mdlist.head(AgeIndex).payload.age == 11);
189     assert(mdlist.tail(NameIndex).payload.name == "Carl");
190     assert(mdlist.tail(AgeIndex).payload.age == 10);
191     auto alice = mdlist.head(NameIndex);
192     auto bob = alice.next(NameIndex);
193     auto carl = bob.next(NameIndex);
194     mdlist.move_to_tail(alice, AgeIndex);
195     assert(mdlist.tail(AgeIndex).payload.age == 11);
196     mdlist.remove(alice);
197     assert(mdlist.head(NameIndex).payload.name == "Bob");
198     assert(mdlist.tail(NameIndex).payload.name == "Carl");
199     assert(mdlist.head(AgeIndex).payload.age == 9);
200     assert(mdlist.tail(AgeIndex).payload.age == 10);
201     mdlist.insert_last(&persons[0]); // B, C, A
202     mdlist.remove(carl); // B, A
203     alice = mdlist.tail(NameIndex);
204     assert(mdlist.length == 2);
205     assert(alice.payload.name == "Alice");
206     assert(alice.payload.age == 11);
207     assert(mdlist.head(NameIndex).payload.name == "Bob");
208     assert(mdlist.head(AgeIndex).payload.age == 9);
209     assert(alice.prev(AgeIndex) == bob);
210     assert(alice.prev(NameIndex) == bob);
211     assert(bob.prev(AgeIndex) is null);
212     assert(bob.prev(NameIndex) is null);
213     assert(bob.next(AgeIndex) == alice);
214     assert(bob.next(NameIndex) == alice);
215     mdlist.insert_last(&persons[2]); // B, A, C
216     carl = mdlist.tail(NameIndex);
217     mdlist.move_to_tail(alice, AgeIndex);
218     assert(bob.next(AgeIndex) == carl);
219     assert(bob.next(NameIndex) == alice);
220 }
221 
222 /// Double linked list
223 struct DList(T, Allocator = Mallocator, bool GCRangesAllowed = true) {
224     this(this) @disable;
225 
226     ///
227     struct Node(T) {
228         /// Node content.
229         T payload;
230         private Node!T* prev;
231         private Node!T* next;
232         alias payload this;
233     }
234     private {
235         alias allocator = Allocator.instance;
236         Node!T* _head;
237         Node!T* _tail;
238         ulong   _length;
239         
240         Node!T* _freelist;
241         uint    _freelist_len;
242         enum    _freelist_len_max = 100;
243     }
244 
245     private struct Range {
246         Node!T* _current;
247         T front() @safe {
248             return _current.payload;
249         }
250         void popFront() @safe {
251             _current = _current.next;
252         }
253         bool empty() @safe {
254             return _current is null;
255         }
256     }
257 
258     invariant {
259         assert
260         (
261             ( _length > 0 && _head !is null && _tail !is null) ||
262             ( _length == 0 && _tail is null && _tail is null) ||
263             ( _length == 1 && _tail == _head && _head !is null ),
264             "length: %s, head: %s, tail: %s".format(_length, _head, _tail)
265         );
266     }
267 
268     ~this() {
269         clear();
270     }
271 
272     /// Iterator over items
273     Range range() @safe {
274         return Range(_head);
275     }
276 
277     /// Number of items in list
278     ulong length() const pure nothrow @safe @nogc {
279         return _length;
280     }
281 
282     private void move_to_feelist(Node!T* n) @safe {
283         if ( _freelist_len < _freelist_len_max )
284         {
285             n.next = _freelist;
286             _freelist = n;
287             ++_freelist_len;
288         }
289         else
290         {
291             () @trusted {
292                 static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) ) {
293                     GC.removeRange(&n.payload);
294                 }
295                 dispose(allocator, n);
296             }();
297         }
298     }
299 
300     private Node!T* peek_from_freelist(ref T v) @safe {
301         if ( _freelist_len )
302         {
303             _freelist_len--;
304             auto r = _freelist;
305             _freelist = r.next;
306             r.next = r.prev = null;
307             r.payload = v;
308             return r;
309         }
310         else
311         {
312             auto n = make!(Node!T)(allocator, v);
313             static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) ) {
314                 () @trusted {
315                     GC.addRange(&n.payload, T.sizeof);
316                 }();
317             }
318             return n;
319         }
320     }
321 
322     /// insert item at list back.
323     alias insertBack = insert_last;
324     /// ditto
325     Node!T* insert_last(T v) @safe nothrow
326     out {
327         assert(_length>0);
328         assert(_head !is null && _tail !is null);
329     }
330     do {
331         Node!T* n = peek_from_freelist(v);
332         if ( _head is null ) {
333             _head = n;
334         }
335         n.prev = _tail;
336         if ( _tail !is null )
337         {
338             _tail.next = n;
339         }
340         _tail = n;
341         _length++;
342         return n;
343     }
344 
345     /// insert item at list front
346     alias insertFront = insert_first;
347     /// ditto
348     Node!T* insert_first(T v) @safe nothrow
349     out {
350         assert(_length>0);
351         assert(_head !is null && _tail !is null);
352     }
353     do {
354         Node!T* n = peek_from_freelist(v);
355         if ( _tail is null ) {
356             _tail = n;
357         }
358         n.next = _head;
359         if ( _head !is null )
360         {
361             _head.prev = n;
362         }
363         _head = n;
364         _length++;
365         return n;
366     }
367 
368     /// remove all items from list
369     void clear() @safe {
370         Node!T* n = _head, next;
371         while(n)
372         {
373             next = n.next;
374             () @trusted {
375                 static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) ) {
376                     GC.removeRange(&n.payload);
377                 }
378                 dispose(allocator, n);
379             }();
380             n = next;
381         }
382         n = _freelist;
383         while(n)
384         {
385             next = n.next;
386             () @trusted {
387                 static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) ) {
388                     GC.removeRange(&n.payload);
389                 }
390                 dispose(allocator, n);
391             }();
392             n = next;
393         }
394         _length = 0;
395         _freelist_len = 0;
396         _head = _tail = _freelist = null;
397     }
398 
399     /** 
400         pop front item.
401         Returns: true if list was not empty
402     **/
403     bool popFront() @safe {
404         if ( _length == 0 )
405         {
406             return false;
407         }
408         return remove(_head);
409     }
410 
411     /** 
412         pop last item.
413         Returns: true if list was not empty
414     **/
415     bool popBack() @safe {
416         if ( _length == 0 )
417         {
418             return false;
419         }
420         return remove(_tail);
421     }
422 
423     /// remove node by pointer. (safe until pointer is correct)
424     bool remove(Node!T* n) @safe @nogc
425     in {assert(_length>0);}
426     do {
427         if ( n.prev ) {
428             n.prev.next = n.next;
429         }
430         if ( n.next ) {
431             n.next.prev = n.prev;
432         }
433         if ( n == _tail ) {
434             _tail = n.prev;
435         }
436         if ( n == _head ) {
437             _head = n.next;
438         }
439         _length--;
440         move_to_feelist(n);
441         return true;
442     }
443 
444     /// move node to tail
445     void move_to_tail(Node!T* n) @safe @nogc
446     in {
447         assert(_length > 0);
448         assert(_head !is null && _tail !is null);
449     }
450     out {
451         assert(_tail == n && n.next is null);
452     }
453     do {
454         if ( n == _tail ) {
455             return;
456         }
457         // unlink
458         if ( n.prev is null )
459         {
460             _head = n.next;
461         }
462         else
463         {
464             n.prev.next = n.next;
465         }
466         n.next.prev = n.prev;
467         // insert back
468         n.prev = _tail;
469         if ( _tail !is null )
470         {
471             _tail.next = n;
472         }
473         n.next = null;
474         _tail = n;
475 
476     }
477 
478     /// move to head
479     void move_to_head(Node!T* n) @safe @nogc
480     in {
481         assert(_length > 0);
482         assert(_head !is null && _tail !is null);
483     }
484     out {
485         assert(_head == n && n.prev is null);
486     }
487     do {
488         if ( n == _head ) {
489             return;
490         }
491         // unlink
492         n.prev.next = n.next;
493         if ( n.next is null )
494         {
495             _tail = n.prev;
496         }
497         else
498         {
499             n.next.prev = n.prev;
500         }
501 
502         // insert front
503         n.next = _head;
504         if ( _head !is null )
505         {
506             _head.prev = n;
507         }
508         n.prev = null;
509         _head = n;
510 
511     }
512 
513     alias front = head;
514     /** 
515         head node
516         Returns: pointer to head node
517     **/
518     Node!T* head() @safe @nogc nothrow {
519         return _head;
520     }
521 
522     alias back = tail;
523     /** Tail node
524         Returns: pointer to tail node.
525     */
526     Node!T* tail() @safe @nogc nothrow {
527         return _tail;
528     }
529 }
530 
531 ///
532 struct SList(T, Allocator = Mallocator, bool GCRangesAllowed = true) {
533     this(this) @safe
534     {
535         // copy items
536         _Node!T* __newFirst, __newLast;
537         auto f = _first;
538         while(f)
539         {
540             auto v = f.v;
541             auto n = make!(_Node!T)(allocator, v);
542             static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) )
543             {
544                 () @trusted {
545                     GC.addRange(&n.v, T.sizeof);
546                 }();
547             }
548             if ( __newLast !is null ) {
549                 __newLast._next = n;
550             } else {
551                 __newFirst = n;
552             }
553             __newLast = n;
554             f = f._next;
555         }
556         _first = __newFirst;
557         _last = __newLast;
558         _freelist = null;
559         _freelist_len = 0;
560     }
561 
562     void opAssign(typeof(this) other) @safe
563     {
564         // copy items
565         debug(cachetools) safe_tracef("opAssign SList");
566         _Node!T* __newFirst, __newLast;
567         auto f = other._first;
568         while(f)
569         {
570             auto v = f.v;
571             auto n = make!(_Node!T)(allocator, v);
572             static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) )
573             {
574                 () @trusted {
575                     GC.addRange(&n.v, T.sizeof);
576                 }();
577             }
578             if ( __newLast !is null ) {
579                 __newLast._next = n;
580             } else {
581                 __newFirst = n;
582             }
583             __newLast = n;
584             f = f._next;
585         }
586         _first = __newFirst;
587         _last = __newLast;
588         _length = other._length;
589         _freelist = null;
590         _freelist_len = 0;
591     }
592 
593     ~this() @safe {
594         clear();
595     }
596 
597     package {
598         struct _Node(T) {
599             T v;
600             _Node!T *_next;
601         }
602         alias allocator = Allocator.instance;
603 
604         ulong _length;
605         _Node!T *_first;
606         _Node!T *_last;
607         
608         _Node!T* _freelist;
609         uint     _freelist_len;
610         enum     _freelist_len_max = 100;
611     }
612 
613     invariant {
614         try
615         {
616             assert (
617                 ( _length > 0 && _first !is null && _last !is null) ||
618                 ( _length == 0 && _first is null && _last is null),
619                 "length: %d, first: %s, last: %s".format(_length, _first, _last)
620             );
621         }
622         catch(Exception e)
623         {
624         }
625     }
626 
627     /// number of items in list
628     ulong length() const pure @nogc @safe nothrow {
629         return _length;
630     }
631     /// item empty?
632     bool empty() @nogc @safe const {
633         return _length == 0;
634     }
635     /// front item
636     T front() pure @nogc @safe {
637         return _first.v;
638     }
639     /// back item
640     T back() pure @nogc @safe {
641         return _last.v;
642     }
643 
644     private void move_to_feelist(_Node!T* n) @safe
645     {
646         if ( _freelist_len < _freelist_len_max )
647         {
648             n._next = _freelist;
649             _freelist = n;
650             ++_freelist_len;
651         }
652         else
653         {
654             (() @trusted {
655                 static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) )
656                 {
657                     GC.removeRange(&n.v);
658                 }
659                 dispose(allocator, n);
660             })();
661         }
662     }
663     private _Node!T* peek_from_freelist() @safe
664     {
665         if ( _freelist_len )
666         {
667             _freelist_len--;
668             auto r = _freelist;
669             _freelist = r._next;
670             r._next = null;
671             return r;
672         }
673         else
674         {
675             auto n = make!(_Node!T)(allocator);
676             static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) )
677             {
678                 () @trusted
679                 {
680                     GC.addRange(&n.v, T.sizeof);
681                 }();
682             }
683             return n;
684         }
685     }
686     /// pop front item
687     T popFront() @nogc @safe nothrow
688     in { assert(_first !is null); }
689     do {
690         T v = _first.v;
691         auto next = _first._next;
692         _length--;
693         move_to_feelist(_first);
694         _first = next;
695         if ( _first is null ) {
696             _last = null;
697         }
698         return v;
699     }
700     /// clear everything
701     void clear() @nogc @safe {
702         _Node!T* n = _first;
703         while( n !is null ) {
704             auto next = n._next;
705             (() @trusted {
706                 static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) )
707                 {
708                     GC.removeRange(&n.v);
709                 }
710                 dispose(allocator, n);
711             })();
712             n = next;
713         }
714         n = _freelist;
715         while( n !is null ) {
716             auto next = n._next;
717             (() @trusted {
718                 static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) )
719                 {
720                     GC.removeRange(&n.v);
721                 }
722                 dispose(allocator, n);
723             })();
724             n = next;
725         }
726         _length = _freelist_len = 0;
727         _first = _last = _freelist = null;
728     }
729     private struct Range(T) {
730         private {
731             _Node!T *current;
732         }
733         auto front() pure nothrow @safe @nogc @property {
734             return &current.v;
735         }
736         void popFront() @safe @nogc nothrow {
737             current = current._next;
738         }
739         bool empty() pure const nothrow @safe @nogc @property {
740             return current is null;
741         }
742     }
743     alias opSlice = range;
744     /// return range over list
745     Range!T range() {
746         return Range!T(_first);
747     }
748     /// insert item at front
749     void insertFront(T v) @safe nothrow
750     out{ assert(_first !is null && _last !is null);}
751     do {
752         _Node!T* n = peek_from_freelist();
753         n.v = v;
754         if ( _first !is null ) {
755             n._next = _first;
756         }
757         _first = n;
758         if ( _last is null ) {
759             _last = n;
760         }
761         _length++;
762     }
763     /// insert item at back
764     void insertBack(T v) @safe nothrow
765     out{ assert(_first !is null && _last !is null);}
766     do {
767         _Node!T* n = peek_from_freelist();
768         n.v = v;
769         if ( _last !is null ) {
770             _last._next = n;
771         } else {
772             _first = n;
773         }
774         _last = n;
775         _length++;
776     }
777     /// remove items by predicate
778     bool remove_by_predicate(scope bool delegate(T) @safe @nogc nothrow f) @nogc @trusted nothrow {
779         bool removed;
780         _Node!T *current = _first;
781         _Node!T *prev = null;
782         while (current !is null) {
783             auto next = current._next;
784             if ( !f(current.v) ) {
785                 prev = current;
786                 current = next;
787                 continue;
788             }
789             // do remove
790             _length--;
791             removed = true;
792             static if ( !is(Allocator == GCAllocator)  && UseGCRanges!T )
793             {
794                 GC.removeRange(current);
795             }
796             dispose(allocator, current);
797             if ( prev is null ) {
798                 _first = next;                    
799             } else {
800                 prev._next = next;
801             }
802             if ( next is null ) {
803                 _last = prev;
804             }
805         }
806         return removed;
807     }
808 }
809 
810 @safe @nogc nothrow unittest {
811     SList!int l;
812     assert(l.length() == 0);
813     l.insertFront(0);
814     assert(l.front() == 0);
815     l.popFront();
816     l.insertBack(1);
817     assert(l.front() == 1);
818     assert(l.length() == 1);
819     l.insertBack(2);
820     assert(l.front() == 1);
821     assert(l.back() == 2);
822     assert(l.length() == 2);
823     //log(l.range());
824     l.popFront();
825     assert(l.front() == 2);
826     assert(l.back() == 2);
827     assert(l.length() == 1);
828     l.insertBack(3);
829     l.insertBack(4);
830     //foreach(v; l[]){
831     //    log("v=%d\n", *v);
832     //}
833     //log("---\n");
834     bool removed;
835     removed = l.remove_by_predicate((n){return n==2;});
836     foreach(v; l[]){
837         //log("v=%d\n", *v);
838     }
839     assert(removed);
840     assert(l.length()==2);
841     //log("---\n");
842     removed = l.remove_by_predicate((n){return n==4;});
843     foreach(v; l[]){
844         //log("v=%d\n", *v);
845     }
846     assert(removed);
847     assert(l.length()==1);
848     //log("---\n");
849     removed = l.remove_by_predicate((n){return n==3;});
850     foreach(v; l[]){
851         //log("v=%d\n", *v);
852     }
853     assert(removed);
854     assert(l.length()==0);
855     //log("---\n");
856     removed = l.remove_by_predicate((n){return n==3;});
857     foreach(v; l[]){
858         //log("v=%d\n", *v);
859     }
860     assert(!removed);
861     assert(l.length()==0);
862     auto l1 = SList!int();
863     foreach(i;0..10000) {
864         l1.insertBack(i);
865     }
866     while(l1.length) {
867         l1.popFront();
868     }
869     foreach(i;0..10000) {
870         l1.insertFront(i);
871     }
872     while(l1.length) {
873         l1.popFront();
874     }
875 }
876 @safe unittest
877 {
878     class C
879     {
880         int c;
881         this(int v) {
882             c = v;
883         }
884     }
885     SList!C l2;
886     foreach(i;0..10000) {
887         l2.insertBack(new C(i));
888     }
889     while(l2.length) {
890         l2.popFront();
891     }
892 }
893 
894 @safe nothrow unittest {
895     import std.algorithm.comparison;
896 
897     DList!int dlist;
898     () @nogc 
899     {
900         auto n0 = dlist.insertFront(0);
901         assert(dlist.head.payload == 0);
902         dlist.remove(n0);
903         auto n1 = dlist.insert_last(1);
904         assert(dlist.length == 1);
905         dlist.remove(n1);
906         assert(dlist.length == 0);
907 
908         n1 = dlist.insert_first(1);
909         assert(dlist.length == 1);
910         dlist.remove(n1);
911         assert(dlist.length == 0);
912 
913         n1 = dlist.insert_first(1);
914         auto n2 = dlist.insert_last(2);
915         assert(dlist.length == 2);
916         dlist.move_to_tail(n1);
917         assert(dlist.head.payload == 2);
918         assert(dlist.tail.payload == 1);
919         dlist.move_to_head(n1);
920         assert(dlist.head.payload == 1);
921         assert(dlist.tail.payload == 2);
922         dlist.clear();
923         auto p = dlist.insertBack(1);
924         dlist.insertBack(2);
925         dlist.insertBack(3);
926         dlist.insertFront(0);
927         dlist.move_to_tail(p);
928         dlist.move_to_tail(p);
929         dlist.move_to_head(p);
930         dlist.move_to_head(p);
931         dlist.remove(p);
932     }();
933     assert(equal(dlist.range(), [0, 2, 3]));
934     dlist.clear();
935     class C
936     {
937         int c;
938         this(int v)
939         {
940             c = v;
941         }
942     }
943     DList!C dlist_c;
944     // test freelist ops
945     foreach(i;0..1000)
946     {
947         dlist_c.insertBack(new C(i));
948     }
949     foreach(i;0..500)
950     {
951         dlist_c.popFront();
952     }
953     assert(dlist_c.length() == 500);
954     dlist_c.clear();
955     dlist_c.popFront();
956     dlist_c.popBack();
957     assert(dlist_c.length() == 0);
958 }
959 
960 private byte useFreePosition(ubyte[] m) @safe @nogc
961 {
962     import core.bitop: bsf;
963     //
964     // find free position, mark it as used and return it
965     // least significant bit in freeMap[0] means _nodes[0]
966     // most significant bit in freeMap[$-1] means nodes[$-1]
967     //
968     auto l = m.length;
969     for(uint i=0; i < l;i++)
970     {
971         ubyte v = m[i];
972         if ( v < 255 )
973         {
974             auto p = bsf(v ^ 0xff);
975             m[i] += 1 << p;
976             return cast(byte)((i<<3)+p);
977         }
978     }
979     assert(0);
980 }
981 private void markFreePosition(ubyte[] m, size_t position) @safe @nogc
982 {
983     auto p = position >> 3;
984     auto b = position & 0x7;
985     m[p] &= (1<<b)^0xff;
986 }
987 
988 private bool isFreePosition(ubyte[] m, size_t position) @safe @nogc
989 {
990     auto p = position >> 3;
991     auto b = position & 0x7;
992     return (m[p] & (1<<b)) == 0;
993 }
994 private ubyte countBusy(ubyte[] m) @safe @nogc
995 {
996     import core.bitop;
997     ubyte s = 0;
998     foreach(b; m)
999     {
1000         s+=popcnt(b);
1001     }
1002     return s;
1003 }
1004 @safe unittest
1005 {
1006     import std.experimental.logger;
1007     globalLogLevel = LogLevel.info;
1008     import std.algorithm.comparison: equal;
1009     ubyte[] map = [0,0];
1010     auto p = useFreePosition(map);
1011     assert(p == 0, "expected 0, got %s".format(p));
1012     assert(map[0] == 1);
1013     assert(!isFreePosition(map, 0));
1014     assert(isFreePosition(map, 1));
1015 
1016     p = useFreePosition(map);
1017     assert(p == 1, "expected 1, got %s".format(p));
1018     map = [255,0];
1019     p = useFreePosition(map);
1020     assert(p == 8, "expected 8, got %s".format(p));
1021     assert(map[1] == 1);
1022     map = [255,0x01];
1023     p = useFreePosition(map);
1024     assert(p == 9, "expected 9, got %s".format(p));
1025     assert(equal(map, [0xff, 0x03]));
1026     markFreePosition(map, 8);
1027     assert(equal(map, [0xff, 0x02]), "got %s".format(map));
1028     markFreePosition(map, 9);
1029     assert(equal(map, [0xff, 0x00]), "got %s".format(map));
1030     markFreePosition(map, 0);
1031     assert(equal(map, [0xfe, 0x00]), "got %s".format(map));
1032 }
1033 
1034 ///
1035 /// Unrolled list
1036 ///
1037 struct CompressedList(T, Allocator = Mallocator, bool GCRangesAllowed = true)
1038 {
1039     alias allocator = Allocator.instance;
1040     alias StoredT = StoredType!T;
1041     //enum MAGIC = 0x00160162;
1042     enum PageSize = 512;    // in bytes
1043     enum NodesPerPage = PageSize/Node.sizeof;
1044     static assert(NodesPerPage >= 1, "Node is too large to use this List, use DList instead");
1045     static assert(NodesPerPage <= 255, "Strange, but Node size is too small to use this List, use DList instead");
1046 
1047     enum BitMapLength = NodesPerPage % 8 ? NodesPerPage/8+1 : NodesPerPage/8;
1048 
1049     ///
1050     /// unrolled list with support only for:
1051     /// 1. insert/delete front
1052     /// 2. insert/delete back
1053     /// 3. keep unstable "pointer" to arbitrary element
1054     /// 4. remove element by pointer
1055 
1056     struct Page {
1057         ///
1058         /// Page is fixed-length array of list Nodes
1059         /// with batteries
1060         ///
1061         //uint                _magic = MAGIC;
1062         //uint                _epoque;    // increment each time we move to freelist
1063         ubyte[BitMapLength] _freeMap;
1064         Page*               _prevPage;
1065         Page*               _nextPage;
1066         byte                _firstNode;
1067         byte                _lastNode;
1068         ubyte               _count;      // nodes counter
1069         Node[NodesPerPage]  _nodes;
1070     }
1071 
1072     struct Node {
1073         StoredT v;
1074         byte    p; // prev index
1075         byte    n; // next index
1076     }
1077 
1078     struct NodePointer {
1079         private
1080         {
1081             Page*   _page;
1082             byte    _index;
1083         }
1084         this(Page* page, byte index)
1085         {
1086             //_epoque = page._epoque;
1087             _page = page;
1088             _index = index;
1089         }
1090         ///
1091         /// This is unsafe as you may refer to deleted node.
1092         /// You are free to wrap it in @trusted code if you know what are you doing.
1093         ///
1094         T opUnary(string s)() @system if (s == "*")
1095         {
1096             assert(_page !is null);
1097             //assert(_page._magic == MAGIC, "Pointer resolution to freed or damaged page");
1098             //assert(_page._epoque == _epoque, "Page were freed");
1099             assert(!isFreePosition(_page._freeMap, _index), "you tried to access already free list element");
1100             return _page._nodes[_index].v;
1101         }
1102     }
1103 
1104     private struct Range {
1105         Page* page;
1106         byte  index;
1107 
1108         T front() @safe {
1109             if ( page !is null && index == -1)
1110             {
1111                 index = page._firstNode;
1112             }
1113             return page._nodes[index].v;
1114         }
1115         void popFront() @safe {
1116             if ( page !is null && index == -1)
1117             {
1118                 index = page._firstNode;
1119             }
1120             index = page._nodes[index].n;
1121             if ( index != -1 )
1122             {
1123                 return;
1124             }
1125             page = page._nextPage;
1126             if ( page is null )
1127             {
1128                 return;
1129             }
1130             index = page._firstNode;
1131         }
1132         bool empty() const @safe {
1133             return page is null;
1134         } 
1135     }
1136     /// Iterator over items.
1137     Range range() @safe {
1138         return Range(_pages_first, -1);
1139     }
1140     private
1141     {
1142         Page*   _pages_first, _pages_last;
1143         ulong   _length;
1144 
1145         Page*   _freelist;
1146         int     _freelist_len;
1147         enum    _freelist_len_max = 100;
1148     }
1149     private void move_to_freelist(Page* page) @safe @nogc {
1150         if ( _freelist_len >= _freelist_len_max )
1151         {
1152             debug(cachetools) safe_tracef("dispose page");
1153             () @trusted {
1154                 static if ( UseGCRanges!(Allocator,T, GCRangesAllowed) ) {
1155                     GC.removeRange(&page._nodes[0]);
1156                 }
1157                 dispose(allocator, page);
1158             }();
1159             return;
1160         }
1161         debug(cachetools) safe_tracef("put page in freelist");
1162         //page._epoque++;
1163         page._nextPage = _freelist;
1164         _freelist = page;
1165         _freelist_len++;
1166     }
1167 
1168     private Page* peek_from_freelist() @safe {
1169         if ( _freelist is null )
1170         {
1171             Page* page = make!Page(allocator);
1172             static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) ) {
1173                 () @trusted {
1174                     GC.addRange(&page._nodes[0], Node.sizeof * NodesPerPage);
1175                 }();
1176             }
1177             _freelist = page;
1178             _freelist_len = 1;
1179         }
1180         Page* p = _freelist;
1181         _freelist = p._nextPage;
1182         _freelist_len--;
1183         assert(_freelist_len>=0 && _freelist_len < _freelist_len_max);
1184         p._nextPage = p._prevPage = null;
1185         p._firstNode = p._lastNode = -1;
1186         return p;
1187     }
1188 
1189     ~this() @safe {
1190         clear();
1191     }
1192 
1193     /// remove anything from list
1194     void clear() @safe {
1195         _length = 0;
1196         Page* page = _pages_first, next;
1197         while(page)
1198         {
1199             next = page._nextPage;
1200             () @trusted {
1201                 static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) ) {
1202                     GC.removeRange(page);
1203                 }
1204                 dispose(allocator, page);
1205             }();
1206             page = next;
1207         }
1208         page = _freelist;
1209         while(page)
1210         {
1211             next = page._nextPage;
1212             () @trusted {
1213                 static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) ) {
1214                     GC.removeRange(page);
1215                 }
1216                 dispose(allocator, page);
1217             }();
1218             page = next;
1219         }
1220         _length = _freelist_len = 0;
1221         _pages_first = _pages_last = _freelist = null;
1222     }
1223 
1224     /// Is list empty?
1225     bool empty() @safe const {
1226         return _length == 0;
1227     }
1228 
1229     /// Items in the list.
1230     ulong length() @safe const {
1231         return _length;
1232     }
1233 
1234     /// remove node (by 'Pointer')
1235     void remove(ref NodePointer p) @system {
1236         if ( empty )
1237         {
1238             assert(0, "Tried to remove from empty list");
1239         }
1240         _length--;
1241         Page *page = p._page;
1242         byte index = p._index;
1243         assert(!isFreePosition(page._freeMap, index), "you tried to remove already free list element");
1244         debug(cachetools) safe_tracef("remove: page before: %s", *page);
1245         with (page)
1246         {
1247             assert(_count>0);
1248             _count--;
1249             // unlink from list
1250             auto next = _nodes[index].n;
1251             auto prev = _nodes[index].p;
1252             if ( prev >= 0)
1253             {
1254                 _nodes[prev].n = next;
1255             }
1256             else
1257             {
1258                 _firstNode = next;
1259             }
1260             if ( next >= 0)
1261             {
1262                 _nodes[next].p = prev;
1263             }
1264             else
1265             {
1266                 _lastNode = prev;
1267             }
1268             //_nodes[index].n = _nodes[index].p = -1;
1269             markFreePosition(_freeMap, index);
1270         }
1271         if ( page._count == 0 )
1272         {
1273             // relase this page
1274             if ( _pages_first == page )
1275             {
1276                 _pages_first = page._nextPage;
1277             }
1278             if ( _pages_last == page )
1279             {
1280                 _pages_last = page._prevPage;
1281             }
1282             if ( page._nextPage !is null )
1283             {
1284                 page._nextPage._prevPage = page._prevPage;
1285             }
1286             if ( page._prevPage !is null )
1287             {
1288                 page._prevPage._nextPage = page._nextPage;
1289             }
1290             move_to_freelist(page);
1291             if ( _pages_first is null )
1292             {
1293                 _pages_last = null;
1294             }
1295         }
1296         assert(page._count == countBusy(page._freeMap));
1297         debug(cachetools) safe_tracef("remove: page after: %s", *page);
1298     }
1299 
1300     /// List front item
1301     T front() @safe {
1302         if ( empty )
1303         {
1304             assert(0, "Tried to access front of empty list");
1305         }
1306         Page* p = _pages_first;
1307         assert( p !is null);
1308         assert( p._count > 0 );
1309         with(p)
1310         {
1311             return _nodes[_firstNode].v;
1312         }
1313     }
1314 
1315     /// Pop front item
1316     void popFront() @safe {
1317         if ( empty )
1318         {
1319             assert(0, "Tried to popFront from empty list");
1320         }
1321         _length--;
1322         Page* page = _pages_first;
1323         debug(cachetools) safe_tracef("popfront: page before: %s", *page);
1324         assert(page !is null);
1325         with (page) {
1326             assert(_count>0);
1327             assert(!isFreePosition(_freeMap, _firstNode));
1328             auto first = _firstNode;
1329             auto next = _nodes[first].n;
1330             markFreePosition(_freeMap, first);
1331             if ( next >= 0 )
1332             {
1333                 _nodes[next].p = -1;
1334             }
1335             //_nodes[first].n = _nodes[first].p = -1;
1336             _count--;
1337             _firstNode = next;
1338         }
1339         if ( page._count == 0 )
1340         {
1341             // relase this page
1342             _pages_first = page._nextPage;
1343             move_to_freelist(page);
1344             if ( _pages_first is null )
1345             {
1346                 _pages_last = null;
1347             }
1348             else
1349             {
1350                 _pages_first._prevPage = null;
1351             }
1352         }
1353         debug(cachetools) safe_tracef("popfront: page after: %s", *page);
1354     }
1355 
1356     /// Insert item at front.
1357     NodePointer insertFront(T v) @safe {
1358         _length++;
1359         Page* page = _pages_first;
1360         if ( page is null )
1361         {
1362             page = peek_from_freelist();
1363             _pages_first = _pages_last = page;
1364         }
1365         if (page._count == NodesPerPage)
1366         {
1367             Page* new_page = peek_from_freelist();
1368             new_page._nextPage = page;
1369             page._prevPage = new_page;
1370             _pages_first = new_page;
1371             page = new_page;
1372         }
1373         // there is free space
1374         auto index = useFreePosition(page._freeMap);
1375         assert(index < NodesPerPage);
1376         page._nodes[index].v = v;
1377         page._nodes[index].p = -1;
1378         page._nodes[index].n = page._firstNode;
1379         if (page._count == 0)
1380         {
1381             page._firstNode = page._lastNode = cast(ubyte)index;
1382         }
1383         else
1384         {
1385             assert(page._firstNode >= 0);
1386             assert(!isFreePosition(page._freeMap, page._firstNode));
1387             page._nodes[page._firstNode].p = cast(ubyte)index;
1388             page._firstNode = cast(ubyte)index;
1389         }
1390         page._count++;
1391         assert(page._count == countBusy(page._freeMap));
1392         debug(cachetools) safe_tracef("page: %s", *page);
1393         return NodePointer(page, index);
1394     }
1395 
1396     /// List back item.
1397     T back() @safe {
1398         if ( empty )
1399         {
1400             assert(0, "Tried to access back of empty list");
1401         }
1402         Page* p = _pages_last;
1403         assert( p !is null);
1404         assert( p._count > 0 );
1405         debug(cachetools) safe_tracef("page: %s", *p);
1406         with(p)
1407         {
1408             return _nodes[_lastNode].v;
1409         }
1410     }
1411 
1412     /// Pop back item from list.
1413     void popBack() @safe {
1414         if ( empty )
1415         {
1416             assert(0, "Tried to popBack from empty list");
1417         }
1418         _length--;
1419         Page* page = _pages_last;
1420         assert(page !is null);
1421         with (page) {
1422             assert(_count>0);
1423             assert(!isFreePosition(_freeMap, _lastNode));
1424             auto last = _lastNode;
1425             auto prev = _nodes[last].p;
1426             markFreePosition(_freeMap, last);
1427             if ( prev >=0 )
1428             {
1429                 _nodes[prev].n = -1;
1430             }
1431             //_nodes[last].n = _nodes[last].p = -1;
1432             _count--;
1433             _lastNode = prev;
1434         }
1435         if ( page._count == 0 )
1436         {
1437             debug(cachetools) safe_tracef("release page");
1438             // relase this page
1439             _pages_last = page._prevPage;
1440             move_to_freelist(page);
1441             if ( _pages_last is null )
1442             {
1443                 _pages_first = null;
1444             }
1445             else
1446             {
1447                 _pages_last._nextPage = null;
1448             }
1449         }
1450     }
1451 
1452     /// Insert item back.
1453     NodePointer insertBack(T v) @safe {
1454         _length++;
1455         Page* page = _pages_last;
1456         if ( page is null )
1457         {
1458             page = peek_from_freelist();
1459             _pages_first = _pages_last = page;
1460         }
1461         if (page._count == NodesPerPage)
1462         {
1463             Page* new_page = peek_from_freelist();
1464             new_page._prevPage = page;
1465             page._nextPage = new_page;
1466             _pages_last = new_page;
1467             page = new_page;
1468         }
1469         // there is free space
1470         auto index = useFreePosition(page._freeMap);
1471         assert(index < NodesPerPage);
1472         page._nodes[index].v = v;
1473         page._nodes[index].n = -1;
1474         page._nodes[index].p = page._lastNode;
1475         if (page._count == 0)
1476         {
1477             page._firstNode = page._lastNode = cast(ubyte)index;
1478         }
1479         else
1480         {
1481             assert(page._lastNode >= 0);
1482             assert(!isFreePosition(page._freeMap, page._lastNode));
1483             page._nodes[page._lastNode].n = cast(ubyte)index;
1484             page._lastNode = cast(ubyte)index;
1485         }
1486         page._count++;
1487         assert(page._count == countBusy(page._freeMap));
1488         debug(cachetools) safe_tracef("page: %s", *page);
1489         return NodePointer(page, index);
1490     }
1491 }
1492 
1493 ///
1494 @safe unittest
1495 {
1496     import std.experimental.logger;
1497     import std.algorithm;
1498     import std.range;
1499 
1500     globalLogLevel = LogLevel.info;
1501     CompressedList!int list;
1502     foreach(i;0..66)
1503     {
1504         list.insertFront(i);
1505         assert(list.front == i);
1506     }
1507     assert(list.length == 66);
1508     assert(list.back == 0);
1509     list.popFront();
1510     assert(list.length == 65);
1511     assert(list.front == 64);
1512     list.popFront();
1513     assert(list.length == 64);
1514     assert(list.front == 63);
1515     while( !list.empty )
1516     {
1517         list.popFront();
1518     }
1519     foreach(i;1..19)
1520     {
1521         list.insertFront(i);
1522         assert(list.front == i);
1523     }
1524     foreach(i;1..19)
1525     {
1526         assert(list.back == i);
1527         assert(list.length == 19-i);
1528         list.popBack();
1529     }
1530     assert(list.empty);
1531     auto p99 = list.insertBack(99);
1532     assert(list.front == 99);
1533     assert(list.back == 99);
1534     auto p100 = list.insertBack(100);
1535     assert(list.front == 99);
1536     assert(list.back == 100);
1537     auto p98 = list.insertFront(98);
1538     auto p101 = list.insertBack(101);
1539     () @trusted // * and remove for poiners is unsafe
1540     {
1541         assert(*p98 == 98);
1542         assert(list.length == 4);
1543         list.remove(p98);
1544         assert(list.length == 3);
1545         assert(list.front == 99);
1546         list.remove(p100);
1547         assert(list.length == 2);
1548         assert(list.front == 99);
1549         assert(list.back == 101);
1550         list.remove(p99);
1551         assert(list.length == 1);
1552         list.clear();
1553 
1554         foreach(i;0..1000)
1555         {
1556             list.insertBack(i);
1557         }
1558         assert(equal(list.range(), iota(0,1000)));
1559         list.clear();
1560 
1561         iota(0, 1000).
1562             map!(i => list.insertBack(i)).
1563             array.
1564             each!(p => list.remove(p));
1565         assert(list.empty);
1566         iota(0, 1000).map!(i => list.insertBack(i));
1567         auto r = list.range();
1568         while(!r.empty)
1569         {
1570             int v = r.front;
1571             r.popFront();
1572         }
1573         assert(list.empty);
1574     }();
1575 
1576     () @nogc
1577     {
1578         struct S {}
1579         CompressedList!(immutable S) islist;
1580         immutable S s = S();
1581         islist.insertFront(s);
1582     }();
1583     class C
1584     {
1585         int c;
1586         this(int v) {
1587             c = v;
1588         }
1589     }
1590     CompressedList!C clist;
1591     foreach(i;0..5000)
1592     {
1593         clist.insertBack(new C(i));
1594     }
1595     foreach(i;0..4500)
1596     {
1597         clist.popBack();
1598     }
1599     assert(clist.length == 500);
1600     clist.clear();
1601 }