1 /**
2  * Singly-linked list.
3  * Copyright: © 2014 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 /**
11  * Single-linked allocator-backed list.
12  * Params:
13  *     T = the element type
14  *     A = the allocator type
15  */
16 struct SList(T)
17 {
18 	/// Disable copying.
19 	this(this) @disable;
20 
21 	~this()
22 	{
23 		Node* current = _front;
24 		Node* prev = null;
25 		while (current !is null)
26 		{
27 			prev = current;
28 			current = current.next;
29 			typeid(Node).destroy(prev);
30 			static if (shouldAddGCRange!T)
31 			{
32 				import core.memory : GC;
33 				GC.removeRange(prev);
34 			}
35 			Mallocator.instance.dispose(prev);
36 		}
37 		_front = null;
38 	}
39 
40 	/**
41 	 * Returns: the most recently inserted item
42 	 */
43 	auto front(this This)() @property
44 	in
45 	{
46 		assert (!empty);
47 	}
48 	body
49 	{
50 		alias ET = ContainerElementType!(This, T);
51 		return cast(ET) _front.value;
52 	}
53 
54 	/**
55 	 * Removes and returns the first item in the list.
56 	 */
57 	T moveFront()
58 	in
59 	{
60 		assert (!empty);
61 	}
62 	body
63 	{
64 		Node* f = _front;
65 		_front = f.next;
66 		T r = f.value;
67 		static if (shouldAddGCRange!T)
68 		{
69 			import core.memory : GC;
70 			GC.removeRange(f);
71 		}
72 		Mallocator.instance.dispose(f);
73 		--_length;
74 		return r;
75 	}
76 
77 	/**
78 	 * Removes the first item in the list.
79 	 */
80 	void popFront()
81 	{
82 		Node* f = _front;
83 		_front = f.next;
84 		static if (shouldAddGCRange!T)
85 		{
86 			import core.memory : GC;
87 			GC.removeRange(f);
88 		}
89 		Mallocator.instance.dispose(f);
90 		--_length;
91 	}
92 
93 	/**
94 	 * Returns: true if this list is empty
95 	 */
96 	bool empty() inout pure nothrow @property @safe @nogc
97 	{
98 		return _front is null;
99 	}
100 
101 	/**
102 	 * Returns: the number of items in the list
103 	 */
104 	size_t length() inout pure nothrow @property @safe @nogc
105 	{
106 		return _length;
107 	}
108 
109 	/**
110 	 * Inserts an item at the front of the list.
111 	 * Params: t = the item to insert into the list
112 	 */
113 	void insert(T t) @trusted
114 	{
115 		_front = Mallocator.instance.make!Node(_front, t);
116 		static if (shouldAddGCRange!T)
117 		{
118 			import core.memory : GC;
119 			GC.addRange(_front, Node.sizeof);
120 		}
121 		_length++;
122 	}
123 
124 	/// ditto
125 	alias insertFront = insert;
126 
127 	/// ditto
128 	alias put = insert;
129 
130 	/// Supports $(B list ~= item) syntax
131 	void opOpAssign(string op)(T t) if (op == "~")
132 	{
133 		put(t);
134 	}
135 
136 	/**
137 	 * Removes the first instance of value found in the list.
138 	 * Returns: true if a value was removed.
139 	 */
140 	bool remove(V)(V value) @trusted
141 	{
142 		Node* prev = null;
143 		Node* cur = _front;
144 		while (cur !is null)
145 		{
146 			if (cur.value == value)
147 			{
148 				if (prev !is null)
149 					prev.next = cur.next;
150 				if (_front is cur)
151 					_front = cur.next;
152 				static if (shouldAddGCRange!T)
153 				{
154 					import core.memory : GC;
155 					GC.removeRange(cur);
156 				}
157 				Mallocator.instance.dispose(cur);
158 				_length--;
159 				return true;
160 			}
161 			prev = cur;
162 			cur = cur.next;
163 		}
164 		return false;
165 	}
166 
167 	/**
168 	 * Forward range interface
169 	 */
170 	auto range(this This)()
171 	{
172 		return Range!(This)(_front);
173 	}
174 
175 	/// ditto
176 	alias opSlice = range;
177 
178 	/**
179 	 * Removes all elements from the range
180 	 */
181 	void clear()
182 	{
183 		Node* prev = null;
184 		Node* cur = _front;
185 		while (cur !is null)
186 		{
187 			prev = cur;
188 			cur = prev.next;
189 			static if (shouldAddGCRange!T)
190 			{
191 				import core.memory : GC;
192 				GC.removeRange(prev);
193 			}
194 			Mallocator.instance.dispose(prev);
195 		}
196 		_front = null;
197 		_length = 0;
198 	}
199 
200 private:
201 
202 	import std.experimental.allocator : make, dispose;
203 	import std.experimental.allocator.mallocator : Mallocator;
204 	import containers.internal.node : shouldAddGCRange;
205 	import containers.internal.element_type : ContainerElementType;
206 
207 	static struct Range(ThisT)
208 	{
209 	public:
210 		ET front() pure nothrow @property @trusted @nogc
211 		{
212 			return cast(typeof(return)) current.value;
213 		}
214 
215 		void popFront() pure nothrow @safe @nogc
216 		{
217 			current = current.next;
218 		}
219 
220 		bool empty() const pure nothrow @property @safe @nogc
221 		{
222 			return current is null;
223 		}
224 
225 	private:
226 		alias ET = ContainerElementType!(ThisT, T);
227 		const(Node)* current;
228 	}
229 
230 	static struct Node
231 	{
232 		Node* next;
233 		T value;
234 	}
235 
236 	Node* _front;
237 
238 	size_t _length;
239 }
240 
241 unittest
242 {
243 	import std.string : format;
244 	import std.algorithm : canFind;
245 	SList!int intList;
246 	foreach (i; 0 .. 100)
247 		intList.put(i);
248 	assert (intList.length == 100, "%d".format(intList.length));
249 	assert (intList.remove(10));
250 	assert (!intList.remove(10));
251 	assert (intList.length == 99);
252 	assert (intList.range.canFind(9));
253 	assert (!intList.range.canFind(10));
254 	SList!string l;
255 	l ~= "abcde";
256 	l ~= "fghij";
257 	assert (l.length == 2);
258 }