1 /************************************************************************************************************************
2 Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
3 License: BSD 3-clause, see LICENSE file in project root folder.
4 */
5 module bubel.ecs.hash_map;
6 
7 import std.traits;
8 
9 import bubel.ecs.vector;
10 import bubel.ecs.traits;
11 
12 enum doNotInline = "version(DigitalMars)pragma(inline,false);version(LDC)pragma(LDC_never_inline);";
13 
14 private enum HASH_EMPTY = 0;
15 private enum HASH_DELETED = 0x1;
16 private enum HASH_FILLED_MARK = ulong(1) << 8 * ulong.sizeof - 1;
17 
18 export ulong defaultHashFunc(T)(auto ref T t) nothrow @nogc
19 {
20 	static if (isIntegral!(T))
21 	{
22 		return hashInt(t);
23 	}
24 	else
25 	{
26 		static if(isArray!T)return hashInt(hash((cast(byte*)t.ptr)[0 .. t.length * ForeachType!(T).sizeof]));
27 		else return hashInt(hash((cast(byte*)&t)[0 .. T.sizeof])); //hashInt(t.hashOf); // hashOf is not giving proper distribution between H1 and H2 hash parts
28 	}
29 }
30 
31 ulong hash(byte[] array) nothrow @nogc
32 {
33 	ulong hash = 0;
34 
35 	foreach(c;array)
36 		hash = c + (hash << 6) + (hash << 16) - hash;
37 
38 	return hash;
39 }
40 
41 // Can turn bad hash function to good one
42 export ulong hashInt(ulong x) nothrow @nogc @safe
43 {
44 	x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
45 	x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
46 	x = x ^ (x >> 31);
47 	return x;
48 }
49 
50 struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc)
51 {
52 	alias Key = KeyPar;
53 	alias Value = ValuePar;
54 nothrow:
55 
56 	enum rehashFactor = 0.75;
57 	enum size_t getIndexEmptyValue = size_t.max;
58 
59 	static struct KeyVal
60 	{
61 		Key key;
62 		Value value;
63 	}
64 
65 	static struct Bucket
66 	{
67 		ulong hash;
68 		KeyVal keyValue;
69 	}
70 
71 	Vector!Bucket elements; // Length should be always power of 2
72 	size_t length; // Used to compute loadFactor
73 	size_t markerdDeleted;
74 
75 	export void clear()
76 	{
77 		elements.clear();
78 		length = 0;
79 		markerdDeleted = 0;
80 	}
81 
82 	export void reset()
83 	{
84 		elements.reset();
85 		length = 0;
86 		markerdDeleted = 0;
87 	}
88 
89 	export bool isIn(ref Key el)
90 	{
91 		return getIndex(el) != getIndexEmptyValue;
92 	}
93 
94 	export bool isIn(Key el)
95 	{
96 		return getIndex(el) != getIndexEmptyValue;
97 	}
98 
99 	export Value* getPtr()(auto ref Key k)
100 	{
101 		size_t index = getIndex(k);
102 		if (index == getIndexEmptyValue)
103 		{
104 			return null;
105 		}
106 		else
107 		{
108 			return &elements[index].keyValue.value;
109 		}
110 	}
111 
112 	export ref Value get()(auto ref Key k)
113 	{
114 		size_t index = getIndex(k);
115 		assert(index != getIndexEmptyValue);
116 		return elements[index].keyValue.value;
117 	}
118 
119 	deprecated("Use get with second parameter.") export auto ref Value getDefault()(
120 			auto ref Key k, auto ref Value defaultValue)
121 	{
122 		return get(k, defaultValue);
123 	}
124 
125 	export auto ref Value get()(auto ref Key k, auto ref Value defaultValue)
126 	{
127 		size_t index = getIndex(k);
128 		if (index == getIndexEmptyValue)
129 		{
130 			return defaultValue;
131 		}
132 		else
133 		{
134 			return elements[index].keyValue.value;
135 		}
136 	}
137 
138 	export ref Value getInsertDefault()(auto ref Key k, auto ref Value defaultValue)
139 	{
140 		size_t index = getIndex(k);
141 		if (index == getIndexEmptyValue)
142 		{
143 			add(k, defaultValue);
144 		}
145 		index = getIndex(k);
146 		assert(index != getIndexEmptyValue);
147 		return elements[index].keyValue.value;
148 
149 	}
150 
151 	export bool tryRemove(Key el)
152 	{
153 		size_t index = getIndex(el);
154 		if (index == getIndexEmptyValue)
155 		{
156 			return false;
157 		}
158 		length--;
159 		elements[index].hash = HASH_DELETED;
160 		markerdDeleted++;
161 		return true;
162 	}
163 
164 	export void remove(Key el)
165 	{
166 		bool ok = tryRemove(el);
167 		assert(ok);
168 	}
169 
170 	export ref Value opIndex()(auto ref Key key)
171 	{
172 		return get(key);
173 	}
174 
175 	export void opIndexAssign()(auto ref Value value, auto ref Key key)
176 	{
177 		add(key, value);
178 	}
179 
180 	export void add()(auto ref Key key, auto ref Value value)
181 	{
182 		size_t index = getIndex(key);
183 		if (index != getIndexEmptyValue)
184 		{
185 			elements[index].keyValue.value = value;
186 			return;
187 		}
188 
189 		if (getLoadFactor(length + 1) > rehashFactor
190 				|| getLoadFactor(length + markerdDeleted) > rehashFactor)
191 		{
192 			rehash();
193 		}
194 		length++;
195 
196 		immutable ulong hash = hashFunc(key) | HASH_FILLED_MARK;
197 		immutable size_t rotateMask = elements.length - 1;
198 		index = hash & rotateMask; // Starting point
199 
200 		while (true)
201 		{
202 			Bucket* gr = &elements[index];
203 			if ((gr.hash & HASH_FILLED_MARK) == 0)
204 			{
205 				if (gr.hash == HASH_DELETED)
206 				{
207 					markerdDeleted--;
208 				}
209 				gr.hash = hash;
210 				gr.keyValue.key = key;
211 				gr.keyValue.value = value;
212 				return;
213 			}
214 
215 			index++;
216 			index = index & rotateMask;
217 		}
218 	}
219 
220 	// For debug
221 	//int numA;
222 	//int numB;
223 
224 	export size_t getIndex(Key el)
225 	{
226 		return getIndex(el);
227 	}
228 
229 	export size_t getIndex(ref Key el)
230 	{
231 		mixin(doNotInline);
232 
233 		immutable size_t groupsLength = elements.length;
234 		if (groupsLength == 0)
235 		{
236 			return getIndexEmptyValue;
237 		}
238 
239 		immutable ulong hash = hashFunc(el) | HASH_FILLED_MARK;
240 		immutable size_t rotateMask = groupsLength - 1;
241 		size_t index = hash & rotateMask; // Starting point
242 
243 		//numA++;
244 		while (true)
245 		{
246 			//numB++;
247 			Bucket* gr = &elements[index];
248 			if (gr.hash == hash && gr.keyValue.key == el)
249 			{
250 				return index;
251 			}
252 			if (gr.hash == HASH_EMPTY)
253 			{
254 				return getIndexEmptyValue;
255 			}
256 
257 			index++;
258 			index = index & rotateMask;
259 		}
260 
261 	}
262 
263 	export float getLoadFactor(size_t forElementsNum)
264 	{
265 		if (elements.length == 0)
266 		{
267 			return 1;
268 		}
269 		return cast(float) forElementsNum / (elements.length);
270 	}
271 
272 	export void rehash()()
273 	{
274 		mixin(doNotInline);
275 		// Get all elements
276 		Vector!KeyVal allElements;
277 		allElements.reserve(elements.length);
278 
279 		foreach (ref Bucket el; elements)
280 		{
281 			if ((el.hash & HASH_FILLED_MARK) == 0)
282 			{
283 				el.hash = HASH_EMPTY;
284 				continue;
285 			}
286 			el.hash = HASH_EMPTY;
287 			allElements ~= el.keyValue;
288 
289 		}
290 
291 		if (getLoadFactor(length + 1) > rehashFactor)
292 		{ // Reallocate
293 			elements.length = (elements.length ? elements.length : 4) << 1; // Power of two, initially 8 elements
294 		}
295 
296 		// Insert elements
297 		foreach (i, ref el; allElements)
298 		{
299 			add(el.key, el.value);
300 		}
301 		length = allElements.length;
302 		markerdDeleted = 0;
303 	}
304 
305 	// foreach support
306 	export int opApply(DG)(scope DG dg)
307 	{
308 		int result;
309 		foreach (ref Bucket gr; elements)
310 		{
311 			if ((gr.hash & HASH_FILLED_MARK) == 0)
312 			{
313 				continue;
314 			}
315 			static if (isForeachDelegateWithTypes!(DG, Key))
316 			{
317 				result = dg(gr.keyValue.key);
318 			}
319 			else static if (isForeachDelegateWithTypes!(DG, Value))
320 			{
321 				result = dg(gr.keyValue.value);
322 			}
323 			else static if (isForeachDelegateWithTypes!(DG, Key, Value))
324 			{
325 				result = dg(gr.keyValue.key, gr.keyValue.value);
326 			}
327 			else
328 			{
329 				static assert(0);
330 			}
331 			if (result)
332 				break;
333 
334 		}
335 
336 		return result;
337 	}
338 
339 	export int byKey(scope int delegate(ref Key k) dg)
340 	{
341 		int result;
342 		foreach (ref Key k; this)
343 		{
344 			result = (cast(int delegate(ref Key k) nothrow)dg)(k);
345 			if (result)
346 				break;
347 		}
348 		return result;
349 	}
350 
351 	export int byValue(scope int delegate(ref Value v) dg)
352 	{
353 		int result;
354 		foreach (ref Value v; this)
355 		{
356 			result = (cast(int delegate(ref Value v) nothrow)dg)(v);
357 			if (result)
358 				break;
359 		}
360 		return result;
361 	}
362 
363 	export int byKeyValue(scope int delegate(ref Key k, ref Value v) dg)
364 	{
365 		int result;
366 		foreach (ref Key k, ref Value v; this)
367 		{
368 			result = (cast(int delegate(ref Key k, ref Value v) nothrow)dg)(k, v);
369 			if (result)
370 				break;
371 		}
372 		return result;
373 	}
374 }