diff --git a/src/Builtin/Components.cs b/src/Builtin/Components.cs index 8d609ad..6cea199 100644 --- a/src/Builtin/Components.cs +++ b/src/Builtin/Components.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS { + [DebugColor(DebugColor.White)] public struct Parent : IEcsAttachComponent { public entlong entity; @@ -36,7 +37,7 @@ namespace DCFApixels.DragonECS public static bool TryGetRoot(this EcsAttachPool parents, EcsSubject conditionSubject, int entityID, out int rootEntityID) { rootEntityID = entityID; - while (parents.Has(rootEntityID) && parents.Read(rootEntityID).entity.TryGetID(out int child) && conditionSubject.IsMatches(child)) + while (parents.Has(rootEntityID) && parents.Read(rootEntityID).entity.TryGetID(out int child) && !conditionSubject.IsMatches(child)) rootEntityID = child; return rootEntityID != entityID; } @@ -47,5 +48,25 @@ namespace DCFApixels.DragonECS rootEntityID = child; return rootEntityID != entityID; } + + public static bool TryFindParentWithSubject(this EcsAttachPool parents, EcsSubject conditionSubject, int entityID, out int resultEntityID) + { + resultEntityID = entityID; + while (parents.Has(resultEntityID) && parents.Read(resultEntityID).entity.TryGetID(out int child) && !conditionSubject.IsMatches(resultEntityID)) + resultEntityID = child; + return conditionSubject.IsMatches(resultEntityID); + } + public static bool TryFindParentWith(this EcsAttachPool parents, int entityID, out int resultEntityID) where TComponent : struct + { + var pool = parents.World.AllPools[parents.World.GetComponentID()]; + resultEntityID = entityID; + while (!pool.Has(resultEntityID) && + parents.Has(resultEntityID) && + parents.Read(resultEntityID).entity.TryGetID(out int child)) + { + resultEntityID = child; + } + return pool.Has(resultEntityID); + } } } diff --git a/src/Debug/Attributes/DebugColorAttribute.cs b/src/Debug/Attributes/DebugColorAttribute.cs index ea70081..314479f 100644 --- a/src/Debug/Attributes/DebugColorAttribute.cs +++ b/src/Debug/Attributes/DebugColorAttribute.cs @@ -67,13 +67,31 @@ namespace DCFApixels.DragonECS /// Magenta. RGB is (255, 0, 255) Magenta = (255 << 24) + (000 << 16) + (255 << 8), - /// Yellow. RGB is (255, 127, 0) - Orange = (255 << 24) + (127 << 16) + (000 << 8), - + /// Yellow. RGB is (255, 165, 0) + Orange = (255 << 24) + (165 << 16) + (000 << 8), + /// Yellow. RGB is (255, 69, 0) + OrangeRed = (255 << 24) + (69 << 16) + (000 << 8), + /// Lime. RGB is (125, 255, 0) + Lime = (125 << 24) + (255 << 16) + (000 << 8), + /// Lime. RGB is (127, 255, 212) + Aquamarine = (127 << 24) + (255 << 16) + (212 << 8), + /// Lime. RGB is (218, 165, 32) + Goldenrod = (218 << 24) + (165 << 16) + (32 << 8), + /// Yellow. RGB is (255, 105, 180) + DeepPink = (255 << 24) + (105 << 16) + (180 << 8), + /// Yellow. RGB is (220, 20, 60) + Crimson = (220 << 24) + (20 << 16) + (60 << 8), + /// Yellow. RGB is (138, 43, 226) + BlueViolet = (138 << 24) + (43 << 16) + (226 << 8), + /// Yellow. RGB is (255, 3, 62) + AmericanRose = (255 << 24) + (3 << 16) + (62 << 8), + /// Grey/Gray. RGB is (127, 127, 127) Gray = (127 << 24) + (127 << 16) + (127 << 8), /// Grey/Gray. RGB is (127, 127, 127) Grey = Gray, + /// Grey/Gray. RGB is (192, 192, 192) + Silver = (192 << 24) + (192 << 16) + (192 << 8), /// White. RGB is (255, 255, 255) White = -1, /// Black. RGB is (0, 0, 0) diff --git a/src/Debug/EcsDebugUtility.cs b/src/Debug/EcsDebugUtility.cs new file mode 100644 index 0000000..a917c97 --- /dev/null +++ b/src/Debug/EcsDebugUtility.cs @@ -0,0 +1,55 @@ +using System; +using System.Reflection; + +namespace DCFApixels.DragonECS +{ + public static class EcsDebugUtility + { + public static string GetGenericTypeName(int maxDepth = 2) => GetGenericTypeName(typeof(T), maxDepth); + public static string GetGenericTypeName(Type type, int maxDepth = 2) + { +#if (DEBUG && !DISABLE_DEBUG) + string friendlyName = type.Name; + if (!type.IsGenericType || maxDepth == 0) + return friendlyName; + + int iBacktick = friendlyName.IndexOf('`'); + if (iBacktick > 0) + friendlyName = friendlyName.Remove(iBacktick); + + friendlyName += "<"; + Type[] typeParameters = type.GetGenericArguments(); + for (int i = 0; i < typeParameters.Length; ++i) + { + string typeParamName = GetGenericTypeName(typeParameters[i], maxDepth - 1); + friendlyName += (i == 0 ? typeParamName : "," + typeParamName); + } + friendlyName += ">"; + return friendlyName; +#else + return type.Name; +#endif + } + + public static string GetName() => GetName(typeof(T)); + public static string GetName(Type type) + { + var atr = type.GetCustomAttribute(); + return atr != null ? atr.name : GetGenericTypeName(type); + } + + public static string GetDescription() => GetDescription(typeof(T)); + public static string GetDescription(Type type) + { + var atr = type.GetCustomAttribute(); + return atr != null ? atr.description : string.Empty; + } + + public static (byte, byte, byte) GetColorRGB() => GetColorRGB(typeof(T)); + public static (byte, byte, byte) GetColorRGB(Type type) + { + var atr = type.GetCustomAttribute(); + return atr != null ? (atr.r, atr.g, atr.b) : ((byte)255, (byte)255, (byte)255); + } + } +} diff --git a/src/EcsPipeline.cs b/src/EcsPipeline.cs index 3e5fb67..a898ecb 100644 --- a/src/EcsPipeline.cs +++ b/src/EcsPipeline.cs @@ -184,6 +184,13 @@ namespace DCFApixels.DragonECS AddInternal(system, layerName, true); return this; } + public Builder Remove() + { + _uniqueTypes.Remove(typeof(TSystem)); + foreach (var list in _systems.Values) + list.RemoveAll(o => o is TSystem); + return this; + } private void AddInternal(IEcsSystem system, string layerName, bool isUnique) { if (layerName == null) layerName = _basicLayer; diff --git a/src/EcsRunner.cs b/src/EcsRunner.cs index 31e866c..d657be1 100644 --- a/src/EcsRunner.cs +++ b/src/EcsRunner.cs @@ -44,7 +44,7 @@ namespace DCFApixels.DragonECS internal static class EcsRunnerActivator { - private static Dictionary _runnerHandlerTypes; //interface guid/Runner handler type pairs; + private static Dictionary _runnerHandlerTypes; //interface base type/Runner handler type pairs; static EcsRunnerActivator() { @@ -69,11 +69,12 @@ namespace DCFApixels.DragonECS } } #endif - _runnerHandlerTypes = new Dictionary(); + _runnerHandlerTypes = new Dictionary(); foreach (var item in runnerHandlerTypes) { Type interfaceType = item.BaseType.GenericTypeArguments[0]; - _runnerHandlerTypes.Add(interfaceType.GUID, item); +// if(!_runnerHandlerTypes.ContainsKey(interfaceType.GUID))//TODO это кажется костыль, изначально все работало без этого ифа + _runnerHandlerTypes.Add(interfaceType.IsGenericType ? interfaceType.GetGenericTypeDefinition() : interfaceType, item); } if (delayedExceptions.Count > 0) @@ -112,14 +113,8 @@ namespace DCFApixels.DragonECS internal static void InitFor() where TInterface : IEcsSystem { Type interfaceType = typeof(TInterface); - Type nonGenericInterfaceType = interfaceType; - if (nonGenericInterfaceType.IsGenericType) - { - nonGenericInterfaceType = nonGenericInterfaceType.GetGenericTypeDefinition(); - } - Guid interfaceGuid = nonGenericInterfaceType.GUID; - if (!_runnerHandlerTypes.TryGetValue(interfaceGuid, out Type runnerType)) + if (!_runnerHandlerTypes.TryGetValue(interfaceType.IsGenericType ? interfaceType.GetGenericTypeDefinition() : interfaceType, out Type runnerType)) { throw new Exception(); } diff --git a/src/EcsSubject.cs b/src/EcsSubject.cs index 1222e0c..eff687f 100644 --- a/src/EcsSubject.cs +++ b/src/EcsSubject.cs @@ -73,7 +73,7 @@ namespace DCFApixels.DragonECS } public sealed override TPool Exclude() { - + ExcludeImplicit(); return _world.GetPool(); } public sealed override TPool Optional() diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index d84f595..32a7fab 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -39,6 +39,7 @@ namespace DCFApixels.DragonECS internal IEcsPoolImplementation[] pools; private EcsNullPool _nullPool; + private int _poolsCount = 0; private EcsSubject[] _subjects; private EcsQueryExecutor[] _executors; @@ -58,7 +59,8 @@ namespace DCFApixels.DragonECS public int Capacity => _entitesCapacity; //_denseEntities.Length; public EcsPipeline Pipeline => _pipeline; public EcsReadonlyGroup Entities => _allEntites.Readonly; - public ReadOnlySpan AllPools => pools; + public ReadOnlySpan AllPools => pools;// new ReadOnlySpan(pools, 0, _poolsCount); + public int PoolsCount => _poolsCount; #endregion #region Constructors/Destroy @@ -151,7 +153,7 @@ namespace DCFApixels.DragonECS var pool = new TPool(); pools[uniqueID] = pool; pool.OnInit(this, uniqueID); - + _poolsCount++; //EcsDebug.Print(pool.GetType().FullName); } @@ -289,7 +291,7 @@ namespace DCFApixels.DragonECS #endregion #region Entity - public int NewEntity() + public int NewEmptyEntity() { int entityID = _entityDispenser.GetFree(); _entitiesCount++; @@ -316,15 +318,16 @@ namespace DCFApixels.DragonECS } foreach (var item in pools) item.OnWorldResize(_gens.Length); + } _gens[entityID] &= GEN_BITS; _entityCreate.OnEntityCreate(entityID); _allEntites.Add(entityID); return entityID; } - public entlong NewEntityLong() + public entlong NewEmptyEntityLong() { - int e = NewEntity(); + int e = NewEmptyEntity(); return GetEntityLong(e); } @@ -355,7 +358,7 @@ namespace DCFApixels.DragonECS _delEntBufferCount = 0; } public short GetGen(int entityID) => _gens[entityID]; - public short GetComponentCount(int entityID) => _componentCounts[entityID]; + public short GetComponentsCount(int entityID) => _componentCounts[entityID]; public void DeleteEmptyEntites() { foreach (var e in _allEntites) @@ -365,6 +368,30 @@ namespace DCFApixels.DragonECS } } + public void CopyEntity(int fromEntityID, int toEntityID) + { + foreach (var pool in pools) + { + if(pool.Has(fromEntityID)) + pool.Copy(fromEntityID, toEntityID); + } + } + public int CloneEntity(int fromEntityID) + { + int newEntity = NewEmptyEntity(); + CopyEntity(fromEntityID, newEntity); + return newEntity; + } + public void CloneEntity(int fromEntityID, int toEntityID) + { + CopyEntity(fromEntityID, toEntityID); + foreach (var pool in pools) + { + if (!pool.Has(fromEntityID)&& pool.Has(toEntityID)) + pool.Del(toEntityID); + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void IncrementEntityComponentCount(int entityID) { @@ -374,7 +401,7 @@ namespace DCFApixels.DragonECS internal void DecrementEntityComponentCount(int entityID) { var count = --_componentCounts[entityID]; - if(count == 0) + if(count == 0 && _allEntites.Has(entityID)) DelEntity(entityID); #if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS @@ -406,39 +433,55 @@ namespace DCFApixels.DragonECS #endregion #region Debug - // public int GetComponents(int entity, ref object[] list) - // { - // var entityOffset = GetRawEntityOffset(entity); - // var itemsCount = _entities[entityOffset + RawEntityOffsets.ComponentsCount]; - // if (itemsCount == 0) { return 0; } - // if (list == null || list.Length < itemsCount) - // { - // list = new object[_pools.Length]; - // } - // var dataOffset = entityOffset + RawEntityOffsets.Components; - // for (var i = 0; i < itemsCount; i++) - // { - // list[i] = _pools[_entities[dataOffset + i]].GetRaw(entity); - // } - // return itemsCount; - // } - // - // public int GetComponentTypes(int entity, ref Type[] list) - // { - // var entityOffset = GetRawEntityOffset(entity); - // var itemsCount = _entities[entityOffset + RawEntityOffsets.ComponentsCount]; - // if (itemsCount == 0) { return 0; } - // if (list == null || list.Length < itemsCount) - // { - // list = new Type[_pools.Length]; - // } - // var dataOffset = entityOffset + RawEntityOffsets.Components; - // for (var i = 0; i < itemsCount; i++) - // { - // list[i] = _pools[_entities[dataOffset + i]].GetComponentType(); - // } - // return itemsCount; - // } + public void GetComponents(int entityID, List list) + { + list.Clear(); + var itemsCount = GetComponentsCount(entityID); + if (itemsCount == 0) + return; + + for (var i = 0; i < pools.Length; i++) + { + if (pools[i].Has(entityID)) + list.Add(pools[i].GetRaw(entityID)); + if (list.Count >= itemsCount) + break; + } + } + + // public int GetComponents(int entity, ref object[] list) + // { + // var entityOffset = GetRawEntityOffset(entity); + // var itemsCount = _entities[entityOffset + RawEntityOffsets.ComponentsCount]; + // if (itemsCount == 0) { return 0; } + // if (list == null || list.Length < itemsCount) + // { + // list = new object[_pools.Length]; + // } + // var dataOffset = entityOffset + RawEntityOffsets.Components; + // for (var i = 0; i < itemsCount; i++) + // { + // list[i] = _pools[_entities[dataOffset + i]].GetRaw(entity); + // } + // return itemsCount; + // } + + // public int GetComponentTypes(int entity, ref Type[] list) + // { + // var entityOffset = GetRawEntityOffset(entity); + // var itemsCount = _entities[entityOffset + RawEntityOffsets.ComponentsCount]; + // if (itemsCount == 0) { return 0; } + // if (list == null || list.Length < itemsCount) + // { + // list = new Type[_pools.Length]; + // } + // var dataOffset = entityOffset + RawEntityOffsets.Components; + // for (var i = 0; i < itemsCount; i++) + // { + // list[i] = _pools[_entities[dataOffset + i]].GetComponentType(); + // } + // return itemsCount; + // } #endregion } @@ -577,4 +620,13 @@ namespace DCFApixels.DragonECS } } #endregion + + // #region Callbacks Interface //TODO + // public interface IWorldCallbacks + // { + // void OnWorldResize(int newSize); + // void OnReleaseDelEntityBuffer(ReadOnlySpan buffer); + // void OnWorldDestroy(); + // } + // #endregion } diff --git a/src/Executors/EcsWhereExecutor.cs b/src/Executors/EcsWhereExecutor.cs index e47b56b..c3121d4 100644 --- a/src/Executors/EcsWhereExecutor.cs +++ b/src/Executors/EcsWhereExecutor.cs @@ -67,6 +67,11 @@ namespace DCFApixels.DragonECS #endif return group.GetEnumerator(); } + + public override string ToString() + { + return group.ToString(); + } } #endregion } diff --git a/src/Pools/EcsAttachPool.cs b/src/Pools/EcsAttachPool.cs index f8d877c..c65968d 100644 --- a/src/Pools/EcsAttachPool.cs +++ b/src/Pools/EcsAttachPool.cs @@ -132,6 +132,16 @@ namespace DCFApixels.DragonECS else Add(toEntityID, Read(fromEntityID).Target); } + public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(fromEntityID)) ThrowNotHaveComponent(fromEntityID); +#endif + if (Has(toEntityID)) + toWorld.GetPool().Set(toEntityID, Read(fromEntityID).Target); + else + toWorld.GetPool().Add(toEntityID, Read(fromEntityID).Target); + } #endregion #region WorldCallbacks diff --git a/src/Pools/EcsNotNullPool.cs b/src/Pools/EcsNotNullPool.cs index 7ed0f16..96283f9 100644 --- a/src/Pools/EcsNotNullPool.cs +++ b/src/Pools/EcsNotNullPool.cs @@ -38,6 +38,7 @@ namespace DCFApixels.DragonECS _count = 0; _componentResetHandler = EcsComponentResetHandler.instance; + _componentCopyHandler = EcsComponentCopyHandler.instance; _poolRunners = new PoolRunners(world.Pipeline); } #endregion @@ -63,6 +64,10 @@ namespace DCFApixels.DragonECS { _componentCopyHandler.Copy(ref Write(fromEntityID), ref Write(toEntityID)); } + public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) + { + _componentCopyHandler.Copy(ref Write(fromEntityID), ref toWorld.GetPool().Write(toEntityID)); + } #endregion #region Callbacks diff --git a/src/Pools/EcsPool.cs b/src/Pools/EcsPool.cs index 8a3c0a4..faeba2a 100644 --- a/src/Pools/EcsPool.cs +++ b/src/Pools/EcsPool.cs @@ -147,10 +147,14 @@ namespace DCFApixels.DragonECS #if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS if (!Has(fromEntityID)) ThrowNotHaveComponent(fromEntityID); #endif - if (Has(toEntityID)) - _componentCopyHandler.Copy(ref Write(fromEntityID), ref Write(toEntityID)); - else - _componentCopyHandler.Copy(ref Write(fromEntityID), ref Add(toEntityID)); + _componentCopyHandler.Copy(ref Write(fromEntityID), ref TryAddOrWrite(toEntityID)); + } + public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(fromEntityID)) ThrowNotHaveComponent(fromEntityID); +#endif + _componentCopyHandler.Copy(ref Write(fromEntityID), ref toWorld.GetPool().TryAddOrWrite(toEntityID)); } #endregion diff --git a/src/Pools/EcsPoolBase.cs b/src/Pools/EcsPoolBase.cs index 4502e92..3c67537 100644 --- a/src/Pools/EcsPoolBase.cs +++ b/src/Pools/EcsPoolBase.cs @@ -1,4 +1,5 @@ -using System; +using DCFApixels.DragonECS.Internal; +using System; using System.Linq; using System.Runtime.CompilerServices; @@ -21,6 +22,7 @@ namespace DCFApixels.DragonECS object GetRaw(int entityID); void SetRaw(int entityID, object dataRaw); void Copy(int fromEntityID, int toEntityID); + void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID); #endregion } public interface IEcsPool @@ -76,7 +78,7 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNullOrDummy(this IEcsPool self) { - return self == null || self is Internal.EcsNullPool; + return self == null || self == EcsNullPool.instance; } } @@ -89,11 +91,11 @@ namespace DCFApixels.DragonECS public static EcsNullPool instance => new EcsNullPool(); #region Properties - int IEcsPool.ComponentID => throw new NotImplementedException(); + int IEcsPool.ComponentID => -1; Type IEcsPool.ComponentType => typeof(NullComponent); EcsWorld IEcsPool.World => throw new NotImplementedException(); - public int Count => 0; - public int Capacity => 0; + public int Count => -1; + public int Capacity => -1; #endregion #region Methods @@ -103,6 +105,7 @@ namespace DCFApixels.DragonECS object IEcsPool.GetRaw(int entityID) => throw new NotImplementedException(); void IEcsPool.SetRaw(int entity, object dataRaw) => throw new NotImplementedException(); void IEcsPool.Copy(int fromEntityID, int toEntityID) => throw new NotImplementedException(); + void IEcsPool.Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) => throw new NotImplementedException(); ref NullComponent IEcsPool.Add(int entityID) => throw new NotImplementedException(); ref readonly NullComponent IEcsPool.Read(int entityID) => throw new NotImplementedException(); ref NullComponent IEcsPool.Write(int entityID) => throw new NotImplementedException(); diff --git a/src/Pools/EcsSinglePool.cs b/src/Pools/EcsSinglePool.cs index c3dacac..5052a50 100644 --- a/src/Pools/EcsSinglePool.cs +++ b/src/Pools/EcsSinglePool.cs @@ -105,6 +105,13 @@ namespace DCFApixels.DragonECS #endif TryAddOrWrite(toEntityID); } + public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(fromEntityID)) ThrowNotHaveComponent(fromEntityID); +#endif + toWorld.GetPool().TryAddOrWrite(toEntityID); + } #endregion #region Callbacks diff --git a/src/Pools/EcsTagPool.cs b/src/Pools/EcsTagPool.cs index 923cb36..5d275de 100644 --- a/src/Pools/EcsTagPool.cs +++ b/src/Pools/EcsTagPool.cs @@ -87,6 +87,13 @@ namespace DCFApixels.DragonECS #endif TryAdd(toEntityID); } + public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(fromEntityID)) ThrowNotHaveComponent(fromEntityID); +#endif + toWorld.GetPool().TryAdd(toEntityID); + } public void Set(int entityID, bool isHas) { if (isHas) @@ -100,6 +107,13 @@ namespace DCFApixels.DragonECS Del(entityID); } } + public void Toggle(int entityID) + { + if (Has(entityID)) + Del(entityID); + else + Add(entityID); + } #endregion #region Callbacks diff --git a/src/Utils/SparseArray.cs b/src/Utils/SparseArray.cs index e89ec0c..493eac7 100644 --- a/src/Utils/SparseArray.cs +++ b/src/Utils/SparseArray.cs @@ -1,42 +1,49 @@ -using System; +//SparseArray. Analogous to Dictionary, but faster. +//Benchmark result of indexer.get speed test with 300 elements: +//[Dictinary: 5.786us] [SparseArray: 2.047us]. +using System; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace DCFApixels +namespace DCFApixels.DragonECS { public class SparseArray { - public const int MIN_CAPACITY = 16; + 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[] _dense; private int _count; private int _freeList; private int _freeCount; + private int _modBitMask; + #region Properties - public TValue this[int key] + public ref TValue this[int key] { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _entries[FindEntry(key)].value; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set => Insert(key, value); + get => ref _entries[FindEntry(key)].value; + //set => Insert(key, value); } public int Count => _count; #endregion #region Constructors - public SparseArray(int capacity = MIN_CAPACITY) + public SparseArray(int minCapacity = MIN_CAPACITY) { - _buckets = new int[capacity]; - for (int i = 0; i < capacity; i++) + minCapacity = NormalizeCapacity(minCapacity); + _buckets = new int[minCapacity]; + for (int i = 0; i < minCapacity; i++) _buckets[i] = EMPTY; - _entries = new Entry[capacity]; + _entries = new Entry[minCapacity]; + _modBitMask = (minCapacity - 1) & 0x7FFFFFFF; } #endregion @@ -53,19 +60,16 @@ namespace DCFApixels #endregion #region Find/Insert/Remove + [MethodImpl(MethodImplOptions.AggressiveInlining)] private int FindEntry(int key) { - key &= 0x7FFFFFFF; - for (int i = _buckets[key % _buckets.Length]; i >= 0; i = _entries[i].next) - { + for (int i = _buckets[key & _modBitMask]; i >= 0; i = _entries[i].next) if (_entries[i].hashKey == key) return i; - } return -1; } private void Insert(int key, TValue value) { - key &= 0x7FFFFFFF; - int targetBucket = key % _buckets.Length; + int targetBucket = key & _modBitMask; for (int i = _buckets[targetBucket]; i >= 0; i = _entries[i].next) { @@ -88,10 +92,9 @@ namespace DCFApixels if (_count == _entries.Length) { Resize(); - targetBucket = key % _buckets.Length; + targetBucket = key & _modBitMask; } - index = _count; - _count++; + index = _count++; } _entries[index].next = _buckets[targetBucket]; @@ -101,8 +104,7 @@ namespace DCFApixels } public bool Remove(int key) { - key &= 0x7FFFFFFF; - int bucket = key % _buckets.Length; + int bucket = key & _modBitMask; int last = -1; for (int i = _buckets[bucket]; i >= 0; last = i, i = _entries[i].next) { @@ -168,6 +170,7 @@ namespace DCFApixels private void Resize() { int newSize = _buckets.Length << 1; + _modBitMask = (newSize - 1) & 0x7FFFFFFF; Contract.Assert(newSize >= _entries.Length); int[] newBuckets = new int[newSize]; @@ -188,14 +191,67 @@ namespace DCFApixels _buckets = newBuckets; _entries = newEntries; } + + private int NormalizeCapacity(int capacity) + { + int result = MIN_CAPACITY; + while (result < capacity) result <<= 1; + return result; + } #endregion + + //#region Enumerator + // public Enumerator GetEnumerator() => new Enumerator(this); + // public struct Enumerator + // { + // private SparseArray _source; + // private int index; + // private int curretnItmeIndex; + // + // public ref readonly TValue Current + // { + // get + // { + // return ref _source._entries[curretnItmeIndex].value; + // } + // } + // + // public Enumerator(SparseArray source) + // { + // _source = source; + // index = 0; + // curretnItmeIndex = 0; + // } + // + // public bool MoveNext() + // { + // // Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends. + // // dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue + // while ((uint)index < (uint)_source.count) + // { + // if (dictionary.entries[index].hashCode >= 0) + // { + // current = new KeyValuePair(dictionary.entries[index].key, dictionary.entries[index].value); + // index++; + // return true; + // } + // index++; + // } + // + // index = dictionary.count + 1; + // current = new KeyValuePair(); + // return false; + // } + // } + // #endregion + #region Utils [StructLayout(LayoutKind.Sequential, Pack = 4)] private struct Entry { public int next; // Index of next entry, -1 if last - public int hashKey; + public int hashKey; public TValue value; } #endregion