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 }