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