Last active
January 25, 2019 10:28
-
-
Save copygirl/a010aada7d7131aee7b5bd83140ab514 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| namespace ProperEngine.ECS.Collections | |
| { | |
| public class ComponentMap<TEntity, TComponent> | |
| where TEntity : struct, IEntity | |
| where TComponent : struct, IComponent | |
| { | |
| private readonly RefDictionary<TEntity, TComponent> _dict | |
| = new RefDictionary<TEntity, TComponent>(); | |
| public ref TComponent TryGetRef(TEntity entity, out bool success) | |
| => ref _dict.TryGetEntry(GetBehavior.Default, entity, out success); | |
| public ref TComponent GetOrCreateRef(TEntity entity) | |
| => ref _dict.TryGetEntry(GetBehavior.Create, entity, out var _); | |
| // Apparently this works, though? | |
| private static bool _; | |
| public ref TComponent GetOrCreateRef2(TEntity entity) | |
| => ref _dict.TryGetEntry(GetBehavior.Create, entity, out _); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| src/ProperEngine.ES/Collections/ES.cs(21,61): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference [/home/copygirl/projects/ProperEngine/ProperEngine/ProperEngine.csproj] | |
| src/ProperEngine.ES/Collections/ES.cs(21,11): error CS8347: Cannot use a result of 'RefDictionary<TEntity, TComponent>.TryGetEntry(GetBehavior, TEntity, out bool)' in this context because it may expose variables referenced by parameter 'found' outside of their declaration scope [/home/copygirl/projects/ProperEngine/ProperEngine/ProperEngine.csproj] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| namespace ProperEngine.ECS.Collections | |
| { | |
| internal class RefDictionary<TKey, TValue> | |
| { | |
| private readonly object _syncRoot = new object(); | |
| private readonly IEqualityComparer<TKey> _comparer; | |
| private int[] _buckets; | |
| private Entry[] _entries; | |
| private int _count; | |
| private int _version; | |
| private int _freeEntry; | |
| private int _freeCount; | |
| public int Count => (_count - _freeCount); | |
| public int Capacity { | |
| get => _entries.Length; | |
| set => Resize(value); | |
| } | |
| public RefDictionary() | |
| : this(0, EqualityComparer<TKey>.Default) { } | |
| public RefDictionary(int capacity) | |
| : this(capacity, EqualityComparer<TKey>.Default) { } | |
| public RefDictionary(IEqualityComparer<TKey> comparer) | |
| : this(0, comparer) { } | |
| public RefDictionary(int capacity, IEqualityComparer<TKey> comparer) | |
| { | |
| if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity)); | |
| if (comparer == null) throw new ArgumentNullException(nameof(comparer)); | |
| if (capacity > 0) Initialize(capacity); | |
| _comparer = comparer; | |
| } | |
| private void Initialize(int capacity) { | |
| int size = HashHelpers.GetPrime(capacity); | |
| _buckets = new int[size]; | |
| _entries = new Entry[size]; | |
| Array.Fill(_buckets, -1); | |
| _freeEntry = -1; | |
| } | |
| // public void Clear() | |
| // { | |
| // if (_count == 0) return; | |
| // Array.Fill(_buckets, -1); | |
| // Array.Clear(_entries, 0, _count); | |
| // _count = 0; | |
| // _freeList = -1; | |
| // _freeCount = 0; | |
| // _version++; | |
| // } | |
| private void Resize() | |
| => Resize(HashHelpers.ExpandPrime(_count)); | |
| private void Resize(int newSize) | |
| { | |
| if (newSize < _entries.Length) throw new ArgumentOutOfRangeException(nameof(newSize)); | |
| var newBuckets = new int[newSize]; | |
| var newEntries = new Entry[newSize]; | |
| Array.Fill(newBuckets, -1); | |
| Array.Copy(_entries, 0, newEntries, 0, _count); | |
| for (int i = 0; i < _count; i++) { | |
| ref var entry = ref newEntries[i]; | |
| if (entry.HashCode < 0) continue; | |
| var bucket = (newEntries[i].HashCode % newSize); | |
| entry.Next = newBuckets[bucket]; | |
| newBuckets[bucket] = i; | |
| } | |
| _buckets = newBuckets; | |
| _entries = newEntries; | |
| } | |
| public ref TValue TryGetEntry( | |
| GetBehavior behavior, TKey key, out bool found) | |
| { | |
| found = false; | |
| if (_buckets == null) { | |
| if (behavior != GetBehavior.Create) | |
| return ref EMPTY_COMPONENT; | |
| Initialize(0); | |
| } | |
| var hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF; | |
| var targetBucket = hashCode & _buckets.Length; | |
| var last = -1; | |
| for (var i = _buckets[targetBucket]; i >= 0; last = i) { | |
| ref var entry = ref _entries[i]; | |
| if ((entry.HashCode == hashCode) && _comparer.Equals(entry.Key, key)) { | |
| found = true; | |
| if (behavior == GetBehavior.Remove) { | |
| _buckets[(last >= 0) ? last : targetBucket] = entry.Next; | |
| entry.HashCode = -1; | |
| entry.Next = _freeEntry; | |
| entry.Key = default(TKey); | |
| // Not resetting allows us to return previous value. | |
| // entry.Value = default(TComponent); | |
| _freeEntry = i; | |
| _freeCount++; | |
| _version++; | |
| } | |
| return ref entry.Value; | |
| } | |
| i = entry.Next; | |
| } | |
| if (behavior != GetBehavior.Create) | |
| return ref EMPTY_COMPONENT; | |
| int index; | |
| if (_freeCount > 0) { | |
| index = _freeEntry; | |
| _freeEntry = _entries[index].Next; | |
| _freeCount--; | |
| } else { | |
| if (_count == _entries.Length) { | |
| Resize(); | |
| targetBucket = hashCode % _buckets.Length; | |
| } | |
| index = _count; | |
| _count++; | |
| } | |
| { | |
| ref var entry = ref _entries[index]; | |
| entry.HashCode = hashCode; | |
| entry.Next = _buckets[targetBucket]; | |
| entry.Key = key; | |
| entry.Value = default(TValue); | |
| _version++; | |
| return ref entry.Value; | |
| } | |
| } | |
| private static TValue EMPTY_COMPONENT | |
| = default(TValue); | |
| private struct Entry | |
| { | |
| public int HashCode; | |
| public int Next; | |
| public TKey Key; | |
| public TValue Value; | |
| } | |
| } | |
| internal enum GetBehavior | |
| { | |
| Default, | |
| Create, | |
| Remove, | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment