From 12f166a01a424894903bf2aaab48396ea3ab1c9c Mon Sep 17 00:00:00 2001
From: Mikhail <99481254+DCFApixels@users.noreply.github.com>
Date: Wed, 22 Nov 2023 16:09:03 +0800
Subject: [PATCH] create temp TestPool
---
src/Pools/EcsTestPool.cs | 325 +++++++++++++++++++++++++++++++++++++++
1 file changed, 325 insertions(+)
create mode 100644 src/Pools/EcsTestPool.cs
diff --git a/src/Pools/EcsTestPool.cs b/src/Pools/EcsTestPool.cs
new file mode 100644
index 0000000..b7abf99
--- /dev/null
+++ b/src/Pools/EcsTestPool.cs
@@ -0,0 +1,325 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace DCFApixels.DragonECS
+{
+ /// Pool for IEcsComponent components
+ public sealed class EcsTestPool : IEcsPoolImplementation, IEcsStructPool, IEnumerable //IEnumerable - IntelliSense hack
+ where T : struct, IEcsTestComponent
+ {
+ private EcsWorld _source;
+ private int _componentID;
+
+
+ public const int MIN_CAPACITY_BITS_OFFSET = 4;
+ public const int MIN_CAPACITY = 1 << MIN_CAPACITY_BITS_OFFSET;
+ private const int EMPTY = -1;
+
+ private int[] _buckets = Array.Empty();
+ private Entry[] _entries = Array.Empty();
+
+ private int _count;
+
+ private int _freeList;
+ private int _freeCount;
+
+ private int _modBitMask;
+
+
+ private IEcsComponentReset _componentResetHandler = EcsComponentResetHandler.instance;
+ private IEcsComponentCopy _componentCopyHandler = EcsComponentCopyHandler.instance;
+
+ private List _listeners = new List();
+
+ #region Properites
+ public int Count => _count;
+ public int Capacity => _entries.Length;
+ public int ComponentID => _componentID;
+ public Type ComponentType => typeof(T);
+ public EcsWorld World => _source;
+ #endregion
+
+ #region Methods
+ public void Copy(int fromEntityID, int toEntityID)
+ {
+#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
+ if (!Has(fromEntityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(fromEntityID);
+#endif
+ _componentCopyHandler.Copy(ref Get(fromEntityID), ref TryAddOrGet(toEntityID));
+ }
+ public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID)
+ {
+#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
+ if (!Has(fromEntityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(fromEntityID);
+#endif
+ _componentCopyHandler.Copy(ref Get(fromEntityID), ref toWorld.GetPool().TryAddOrGet(toEntityID));
+ }
+ #endregion
+
+
+ #region Callbacks
+ void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID)
+ {
+ _source = world;
+ _componentID = componentID;
+
+ int minCapacity = 512;
+ minCapacity = NormalizeCapacity(minCapacity);
+ _buckets = new int[minCapacity];
+ for (int i = 0; i < minCapacity; i++)
+ _buckets[i] = EMPTY;
+ _entries = new Entry[minCapacity];
+ _modBitMask = (minCapacity - 1) & 0x7FFFFFFF;
+ }
+ void IEcsPoolImplementation.OnWorldResize(int newSize) { }
+ void IEcsPoolImplementation.OnWorldDestroy() { }
+ void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan buffer)
+ {
+ foreach (var entityID in buffer)
+ TryDel(entityID);
+ }
+ #endregion
+
+ #region Other
+ void IEcsPool.AddRaw(int entityID, object dataRaw) => Add(entityID) = (T)dataRaw;
+ object IEcsPool.GetRaw(int entityID) => Read(entityID);
+ void IEcsPool.SetRaw(int entityID, object dataRaw) => Get(entityID) = (T)dataRaw;
+ ref readonly T IEcsStructPool.Read(int entityID) => ref Read(entityID);
+ ref T IEcsStructPool.Get(int entityID) => ref Get(entityID);
+ #endregion
+
+ #region Listeners
+ public void AddListener(IEcsPoolEventListener listener)
+ {
+ if (listener == null) { throw new ArgumentNullException("listener is null"); }
+ _listeners.Add(listener);
+ }
+ public void RemoveListener(IEcsPoolEventListener listener)
+ {
+ if (listener == null) { throw new ArgumentNullException("listener is null"); }
+ _listeners.Remove(listener);
+ }
+ #endregion
+
+ #region IEnumerator - IntelliSense hack
+ IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
+ IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
+ #endregion
+
+
+ #region Find/Insert/Remove
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private int FindEntry(long x, long y)
+ {
+ return FindEntry(x << 32 | y);
+ }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private int FindEntry(long key)
+ {
+ for (int i = _buckets[unchecked((int)key & _modBitMask)]; i >= 0; i = _entries[i].next)
+ if (_entries[i].key == key) return i;
+ return -1;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool Has(int entityID) => Has(entityID, entityID);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private bool Has(long x, long y) => Has(x << 32 | y);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private bool Has(long key) {
+ for (int i = _buckets[unchecked((int)key & _modBitMask)]; i >= 0; i = _entries[i].next)
+ if (_entries[i].key == key) return true;
+ return false;
+ }
+
+ public ref T Add(int entityID) => ref Add(entityID, entityID);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private ref T Add(long x, long y) => ref Add(x << 32 | y);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private ref T Add(long key)
+ {
+ int entityID = unchecked((int)key);
+#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
+ if (Has(key: key)) EcsPoolThrowHalper.ThrowAlreadyHasComponent(entityID);
+#endif
+
+ int index;
+ if (_freeCount > 0)
+ {
+ index = _freeList;
+ _freeList = _entries[index].next;
+ _freeCount--;
+ }
+ else
+ {
+ if (_count == _entries.Length)
+ Resize();
+ index = _count++;
+ }
+ int targetBucket = unchecked((int)key & _modBitMask);
+
+ ref var entry = ref _entries[index];
+ entry.next = _buckets[targetBucket];
+ entry.key = key;
+ entry.value = default;
+ _buckets[targetBucket] = index;
+ this.IncrementEntityComponentCount(entityID);
+ _listeners.InvokeOnAddAndGet(entityID);
+ return ref entry.value;
+ }
+ public ref T TryAddOrGet(int entityID)
+ {
+ throw new NotImplementedException();
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ref T Get(int entityID) => ref Get(entityID, entityID);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private ref T Get(long x, long y) => ref Get(x << 32 | y);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private ref T Get(long key)
+ {
+#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
+ if (!Has(key: key)) EcsPoolThrowHalper.ThrowNotHaveComponent(unchecked((int)key));
+#endif
+ _listeners.InvokeOnGet(unchecked((int)key));
+ return ref _entries[FindEntry(key)].value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ref readonly T Read(int entityID) => ref Read(entityID, entityID);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ref readonly T Read(long x, long y) => ref Read(x << 32 | y);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ref readonly T Read(long key)
+ {
+#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
+ if (!Has(key: key)) EcsPoolThrowHalper.ThrowNotHaveComponent(unchecked((int)key));
+#endif
+ return ref _entries[FindEntry(key)].value;
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Del(int entityID) => Del(entityID, entityID);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void Del(long keyX, long keyY) => Del(keyX + (keyY << 32));
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void Del(long key)
+ {
+ int entityID = unchecked((int)key);
+#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
+ if (!Has(key: key)) EcsPoolThrowHalper.ThrowNotHaveComponent(entityID);
+#endif
+ int bucket = unchecked((int)key & _modBitMask);
+ int last = -1;
+ for (int i = _buckets[bucket]; i >= 0; last = i, i = _entries[i].next)
+ {
+ if (_entries[i].key == key)
+ {
+ if (last < 0)
+ {
+ _buckets[bucket] = _entries[i].next;
+ }
+ else
+ {
+ _entries[last].next = _entries[i].next;
+ }
+ _entries[i].next = _freeList;
+ _entries[i].key = -1;
+ //_entries[i].value = default;
+ _componentResetHandler.Reset(ref _entries[i].value);
+ _freeList = i;
+ _freeCount++;
+ this.DecrementEntityComponentCount(entityID);
+ _listeners.InvokeOnDel(entityID);
+ return;
+ }
+ }
+ }
+ public void TryDel(int entityID)
+ {
+ if (Has(entityID)) Del(entityID);
+ }
+ #endregion
+
+ #region Resize
+ private void Resize()
+ {
+ int newSize = _buckets.Length << 1;
+ _modBitMask = (newSize - 1) & 0x7FFFFFFF;
+
+ Contract.Assert(newSize >= _entries.Length);
+ int[] newBuckets = new int[newSize];
+ for (int i = 0; i < newBuckets.Length; i++)
+ newBuckets[i] = EMPTY;
+
+ Entry[] newEntries = new Entry[newSize];
+ Array.Copy(_entries, 0, newEntries, 0, _count);
+ for (int i = 0; i < _count; i++)
+ {
+ if (newEntries[i].key >= 0)
+ {
+ int bucket = unchecked((int)newEntries[i].key & _modBitMask);
+ newEntries[i].next = newBuckets[bucket];
+ newBuckets[bucket] = i;
+ }
+ }
+ _buckets = newBuckets;
+ _entries = newEntries;
+ }
+
+ private 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 long key;
+ public T value;
+ }
+ #endregion
+ }
+ /// Standard component
+ public interface IEcsTestComponent { }
+ public static class EcsTestPoolExt
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static EcsTestPool GetPool(this EcsWorld self) where TComponent : struct, IEcsTestComponent
+ {
+ return self.GetPool>();
+ }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static EcsTestPool GetPoolUnchecked(this EcsWorld self) where TComponent : struct, IEcsTestComponent
+ {
+ return self.GetPoolUnchecked>();
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static EcsTestPool Include(this EcsAspectBuilderBase self) where TComponent : struct, IEcsTestComponent
+ {
+ return self.Include>();
+ }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static EcsTestPool Exclude(this EcsAspectBuilderBase self) where TComponent : struct, IEcsTestComponent
+ {
+ return self.Exclude>();
+ }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static EcsTestPool Optional(this EcsAspectBuilderBase self) where TComponent : struct, IEcsTestComponent
+ {
+ return self.Optional>();
+ }
+ }
+}