1 module collections.linkedhashmap;
2 
3 import collections.commons;
4 import std.functional : binaryFun;
5 import std.traits;
6 
7 import collections.linkedmap;
8 
9 /**
10  * Implements a linked hashmap.
11  * If retainKeys enabled, the map will retain the original keys and hash collisions can be resorted, but can be disabled
12  * by setting keyEqual to null.
13  * It is a modified linked map, and the `ovrwrtBhvr` still works as with the linked map.
14  */
15 public struct LinkedHashMap(K, E, alias hashFunc = defaultHash128!(K), alias equal = "a == b", bool retainKeys = false,
16 		alias keyEqual = "a == b", bool ovrwrtBhvr = true) {
17 	///true if nogc Indexing is enabled.
18 	static enum bool nogcIndexing = hasFunctionAttributes!(hashFunc, "@nogc");
19 	alias HashType = ReturnType!hashFunc;
20 	
21 	private struct Node {
22 		HashType	hashCode;	///Identifier hash
23 		static if (retainKeys) {
24 			K			key;		///The key for this node
25 		}
26 		E			elem;		///Element stored within the node
27 		Node*		next;		///Next node, null if none exists
28 		Node*		prev;		///Previous node, null if none exists
29 		///Keturns a string representation of the node.
30 		string toString() const {
31 			import std.conv : to;
32 			string result = "hashCode: " ~ to!string(hashCode) ~ " ; elem: " ~ to!string(elem);
33 			static if (retainKeys) result ~= " ; key: " ~ to!string(key);
34 			if (next) result ~= " ; next: [" ~ next.toString ~ "]";
35 			return result;
36 		}
37 	}
38 	
39 	protected Node*		root;	///Root element.
40 	protected Node*		last;	///Last element.
41 	protected size_t	_length;///N. of currently stored elements
42 	static if (retainKeys) {
43 		/**
44 		 * opApply override for foreach
45 		 */
46 		int opApply(scope int delegate(ref E) dg) {
47 			Node* crnt = root;
48 			while (crnt) {
49 				if (dg(crnt.elem)) return 1;
50 				crnt = crnt.next;
51 			}
52 			return 0;
53 		}
54 		/**
55 		 * opApply override for foreach
56 		 */
57 		int opApply(scope int delegate(K, ref E) dg) {
58 			Node* crnt = root;
59 			while (crnt) {
60 				if (dg(crnt.key, crnt.elem)) return 1;
61 				crnt = crnt.next;
62 			}
63 			return 0;
64 		}
65 		/**
66 		 * opApplyReverse override for foreach_reverse
67 		 */
68 		int opApplyReverse(scope int delegate(ref E) dg) {
69 			Node* crnt = last;
70 			while (crnt) {
71 				if (dg(crnt.elem)) return 1;
72 				crnt = crnt.prev;
73 			}
74 			return 0;
75 		}
76 		/**
77 		 * opApplyReverse override for foreach_reverse
78 		 */
79 		int opApplyReverse(scope int delegate(K, ref E) dg) {
80 			Node* crnt = last;
81 			while (crnt) {
82 				if (dg(crnt.key, crnt.elem)) return 1;
83 				crnt = crnt.prev;
84 			}
85 			return 0;
86 		}
87 		package static string makeFunc() {
88 			string makeFuncIndiv(string args) {
89 				return `
90 				int opApply(scope int delegate(ref E) ` ~ args ~ ` dg) ` ~ args ~ ` {
91 					Node* crnt = root;
92 					while (crnt) {
93 						if (dg(crnt.elem)) return 1;
94 						crnt = crnt.next;
95 					}
96 					return 0;
97 				}
98 				int opApply(scope int delegate(K, ref E) ` ~ args ~ ` dg) ` ~ args ~ ` {
99 					Node* crnt = root;
100 					while (crnt) {
101 						if (dg(crnt.key, crnt.elem)) return 1;
102 						crnt = crnt.next;
103 					}
104 					return 0;
105 				}
106 				int opApplyReverse(scope int delegate(ref E) ` ~ args ~ ` dg) ` ~ args ~ ` {
107 					Node* crnt = last;
108 					while (crnt) {
109 						if (dg(crnt.elem)) return 1;
110 						crnt = crnt.prev;
111 					}
112 					return 0;
113 				}
114 				int opApplyReverse(scope int delegate(K, ref E) ` ~ args ~ ` dg) ` ~ args ~ ` {
115 					Node* crnt = last;
116 					while (crnt) {
117 						if (dg(crnt.key, crnt.elem)) return 1;
118 						crnt = crnt.prev;
119 					}
120 					return 0;
121 				}`;
122 			}
123 			string result;
124 			foreach (attr; attrList) {
125 				result ~= makeFuncIndiv(attr);
126 			}
127 			return result;
128 		}
129 		mixin(makeFunc);
130 		/**
131 		 * Assigns a value to the given key.
132 		 */
133 		auto opIndexAssign(E value, K key) @safe pure nothrow {
134 			const HashType hashCode = hashFunc(key);
135 			Node** crnt = &root;
136 			while (*crnt) {
137 				static if (ovrwrtBhvr) {
138 					static if (keyEqual !is null) {
139 						if (binaryFun!equal((*crnt).hashCode, hashCode) && binaryFun!keyEqual((*crnt).key, key)) {
140 							return (*crnt).elem = value;
141 						}
142 					} else {
143 						if (binaryFun!equal((*crnt).hashCode, hashCode)) {
144 							(*crnt).key = key;
145 							return (*crnt).elem = value;
146 						}
147 					}
148 				} else {
149 					if (binaryFun!equal((*crnt).hashCode, hashCode)) {
150 						if ((*crnt).prev) (*crnt).prev.next = (*crnt).next;
151 						*crnt = (*crnt).next;
152 						_length--;
153 					}
154 				}
155 				crnt = &(*crnt).next;
156 			}
157 			//prev.next = new Node(key, value, null);
158 			*crnt = new Node(hashCode, key, value, null, *crnt);
159 			last = *crnt;
160 			_length++;
161 			return value;
162 		}
163 		static if (nogcIndexing) {
164 			
165 			/**
166 			 * Returns true if key is found.
167 			 */
168 			bool has(K key) @nogc @safe pure nothrow {
169 				const HashType hash = hashFunc(key);
170 				Node* crnt = root;
171 				while (crnt) {
172 					static if (keyEqual !is null) { 
173 						if (binaryFun!equal(crnt.hashCode, hash) && binaryFun!keyEqual(crnt.key , key)) {
174 							return true;
175 						}
176 					} else {
177 						if (binaryFun!equal(crnt.hashCode, hash)) {
178 							return true;
179 						}
180 					}
181 					crnt = crnt.next;
182 				}
183 				return false;
184 			}
185 			auto opBinaryRight(string op)(const K key) @nogc @safe pure nothrow {
186 				static if (op == "in") {
187 					return has(key);
188 				} else static assert(0, "Operator not supported!");
189 			}
190 		} else {
191 			/**
192 			 * Returns true if key is found.
193 			 */
194 			bool has(K key) @safe pure nothrow {
195 				const HashType hash = hashFunc(key);
196 				Node* crnt = root;
197 				while (crnt) {
198 					static if (keyEqual !is null) { 
199 						if (binaryFun!equal(crnt.hashCode, hash) && binaryFun!keyEqual(crnt.key , key)) {
200 							return true;
201 						}
202 					} else {
203 						if (binaryFun!equal(crnt.hashCode, hash)) {
204 							return true;
205 						}
206 					}
207 					crnt = crnt.next;
208 				}
209 				return false;
210 			}
211 			auto opBinaryRight(string op)(const K key) @safe pure nothrow {
212 				static if (op == "in") {
213 					return has(key);
214 				} else static assert(0, "Operator not supported!");
215 			}
216 		}
217 	} else {
218 		/**
219 		 * Removes the element with the specified key.
220 		 * Returns the removed element.
221 		 */
222 		E remove(K key) @safe pure nothrow {
223 			return remove(hashFunc(key));
224 		}
225 		/**
226 		 * Removes the element with the specified hashcode.
227 		 * Returns the removed element.
228 		 */
229 		E remove(HashType hashcode) @safe pure nothrow {
230 			Node** crnt = &root;
231 			while (*crnt) {
232 				if (binaryFun!equal((*crnt).hashCode, hashcode)) {
233 					E result = (*crnt).elem;
234 					//if ((*crnt).prev) (*crnt).prev.next = (*crnt).next;
235 					if ((*crnt).next is null) last = (*crnt).prev;
236 					else (*crnt).next.prev = (*crnt).prev;
237 					*crnt = (*crnt).next;
238 					_length--;
239 					return result;
240 				}
241 				crnt = &(*crnt).next;
242 			}
243 			return E.init;
244 		}
245 		/**
246 		 * opApply override for foreach
247 		 */
248 		int opApply(scope int delegate(ref E) dg) {
249 			Node* crnt = root;
250 			while (crnt) {
251 				if (dg(crnt.elem)) return 1;
252 				crnt = crnt.next;
253 			}
254 			return 0;
255 		}
256 		/**
257 		 * opApply override for foreach
258 		 */
259 		int opApply(scope int delegate(HashType, ref E) dg) {
260 			Node* crnt = root;
261 			while (crnt) {
262 				if (dg(crnt.hashCode, crnt.elem)) return 1;
263 				crnt = crnt.next;
264 			}
265 			return 0;
266 		}
267 		/**
268 		 * opApplyReverse override for foreach_reverse
269 		 */
270 		int opApplyReverse(scope int delegate(ref E) dg) {
271 			Node* crnt = last;
272 			while (crnt) {
273 				if (dg(crnt.elem)) return 1;
274 				crnt = crnt.prev;
275 			}
276 			return 0;
277 		}
278 		/**
279 		 * opApplyReverse override for foreach_reverse
280 		 */
281 		int opApplyReverse(scope int delegate(HashType, ref E) dg) {
282 			Node* crnt = last;
283 			while (crnt) {
284 				if (dg(crnt.hashCode, crnt.elem)) return 1;
285 				crnt = crnt.prev;
286 			}
287 			return 0;
288 		}
289 		package static string makeFunc() {
290 			string makeFuncIndiv(string args) {
291 				return `
292 				int opApply(scope int delegate(ref E) ` ~ args ~ ` dg) ` ~ args ~ ` {
293 					Node* crnt = root;
294 					while (crnt) {
295 						if (dg(crnt.elem)) return 1;
296 						crnt = crnt.next;
297 					}
298 					return 0;
299 				}
300 				int opApply(scope int delegate(HashType, ref E) ` ~ args ~ ` dg) ` ~ args ~ ` {
301 					Node* crnt = root;
302 					while (crnt) {
303 						if (dg(crnt.hashCode, crnt.elem)) return 1;
304 						crnt = crnt.next;
305 					}
306 					return 0;
307 				}
308 				int opApplyReverse(scope int delegate(ref E) ` ~ args ~ ` dg) ` ~ args ~ ` {
309 					Node* crnt = last;
310 					while (crnt) {
311 						if (dg(crnt.elem)) return 1;
312 						crnt = crnt.prev;
313 					}
314 					return 0;
315 				}
316 				int opApplyReverse(scope int delegate(HashType, ref E) ` ~ args ~ ` dg) ` ~ args ~ ` {
317 					Node* crnt = last;
318 					while (crnt) {
319 						if (dg(crnt.hashCode, crnt.elem)) return 1;
320 						crnt = crnt.prev;
321 					}
322 					return 0;
323 				}`;
324 			}
325 			string result;
326 			foreach (attr; attrList) {
327 				result ~= makeFuncIndiv(attr);
328 			}
329 			return result;
330 		}
331 		mixin(makeFunc);
332 		/**
333 		 * Assigns a value to the given key.
334 		 */
335 		auto opIndexAssign(E value, K key) @safe pure nothrow {
336 			const HashType hashCode = hashFunc(key);
337 			Node** crnt = &root;
338 			while (*crnt) {
339 				static if (ovrwrtBhvr) {
340 					if (binaryFun!equal((*crnt).hashCode, hashCode)) return (*crnt).elem = value;
341 				} else {
342 					if (binaryFun!equal((*crnt).hashCode, hashCode)) {
343 						if (&(*crnt).prev) (*crnt).prev.next = (*crnt).next;
344 						*crnt = &(*crnt).next;
345 						length--;
346 					}
347 				}
348 				crnt = &(*crnt).next;
349 			}
350 			//prev.next = new Node(key, value, null);
351 			*crnt = new Node(hashCode, value, null, last);
352 			last = *crnt;
353 			_length++;
354 			return value;
355 		}
356 		static if (nogcIndexing) {
357 			/**
358 			 * Returns true if key is found.
359 			 */
360 			bool has(K key) @nogc @safe pure nothrow {
361 				return has(hashFunc(key));
362 			}
363 			auto opBinaryRight(string op, K)(const K key) @nogc @safe pure nothrow {
364 				static if (op == "in") {
365 					return has(key);
366 				} else static assert(0, "Operator not supported!");
367 			}
368 		} else {
369 			/**
370 			 * Returns true if key is found.
371 			 */
372 			bool has(K key) @safe pure {
373 				return has(hashFunc(key));
374 			}
375 			auto opBinaryRight(string op, K)(const K key) @safe pure {
376 				static if (op == "in") {
377 					return has(key);
378 				} else static assert(0, "Operator not supported!");
379 			}
380 		}
381 	}
382 	static if (!retainKeys || keyEqual !is null) {
383 		static if (nogcIndexing) {
384 			/**
385 			 * Returns the element with the given key, or E.init if key is not found.
386 			 */
387 			E opIndex(K key) @nogc @safe pure nothrow {
388 				const HashType hashCode = hashFunc(key);
389 				Node* crnt = root;
390 				while (crnt) {
391 					if (binaryFun!equal(crnt.hashCode, hashCode)) return crnt.elem;
392 					crnt = crnt.next;
393 				}
394 				return E.init;
395 			}
396 			/**
397 			 * Returns the element with the given key, or E.init if key is not found.
398 			 */
399 			E opIndex(HashType hashCode) @nogc @safe pure nothrow {
400 				Node* crnt = root;
401 				while (crnt) {
402 					if (binaryFun!equal(crnt.hashCode, hashCode)) return crnt.elem;
403 					crnt = crnt.next;
404 				}
405 				return E.init;
406 			}
407 			/**
408 			 * Returns the pointer to the element with the given key, of null if key is not found.
409 			 */
410 			E* getPtr(K key) @nogc @safe pure nothrow {
411 				const HashType hashCode = hashFunc(key);
412 				Node* crnt = root;
413 				while (crnt) {
414 					if (binaryFun!equal(crnt.hashCode, hashCode)) return &(crnt.elem);
415 					crnt = crnt.next;
416 				}
417 				return null;
418 			}
419 			/**
420 			 * Returns the pointer to the element with the given key, of null if key is not found.
421 			 */
422 			E* getPtr(HashType hashCode) @nogc @safe pure nothrow {
423 				Node* crnt = root;
424 				while (crnt) {
425 					if (binaryFun!equal(crnt.hashCode, hashCode)) return &(crnt.elem);
426 					crnt = crnt.next;
427 				}
428 				return null;
429 			}
430 		} else {
431 			/**
432 			 * Returns the element with the given key, or E.init if key is not found.
433 			 */
434 			ref E opIndex(K key) @safe pure {
435 				const HashType hashCode = hashFunc(key);
436 				Node* crnt = root;
437 				while (crnt) {
438 					if (binaryFun!equal(crnt.hashCode, hashCode)) return crnt.elem;
439 					crnt = crnt.next;
440 				}
441 				throw new ElementNotFoundException("Key not found!");
442 			}
443 			/**
444 			 * Returns the element with the given key, or E.init if key is not found.
445 			 */
446 			ref E opIndex(HashType hashCode) @safe pure {
447 				Node* crnt = root;
448 				while (crnt) {
449 					if (binaryFun!equal(crnt.hashCode, hashCode)) return crnt.elem;
450 					crnt = crnt.next;
451 				}
452 				throw new ElementNotFoundException("Key not found!");
453 			}
454 		}
455 	} else {
456 		static if (nogcIndexing) {
457 			/**
458 			 * Returns the element with the given key, or E.init if key is not found.
459 			 */
460 			E opIndex(K key) @nogc @safe pure nothrow {
461 				const HashType hashCode = hashFunc(key);
462 				Node* crnt = root;
463 				while (crnt) {
464 					if (binaryFun!equal(crnt.hashCode, hashCode) && binaryFun!keyEqual(crnt.key, key)) return crnt.elem;
465 					crnt = crnt.next;
466 				}
467 				return E.init;
468 			}
469 			/**
470 			 * Returns the pointer to the element with the given key, of null if key is not found.
471 			 */
472 			E* getPtr(K key) @nogc @safe pure nothrow {
473 				const HashType hashCode = hashFunc(key);
474 				Node* crnt = root;
475 				while (crnt) {
476 					if (binaryFun!equal(crnt.hashCode, hashCode) && binaryFun!keyEqual(crnt.key, key)) return &(crnt.elem);
477 					crnt = crnt.next;
478 				}
479 				return null;
480 			}
481 		} else {
482 			/**
483 			 * Returns the element with the given key, or E.init if key is not found.
484 			 */
485 			ref E opIndex(K key) @safe pure {
486 				const HashType hashCode = hashFunc(key);
487 				Node* crnt = root;
488 				while (crnt) {
489 					if (binaryFun!equal(crnt.hashCode, hashCode) && binaryFun!keyEqual(crnt.key, key)) return crnt.elem;
490 					crnt = crnt.next;
491 				}
492 				throw new ElementNotFoundException("Key not found!");
493 			}
494 		}
495 	}
496 	/**
497 	 * Returns true if key is found.
498 	 */
499 	bool has(HashType hashCode) @nogc @safe pure nothrow {
500 		Node* crnt = root;
501 		while (crnt) {
502 			if (binaryFun!equal(crnt.hashCode, hashCode)) return true;
503 			crnt = crnt.next;
504 		}
505 		return false;
506 	}
507 	auto opBinaryRight(string op)(const HashType key) @nogc @safe pure nothrow {
508 		static if (op == "in") {
509 			return has(key);
510 		} else static assert(0, "Operator not supported!");
511 	}
512 	/**
513 	 * Returns the number of elements in the LinkedMap.
514 	 */
515 	@property size_t length() @nogc @safe pure nothrow const {
516 		return _length;
517 	}
518 	///Returns the string representation of this container format
519 	string toString() const {
520 		if (root) return root.toString;
521 		else return "empty";
522 	}
523 }
524 
525 unittest {
526 	import std.random : uniform;
527 	import std.exception : assertThrown;
528 	import std.stdio : writeln;
529 	{
530 		alias StringMap = LinkedHashMap!(string, string);
531 		StringMap d;
532 		d["AAAAAAAAA"] = "AAAAAAAAA";
533 		d["Hello World!"] = "Hello Vilag!";
534 		assert(d["AAAAAAAAA"] == "AAAAAAAAA");
535 		assert(d["Hello World!"] == "Hello Vilag!");
536 		assert(d.remove("AAAAAAAAA") == "AAAAAAAAA");
537 		assert(d.remove("a") == string.init);
538 		foreach (key; d) {}
539 	}
540 	{
541 		alias StringMap = LinkedHashMap!(string, string, defaultHash128!(string), "a == b", true, "a == b", true);
542 		StringMap d;
543 		d["AAAA"] = "AAAA";
544 		d["BBBB"] = "BBBB";
545 		d["CCCC"] = "CCCC";
546 		d["DDDD"] = "DDDD";
547 		assert("AAAA" == "AAAA");
548 		assert("BBBB" == "BBBB");
549 		assert("CCCC" == "CCCC");
550 		assert("DDDD" == "DDDD");
551 		writeln(d);
552 		d["CCCC"] = "eeee";
553 		writeln(d);
554 		foreach (key, elem; d) {}
555 		foreach_reverse (key, elem; d) {}
556 		foreach (key; d) {}
557 		foreach_reverse (key; d) {}
558 	}
559 	{
560 		alias StringMap = LinkedHashMap!(string, string, defaultHash128!(string), "a == b", true, "a == b", false);
561 		StringMap d;
562 		d["AAAA"] = "AAAA";
563 		d["BBBB"] = "BBBB";
564 		d["CCCC"] = "CCCC";
565 		d["DDDD"] = "DDDD";
566 		assert("AAAA" == "AAAA");
567 		assert("BBBB" == "BBBB");
568 		assert("CCCC" == "CCCC");
569 		assert("DDDD" == "DDDD");
570 		writeln(d);
571 		d["CCCC"] = "eeee";
572 		writeln(d);
573 		foreach (key, elem; d) {}
574 		foreach_reverse (key, elem; d) {}
575 		foreach (key; d) {}
576 		foreach_reverse (key; d) {}
577 	}
578 }