diff --git a/src/EcsArc.cs b/src/EcsArc.cs index ad540ac..48db565 100644 --- a/src/EcsArc.cs +++ b/src/EcsArc.cs @@ -19,8 +19,8 @@ namespace DCFApixels.DragonECS private readonly EndWorldHandler _endWorldHandler; private readonly LoopWorldHandler _loopWorldHandler; - private EcsGroup _relEntities; private RelEntityInfo[] _relEntityInfos; //N * (N - 1) / 2 + private readonly SparseMatrix _matrix; private bool _isLoop; private bool _isInit = false; @@ -51,11 +51,6 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _arcWorld.id; } } - public EcsReadonlyGroup RelEntities - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get { return _relEntities.Readonly; } - } public bool IsLoop { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -73,9 +68,7 @@ namespace DCFApixels.DragonECS _isLoop = startWorld == endWorld; _relEntityInfos = new RelEntityInfo[arcWorld.Capacity]; - - - _relEntities = EcsGroup.New(_arcWorld); + _matrix = new SparseMatrix(arcWorld.Capacity); _arcWorldHandler = new ArcWorldHandler(this); if (_isLoop) @@ -106,24 +99,66 @@ namespace DCFApixels.DragonECS } #endregion - #region New/Del + #region New + [MethodImpl(MethodImplOptions.AggressiveInlining)] public int NewRelation(int startEntityID, int endEntityID) { - int relEntity = _arcWorld.NewEntity(); - _relEntityInfos[relEntity] = new RelEntityInfo(startEntityID, endEntityID); - _relEntities.Add(relEntity); - return relEntity; + return NewRelationInternal(startEntityID, endEntityID); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetOrNewRelation(int startEntityID, int endEntityID) + { + if (_matrix.TryGetValue(startEntityID, endEntityID, out int relEntityID)) + { + return relEntityID; + } + return NewRelationInternal(startEntityID, endEntityID); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int NewRelationInternal(int startEntityID, int endEntityID) + { + int relEntityID = _arcWorld.NewEntity(); + _matrix.Add(startEntityID, endEntityID, relEntityID); + _relEntityInfos[relEntityID] = new RelEntityInfo(startEntityID, endEntityID); + return relEntityID; + } + #endregion + #region Has + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool HasRelation(int startEntityID, int endEntityID) + { + return _matrix.HasKey(startEntityID, endEntityID); + } + #endregion + + #region Get + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetRelation(int startEntityID, int endEntityID) + { + return _matrix.GetValue(startEntityID, endEntityID); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryGetRelation(int startEntityID, int endEntityID, out int relEntityID) + { + return _matrix.TryGetValue(startEntityID, endEntityID, out relEntityID); + } + #endregion + + #region Del + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void DelRelation(int relEntityID) { _arcWorld.DelEntity(relEntityID); + ClearRelation_Internal(relEntityID); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ClearRelation_Internal(int relEntityID) { - _relEntities.Remove(relEntityID); - _relEntityInfos[relEntityID] = RelEntityInfo.Empty; + ref RelEntityInfo info = ref _relEntityInfos[relEntityID]; + _matrix.TryDel(info.start, info.end); + info = RelEntityInfo.Empty; } #endregion diff --git a/src/Utils/BitsUtility.cs b/src/Utils/BitsUtility.cs new file mode 100644 index 0000000..2bf2725 --- /dev/null +++ b/src/Utils/BitsUtility.cs @@ -0,0 +1,1063 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; +using static DCFApixels.DragonECS.Relations.Internal.BitsUtility; + +namespace DCFApixels.DragonECS.Relations.Internal +{ + internal unsafe static class BitsUtility + { + private const char DEFAULT_SEPARATOR = '_'; + private const int BYTE_BITS = 8; + + #region CountBits + public static unsafe int CountBits8(T bits) where T : unmanaged + { + return CountBits((uint)*(byte*)&bits); + } + public static unsafe int CountBits16(T bits) where T : unmanaged + { + return CountBits((uint)*(ushort*)&bits); + } + + public static unsafe int CountBits32(T bits) where T : unmanaged + { + return CountBits(*(uint*)&bits); + } + public static unsafe int CountBits(float bits) + { + return CountBits32(bits); + } + public static unsafe int CountBits(int bits) + { + return CountBits((uint)bits); + } + public static unsafe int CountBits(uint bits) + { + bits = bits - ((bits >> 1) & 0x55555555); + bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333); + return (int)(((bits + (bits >> 4) & 0xF0F0F0F) * 0x1010101) >> 24); + } + + public static unsafe int CountBits64(T bits) where T : unmanaged + { + return CountBits(*(ulong*)&bits); + } + public static unsafe int CountBits(double bits) + { + return CountBits64(bits); + } + public static unsafe int CountBits(long bits) + { + return CountBits((ulong)bits); + } + public static unsafe int CountBits(ulong bits) + { + bits = bits - ((bits >> 1) & 0x55555555_55555555); + bits = (bits & 0x33333333_33333333) + ((bits >> 2) & 0x33333333_33333333); + return (int)(((bits + (bits >> 4) & 0x0F0F0F0F_0F0F0F0F) * 0x01010101_01010101) >> (24 + 32)); + } + #endregion + + #region GetHighBitNumber + public static unsafe int GetHighBitNumber8(T bits) where T : unmanaged + { + return GetHighBitNumber(*(byte*)&bits); + } + public static int GetHighBitNumber(sbyte bits) + { + return GetHighBitNumber((byte)bits); + } + public static int GetHighBitNumber(byte bits) + { + if (bits == 0) + { + return -1; + } + int bit = 0; + if ((bits & 0xF0) != 0) + { + bits >>= 4; + bit |= 4; + } + if ((bits & 0xC) != 0) + { + bits >>= 2; + bit |= 2; + } + if ((bits & 0x2) != 0) + { + bit |= 1; + } + return bit; + } + + public static unsafe int GetHighBitNumber16(T bits) where T : unmanaged + { + return GetHighBitNumber(*(ushort*)&bits); + } + public static int GetHighBitNumber(short bits) + { + return GetHighBitNumber((ushort)bits); + } + public static int GetHighBitNumber(ushort bits) + { + if (bits == 0) + { + return -1; + } + int bit = 0; + if ((bits & 0xFF00) != 0) + { + bits >>= 8; + bit |= 8; + } + if ((bits & 0xF0) != 0) + { + bits >>= 4; + bit |= 4; + } + if ((bits & 0xC) != 0) + { + bits >>= 2; + bit |= 2; + } + if ((bits & 0x2) != 0) + { + bit |= 1; + } + return bit; + } + + public static unsafe int GetHighBitNumber32(T bits) where T : unmanaged + { + return GetHighBitNumber(*(uint*)&bits); + } + public static int GetHighBitNumber(float bits) + { + return GetHighBitNumber(*(uint*)&bits); + } + public static int GetHighBitNumber(int bits) + { + return GetHighBitNumber((uint)bits); + } + public static int GetHighBitNumber(uint bits) + { + if (bits == 0) + { + return -1; + } + int bit = 0; + if ((bits & 0xFFFF0000) != 0) + { + bits >>= 16; + bit |= 16; + } + if ((bits & 0xFF00) != 0) + { + bits >>= 8; + bit |= 8; + } + if ((bits & 0xF0) != 0) + { + bits >>= 4; + bit |= 4; + } + if ((bits & 0xC) != 0) + { + bits >>= 2; + bit |= 2; + } + if ((bits & 0x2) != 0) + { + bit |= 1; + } + return bit; + } + + public static unsafe int GetHighBitNumber64(T bits) where T : unmanaged + { + return GetHighBitNumber(*(ulong*)&bits); + } + public static int GetHighBitNumber(double bits) + { + return GetHighBitNumber(*(ulong*)&bits); + } + public static int GetHighBitNumber(long bits) + { + return GetHighBitNumber((ulong)bits); + } + public static int GetHighBitNumber(ulong bits) + { + if (bits == 0) + { + return -1; + } + int bit = 0; + if ((bits & 0xFFFFFFFF00000000) != 0) + { + bits >>= 32; + bit |= 32; + } + if ((bits & 0xFFFF0000) != 0) + { + bits >>= 16; + bit |= 16; + } + if ((bits & 0xFF00) != 0) + { + bits >>= 8; + bit |= 8; + } + if ((bits & 0xF0) != 0) + { + bits >>= 4; + bit |= 4; + } + if ((bits & 0xC) != 0) + { + bits >>= 2; + bit |= 2; + } + if ((bits & 0x2) != 0) + { + bit |= 1; + } + return bit; + } + #endregion + + #region GetBitNumbers + public static unsafe int[] GetBitNumbers32(T bits) where T : unmanaged + { + return GetBitNumbers(*(uint*)&bits); + } + public static unsafe int[] GetBitNumbers(float bits) + { + return GetBitNumbers(*(uint*)&bits); + } + public static int[] GetBitNumbers(int bits) + { + return GetBitNumbers((uint)bits); + } + public static int[] GetBitNumbers(uint bits) + { + int[] result = new int[CountBits(bits)]; + for (int i = 0; i < result.Length; i++) + { + int number = GetHighBitNumber(bits); + result[i] = number; + bits ^= 1u << number; + } + return result; + } + + public static unsafe int GetBitNumbersNoAlloc32(T bits, ref int[] numbers) where T : unmanaged + { + return GetBitNumbersNoAlloc(*(uint*)&bits, ref numbers); + } + public static unsafe int GetBitNumbersNoAlloc(float bits, ref int[] numbers) + { + return GetBitNumbersNoAlloc(*(uint*)&bits, ref numbers); + } + public static int GetBitNumbersNoAlloc(int bits, ref int[] numbers) + { + return GetBitNumbersNoAlloc((uint)bits, ref numbers); + } + public static int GetBitNumbersNoAlloc(uint bits, ref int[] numbers) + { + int iMax = CountBits(bits); + if (iMax >= numbers.Length) + Array.Resize(ref numbers, iMax); + for (int i = 0; i < iMax; i++) + { + int number = GetHighBitNumber(bits); + numbers[i] = number; + bits ^= 1u << number; + } + return iMax; + } + public static unsafe void GetBitNumbersNoAlloc32(T bits, List numbers) where T : unmanaged + { + GetBitNumbersNoAlloc(*(uint*)&bits, numbers); + } + public static unsafe void GetBitNumbersNoAlloc(float bits, List numbers) + { + GetBitNumbersNoAlloc(*(uint*)&bits, numbers); + } + public static void GetBitNumbersNoAlloc(int bits, List numbers) + { + GetBitNumbersNoAlloc((uint)bits, numbers); + } + public static void GetBitNumbersNoAlloc(uint bits, List numbers) + { + numbers.Clear(); + int iMax = CountBits(bits); + for (int i = 0; i < iMax; i++) + { + int number = GetHighBitNumber(bits); + numbers[i] = number; + bits ^= 1u << number; + } + } + + + + public static unsafe int[] GetBitNumbers64(T bits) where T : unmanaged + { + return GetBitNumbers(*(ulong*)&bits); + } + public static unsafe int[] GetBitNumbers(double bits) + { + return GetBitNumbers(*(ulong*)&bits); + } + public static int[] GetBitNumbers(long bits) + { + return GetBitNumbers((ulong)bits); + } + public static int[] GetBitNumbers(ulong bits) + { + int[] result = new int[CountBits(bits)]; + for (int i = 0; i < result.Length; i++) + { + int number = GetHighBitNumber(bits); + result[i] = number; + bits ^= 1LU << number; + } + return result; + } + + public static unsafe int GetBitNumbersNoAlloc64(T bits, ref int[] numbers) where T : unmanaged + { + return GetBitNumbersNoAlloc(*(ulong*)&bits, ref numbers); + } + public static unsafe int GetBitNumbersNoAlloc(double bits, ref int[] numbers) + { + return GetBitNumbersNoAlloc(*(ulong*)&bits, ref numbers); + } + public static int GetBitNumbersNoAlloc(long bits, ref int[] numbers) + { + return GetBitNumbersNoAlloc((ulong)bits, ref numbers); + } + public static int GetBitNumbersNoAlloc(ulong bits, ref int[] numbers) + { + int iMax = CountBits(bits); + if (iMax >= numbers.Length) + Array.Resize(ref numbers, iMax); + for (int i = 0; i < iMax; i++) + { + int number = GetHighBitNumber(bits); + numbers[i] = number; + bits ^= 1u << number; + } + return iMax; + } + public static unsafe void GetBitNumbersNoAlloc64(T bits, List numbers) where T : unmanaged + { + GetBitNumbersNoAlloc(*(ulong*)&bits, numbers); + } + public static unsafe void GetBitNumbersNoAlloc(double bits, List numbers) + { + GetBitNumbersNoAlloc(*(ulong*)&bits, numbers); + } + public static void GetBitNumbersNoAlloc(long bits, List numbers) + { + GetBitNumbersNoAlloc((ulong)bits, numbers); + } + public static void GetBitNumbersNoAlloc(ulong bits, List numbers) + { + numbers.Clear(); + int iMax = CountBits(bits); + for (int i = 0; i < iMax; i++) + { + int number = GetHighBitNumber(bits); + numbers[i] = number; + bits ^= 1u << number; + } + } + #endregion + + #region ToBitsString + public static string ToBitsString(T value, bool withSeparator) where T : unmanaged + { + return ToBitsStringInaternal(value, withSeparator ? BYTE_BITS : 0, DEFAULT_SEPARATOR); + } + public static string ToBitsString(T value, int separateRange) where T : unmanaged + { + return ToBitsStringInaternal(value, separateRange, DEFAULT_SEPARATOR); + } + public static string ToBitsString(T value, char separator = DEFAULT_SEPARATOR, int separateRange = BYTE_BITS) where T : unmanaged + { + return ToBitsStringInaternal(value, separateRange, separator); + } + private static unsafe string ToBitsStringInaternal(T value, int separateRange, char separator) where T : unmanaged + { + int size = sizeof(T); + int length = size * BYTE_BITS; + //byte* bytes = stackalloc byte[size / BYTE_BITS]; + byte* bytes = (byte*)&value; + char* str = stackalloc char[length]; + + for (int i = 0; i < length; i++) + str[length - i - 1] = (bytes[i / BYTE_BITS] & 1 << (i % BYTE_BITS)) > 0 ? '1' : '0'; + + if (separateRange > 0) + return Regex.Replace(new string(str, 0, length), ".{" + separateRange + "}", "$0" + separator + ""); + else + return new string(str, 0, length); + } + #endregion + + #region ParceBitString + public static ulong ToULong(string bitsString) + { + const int BIT_SIZE = 64; + ulong result = 0; + int stringMouse = 0; + for (int i = 0; i < BIT_SIZE && stringMouse < bitsString.Length; i++, stringMouse++) + { + char chr = bitsString[stringMouse]; + + if (chr == '1') + { + result |= (ulong)1 << (BIT_SIZE - i - 1); + continue; + } + + if (chr != '0') + { + i--; + continue; + } + } + return result; + } + + public static uint ToUInt(string bitsString) + { + const int BIT_SIZE = 32; + uint result = 0; + int stringMouse = 0; + for (int i = 0; i < BIT_SIZE && stringMouse < bitsString.Length; i++, stringMouse++) + { + char chr = bitsString[stringMouse]; + + if (chr == '1') + { + result |= (uint)1 << (BIT_SIZE - i - 1); + continue; + } + + if (chr != '0') + { + i--; + continue; + } + } + return result; + } + + public static ushort ToUShort(string bitsString) + { + const int BIT_SIZE = 16; + ushort result = 0; + int stringMouse = 0; + for (int i = 0; i < BIT_SIZE && stringMouse < bitsString.Length; i++, stringMouse++) + { + char chr = bitsString[stringMouse]; + + if (chr == '1') + { + result |= (ushort)(1 << (BIT_SIZE - i - 1)); + continue; + } + + if (chr != '0') + { + i--; + continue; + } + } + return result; + } + + public static byte ToByte(string bitsString) + { + const int BIT_SIZE = 8; + byte result = 0; + int stringMouse = 0; + for (int i = 0; i < BIT_SIZE && stringMouse < bitsString.Length; i++, stringMouse++) + { + char chr = bitsString[stringMouse]; + + if (chr == '1') + { + result |= (byte)(1 << (BIT_SIZE - i - 1)); + continue; + } + + if (chr != '0') + { + i--; + continue; + } + } + return result; + } + + public static bool ToBool(string bitsString) + { + byte result = ToByte(bitsString); + return *(bool*)&result; + } + public static short ToShort(string bitsString) => (short)ToUShort(bitsString); + public static int ToInt(string bitsString) => (int)ToUInt(bitsString); + public static long ToLong(string bitsString) => (long)ToULong(bitsString); + #endregion + + #region XorShift + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int NextXorShiftState(int state) + { + unchecked + { + return (int)NextXorShiftState((uint)state); + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint NextXorShiftState(uint state) + { + unchecked + { + state ^= state << 13; + state ^= state >> 17; + state ^= state << 5; + return state; + }; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long NextXorShiftState(long state) + { + unchecked + { + return (long)NextXorShiftState((ulong)state); + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong NextXorShiftState(ulong state) + { + unchecked + { + const ulong m = 0x2545F491_4F6CDD1D; + state ^= state >> 13; + state ^= state << 25; + state ^= state >> 27; + state *= m; + return state; + } + } + #endregion + + #region Q32/64/s31/s63 To Float/Double + ///Fast operation: float result = (float)value / int.MaxValue. + ///-1.0f < x < 1.0f + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe float Qs31ToFloat(int value) + { + unchecked + { + if (value < 0) + { + uint bits = (((uint)value ^ uint.MaxValue) >> 8) | 0xBF80_0000; + return (*(float*)&bits) + 1f; + } + else + { + uint bits = (((uint)value) >> 8) | 0x3F80_0000; + return (*(float*)&bits) - 1f; + } + } + } + ///Fast operation: float result = (float)value / uint.MaxValue. + ///0.0f <= x < 1.0f + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe float Q32ToFloat(uint value) + { + unchecked + { + uint bits = (value >> 9) | 0x3F80_0000; + return (*(float*)&bits) - 1f; + } + } + ///Fast operation: double result = (double)value / long.MaxValue. + ///-1.0d < x < 1.0d + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe double Qs63ToDouble(long value) + { + unchecked + { + if (value < 0) + { + ulong bits = (((ulong)value ^ long.MaxValue) >> 11) | 0xBFF0_0000_0000_0000; + return (*(double*)&bits) + 1d; + } + else + { + ulong bits = (((ulong)value) >> 11) | 0x3FF0_0000_0000_0000; + return (*(double*)&bits) - 1d; + } + } + } + + ///Fast operation: double result = (double)value / ulong.MaxValue. + ///0.0d <= x < 1.0d + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe double Q64ToDouble(ulong value) + { + unchecked + { + ulong bits = (value >> 12) | 0x3FF0_0000_0000_0000; + return (*(double*)&bits) - 1d; + } + } + #endregion + } + + #region FindBitsResult removed + /* + public unsafe struct FindBitsResult8 : IEnumerable + { + private fixed byte _numbers[8]; + public readonly byte Count; + public int this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _numbers[index]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsResult8(uint bits) + { + unchecked + { + Count = (byte)CountBits(bits); + for (int i = 0; i < Count; i++) + { + int number = GetHighBitNumber(bits); + _numbers[i] = (byte)number; + bits ^= 1u << number; + } + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ReadOnlySpan CreateSpanInternal(FindBitsResult8 a) + { + return new ReadOnlySpan(a._numbers, a.Count); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan ToSpan() + { + return CreateSpanInternal(this); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static FindBitsResultEnumerator GetEnumeratorInternal(FindBitsResult8 a) + { + return new FindBitsResultEnumerator(a._numbers, a.Count); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsResultEnumerator GetEnumerator() + { + return GetEnumeratorInternal(this); + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public unsafe struct FindBitsResult16 : IEnumerable + { + private fixed byte _numbers[16]; + public readonly byte Count; + public int this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _numbers[index]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsResult16(uint bits) + { + unchecked + { + Count = (byte)CountBits(bits); + for (int i = 0; i < Count; i++) + { + int number = GetHighBitNumber(bits); + _numbers[i] = (byte)number; + bits ^= 1u << number; + } + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ReadOnlySpan CreateSpanInternal(FindBitsResult16 a) + { + return new ReadOnlySpan(a._numbers, a.Count); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan ToSpan() + { + return CreateSpanInternal(this); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static FindBitsResultEnumerator GetEnumeratorInternal(FindBitsResult16 a) + { + return new FindBitsResultEnumerator(a._numbers, a.Count); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsResultEnumerator GetEnumerator() + { + return GetEnumeratorInternal(this); + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public unsafe struct FindBitsResult32 : IEnumerable + { + public fixed byte _numbers[32]; + public readonly byte Count; + public int this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _numbers[index]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsResult32(uint bits) + { + unchecked + { + Count = (byte)CountBits(bits); + for (int i = 0; i < Count; i++) + { + int number = GetHighBitNumber(bits); + _numbers[i] = (byte)number; + bits ^= 1u << number; + } + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ReadOnlySpan CreateSpanInternal(FindBitsResult32 a) + { + return new ReadOnlySpan(a._numbers, a.Count); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan ToSpan() + { + return CreateSpanInternal(this); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static FindBitsResultEnumerator GetEnumeratorInternal(FindBitsResult32 a) + { + return new FindBitsResultEnumerator(a._numbers, a.Count); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsResultEnumerator GetEnumerator() + { + return GetEnumeratorInternal(this); + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public unsafe struct FindBitsResult64 : IEnumerable + { + private fixed byte _numbers[64]; + public readonly byte Count; + public int this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _numbers[index]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsResult64(ulong bits) + { + unchecked + { + Count = (byte)CountBits(bits); + for (int i = 0; i < Count; i++) + { + int number = GetHighBitNumber(bits); + _numbers[i] = (byte)number; + bits ^= 1u << number; + } + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ReadOnlySpan CreateSpanInternal(FindBitsResult64 a) + { + return new ReadOnlySpan(a._numbers, a.Count); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan ToSpan() + { + return CreateSpanInternal(this); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static FindBitsResultEnumerator GetEnumeratorInternal(FindBitsResult64 a) + { + return new FindBitsResultEnumerator(a._numbers, a.Count); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsResultEnumerator GetEnumerator() + { + return GetEnumeratorInternal(this); + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public unsafe struct FindBitsResultEnumerator : IEnumerator + { + private byte* _numbers; + private byte _count; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsResultEnumerator(byte* numbers, byte count) + { + _numbers = numbers; + _count = count; + } + public byte Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return *_numbers; + } + } + object IEnumerator.Current => Current; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + return _count-- > 0; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() { } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Reset() { } + } + */ + #endregion + + #region FindBitsIterator + public struct FindBitsIterator8 : IEnumerable + { + private Enumerator _enumerator; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsIterator8(sbyte bits) + { + _enumerator = new Enumerator((byte)bits); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsIterator8(byte bits) + { + _enumerator = new Enumerator(bits); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() + { + return _enumerator; + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public struct Enumerator : IEnumerator + { + private uint _bits; + private int _count; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator(byte bits) + { + _count = CountBits(bits); + _bits = bits; + } + public int Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + int number = GetHighBitNumber((byte)_bits); + _bits ^= 1u << number; + return number; + } + } + object IEnumerator.Current => Current; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + return _count-- > 0; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void IDisposable.Dispose() { } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void IEnumerator.Reset() { } + } + } + + public struct FindBitsIterator16 : IEnumerable + { + private Enumerator _enumerator; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsIterator16(short bits) + { + _enumerator = new Enumerator((ushort)bits); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsIterator16(ushort bits) + { + _enumerator = new Enumerator(bits); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() + { + return _enumerator; + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public struct Enumerator : IEnumerator + { + private uint _bits; + private int _count; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator(ushort bits) + { + _count = CountBits(bits); + _bits = bits; + } + public int Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + int number = GetHighBitNumber((ushort)_bits); + _bits ^= 1u << number; + return number; + } + } + object IEnumerator.Current => Current; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + return _count-- > 0; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void IDisposable.Dispose() { } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void IEnumerator.Reset() { } + } + } + + public struct FindBitsIterator32 : IEnumerable + { + private Enumerator _enumerator; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsIterator32(int bits) + { + _enumerator = new Enumerator((uint)bits); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsIterator32(uint bits) + { + _enumerator = new Enumerator(bits); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() + { + return _enumerator; + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public struct Enumerator : IEnumerator + { + private uint _bits; + private int _count; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator(uint bits) + { + _count = CountBits(bits); + _bits = bits; + } + public int Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + int number = GetHighBitNumber(_bits); + _bits ^= 1u << number; + return number; + } + } + object IEnumerator.Current => Current; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + return _count-- > 0; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void IDisposable.Dispose() { } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void IEnumerator.Reset() { } + } + } + + public struct FindBitsIterator64 : IEnumerable + { + private Enumerator _enumerator; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsIterator64(long bits) + { + _enumerator = new Enumerator((ulong)bits); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsIterator64(ulong bits) + { + _enumerator = new Enumerator(bits); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() + { + return _enumerator; + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public struct Enumerator : IEnumerator + { + private ulong _bits; + private int _count; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator(ulong bits) + { + _count = CountBits(bits); + _bits = bits; + } + public int Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + int number = GetHighBitNumber(_bits); + _bits ^= 1u << number; + return number; + } + } + object IEnumerator.Current => Current; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + return _count-- > 0; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void IDisposable.Dispose() { } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void IEnumerator.Reset() { } + } + } + #endregion +} \ No newline at end of file diff --git a/src/Utils/SparseMatrix.cs b/src/Utils/SparseMatrix.cs new file mode 100644 index 0000000..6901347 --- /dev/null +++ b/src/Utils/SparseMatrix.cs @@ -0,0 +1,400 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using TValue = System.Int32; + +namespace DCFApixels.DragonECS.Relations.Internal +{ + internal sealed unsafe class SparseMatrix + { + public const int MIN_CAPACITY_BITS_OFFSET = 4; + public const int MIN_CAPACITY = 1 << MIN_CAPACITY_BITS_OFFSET; + + + private const int MAX_CHAIN_LENGTH = 5; + + private Basket* _buckets; + private Entry* _entries; + private int _capacity; + + private int _count; + + private int _freeList; + private int _freeCount; + + private int _modBitMask; + + #region Properties + public int Count + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _count; } + } + public int Capacity + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + //get { return _buckets.Length; } + get { return _capacity; } + } + #endregion + + #region Constructors + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public SparseMatrix(int minCapacity = MIN_CAPACITY) + { + minCapacity = NormalizeCapacity(minCapacity); + //_buckets = new Basket[minCapacity]; + //_buckets = new UnsafeArray(minCapacity); + _buckets = UnmanagedArrayUtility.New(minCapacity); + for (int i = 0; i < minCapacity; i++) + { + _buckets[i] = Basket.Empty; + } + //_entries = new Entry[minCapacity]; + //_entries = new UnsafeArray(minCapacity, true); + _entries = UnmanagedArrayUtility.NewAndInit(minCapacity); + _modBitMask = (minCapacity - 1) & 0x7FFFFFFF; + + _count = 0; + _freeList = 0; + _freeCount = 0; + + // + _capacity = minCapacity; + } + #endregion + + + + #region Add/TryAdd/Set + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(int x, int y, TValue value) + { + Key key = Key.FromXY(x, y); +#if DEBUG + if (FindEntry(key) >= 0) + { + throw new ArgumentException("Has(x, y) is true"); + } +#endif + int targetBucket = key.yHash & _modBitMask; + AddInternal(key, targetBucket, value); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryAdd(int x, int y, TValue value) + { + Key key = Key.FromXY(x, y); + if (FindEntry(key) >= 0) + { + return false; + } + int targetBucket = key.yHash & _modBitMask; + AddInternal(key, targetBucket, value); + return true; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Set(int x, int y, TValue value) + { + Key key = Key.FromXY(x, y); + int targetBucket = key.yHash & _modBitMask; + + for (int i = _buckets[targetBucket].index; i >= 0; i = _entries[i].next) + { + if (_entries[i].key == key) + { + _entries[i].value = value; + return; + } + } + AddInternal(key, targetBucket, value); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void AddInternal(Key key, int targetBucket, int value) + { + int index; + if (_freeCount == 0) + { + //if (_count == _entries.Length) + if (_count == _capacity) + { + Resize(); + targetBucket = key.yHash & _modBitMask; + } + index = _count++; + } + else + { + //_freeCount > 0 + index = _freeList; + _freeList = _entries[index].next; + _freeCount--; + } + +#if DEBUG + if(_freeCount < 0) { throw new Exception(); } +#endif + + ref Basket basket = ref _buckets[targetBucket]; + ref Entry entry = ref _entries[index]; + + + entry.next = basket.index; + entry.key = key; + entry.value = value; + basket.count++; + basket.index = index; + //Console.WriteLine($"{targetBucket} {basket.count}"); + + if (basket.count >= MAX_CHAIN_LENGTH && Count / Capacity >= 0.7f) + { + Resize(); + } + } + #endregion + + #region FindEntry/Has + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int FindEntry(int x, int y) + { + Key key = Key.FromXY(x, y); + for (int i = _buckets[key.yHash & _modBitMask].index; i >= 0; i = _entries[i].next) + { + if (_entries[i].key == key) + { + return i; + } + } + return -1; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int FindEntry(Key key) + { + for (int i = _buckets[key.yHash & _modBitMask].index; i >= 0; i = _entries[i].next) + { + if (_entries[i].key == key) + { + return i; + } + } + return -1; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool HasKey(int x, int y) + { + return FindEntry(x, y) >= 0; + } + #endregion + + #region GetValue/TryGetValue + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public TValue GetValue(int x, int y) + { + int index = FindEntry(x, y); +#if DEBUG + if(index < 0) + { + throw new KeyNotFoundException(); + } +#endif + return _entries[index].value; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryGetValue(int x, int y, out TValue value) + { + int index = FindEntry(x, y); + if (index < 0) + { + value = default; + return false; + } + value = _entries[index].value; + return true; + } + #endregion + + #region TryDel + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryDel(int x, int y) + { + Key key = Key.FromXY(x, y); + int targetBucket = key.yHash & _modBitMask; + ref Basket basket = ref _buckets[targetBucket]; + + int last = -1; + for (int i = basket.index; i >= 0; last = i, i = _entries[i].next) + { + if (_entries[i].key == key) + { + if (last < 0) + { + basket.index = _entries[i].next; + } + else + { + _entries[last].next = _entries[i].next; + } + _entries[i].next = _freeList; + _entries[i].key = Key.Null; + _entries[i].value = default; + _freeList = i; + _freeCount++; + basket.count--; + return true; + } + } + return false; + } + #endregion + + + + + #region Clear + public void Clear() + { + if (_count > 0) + { + //for (int i = 0; i < _buckets.Length; i++) + for (int i = 0; i < _capacity; i++) + { + _buckets[i] = Basket.Empty; + } + //Array.Clear(_entries, 0, _count); + //UnsafeArray.Clear(ref _entries); + for (int i = 0; i < _capacity; i++) + { + _entries[i] = default; + } + _count = 0; + } + } + #endregion + + #region Resize + [MethodImpl(MethodImplOptions.NoInlining)] + private void Resize() + { + //int newSize = _buckets.Length << 1; + int newSize = _capacity << 1; + _modBitMask = (newSize - 1) & 0x7FFFFFFF; + + //Contract.Assert(newSize >= _entries.Length); + + //Basket[] newBuckets = new Basket[newSize]; + //UnsafeArray newBuckets = new UnsafeArray(newSize); + Basket* newBuckets = UnmanagedArrayUtility.New(newSize); + //for (int i = 0; i < newBuckets.Length; i++) + for (int i = 0; i < _capacity; i++) + { + newBuckets[i] = Basket.Empty; + } + + //Entry[] newEntries = new Entry[newSize]; + //Array.Copy(_entries, 0, newEntries, 0, _count); + //UnsafeArray newEntries = UnsafeArray.Resize(_entries, newSize); + Entry* newEntries = UnmanagedArrayUtility.ResizeAndInit(_entries, _capacity, newSize); + + for (int i = 0; i < _count; i++) + { + if (newEntries[i].key.yHash >= 0) + { + int targetBusket = newEntries[i].key.yHash % newSize; + ref Basket basket = ref _buckets[targetBusket]; + newEntries[i].next = basket.index; + basket.index = i; + basket.count++; + } + } + _buckets = newBuckets; + _entries = newEntries; + + _capacity = newSize; + + Console.WriteLine($"----- {Capacity} {Count}"); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int NormalizeCapacity(int capacity) + { + int result = MIN_CAPACITY; + while (result < capacity) result <<= 1; + return result; + } + #endregion + + #region Utils + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct Entry + { + public int next; // Index of next entry, -1 if last + public Key key; + public TValue value; + + public override string ToString() + { + return key.x == 0 ? "NULL" : value.ToString(); + } + } + + [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)] + public struct Basket + { + public static readonly Basket Empty = new Basket(-1, 0); + + public int index; + public int count; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Basket(int index, int length) + { + this.index = index; + this.count = length; + } + public override string ToString() + { + return index < 0 ? "NULL" : $"{index} {count}"; + } + } + + [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)] + public readonly struct Key : IEquatable + { + public static readonly Key Null = new Key(-1, -1); + public readonly int x; + public readonly int yHash; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Key(int x, int yHash) + { + this.x = x; + this.yHash = yHash; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Key FromXY(int x, int y) + { + //return new Key(x, BitsUtility.NextXorShiftState(y)); + //return new Key(x, (~x) ^ y ^ 1_431_655_765); + return new Key(x, x ^ y ^ BitsUtility.NextXorShiftState(y)); + } + public static bool operator ==(Key a, Key b) + { + return a.x == b.x && a.yHash == b.yHash; + } + public static bool operator !=(Key a, Key b) + { + return a.x != b.x || a.yHash != b.yHash; + } + public override int GetHashCode() + { + return yHash; + } + public bool Equals(Key other) + { + return this == other; + } + public override bool Equals(object obj) + { + return obj is Key && Equals((Key)obj); + } + } + #endregion + } +} \ No newline at end of file diff --git a/src/Utils/UnsafeArray.cs b/src/Utils/UnsafeArray.cs index 7cab469..9c6a91e 100644 --- a/src/Utils/UnsafeArray.cs +++ b/src/Utils/UnsafeArray.cs @@ -9,18 +9,30 @@ namespace DCFApixels.DragonECS.Relations.Internal { internal unsafe static class UnsafeArray { + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Resize(ref UnsafeArray array, int newSize) where T : unmanaged { array.ptr = UnmanagedArrayUtility.Resize(array.ptr, newSize); array.Length = newSize; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ResizeAndInit(ref UnsafeArray array, int newSize) where T : unmanaged { array.ptr = UnmanagedArrayUtility.ResizeAndInit(array.ptr, array.Length, newSize); array.Length = newSize; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Clear(ref UnsafeArray array) + where T : unmanaged + { + T* ptr = array.ptr; + for (int i = 0; i < array.Length; i++) + { + ptr[i] = default; + } + } } [DebuggerTypeProxy(typeof(UnsafeArray<>.DebuggerProxy))] internal unsafe struct UnsafeArray : IDisposable, IEnumerable