using System; namespace ARMeilleure.Common { /// /// Represents a fixed size table of the type , whose entries will remain at the same /// address through out the table's lifetime. /// /// Type of the entry in the table class EntryTable where TEntry : unmanaged { private int _freeHint; private readonly TEntry[] _table; private readonly BitMap _allocated; /// /// Initializes a new instance of the class with the specified capacity. /// /// Capacity of the table /// is less than 0 public EntryTable(int capacity) { if (capacity < 0) { throw new ArgumentOutOfRangeException(nameof(capacity)); } _freeHint = 0; _allocated = new BitMap(); _table = GC.AllocateArray(capacity, pinned: true); } /// /// Tries to allocate an entry in the . Returns if /// success; otherwise returns . /// /// Index of entry allocated in the table /// if success; otherwise public bool TryAllocate(out int index) { lock (_allocated) { if (_allocated.IsSet(_freeHint)) { _freeHint = _allocated.FindFirstUnset(); } if (_freeHint >= 0 && _freeHint < _table.Length) { index = _freeHint++; _allocated.Set(index); GetValue(index) = default; return true; } } index = 0; return false; } /// /// Frees the entry at the specified . /// /// Index of entry to free public void Free(int index) { lock (_allocated) { _allocated.Clear(index); } } /// /// Gets a reference to the entry at the specified allocated . /// /// Index of the entry /// Reference to the entry at the specified index /// Entry at is not allocated /// is outside of the table public ref TEntry GetValue(int index) { if (index < 0 || index >= _table.Length) { throw new ArgumentOutOfRangeException(nameof(index)); } lock (_allocated) { if (!_allocated.IsSet(index)) { throw new ArgumentException("Entry at the specified index was not allocated", nameof(index)); } } return ref _table[index]; } } }