The OpenD Programming Language

1 /**
2  * Singly-linked list.
3  * Copyright: © 2015 Economic Modeling Specialists, Intl.
4  * Authors: Brian Schott
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
6  */
7 
8 module containers.slist;
9 
10 private import containers.internal.node : shouldAddGCRange;
11 private import std.experimental.allocator.mallocator : Mallocator;
12 
13 /**
14  * Single-linked allocator-backed list.
15  * Params:
16  *     T = the element type
17  *     Allocator = the allocator to use. Defaults to `Mallocator`.
18  *     supportGC = true if the container should support holding references to
19  *         GC-allocated memory.
20  */
21 struct SList(T, Allocator = Mallocator, bool supportGC = shouldAddGCRange!T)
22 {
23 	/// Disable copying.
24 	this(this) @disable;
25 
26 	private import std.experimental.allocator.common : stateSize;
27 
28 	static if (stateSize!Allocator != 0)
29 	{
30 		/// No default construction if an allocator must be provided.
31 		this() @disable;
32 
33 		/**
34 		 * Use the given `allocator` for allocations.
35 		 */
36 		this(Allocator allocator)
37 		in
38 		{
39 			static if (is(typeof(allocator is null)))
40 				assert(allocator !is null, "Allocator must not be null");
41 		}
42 		do
43 		{
44 			this.allocator = allocator;
45 		}
46 	}
47 
48 	~this()
49 	{
50 		Node* current = _front;
51 		Node* prev = null;
52 		while (current !is null)
53 		{
54 			prev = current;
55 			current = current.next;
56 			typeid(Node).destroy(prev);
57 			static if (useGC)
58 			{
59 				import core.memory : GC;
60 				GC.removeRange(prev);
61 			}
62 			allocator.dispose(prev);
63 		}
64 		_front = null;
65 	}
66 
67 	/**
68 	 * Complexity: O(1)
69 	 * Returns: the most recently inserted item
70 	 */
71 	auto front(this This)() inout @property
72 	in
73 	{
74 		assert (!empty, "Accessing .front of empty SList");
75 	}
76 	do
77 	{
78 		alias ET = ContainerElementType!(This, T);
79 		return cast(ET) _front.value;
80 	}
81 
82 	/**
83 	 * Complexity: O(length)
84 	 * Returns: the least recently inserted item.
85 	 */
86 	auto back(this This)() inout @property
87 	in
88 	{
89 		assert (!empty, "Accessing .back of empty SList");
90 	}
91 	do
92 	{
93 		alias ET = ContainerElementType!(This, T);
94 
95 		auto n = _front;
96 		for (; front.next !is null; n = n.next) {}
97 		return cast(ET) n.value;
98 	}
99 
100 	/**
101 	 * Removes the first item in the list.
102 	 *
103 	 * Complexity: O(1)
104 	 * Returns: the first item in the list.
105 	 */
106 	T moveFront()
107 	in
108 	{
109 		assert (!empty, "Accessing .moveFront of empty SList");
110 	}
111 	do
112 	{
113 		Node* f = _front;
114 		_front = f.next;
115 		T r = f.value;
116 		static if (useGC)
117 		{
118 			import core.memory : GC;
119 			GC.removeRange(f);
120 		}
121 		allocator.dispose(f);
122 		--_length;
123 		return r;
124 	}
125 
126 	/**
127 	 * Removes the first item in the list.
128 	 *
129 	 * Complexity: O(1)
130 	 */
131 	void popFront()
132 	{
133 		Node* f = _front;
134 		_front = f.next;
135 		static if (useGC)
136 		{
137 			import core.memory : GC;
138 			GC.removeRange(f);
139 		}
140 		allocator.dispose(f);
141 		--_length;
142 	}
143 
144 	/**
145 	 * Complexity: O(1)
146 	 * Returns: true if this list is empty
147 	 */
148 	bool empty() inout pure nothrow @property @safe @nogc
149 	{
150 		return _front is null;
151 	}
152 
153 	/**
154 	 * Complexity: O(1)
155 	 * Returns: the number of items in the list
156 	 */
157 	size_t length() inout pure nothrow @property @safe @nogc
158 	{
159 		return _length;
160 	}
161 
162 	/**
163 	 * Inserts an item at the front of the list.
164 	 *
165 	 * Complexity: O(1)
166 	 * Params: t = the item to insert into the list
167 	 */
168 	void insertFront(T t) @trusted
169 	{
170 		_front = make!Node(allocator, _front, t);
171 		static if (useGC)
172 		{
173 			import core.memory : GC;
174 			GC.addRange(_front, Node.sizeof);
175 		}
176 		_length++;
177 	}
178 
179 	/// ditto
180 	alias insert = insertFront;
181 
182 	/// ditto
183 	alias insertAnywhere = insertFront;
184 
185 	/// ditto
186 	alias put = insertFront;
187 
188 	/**
189 	 * Supports `list ~= item` syntax
190 	 *
191 	 * Complexity: O(1)
192 	 */
193 	void opOpAssign(string op)(T t) if (op == "~")
194 	{
195 		put(t);
196 	}
197 
198 	/**
199 	 * Removes the first instance of value found in the list.
200 	 *
201 	 * Complexity: O(length)
202 	 * Returns: true if a value was removed.
203 	 */
204 	bool remove(V)(V value) @trusted
205 	{
206 		Node* prev = null;
207 		Node* cur = _front;
208 		while (cur !is null)
209 		{
210 			if (cur.value == value)
211 			{
212 				if (prev !is null)
213 					prev.next = cur.next;
214 				if (_front is cur)
215 					_front = cur.next;
216 				static if (shouldAddGCRange!T)
217 				{
218 					import core.memory : GC;
219 					GC.removeRange(cur);
220 				}
221 				allocator.dispose(cur);
222 				_length--;
223 				return true;
224 			}
225 			prev = cur;
226 			cur = cur.next;
227 		}
228 		return false;
229 	}
230 
231 	/**
232 	 * Forward range interface
233 	 */
234 	auto opSlice(this This)() inout
235 	{
236 		return Range!(This)(_front);
237 	}
238 
239 	/**
240 	 * Removes all elements from the range
241 	 *
242 	 * Complexity: O(length)
243 	 */
244 	void clear()
245 	{
246 		Node* prev = null;
247 		Node* cur = _front;
248 		while (cur !is null)
249 		{
250 			prev = cur;
251 			cur = prev.next;
252 			static if (shouldAddGCRange!T)
253 			{
254 				import core.memory : GC;
255 				GC.removeRange(prev);
256 			}
257 			allocator.dispose(prev);
258 		}
259 		_front = null;
260 		_length = 0;
261 	}
262 
263 private:
264 
265 	import std.experimental.allocator : make, dispose;
266 	import containers.internal.node : shouldAddGCRange;
267 	import containers.internal.element_type : ContainerElementType;
268 	import containers.internal.mixins : AllocatorState;
269 
270 	enum bool useGC = supportGC && shouldAddGCRange!T;
271 
272 	static struct Range(ThisT)
273 	{
274 	public:
275 		ET front() pure nothrow @property @trusted @nogc
276 		{
277 			return cast(typeof(return)) current.value;
278 		}
279 
280 		void popFront() pure nothrow @safe @nogc
281 		{
282 			current = current.next;
283 		}
284 
285 		bool empty() const pure nothrow @property @safe @nogc
286 		{
287 			return current is null;
288 		}
289 
290 	private:
291 		alias ET = ContainerElementType!(ThisT, T);
292 		const(Node)* current;
293 	}
294 
295 	static struct Node
296 	{
297 		Node* next;
298 		T value;
299 	}
300 
301 	mixin AllocatorState!Allocator;
302 	Node* _front;
303 	size_t _length;
304 }
305 
306 version(emsi_containers_unittest) unittest
307 {
308 	import std.string : format;
309 	import std.algorithm : canFind;
310 	SList!int intList;
311 	foreach (i; 0 .. 100)
312 		intList.put(i);
313 	assert(intList.length == 100, "%d".format(intList.length));
314 	assert(intList.remove(10));
315 	assert(!intList.remove(10));
316 	assert(intList.length == 99);
317 	assert(intList[].canFind(9));
318 	assert(!intList[].canFind(10));
319 	SList!string l;
320 	l ~= "abcde";
321 	l ~= "fghij";
322 	assert (l.length == 2);
323 }
324 
325 version(emsi_containers_unittest) unittest
326 {
327 	static class Foo
328 	{
329 		string name;
330 	}
331 
332 	SList!Foo hs;
333 	auto f = new Foo;
334 	hs.put(f);
335 }