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 @nogc)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 @nogc)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 @nogc)dg)(k, v); 369 if (result) 370 break; 371 } 372 return result; 373 } 374 }