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