1 /**
2 Copyright: Copyright (c) 2015-2017 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 
7 module voxelman.utils.queue;
8 
9 import std.range;
10 
11 // Any reference type items are set to Item.init when reused.
12 // Traversing with foreach does not modify range. You can remove elements during foreach using NodeAccessor.
13 // To use Queue as consumable range use valueRange prroperty.
14 // To use as reference range use slice operator -> queue[]
15 struct Queue(Item)
16 {
17 	static struct NodeAccessor
18 	{
19 		ref Item value() @property
20 		{
21 			return node.data;
22 		}
23 		void remove()
24 		{
25 			queue.removeNode(prev, node);
26 		}
27 
28 		private Node* prev;
29 		private Node* node;
30 		private Queue!(Item)* queue;
31 	}
32 
33 	int opApply(scope int delegate(NodeAccessor) dg)
34     {
35         int result = 0;
36         Node* prev;
37 		Node* pointer = first;
38 
39 		while(pointer)
40 		{
41 			auto accessor = NodeAccessor(prev, pointer, &this);
42 
43 			// advance pointers since accessor can remove current one
44 			prev = pointer;
45 			pointer = pointer.next;
46 
47 			result = dg(accessor);
48             if (result)
49                 break;
50 		}
51 
52         return result;
53     }
54 
55 	void put(Item item)
56 	{
57 		Node* node;
58 		if (firstFree !is null)
59 		{
60 			node = firstFree;
61 			firstFree = firstFree.next;
62 			node.next = null;
63 			node.data = item;
64 		}
65 		else
66 		{
67 			node = new Node(item, null);
68 		}
69 		// first will be also null
70 		if (last is null)
71 		{
72 			first = last = node;
73 		}
74 		else
75 		{
76 			last.next = node;
77 			last = node;
78 		}
79 
80 		++length;
81 	}
82 
83 	ValueRange valueRange() @property
84     {
85         return ValueRange(&this);
86     }
87 
88     private static struct ValueRange
89     {
90     	private Queue!(Item)* queue;
91 
92 		bool empty() @property
93 		{
94 			return queue.first is null;
95 		}
96 
97 		Item front() @property
98 		{
99 			assert(!queue.empty);
100 			return queue.first.data;
101 		}
102 
103 		void popFront()
104 		{
105 			queue.popFrontImpl();
106 		}
107 	}
108 
109 	RefRange opSlice()
110 	{
111 		return RefRange(first);
112 	}
113 
114 	private static struct RefRange
115 	{
116 		private Node* pointer;
117 		private Node* prev;
118 
119 		bool empty() @property
120 		{
121 			return pointer is null;
122 		}
123 
124 		Item front() @property
125 		{
126 			assert(!empty);
127 			return pointer.data;
128 		}
129 
130 		void popFront()
131 		{
132 			assert(!empty);
133 			prev = pointer;
134 			pointer = pointer.next;
135 		}
136 	}
137 
138 	bool empty() @property
139 	{
140 		return first is null;
141 	}
142 
143 	bool remove(Item item)
144 	{
145 		Node* prev;
146 		Node* pointer = first;
147 
148 		while(pointer)
149 		{
150 			if (pointer.data == item)
151 			{
152 				removeNode(prev, pointer);
153 				return true;
154 			}
155 
156 			prev = pointer;
157 			pointer = pointer.next;
158 		}
159 
160 		return false;
161 	}
162 
163 	private void popFrontImpl()
164 	{
165 		assert(!empty);
166 
167 		Node* node = first;
168 		first = first.next;
169 
170 		freeNode(node);
171 
172 		if (first is null)
173 		{
174 			last = null;
175 		}
176 		--length;
177 	}
178 
179 	private void removeNode(Node* prev, Node* pointer)
180 	{
181 		if (pointer == first)
182 			popFrontImpl();
183 		else
184 		{
185 			Node* node = pointer;
186 
187 			if (pointer == last)
188 			{
189 				last = prev;
190 			}
191 
192 			prev.next = node.next;
193 
194 			freeNode(node);
195 			--length;
196 		}
197 	}
198 
199 	private void freeNode(Node* node)
200 	{
201 		import std.traits : hasIndirections;
202 		static if (hasIndirections!Item)
203 			node.data = Item.init;
204 
205 		node.next = firstFree;
206 		firstFree = node;
207 	}
208 
209 	Node* first;
210 	Node* last;
211 	Node* firstFree;
212 	size_t length;
213 
214 	struct Node
215 	{
216 		Item data;
217 		Node* next;
218 	}
219 }
220 
221 unittest
222 {
223 	import std.algorithm : equal;
224 	import voxelman.log;
225 	Queue!int q;
226 	q.put(1);
227 	q.put(2);
228 	q.put(3);
229 	q.put(4);
230 	assert(q.length == 4);
231 	assert(equal(q[], [1,2,3,4]));
232 
233 	// test remove
234 	q = typeof(q).init;
235 	q.put(1);
236 	q.put(2);
237 	q.put(3);
238 	q.put(4);
239 
240 	assert(q.remove(5) == false);
241 	assert(q.remove(2));
242 	assert(q.length == 3);
243 	q.remove(3);
244 	assert(q.length == 2);
245 	q.remove(1);
246 	q.remove(4);
247 	assert(q.empty);
248 
249 	// test remove from foreach
250 	q = typeof(q).init;
251 	q.put(1);
252 	q.put(2);
253 	q.put(3);
254 	q.put(4);
255 
256 	uint i;
257 	foreach(nodeAccess; q)
258 	{
259 		if (i == 0)
260 			nodeAccess.remove();
261 		++i;
262 	}
263 
264 	assert(equal(q[], [2,3,4]));
265 
266 	i = 0;
267 	foreach(nodeAccess; q)
268 	{
269 		if (i == 2)
270 			nodeAccess.remove();
271 		++i;
272 	}
273 
274 	assert(equal(q[], [2,3]));
275 }