1 module cachetools.containers.lists;
2 
3 private import std.experimental.allocator;
4 private import std.experimental.allocator.mallocator : Mallocator;
5 private import std.experimental.logger;
6 private import std.format;
7 
8 ///
9 /// N-way multilist
10 struct MultiDList(T, int N, Allocator = Mallocator) {
11     alias allocator = Allocator.instance;
12     struct Node {
13         T payload;
14     private:
15         Link[N] links;
16         Node* next(size_t i) @safe @nogc {
17             return links[i].next;
18         }
19 
20         Node* prev(size_t i) @safe @nogc {
21             return links[i].prev;
22         }
23 
24         alias payload this;
25     }
26 
27     private {
28         struct Link {
29             Node* prev;
30             Node* next;
31         }
32 
33         Node*[N] _heads;
34         Node*[N] _tails;
35         size_t _length;
36 
37     }
38     size_t length() const pure nothrow @safe @nogc {
39         return _length;
40     }
41 
42     Node* insert_last(T v) @safe nothrow
43     out {
44         assert(_length > 0);
45     }
46     do {
47         auto n = make!(Node)(allocator, v);
48         static foreach (index; 0 .. N) {
49             if (_heads[index] is null) {
50                 _heads[index] = n;
51             }
52             n.links[index].prev = _tails[index];
53             if (_tails[index]!is null) {
54                 _tails[index].links[index].next = n;
55             }
56             _tails[index] = n;
57         }
58         _length++;
59         return n;
60     }
61 
62     void move_to_tail(Node* n, size_t i) @safe @nogc
63     in {
64         assert(i < N);
65         assert(_length > 0);
66     }
67     out {
68         assert(_heads[i]!is null && _tails[i]!is null);
69     }
70     do {
71         if (n == _tails[i]) {
72             return;
73         }
74         // unlink
75         if (n.links[i].prev is null) {
76             _heads[i] = n.links[i].next;
77         } else {
78             n.links[i].prev.links[i].next = n.links[i].next;
79         }
80         if (n.links[i].next is null) {
81             _tails[i] = n.links[i].prev;
82         } else {
83             n.links[i].next.links[i].prev = n.links[i].prev;
84         }
85         // insert back
86         if (_heads[i] is null) {
87             _heads[i] = n;
88         }
89         n.links[i].prev = _tails[i];
90         if (_tails[i]!is null) {
91             _tails[i].links[i].next = n;
92         }
93         n.links[i].next = null;
94         _tails[i] = n;
95     }
96 
97     void remove(Node* n) nothrow @safe @nogc {
98         if (n is null || _length == 0) {
99             return;
100         }
101         static foreach (i; 0 .. N) {
102             if (n.links[i].prev !is null) {
103                 n.links[i].prev.links[i].next = n.links[i].next;
104             }
105             if (n.links[i].next !is null) {
106                 n.links[i].next.links[i].prev = n.links[i].prev;
107             }
108             if (n == _tails[i]) {
109                 _tails[i] = n.links[i].prev;
110             }
111             if (n == _heads[i]) {
112                 _heads[i] = n.links[i].next;
113             }
114         }
115         (() @trusted { dispose(allocator, n); })();
116         _length--;
117     }
118 
119     Node* tail(size_t i) pure nothrow @safe @nogc {
120         return _tails[i];
121     }
122 
123     Node* head(size_t i) pure nothrow @safe @nogc {
124         return _heads[i];
125     }
126 
127     void clear() nothrow @safe @nogc {
128         while (_length > 0) {
129             auto n = _heads[0];
130             remove(n);
131         }
132     }
133 }
134 
135 @safe unittest {
136     import std.algorithm;
137     import std.stdio;
138     import std.range;
139 
140     struct Person {
141         string name;
142         int age;
143     }
144 
145     MultiDList!(Person*, 2) mdlist;
146     Person[3] persons = [{"Alice", 11}, {"Bob", 9}, {"Carl", 10}];
147     foreach (i; 0 .. persons.length) {
148         mdlist.insert_last(&persons[i]);
149     }
150     enum NameIndex = 0;
151     enum AgeIndex = 1;
152     assert(mdlist.head(NameIndex).payload.name == "Alice");
153     assert(mdlist.head(AgeIndex).payload.age == 11);
154     assert(mdlist.tail(NameIndex).payload.name == "Carl");
155     assert(mdlist.tail(AgeIndex).payload.age == 10);
156     auto alice = mdlist.head(NameIndex);
157     auto bob = alice.next(NameIndex);
158     auto carl = bob.next(NameIndex);
159     mdlist.move_to_tail(alice, AgeIndex);
160     assert(mdlist.tail(AgeIndex).payload.age == 11);
161     mdlist.remove(alice);
162     assert(mdlist.head(NameIndex).payload.name == "Bob");
163     assert(mdlist.tail(NameIndex).payload.name == "Carl");
164     assert(mdlist.head(AgeIndex).payload.age == 9);
165     assert(mdlist.tail(AgeIndex).payload.age == 10);
166     mdlist.insert_last(&persons[0]); // B, C, A
167     mdlist.remove(carl); // B, A
168     alice = mdlist.tail(NameIndex);
169     assert(mdlist.length == 2);
170     assert(alice.payload.name == "Alice");
171     assert(alice.payload.age == 11);
172     assert(mdlist.head(NameIndex).payload.name == "Bob");
173     assert(mdlist.head(AgeIndex).payload.age == 9);
174     assert(alice.prev(AgeIndex) == bob);
175     assert(alice.prev(NameIndex) == bob);
176     assert(bob.prev(AgeIndex) is null);
177     assert(bob.prev(NameIndex) is null);
178     assert(bob.next(AgeIndex) == alice);
179     assert(bob.next(NameIndex) == alice);
180     mdlist.insert_last(&persons[2]); // B, A, C
181     carl = mdlist.tail(NameIndex);
182     mdlist.move_to_tail(alice, AgeIndex);
183     assert(bob.next(AgeIndex) == carl);
184     assert(bob.next(NameIndex) == alice);
185 }
186 
187 struct DList(T, Allocator = Mallocator) {
188     this(this) @disable;
189     struct Node(T) {
190         T payload;
191         private Node!T* prev;
192         private Node!T* next;
193         alias payload this;
194     }
195 
196     private {
197         alias allocator = Allocator.instance;
198         Node!T* _head;
199         Node!T* _tail;
200         ulong _length;
201     }
202 
203     invariant {
204         assert((_length > 0 && _head !is null && _tail !is null) || (_length == 0
205                 && _tail is null && _tail is null) || (_length == 1 && _tail == _head && _head !is null),
206                 "length: %s, head: %s, tail: %s".format(_length, _head, _tail));
207     }
208 
209     ulong length() const pure nothrow @safe @nogc {
210         return _length;
211     }
212 
213     Node!T* insert_last(T v) @safe nothrow
214     out {
215         assert(_length > 0);
216         assert(_head !is null && _tail !is null);
217     }
218     do {
219         auto n = make!(Node!T)(allocator);
220         n.payload = v;
221         if (_head is null) {
222             _head = n;
223         }
224         n.prev = _tail;
225         if (_tail !is null) {
226             _tail.next = n;
227         }
228         _tail = n;
229         _length++;
230         return n;
231     }
232 
233     alias insertFront = insert_first;
234     Node!T* insert_first(T v) @safe nothrow
235     out {
236         assert(_length > 0);
237         assert(_head !is null && _tail !is null);
238     }
239     do {
240         auto n = make!(Node!T)(allocator);
241         n.payload = v;
242         if (_tail is null) {
243             _tail = n;
244         }
245         n.next = _head;
246         if (_head !is null) {
247             _head.prev = n;
248         }
249         _head = n;
250         _length++;
251         return n;
252     }
253 
254     bool remove(Node!T* n) @safe @nogc
255     in {
256         assert(_length > 0);
257     }
258     do {
259         if (n.prev) {
260             n.prev.next = n.next;
261         }
262         if (n.next) {
263             n.next.prev = n.prev;
264         }
265         if (n == _tail) {
266             _tail = n.prev;
267         }
268         if (n == _head) {
269             _head = n.next;
270         }
271         (() @trusted { dispose(allocator, n); })();
272         _length--;
273         return true;
274     }
275 
276     void move_to_tail(Node!T* n) @safe @nogc
277     in {
278         assert(_length > 0);
279         assert(_head !is null && _tail !is null);
280     }
281     out {
282         assert(_tail == n && n.next is null);
283     }
284     do {
285         if (n == _tail) {
286             return;
287         }
288         // unlink
289         if (n.prev is null) {
290             _head = n.next;
291         } else {
292             n.prev.next = n.next;
293         }
294         if (n.next is null) {
295             _tail = n.prev;
296         } else {
297             n.next.prev = n.prev;
298         }
299         // insert back
300         if (_head is null) {
301             _head = n;
302         }
303         n.prev = _tail;
304         if (_tail !is null) {
305             _tail.next = n;
306         }
307         n.next = null;
308         _tail = n;
309 
310         ////debug(cachetools) tracef("n: %s".format(*n));
311         //assert(n.next !is null);
312         ////debug tracef("m-t-t: %s, tail: %s", *n, *_tail);
313         //assert(n.next, "non-tail entry have no 'next' pointer?");
314         //if ( _head == n ) {
315         //    assert(n.prev is null);
316         //    _head = n.next;
317         //} else {
318         //    n.prev.next = n.next;
319         //}
320         //// move this node to end
321         //n.next.prev = n.prev;
322         //n.next = null;
323         //tail.next = n;
324         //_tail = n;
325     }
326 
327     Node!T* head() @safe @nogc nothrow {
328         return _head;
329     }
330 
331     Node!T* tail() @safe @nogc nothrow {
332         return _tail;
333     }
334 }
335 
336 struct SList(T, Allocator = Mallocator) {
337     this(this) @safe {
338         // copy items
339         _Node!T* __newFirst, __newLast;
340         auto f = _first;
341         while (f) {
342             auto v = f.v;
343             auto n = make!(_Node!T)(allocator, v);
344             if (__newLast !is null) {
345                 __newLast._next = n;
346             } else {
347                 __newFirst = n;
348             }
349             __newLast = n;
350             f = f._next;
351         }
352         _first = __newFirst;
353         _last = __newLast;
354     }
355 
356     package {
357         struct _Node(T) {
358             T v;
359             _Node!T* _next;
360         }
361 
362         alias allocator = Allocator.instance;
363 
364         ulong _length;
365         _Node!T* _first;
366         _Node!T* _last;
367     }
368 
369     invariant {
370         assert((_length > 0 && _first !is null && _last !is null) || (_length == 0
371                 && _first is null && _last is null));
372     }
373 
374     ~this() {
375         clear();
376     }
377 
378     ulong length() const pure @nogc @safe nothrow {
379         return _length;
380     }
381 
382     bool empty() @nogc @safe const {
383         return _length == 0;
384     }
385 
386     T front() pure @nogc @safe {
387         return _first.v;
388     }
389 
390     T back() pure @nogc @safe {
391         return _last.v;
392     }
393 
394     T popFront() @nogc @safe nothrow
395     in {
396         assert(_first !is null);
397     }
398     do {
399         T v = _first.v;
400         auto next = _first._next;
401         (() @trusted { dispose(allocator, _first); })();
402         _first = next;
403         if (_first is null) {
404             _last = null;
405         }
406         _length--;
407         return v;
408     }
409 
410     void clear() @nogc @safe {
411         _Node!T* n = _first;
412         while (n !is null) {
413             auto next = n._next;
414             (() @trusted { dispose(allocator, n); })();
415             n = next;
416         }
417     }
418 
419     private struct Range(T) {
420         private {
421             _Node!T* current;
422         }
423         auto front() pure nothrow @safe @nogc @property {
424             return &current.v;
425         }
426 
427         void popFront() @safe @nogc nothrow {
428             current = current._next;
429         }
430 
431         bool empty() pure const nothrow @safe @nogc @property {
432             return current is null;
433         }
434     }
435 
436     alias opSlice = range;
437     auto range() {
438         return Range!T(_first);
439     }
440 
441     void insertFront(T v) @safe nothrow
442     out {
443         assert(_first !is null && _last !is null);
444     }
445     do {
446         auto n = make!(_Node!T)(allocator, v);
447         if (_first !is null) {
448             n._next = _first;
449         }
450         _first = n;
451         if (_last is null) {
452             _last = n;
453         }
454         _length++;
455     }
456 
457     void insertBack(T v) @safe nothrow
458     out {
459         assert(_first !is null && _last !is null);
460     }
461     do {
462         auto n = make!(_Node!T)(allocator, v);
463         if (_last !is null) {
464             _last._next = n;
465         } else {
466             _first = n;
467         }
468         _last = n;
469         _length++;
470     }
471 
472     bool remove_by_predicate(scope bool delegate(T) @safe @nogc nothrow f) @nogc @trusted nothrow {
473         bool removed;
474         _Node!T* current = _first;
475         _Node!T* prev = null;
476         while (current !is null) {
477             auto next = current._next;
478             if (!f(current.v)) {
479                 prev = current;
480                 current = next;
481                 continue;
482             }
483             // do remove
484             _length--;
485             removed = true;
486             dispose(allocator, current);
487             if (prev is null) {
488                 _first = next;
489             } else {
490                 prev._next = next;
491             }
492             if (next is null) {
493                 _last = prev;
494             }
495         }
496         return removed;
497     }
498 }
499 
500 @safe @nogc nothrow unittest {
501     SList!int l;
502     assert(l.length() == 0);
503     l.insertFront(0);
504     assert(l.front() == 0);
505     l.popFront();
506     l.insertBack(1);
507     assert(l.front() == 1);
508     assert(l.length() == 1);
509     l.insertBack(2);
510     assert(l.front() == 1);
511     assert(l.back() == 2);
512     assert(l.length() == 2);
513     //log(l.range());
514     l.popFront();
515     assert(l.front() == 2);
516     assert(l.back() == 2);
517     assert(l.length() == 1);
518     l.insertBack(3);
519     l.insertBack(4);
520     //foreach(v; l[]){
521     //    log("v=%d\n", *v);
522     //}
523     //log("---\n");
524     bool removed;
525     removed = l.remove_by_predicate((n) { return n == 2; });
526     foreach (v; l[]) {
527         //log("v=%d\n", *v);
528     }
529     assert(removed);
530     assert(l.length() == 2);
531     //log("---\n");
532     removed = l.remove_by_predicate((n) { return n == 4; });
533     foreach (v; l[]) {
534         //log("v=%d\n", *v);
535     }
536     assert(removed);
537     assert(l.length() == 1);
538     //log("---\n");
539     removed = l.remove_by_predicate((n) { return n == 3; });
540     foreach (v; l[]) {
541         //log("v=%d\n", *v);
542     }
543     assert(removed);
544     assert(l.length() == 0);
545     //log("---\n");
546     removed = l.remove_by_predicate((n) { return n == 3; });
547     foreach (v; l[]) {
548         //log("v=%d\n", *v);
549     }
550     assert(!removed);
551     assert(l.length() == 0);
552     auto l1 = SList!int();
553     foreach (i; 0 .. 100) {
554         l1.insertBack(i);
555     }
556     while (l1.length) {
557         l1.popFront();
558     }
559     foreach (i; 0 .. 100) {
560         l1.insertFront(i);
561     }
562     while (l1.length) {
563         l1.popFront();
564     }
565 }
566 
567 @safe @nogc nothrow unittest {
568     DList!int dlist;
569     auto n0 = dlist.insertFront(0);
570     assert(dlist.head.payload == 0);
571     dlist.remove(n0);
572     auto n1 = dlist.insert_last(1);
573     assert(dlist.length == 1);
574     dlist.remove(n1);
575     assert(dlist.length == 0);
576 
577     n1 = dlist.insert_first(1);
578     assert(dlist.length == 1);
579     dlist.remove(n1);
580     assert(dlist.length == 0);
581 
582     n1 = dlist.insert_first(1);
583     auto n2 = dlist.insert_last(2);
584     assert(dlist.length == 2);
585     dlist.move_to_tail(n1);
586     assert(dlist.head.payload == 2);
587     assert(dlist.tail.payload == 1);
588 }