1 module collections.hashmap;
2 
3 import collections.treemap;
4 import std.traits;
5 import std.digest.murmurhash;
6 public import collections.commons;
7 /**
8  * Implements a hashmap using collections.treemap as a backend.
9  * See collections.treemap for more info on certain things.
10  */
11 public struct HashMap(K, E, alias hashFunc = defaultHash!K, alias less = "a < b") {
12 	static enum bool nogcIndexing = hasFunctionAttributes!(hashFunc, "@nogc");
13 	alias HashType = ReturnType!hashFunc;
14 	private TreeMap!(HashType, E, nogcIndexing, less)     backend;
15 
16 	public void rebalance() @nogc @safe pure nothrow {
17 		backend.rebalance();
18 	}
19 	static if (nogcIndexing) {
20 		/**
21 		 * @nogc capable indexing.
22 		 * Returns the found element if match found.
23 		 * Returns E.init if match not found.
24 		 */
25 		E opIndex(K key) @nogc @safe pure nothrow {
26 			return backend.opIndex(hashFunc(key));
27 		}
28         ///Ditto
29         E opIndex(HashType key) @nogc @safe pure nothrow {
30             return backend.opIndex(key);
31         }
32 		/**
33 		 * Returns the pointer of the element, or null if key not found.
34 		 */
35 		E* ptrOf(K key) @nogc @safe pure nothrow {
36 			return backend.ptrOf(hashFunc(key));
37 		}
38         ///Ditto
39         E* ptrOf(HashType key) @nogc @safe pure nothrow {
40             return backend.ptrOf(key);
41         }
42 		/**
43 		 * Returns true if the element exists within the set, false otherwise.
44 		 */
45 		bool has(K key) @nogc @safe pure nothrow {
46 			return backend.has(hashFunc(key));
47 		}
48 		auto opBinaryRight(string op)(const K key) @nogc @safe pure nothrow {
49 			static if (op == "in") {
50 				return has(key);
51 			} else static assert(0, "Operator not supported!");
52 		}
53 		/**
54 		 * Returns true if the element exists within the set, false otherwise.
55 		 */
56 		bool has(HashType key) @nogc @safe pure nothrow {
57 			return backend.has(key);
58 		}
59 		auto opBinaryRight(string op)(const HashType key) @nogc @safe pure nothrow {
60 			static if (op == "in") {
61 				return has(key);
62 			} else static assert(0, "Operator not supported!");
63 		}
64 	} else {
65 		/**
66 		 * Indexing function that relies on the GC, and throws if no match found.
67 		 * Returns the found element if match found.
68 		 */
69 		ref E opIndex(K key) @safe pure {
70 			return backend.opIndex(hashFunc(key));
71 		}
72         ///Ditto
73         ref E opIndex(HashType key) @safe pure {
74 			return backend.opIndex(key);
75 		}
76 		/**
77 		 * Returns true if the element exists within the set, false otherwise.
78 		 */
79 		bool has(K key) @safe pure {
80 			return backend.has(hashFunc(key));
81 		}
82 		auto opBinaryRight(string op)(const K key) @safe pure {
83 			static if (op == "in") {
84 				return has(key);
85 			} else static assert(0, "Operator not supported!");
86 		}
87 		/**
88 		 * Returns true if the element exists within the set, false otherwise.
89 		 */
90 		bool has(HashType key) @safe pure {
91 			return backend.has(hashFunc(key));
92 		}
93 		auto opBinaryRight(string op)(const HashType key) @safe pure {
94 			static if (op == "in") {
95 				return has(key);
96 			} else static assert(0, "Operator not supported!");
97 		}
98 	}
99 	/**
100 	 * Assigns an element to the given key.
101 	 */
102 	E opIndexAssign(E value, K key) @safe pure nothrow{
103 		return backend.opIndexAssign(value, hashFunc(key));
104 	}
105 	/**
106 	 * Implements a simple left-to-right tree traversal.
107 	 */
108 	int opApply(scope int delegate(ref E) dg) {
109 		return backend.opApply(dg);
110 	}
111 	/**
112 	 * Implements a simple left-to-right tree traversal.
113 	 */
114 	int opApply(scope int delegate(HashType, ref E) dg) {
115 		return backend.opApply(dg);
116 	}
117 	/**
118 	 * Implements a simple right-to-left tree traversal.
119 	 */
120 	int opApplyReverse(scope int delegate(ref E) dg) {
121 		return backend.opApplyReverse(dg);
122 	}
123 	/**
124 	 * Implements a simple right-to-left tree traversal.
125 	 */
126 	int opApplyReverse(scope int delegate(HashType, ref E) dg) {
127 		return backend.opApplyReverse(dg);
128 	}
129 	package static string makeFunc() {
130 		string makeFundIndiv(string attr) {
131 			return `int opApply(scope int delegate(ref E) ` ~ attr ~ ` dg) ` ~ attr ~ ` {
132 		return backend.opApply(dg);
133 	}
134 	int opApply(scope int delegate(HashType, ref E) ` ~ attr ~ ` dg) ` ~ attr ~ ` {
135 		return backend.opApply(dg);
136 	}
137 	int opApplyReverse(scope int delegate(ref E) ` ~ attr ~ ` dg) ` ~ attr ~ ` {
138 		return backend.opApplyReverse(dg);
139 	}
140 	int opApplyReverse(scope int delegate(HashType, ref E) ` ~ attr ~ ` dg) ` ~ attr ~ ` {
141 		return backend.opApplyReverse(dg);
142 	}`;
143 		}
144 		string result;
145 		foreach (attr; attrList) result ~= makeFundIndiv(attr);
146 		return result;
147 	}
148 	mixin(makeFunc);
149 	/**
150 	 * Returns the number of currently held elements within the tree.
151 	 */
152 	public @property size_t length() @nogc @safe pure nothrow const {
153 		return backend.length;
154 	}
155 	/**
156 	 * returns the string representation of the tree.
157 	 */
158 	public string toString() const {
159 		return backend.toString();
160 	}
161 }
162 
163 unittest {
164 	alias Dictionary = HashMap!(string, string);
165 	Dictionary d;
166 	d["AAAAAAAAA"] = "AAAAAAAAA";
167 	d["Hello World!"] = "Hello Vilag!";
168 	assert(!(0 in d));
169 	assert("AAAAAAAAA" in d);
170 	assert(d["AAAAAAAAA"] == "AAAAAAAAA");
171 	assert(d["Hello World!"] == "Hello Vilag!");
172 }