1 /*
2  * This file is part of gtkD.
3  *
4  * gtkD is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License
6  * as published by the Free Software Foundation; either version 3
7  * of the License, or (at your option) any later version, with
8  * some exceptions, please read the COPYING file.
9  *
10  * gtkD is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with gtkD; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
18  */
19 
20 module utils.LinkedHasMap;
21 
22 import core.memory;
23 
24 /**
25  * An hash map that allows iteration in the insertion-order.
26  */
27 struct LinkedHashMap(Key, Value)
28 {
29 	static struct Node
30 	{
31 		Value val;
32 		Key key;
33 		Node* next;
34 		Node* previous;
35 	}
36 
37 	private Node*[Key] data;
38 	private Node* front;
39 	private Node* back;
40 
41 	/**
42 	 * Looks up key, if it exsists returns the corresponding value
43 	 * else evaluates and returns defaultValue.
44 	 */
45 	inout(Value) get(Key key, lazy inout(Value) defaultValue = Value.init) inout pure @safe
46 	{
47 		if ( key !in data )
48 			return defaultValue;
49 
50 		return data[key].val;
51 	}
52 
53 	/**
54 	 * remove(key) does nothing if the given key does not exist and returns false.
55 	 * If the given key does exist, it removes it from the HashMap and returns true.
56 	 */
57 	bool remove(Key key) pure nothrow @trusted
58 	{
59 		Node** nodeP = key in data;
60 
61 		if ( nodeP is null )
62 			return false;
63 
64 		Node* node = *nodeP;
65 
66 		if ( node is front )
67 			front = node.next;
68 		if ( node is back )
69 			back = node.previous;
70 
71 		if ( node.previous )
72 			node.previous.next = node.next;
73 		if ( node.next )
74 			node.next.previous = node.previous;
75 
76 		data.remove(key);
77 		GC.free(node);
78 
79 		return true;
80 	}
81 
82 	/**
83 	 * Removes all contents from the LinkedHashMap
84 	 */
85 	void clear() pure nothrow @trusted
86 	{
87 		Node* node = front;
88 		Node* previous;
89 
90 		while ( node !is null )
91 		{
92 			previous = node;
93 			node = node.next;
94 
95 			GC.free(previous);
96 		}
97 
98 		data.destroy();
99 		front = null;
100 		back = null;
101 	}
102 
103 	/**
104 	 * Returns: the number of values in the LinkedHasMap.
105 	 */
106 	@property size_t length() nothrow pure const @trusted
107 	{
108 		return data.length;
109 	}
110 
111 	/**
112 	 * Returns: true if the LinkedHasmap has no elements.
113 	 */
114 	@property bool empty() nothrow pure const @safe
115 	{
116 		return front is null;
117 	}
118 
119 	/**
120 	 * Indexing operators yield or modify the value at a specified index.
121 	 */
122 	inout(Value) opIndex(Key key) inout pure @safe
123 	{
124 		return data[key].val;
125 	}
126 
127 	/// ditto
128 	void opIndexAssign(Value value, Key key) @safe
129 	{
130 		Node* node;
131 
132 		if ( key !in data )
133 		{
134 			node = new Node;
135 		}
136 		else
137 		{
138 			node = data[key];
139 			node.val = value;
140 			return;
141 		}
142 
143 		node.val = value;
144 		node.key = key;
145 
146 		data[key] = node;
147 
148 		if ( front is null )
149 		{
150 			front = node;
151 			back = node;
152 			return;
153 		}
154 
155 		node.previous = back;
156 		back.next = node;
157 		back = node;
158 	}
159 
160 	/// ditto
161 	Value opIndexUnary(string op)(Key key) @safe
162 	{
163 		Node* node = data[key];
164 		return mixin(op ~"node.val;");
165 	}
166 
167 	/// ditto
168 	Value opIndexOpAssign(string op)(Value v, Key key) @safe
169 	{
170 		Node* node = data[key];
171 		return mixin("node.val" ~ op ~ "=v");
172 	}
173 
174 	/**
175 	 * in operator. Check to see if the given element exists in the container.
176 	 */
177 	inout(Value)* opBinaryRight(string op)(Key key) inout nothrow pure @safe
178 	if (op == "in")
179 	{
180 		inout(Node*)* node = key in data;
181 
182 		if ( node is null )
183 			return null;
184 
185 		return &((*node).val);
186 	}
187 
188 	/**
189 	 * foreach iteration uses opApply.
190 	 */
191 	int opApply(scope int delegate(ref Value) dg)
192 	{
193 		Node* node = front;
194 
195 		while ( node !is null )
196 		{
197 			int result = dg(node.val);
198 			if ( result )
199 				return result;
200 
201 			node = node.next;
202 		}
203 
204 		return 0;
205 	}
206 
207 	/// ditto
208 	int opApply(scope int delegate(ref Key, ref Value) dg)
209 	{
210 		Node* node = front;
211 
212 		while ( node !is null )
213 		{
214 			int result = dg(node.key, node.val);
215 			if ( result )
216 				return result;
217 
218 			node = node.next;
219 		}
220 
221 		return 0;
222 	}
223 
224 	/**
225 	 * Returns: true if this and that are equal.
226 	 */
227 	bool opEquals(inout typeof(this) that) inout nothrow pure @safe
228 	{
229 		return data == that.data;
230 	}
231 
232 	/**
233 	 * Returns: An dynamic array, the elements of which are the keys in the LinkedHashmap.
234 	 */
235 	@property inout(Key)[] keys() inout @safe
236 	{
237 		inout(Key)[] k;
238 
239 		inout(Node)* node = front;
240 
241 		while ( node !is null )
242 		{
243 			k ~= node.key;
244 			node = node.next;
245 		}
246 
247 		return k;
248 	}
249 
250 	/**
251 	 * Returns: An dynamic array, the elements of which are the values in the LinkedHashmap.
252 	 */
253 	@property inout(Value)[] values() inout @safe
254 	{
255 		inout(Value)[] v;
256 
257 		inout(Node)* node = front;
258 
259 		while ( node !is null )
260 		{
261 			v ~= node.val;
262 			node = node.next;
263 		}
264 
265 		return v;
266 	}
267 
268 	/**
269 	 * Reorganizes the LinkedHashMap in place so that lookups are more efficient,
270 	 * rehash is effective when, for example, the program is done loading up
271 	 * a symbol table and now needs fast lookups in it.
272 	 */
273 	void rehash() @trusted
274 	{
275 		data = data.rehash();
276 	}
277 
278 	/**
279 	 * Create a new LinkedHashMap of the same size and copy the contents of the LinkedHashMap into it.
280 	 */
281 	LinkedHashMap!(Key, Value) dub() @safe
282 	{
283 		LinkedHashMap!(Key, Value) copy;
284 		Node* node = front;
285 
286 		while ( node !is null )
287 		{
288 			copy[node.key] = node.val;
289 			node = node.next;
290 		}
291 
292 		return copy;
293 	}
294 
295 	/**
296 	 * Returns a delegate suitable for use as a ForeachAggregate to
297 	 * a ForeachStatement which will iterate over the keys of the LinkedHashMap.
298 	 */
299 	@property auto byKey() pure nothrow @safe
300 	{
301 		static struct KeyRange
302 		{
303 			Node* node;
304 
305 			this(Node* node) pure nothrow @safe
306 			{
307 				this.node = node;
308 			}
309 
310 			@property Key front() pure nothrow @safe
311 			{
312 				return node.key;
313 			}
314 
315 			void popFront() pure nothrow @safe
316 			{
317 				node = node.next;
318 			}
319 
320 			@property bool empty() pure const nothrow @safe
321 			{
322 				return node is null;
323 			}
324 		}
325 
326 		return KeyRange(front);
327 	}
328 
329 	/**
330 	 * Returns a delegate suitable for use as a ForeachAggregate to
331 	 * a ForeachStatement which will iterate over the values of the LinkedHashMap.
332 	 */
333 	@property auto byValue() pure nothrow @safe
334 	{
335 		static struct ValueRange
336 		{
337 			Node* node;
338 
339 			this(Node* node) pure nothrow @safe
340 			{
341 				this.node = node;
342 			}
343 
344 			@property Value front() pure nothrow @safe
345 			{
346 				return node.val;
347 			}
348 
349 			void popFront() pure nothrow @safe
350 			{
351 				node = node.next;
352 			}
353 
354 			@property bool empty() pure const nothrow @safe
355 			{
356 				return node is null;
357 			}
358 		}
359 
360 		return ValueRange(front);
361 	}
362 }