// D import file generated from 'source\ecs\hash_map.d' module ecs.hash_map; import std.traits; import ecs.vector; import ecs.traits; enum doNotInline = "version(DigitalMars)pragma(inline,false);version(LDC)pragma(LDC_never_inline);"; private enum HASH_EMPTY = 0; private enum HASH_DELETED = 1; private enum HASH_FILLED_MARK = ulong(1) << 8 * (ulong).sizeof - 1; export ulong defaultHashFunc(T)(auto ref T t) { static if (isIntegral!T) { return hashInt(t); } else { return hashInt(t.hashOf); } } export nothrow @nogc @safe ulong hashInt(ulong x); struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { alias Key = KeyPar; alias Value = ValuePar; enum rehashFactor = 0.75; enum size_t getIndexEmptyValue = size_t.max; static struct KeyVal { Key key; Value value; } static struct Bucket { ulong hash; KeyVal keyValue; } Vector!Bucket elements; size_t length; size_t markerdDeleted; export void clear() { elements.clear(); length = 0; markerdDeleted = 0; } export void reset() { elements.reset(); length = 0; markerdDeleted = 0; } export bool isIn(ref Key el) { return getIndex(el) != getIndexEmptyValue; } export bool isIn(Key el) { return getIndex(el) != getIndexEmptyValue; } export Value* getPtr()(auto ref Key k) { size_t index = getIndex(k); if (index == getIndexEmptyValue) { return null; } else { return &elements[index].keyValue.value; } } export ref Value get()(auto ref Key k) { size_t index = getIndex(k); assert(index != getIndexEmptyValue); return elements[index].keyValue.value; } deprecated("Use get with second parameter.") export auto ref Value getDefault()(auto ref Key k, auto ref Value defaultValue) { return get(k, defaultValue); } export auto ref Value get()(auto ref Key k, auto ref Value defaultValue) { size_t index = getIndex(k); if (index == getIndexEmptyValue) { return defaultValue; } else { return elements[index].keyValue.value; } } export ref Value getInsertDefault()(auto ref Key k, auto ref Value defaultValue) { size_t index = getIndex(k); if (index == getIndexEmptyValue) { add(k, defaultValue); } index = getIndex(k); assert(index != getIndexEmptyValue); return elements[index].keyValue.value; } export bool tryRemove(Key el) { size_t index = getIndex(el); if (index == getIndexEmptyValue) { return false; } length--; elements[index].hash = HASH_DELETED; markerdDeleted++; return true; } export void remove(Key el) { bool ok = tryRemove(el); assert(ok); } export ref Value opIndex()(auto ref Key key) { return get(key); } export void opIndexAssign()(auto ref Value value, auto ref Key key) { add(key, value); } export void add()(auto ref Key key, auto ref Value value) { size_t index = getIndex(key); if (index != getIndexEmptyValue) { elements[index].keyValue.value = value; return ; } if (getLoadFactor(length + 1) > rehashFactor || getLoadFactor(length + markerdDeleted) > rehashFactor) { rehash(); } length++; immutable ulong hash = hashFunc(key) | HASH_FILLED_MARK; immutable size_t rotateMask = elements.length - 1; index = hash & rotateMask; while (true) { Bucket* gr = &elements[index]; if ((gr.hash & HASH_FILLED_MARK) == 0) { if (gr.hash == HASH_DELETED) { markerdDeleted--; } gr.hash = hash; gr.keyValue.key = key; gr.keyValue.value = value; return ; } index++; index = index & rotateMask; } } export size_t getIndex(Key el) { return getIndex(el); } export size_t getIndex(ref Key el) { mixin(doNotInline); immutable size_t groupsLength = elements.length; if (groupsLength == 0) { return getIndexEmptyValue; } immutable ulong hash = hashFunc(el) | HASH_FILLED_MARK; immutable size_t rotateMask = groupsLength - 1; size_t index = hash & rotateMask; while (true) { Bucket* gr = &elements[index]; if (gr.hash == hash && (gr.keyValue.key == el)) { return index; } if (gr.hash == HASH_EMPTY) { return getIndexEmptyValue; } index++; index = index & rotateMask; } } export float getLoadFactor(size_t forElementsNum) { if (elements.length == 0) { return 1; } return cast(float)forElementsNum / elements.length; } export void rehash() { mixin(doNotInline); Vector!KeyVal allElements; allElements.reserve(elements.length); foreach (ref Bucket el; elements) { if ((el.hash & HASH_FILLED_MARK) == 0) { el.hash = HASH_EMPTY; continue; } el.hash = HASH_EMPTY; allElements ~= el.keyValue; } if (getLoadFactor(length + 1) > rehashFactor) { elements.length = (elements.length ? elements.length : 4) << 1; } foreach (i, ref el; allElements) { add(el.key, el.value); } length = allElements.length; markerdDeleted = 0; } export int opApply(DG)(scope DG dg) { int result; foreach (ref Bucket gr; elements) { if ((gr.hash & HASH_FILLED_MARK) == 0) { continue; } static if (isForeachDelegateWithTypes!(DG, Key)) { result = dg(gr.keyValue.key); } else { static if (isForeachDelegateWithTypes!(DG, Value)) { result = dg(gr.keyValue.value); } else { static if (isForeachDelegateWithTypes!(DG, Key, Value)) { result = dg(gr.keyValue.key, gr.keyValue.value); } else { static assert(0); } } } if (result) break; } return result; } export int byKey(scope int delegate(Key k) dg) { int result; foreach (ref Key k; this) { result = dg(k); if (result) break; } return result; } export int byValue(scope int delegate(ref Value k) dg) { int result; foreach (ref Value v; this) { result = dg(v); if (result) break; } return result; } export int byKeyValue(scope int delegate(ref Key k, ref Value v) dg) { int result; foreach (ref Key k, ref Value v; this) { result = dg(k, v); if (result) break; } return result; } import std.format : FormatSpec, formatValue; export void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) { formatValue(sink, '[', fmt); foreach (ref k, ref v; &byKeyValue) { formatValue(sink, k, fmt); formatValue(sink, ':', fmt); formatValue(sink, v, fmt); formatValue(sink, ", ", fmt); } formatValue(sink, ']', fmt); } } static void dumpHashMapToJson(T)(ref T map, string path = "HashMapDump.json") { Vector!char data; import std.file; import mutils.serializer.json; JSONSerializer.instance.serialize!(Load.no)(map, data); std.file.write(path, data[]); } static void printHashMap(T)(ref T map) { import std.stdio; writeln(T.stringof, " dump:\x0a"); foreach (k, v; &map.byKeyValue) { writefln("%20s : %20s", k, v); } }