1 /// An event structure representing a one-to-many function/delegate relationship.
2 module subscribed.slist;
3 
4 import std.experimental.allocator;
5 import std.exception : enforce, basicExceptionCtors;
6 
7 ///
8 class SListException : Exception
9 {
10     ///
11     mixin basicExceptionCtors;
12 }
13 
14 /**
15  * A singly linked list based on theAllocator from std.experimental.allocator.
16  *
17  * Params:
18  *  T = The type of the list items.
19  */
20 struct SList(T)
21 {
22     private
23     {
24         SListNode!T* payload;
25         void assertNonEmpty() const
26         {
27             if (empty)
28                 throw new SListException("The list is empty");
29         }
30 
31         struct SListNode(T)
32         {
33             SListNode!T* next;
34             T value;
35         }
36     }
37 
38     this(this)
39     {
40         if (payload is null)
41             return;
42 
43         SListNode!T* srcNode = payload;
44         SListNode!T* destNode = null;
45         payload = null;
46 
47         while (srcNode !is null)
48         {
49             auto newNode = theAllocator.make!(SListNode!T);
50             newNode.value = srcNode.value;
51 
52             if (destNode !is null)
53                 destNode.next = newNode;
54 
55             destNode = newNode;
56             srcNode = srcNode.next;
57 
58             if (payload is null)
59                 payload = destNode;
60         }
61     }
62 
63     ~this()
64     {
65         auto node = payload;
66 
67         while (node !is null)
68         {
69             auto nextNode = node.next;
70             theAllocator.dispose(node);
71             node = nextNode;
72         }
73     }
74 
75     /**
76      * A boolean property indicating whether there are list items.
77      * Part of the bidirectional range interface.
78      */
79     bool empty() const
80     {
81         return payload is null;
82     }
83 
84     /**
85      * Get the front element or throw an error if the list is empty.
86      * Part of the bidirectional range interface.
87      */
88     T front() const
89     {
90         assertNonEmpty();
91         return payload.value;
92     }
93 
94     /**
95      * Pop the front element or throw an error if the list is empty.
96      * Part of the bidirectional range interface.
97      */
98     void popFront()
99     {
100         assertNonEmpty();
101         auto newNode = payload.next;
102         theAllocator.dispose(payload);
103         payload = newNode;
104     }
105 
106     ///
107     void insertFront(T value)
108     {
109         auto node = theAllocator.make!(SListNode!T);
110         node.value = value;
111         node.next = payload;
112         payload = node;
113     }
114 
115     /**
116      * Get the back element or throw an error if the list is empty.
117      * Part of the bidirectional range interface.
118      */
119     T back() const
120     {
121         assertNonEmpty();
122         SListNode!T* last = cast(SListNode!T*)payload;
123 
124         while (last.next !is null)
125             last = last.next;
126 
127         return last.value;
128     }
129 
130     /**
131      * Pop the back element or throw an error if the list is empty.
132      * Part of the bidirectional range interface.
133      */
134     void popBack()
135     {
136         assertNonEmpty();
137         auto last = payload;
138 
139         while (last.next !is null && last.next.next !is null)
140             last = last.next;
141 
142         theAllocator.dispose(last.next);
143         last.next = null;
144     }
145 
146     ///
147     void insertBack(T value)
148     {
149         auto newNode = theAllocator.make!(SListNode!T);
150         newNode.value = value;
151 
152         if (payload is null)
153         {
154             payload = newNode;
155         }
156         else
157         {
158             auto last = payload;
159 
160             while (last.next !is null)
161                 last = last.next;
162 
163             last.next = newNode;
164         }
165     }
166 
167     /**
168      * Remove all occurrences of a given value.
169      *
170      * Params:
171      *  value = the value to remove.
172      *
173      * Returns:
174      *  The number of items removed from the list.
175      */
176     size_t removeAll(T value)
177     {
178         SListNode!T* node = payload;
179         SListNode!T* prev = null;
180         size_t removedCount = 0;
181 
182         while (node !is null)
183         {
184             auto next = node.next;
185 
186             if (node.value == value)
187             {
188                 if (node == payload)
189                     payload = next;
190                 else
191                     prev.next = next;
192 
193                 theAllocator.dispose(node);
194                 removedCount++;
195             }
196             else
197                 prev = node;
198 
199             node = next;
200         }
201 
202         return removedCount;
203     }
204 
205     ///
206     void clear()
207     {
208         while (!empty)
209             popFront();
210     }
211 
212     /**
213      * Copies the list to allow multiple range-like iteration.
214      * Part of the bidirectional range interface.
215      */
216     auto save()
217     {
218         return this;
219     }
220 
221     ///
222     auto opSlice()
223     {
224         return this;
225     }
226 }