Mostly bugfix update + empty components support and remove EntityID from Event structure

-empty components now take no memory, so flag components is now far better
-added test for critical bug
-fixed critical bug with adding/removing entities form inside events
-fixed small bug with TestRunner
-improve basic tests
-fixed betterC compilation on DMD
-remove EntityID form Event structure
-added "return" attribute to some functions
-moved some code from Tempalte side to actual implementation
-fixed bug with EntityTemplate copying
-commented out some possibliy unused code
-use code formatter
This commit is contained in:
Mergul 2020-05-27 17:03:44 +02:00
parent 15cd57dbcb
commit 6929f5a748
16 changed files with 988 additions and 544 deletions

View file

@ -11,36 +11,44 @@ private enum HASH_EMPTY = 0;
private enum HASH_DELETED = 0x1;
private enum HASH_FILLED_MARK = ulong(1) << 8 * ulong.sizeof - 1;
export ulong defaultHashFunc(T)(auto ref T t) nothrow @nogc {
static if (isIntegral!(T)) {
export ulong defaultHashFunc(T)(auto ref T t) nothrow @nogc
{
static if (isIntegral!(T))
{
return hashInt(t);
} else {
return 0;//hashInt(t.hashOf); // hashOf is not giving proper distribution between H1 and H2 hash parts
}
else
{
return 0; //hashInt(t.hashOf); // hashOf is not giving proper distribution between H1 and H2 hash parts
}
}
// Can turn bad hash function to good one
export ulong hashInt(ulong x) nothrow @nogc @safe {
export ulong hashInt(ulong x) nothrow @nogc @safe
{
x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
x = x ^ (x >> 31);
return x;
}
struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc)
{
alias Key = KeyPar;
alias Value = ValuePar;
nothrow:
nothrow:
enum rehashFactor = 0.75;
enum size_t getIndexEmptyValue = size_t.max;
static struct KeyVal {
static struct KeyVal
{
Key key;
Value value;
}
static struct Bucket {
static struct Bucket
{
ulong hash;
KeyVal keyValue;
}
@ -49,58 +57,74 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
size_t length; // Used to compute loadFactor
size_t markerdDeleted;
export void clear() {
export void clear()
{
elements.clear();
length = 0;
markerdDeleted = 0;
}
export void reset() {
export void reset()
{
elements.reset();
length = 0;
markerdDeleted = 0;
}
export bool isIn(ref Key el) {
export bool isIn(ref Key el)
{
return getIndex(el) != getIndexEmptyValue;
}
export bool isIn(Key el) {
export bool isIn(Key el)
{
return getIndex(el) != getIndexEmptyValue;
}
export Value* getPtr()(auto ref Key k) {
export Value* getPtr()(auto ref Key k)
{
size_t index = getIndex(k);
if (index == getIndexEmptyValue) {
if (index == getIndexEmptyValue)
{
return null;
} else {
}
else
{
return &elements[index].keyValue.value;
}
}
export ref Value get()(auto ref Key k) {
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) {
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) {
export auto ref Value get()(auto ref Key k, auto ref Value defaultValue)
{
size_t index = getIndex(k);
if (index == getIndexEmptyValue) {
if (index == getIndexEmptyValue)
{
return defaultValue;
} else {
}
else
{
return elements[index].keyValue.value;
}
}
export ref Value getInsertDefault()(auto ref Key k, auto ref Value defaultValue) {
export ref Value getInsertDefault()(auto ref Key k, auto ref Value defaultValue)
{
size_t index = getIndex(k);
if (index == getIndexEmptyValue) {
if (index == getIndexEmptyValue)
{
add(k, defaultValue);
}
index = getIndex(k);
@ -109,9 +133,11 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
}
export bool tryRemove(Key el) {
export bool tryRemove(Key el)
{
size_t index = getIndex(el);
if (index == getIndexEmptyValue) {
if (index == getIndexEmptyValue)
{
return false;
}
length--;
@ -120,28 +146,34 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
return true;
}
export void remove(Key el) {
export void remove(Key el)
{
bool ok = tryRemove(el);
assert(ok);
}
export ref Value opIndex()(auto ref Key key) {
export ref Value opIndex()(auto ref Key key)
{
return get(key);
}
export void opIndexAssign()(auto ref Value value, auto ref Key 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) {
export void add()(auto ref Key key, auto ref Value value)
{
size_t index = getIndex(key);
if (index != getIndexEmptyValue) {
if (index != getIndexEmptyValue)
{
elements[index].keyValue.value = value;
return;
}
if (getLoadFactor(length + 1) > rehashFactor
|| getLoadFactor(length + markerdDeleted) > rehashFactor) {
|| getLoadFactor(length + markerdDeleted) > rehashFactor)
{
rehash();
}
length++;
@ -150,10 +182,13 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
immutable size_t rotateMask = elements.length - 1;
index = hash & rotateMask; // Starting point
while (true) {
while (true)
{
Bucket* gr = &elements[index];
if ((gr.hash & HASH_FILLED_MARK) == 0) {
if (gr.hash == HASH_DELETED) {
if ((gr.hash & HASH_FILLED_MARK) == 0)
{
if (gr.hash == HASH_DELETED)
{
markerdDeleted--;
}
gr.hash = hash;
@ -171,15 +206,18 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
//int numA;
//int numB;
export size_t getIndex(Key el) {
export size_t getIndex(Key el)
{
return getIndex(el);
}
export size_t getIndex(ref Key el) {
export size_t getIndex(ref Key el)
{
mixin(doNotInline);
immutable size_t groupsLength = elements.length;
if (groupsLength == 0) {
if (groupsLength == 0)
{
return getIndexEmptyValue;
}
@ -188,13 +226,16 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
size_t index = hash & rotateMask; // Starting point
//numA++;
while (true) {
while (true)
{
//numB++;
Bucket* gr = &elements[index];
if (gr.hash == hash && gr.keyValue.key == el) {
if (gr.hash == hash && gr.keyValue.key == el)
{
return index;
}
if (gr.hash == HASH_EMPTY) {
if (gr.hash == HASH_EMPTY)
{
return getIndexEmptyValue;
}
@ -204,21 +245,26 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
}
export float getLoadFactor(size_t forElementsNum) {
if (elements.length == 0) {
export float getLoadFactor(size_t forElementsNum)
{
if (elements.length == 0)
{
return 1;
}
return cast(float) forElementsNum / (elements.length);
}
export void rehash()() {
export void rehash()()
{
mixin(doNotInline);
// Get all elements
Vector!KeyVal allElements;
allElements.reserve(elements.length);
foreach (ref Bucket el; elements) {
if ((el.hash & HASH_FILLED_MARK) == 0) {
foreach (ref Bucket el; elements)
{
if ((el.hash & HASH_FILLED_MARK) == 0)
{
el.hash = HASH_EMPTY;
continue;
}
@ -227,12 +273,14 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
}
if (getLoadFactor(length + 1) > rehashFactor) { // Reallocate
if (getLoadFactor(length + 1) > rehashFactor)
{ // Reallocate
elements.length = (elements.length ? elements.length : 4) << 1; // Power of two, initially 8 elements
}
// Insert elements
foreach (i, ref el; allElements) {
foreach (i, ref el; allElements)
{
add(el.key, el.value);
}
length = allElements.length;
@ -240,19 +288,29 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
}
// foreach support
export int opApply(DG)(scope DG dg) {
export int opApply(DG)(scope DG dg)
{
int result;
foreach (ref Bucket gr; elements) {
if ((gr.hash & HASH_FILLED_MARK) == 0) {
foreach (ref Bucket gr; elements)
{
if ((gr.hash & HASH_FILLED_MARK) == 0)
{
continue;
}
static if (isForeachDelegateWithTypes!(DG, Key)) {
static if (isForeachDelegateWithTypes!(DG, Key))
{
result = dg(gr.keyValue.key);
} else static if (isForeachDelegateWithTypes!(DG, Value)) {
}
else static if (isForeachDelegateWithTypes!(DG, Value))
{
result = dg(gr.keyValue.value);
} else static if (isForeachDelegateWithTypes!(DG, Key, Value)) {
}
else static if (isForeachDelegateWithTypes!(DG, Key, Value))
{
result = dg(gr.keyValue.key, gr.keyValue.value);
} else {
}
else
{
static assert(0);
}
if (result)
@ -263,9 +321,11 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
return result;
}
export int byKey(scope int delegate(Key k) nothrow dg) {
export int byKey(scope int delegate(Key k) nothrow dg)
{
int result;
foreach (ref Key k; this) {
foreach (ref Key k; this)
{
result = dg(k);
if (result)
break;
@ -273,9 +333,11 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
return result;
}
export int byValue(scope int delegate(ref Value k) nothrow dg) {
export int byValue(scope int delegate(ref Value k) nothrow dg)
{
int result;
foreach (ref Value v; this) {
foreach (ref Value v; this)
{
result = dg(v);
if (result)
break;
@ -283,13 +345,15 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
return result;
}
export int byKeyValue(scope int delegate(ref Key k, ref Value v) nothrow dg) {
export int byKeyValue(scope int delegate(ref Key k, ref Value v) nothrow dg)
{
int result;
foreach (ref Key k, ref Value v; this) {
foreach (ref Key k, ref Value v; this)
{
result = dg(k, v);
if (result)
break;
}
return result;
}
}
}