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