1 module libd.datastructures.linkedlist;
2 
3 import libd.util.errorhandling : onOutOfMemoryError;
4 import libd.memory;
5 import libd.memory.ptr;
6 import libd.algorithm : OptimisationHint;
7 
8 // "Default" linked list is a double linked list since that's easier for me to implement >xP
9 @nogc nothrow
10 @(OptimisationHint.rangeFasterThanIndex)
11 struct LinkedList(alias T, alias AllocT = SystemAllocator)
12 {
13     static struct Node
14     {
15         T value;
16         Node* next;
17         Node* prev;
18     }
19 
20     private size_t _length;
21     private MaybeNullPtr!(Node, AllocT.Tag) _head;
22     private MaybeNullPtr!(Node, AllocT.Tag) _tail;
23     private AllocatorWrapperOf!AllocT _alloc;
24 
25     @disable this(this) {}
26 
27     this()(AllocatorWrapperOf!AllocT alloc)
28     {
29         this._alloc = alloc;
30     }
31 
32     @nogc nothrow
33     ~this()
34     {
35         while(this._head !is null)
36         {
37             auto head = this._head;
38             this._head = head.next;
39             this._alloc.dispose(head);
40         }
41     }
42 
43     alias put = putTail;
44     void putTail()(auto ref T value)
45     {
46         auto node = this._alloc.make!Node(value);
47         if(node is null)
48             onOutOfMemoryError(null);
49 
50         if(this._head is null)
51         {
52             this._head = node;
53             this._tail = node;
54         }
55         else
56         {
57             auto tail = this._tail;
58             this._tail.next = node;
59             node.prev = tail;
60             this._tail = node;
61         }
62 
63         this._length++;
64     }
65 
66     void putTail(Args...)(scope auto ref Args args)
67     {
68         foreach(ref value; args)
69             this.putTail(value);        
70     }
71 
72     void moveTail()(auto ref T value)
73     {        
74         auto node = this._alloc.make!Node();
75         if(node is null)
76             onOutOfMemoryError(null);
77         move(value, node.value);
78 
79         if(this._head is null)
80         {
81             this._head = node;
82             this._tail = node;
83         }
84         else
85         {
86             auto tail = this._tail;
87             this._tail.next = node;
88             node.prev = tail;
89             this._tail = node;
90         }
91 
92         this._length++;
93     }
94 
95     void putHead()(auto ref T value)
96     {
97         auto node = this._alloc.make!Node(value);
98         if(node is null)
99             onOutOfMemoryError(null);
100         
101         if(this._head is null)
102         {
103             this._head = node;
104             this._tail = node;
105         }
106         else
107         {
108             auto head = this._head;
109             this._head.prev = node;
110             node.next = head;
111             this._head = node;
112         }
113 
114         this._length++;
115     }
116 
117     void insertAt()(size_t index, auto ref T value)
118     {
119         auto node = this._alloc.make!Node(value);
120         if(node is null)
121             onOutOfMemoryError(null);
122 
123         assert(index <= this._length, "Index out of bounds.");
124 
125         if(this._length == 0) // Special case
126         {
127             this._head = node;
128             this._tail = node;
129         }
130         else if(index == 0) // Special case
131         {
132             this._head.prev = node;
133             node.next = this._head;
134             this._head = node;
135         }
136         else if(index == this._length) // Special case
137         {
138             this._tail.next = node;
139             node.prev = this._tail;
140             this._tail = node;
141         }
142         else
143         {
144             auto head = this._head;
145             foreach(i; 0..index)
146                 head = head.next;
147             assert(head !is null);
148             head = head.prev;
149             
150             auto next = head.next;
151             head.next = node;
152             node.next = next;
153             node.prev = head;
154             if(next !is null)
155                 next.prev = node;
156         }
157 
158         this._length++;
159     }
160 
161     alias getAt = getAtHead;
162     ref inout(T) getAtHead()(size_t index) inout
163     {
164         return this.getNodeAtHead(index).value;
165     }
166 
167     alias removeAt = removeAtHead;
168     void removeAtHead()(size_t index, scope ref T dest)
169     {
170         this.removeAtImpl!getNodeAtHead(index, dest);
171     }
172 
173     T removeAtHead()(size_t index)
174     {
175         T value;
176         this.removeAtHead(index, value);
177         return value;
178     }
179 
180     void removeAtTail()(size_t index, scope ref T dest)
181     {
182         this.removeAtImpl!getNodeAtTail(index, dest);
183     }
184 
185     T removeAtTail()(size_t index)
186     {
187         T value;
188         this.removeAtTail(index, value);
189         return value;
190     }
191 
192     @property @trusted
193     auto range() inout
194     {
195         static struct Range
196         {
197             Node* node;
198 
199             @nogc nothrow:
200 
201             bool empty() { return this.node is null; }
202             void popFront()
203             {
204                 assert(!this.empty, "Cannot pop an empty range.");
205                 this.node = this.node.next;
206             }
207             ref T front()
208             {
209                 assert(!this.empty, "Cannot access front of an empty range.");
210                 return this.node.value;
211             }
212         }
213 
214         return Range(cast(Node*)this._head.ptr);
215     }
216 
217     @property @safe
218     size_t length() const
219     {
220         return this._length;
221     }
222 
223     @safe size_t opDollar() const { return this.length; }
224     
225     @safe
226     ref inout(T) opIndex(size_t index) inout
227     {
228         return this.getAt(index);
229     }
230 
231     private void removeAtImpl(alias GetterFunc)(size_t index, scope ref T dest)
232     {        
233         auto node = GetterFunc(index);
234         move(node.value, dest);
235 
236         if(this._length == 1)
237         {
238             this._head = null;
239             this._tail = null;
240         }
241         else if(index == 0)
242         {
243             if(node.next !is null)
244                 node.next.prev = null;
245             this._head = node.next;
246         }
247         else if(index == this._length-1)
248         {
249             if(node.prev !is null)
250                 node.prev.next = null;
251             this._tail = node.prev;
252         }
253         else
254         {
255             node.prev.next = node.next;
256             node.next.prev = node.prev;
257         }
258 
259         this._alloc.dispose(node);
260         this._length--;
261     }
262 
263     private inout(Node)* getNodeAtHead()(size_t index) inout
264     {
265         assert(index < this._length, "Index out of bounds.");
266 
267         auto result = cast()this._head.ptr;
268         foreach(i; 0..index)
269             result = result.next;
270 
271         assert(result !is null, "Could not find result?");
272         return result;
273     }
274 
275     private inout(Node)* getNodeAtTail()(size_t index) inout
276     {
277         assert(index < this._length, "Index out of bounds.");
278         
279         auto result = cast()this._tail.ptr;
280         const iterations = (this._length - index) - 1;
281         foreach(i; 0..iterations)
282             result = result.prev;
283         
284         assert(result !is null, "Could not find result?");
285         return result;
286     }
287 }
288 ///
289 @("LinkedList - basic")
290 unittest
291 {    
292     import libd.algorithm : isCollection, isInputRange;
293     import libd.memory    : emplaceInit;
294     static assert(isCollection!(LinkedList!int, int));
295     static assert(isInputRange!(typeof(LinkedList!int.range())));
296 
297     LinkedList!int list;
298     
299     list.put(2);
300     assert(list.length == 1);
301     assert(list._head is list._tail);
302     assert(list.getAt(0) == 2);
303     list.put(4);
304     assert(list.length == 2);
305     assert(list._head !is list._tail);
306     assert(list.getAt(0) == 2);
307     assert(list.getAt(1) == 4);
308     list.putHead(0);
309     assert(list.length == 3);
310     assert(list.getAt(0) == 0);
311     assert(list.getAt(1) == 2);
312     assert(list.getAt(2) == 4);
313     list.getAt(1) /= 2;
314     assert(list.getAt(1) == 1);
315     list.removeAt(1);
316     assert(list.length == 2);
317     assert(list.getAt(0) == 0);
318     assert(list.getAt(1) == 4);
319     list.removeAt(0);
320     assert(list.length == 1);
321     assert(list.getAt(0) == 4);
322     assert(list.removeAt(0));
323     assert(list.length == 0);
324     assert(list._head is list._tail);
325     assert(list._head is null);
326     list.put(0);
327     list.put(0);
328     list.put(0);
329 
330     int next = 2;
331     foreach(ref num; list.range)
332     {
333         num = next;
334         next += 2;
335     }
336     assert(list[0] == 2);
337     assert(list[1] == 4);
338     assert(list[2] == 6);
339     
340     emplaceInit(list);
341     list.insertAt(0, 2); // 2
342     list.insertAt(0, 0); // 0 2
343     list.insertAt(2, 3); // 0 2 3
344     list.insertAt(1, 1); // 0 1 2 3
345     assert(list.length == 4);
346     assert(list[0] == 0);
347     assert(list[1] == 1);
348     assert(list[2] == 2);
349     assert(list[3] == 3);
350     assert(list.removeAtTail(1) == 1);
351     assert(list.removeAtTail(1) == 2);
352     assert(list.removeAtTail(0) == 0);
353     assert(list.removeAtTail(0) == 3);
354 }