BitMap.cs
1 using System; 2 using System.Collections; 3 using System.Collections.Generic; 4 using System.Numerics; 5 using System.Runtime.CompilerServices; 6 7 namespace ARMeilleure.Common 8 { 9 unsafe class BitMap : IEnumerable<int>, IDisposable 10 { 11 private const int IntSize = 64; 12 private const int IntMask = IntSize - 1; 13 14 private int _count; 15 private long* _masks; 16 private readonly Allocator _allocator; 17 18 public BitMap(Allocator allocator) 19 { 20 _allocator = allocator; 21 } 22 23 public BitMap(Allocator allocator, int capacity) : this(allocator) 24 { 25 EnsureCapacity(capacity); 26 } 27 28 public bool Set(int bit) 29 { 30 EnsureCapacity(bit + 1); 31 32 int wordIndex = bit / IntSize; 33 int wordBit = bit & IntMask; 34 35 long wordMask = 1L << wordBit; 36 37 if ((_masks[wordIndex] & wordMask) != 0) 38 { 39 return false; 40 } 41 42 _masks[wordIndex] |= wordMask; 43 44 return true; 45 } 46 47 public void Clear(int bit) 48 { 49 EnsureCapacity(bit + 1); 50 51 int wordIndex = bit / IntSize; 52 int wordBit = bit & IntMask; 53 54 long wordMask = 1L << wordBit; 55 56 _masks[wordIndex] &= ~wordMask; 57 } 58 59 public bool IsSet(int bit) 60 { 61 EnsureCapacity(bit + 1); 62 63 int wordIndex = bit / IntSize; 64 int wordBit = bit & IntMask; 65 66 return (_masks[wordIndex] & (1L << wordBit)) != 0; 67 } 68 69 public int FindFirstUnset() 70 { 71 for (int index = 0; index < _count; index++) 72 { 73 long mask = _masks[index]; 74 75 if (mask != -1L) 76 { 77 return BitOperations.TrailingZeroCount(~mask) + index * IntSize; 78 } 79 } 80 81 return _count * IntSize; 82 } 83 84 public bool Set(BitMap map) 85 { 86 EnsureCapacity(map._count * IntSize); 87 88 bool modified = false; 89 90 for (int index = 0; index < _count; index++) 91 { 92 long newValue = _masks[index] | map._masks[index]; 93 94 if (_masks[index] != newValue) 95 { 96 _masks[index] = newValue; 97 98 modified = true; 99 } 100 } 101 102 return modified; 103 } 104 105 public bool Clear(BitMap map) 106 { 107 EnsureCapacity(map._count * IntSize); 108 109 bool modified = false; 110 111 for (int index = 0; index < _count; index++) 112 { 113 long newValue = _masks[index] & ~map._masks[index]; 114 115 if (_masks[index] != newValue) 116 { 117 _masks[index] = newValue; 118 119 modified = true; 120 } 121 } 122 123 return modified; 124 } 125 126 private void EnsureCapacity(int size) 127 { 128 int count = (size + IntMask) / IntSize; 129 130 if (count > _count) 131 { 132 var oldMask = _masks; 133 var oldSpan = new Span<long>(_masks, _count); 134 135 _masks = _allocator.Allocate<long>((uint)count); 136 _count = count; 137 138 var newSpan = new Span<long>(_masks, _count); 139 140 oldSpan.CopyTo(newSpan); 141 newSpan[oldSpan.Length..].Clear(); 142 143 _allocator.Free(oldMask); 144 } 145 } 146 147 public void Dispose() 148 { 149 if (_masks != null) 150 { 151 _allocator.Free(_masks); 152 153 _masks = null; 154 } 155 } 156 157 IEnumerator IEnumerable.GetEnumerator() 158 { 159 return GetEnumerator(); 160 } 161 162 IEnumerator<int> IEnumerable<int>.GetEnumerator() 163 { 164 return GetEnumerator(); 165 } 166 167 public Enumerator GetEnumerator() 168 { 169 return new Enumerator(this); 170 } 171 172 public struct Enumerator : IEnumerator<int> 173 { 174 private long _index; 175 private long _mask; 176 private int _bit; 177 private readonly BitMap _map; 178 179 public readonly int Current => (int)_index * IntSize + _bit; 180 readonly object IEnumerator.Current => Current; 181 182 public Enumerator(BitMap map) 183 { 184 _index = -1; 185 _mask = 0; 186 _bit = 0; 187 _map = map; 188 } 189 190 [MethodImpl(MethodImplOptions.AggressiveInlining)] 191 public bool MoveNext() 192 { 193 if (_mask != 0) 194 { 195 _mask &= ~(1L << _bit); 196 } 197 198 // Manually hoist these loads, because RyuJIT does not. 199 long count = (uint)_map._count; 200 long* masks = _map._masks; 201 202 while (_mask == 0) 203 { 204 if (++_index >= count) 205 { 206 return false; 207 } 208 209 _mask = masks[_index]; 210 } 211 212 _bit = BitOperations.TrailingZeroCount(_mask); 213 214 return true; 215 } 216 217 public readonly void Reset() { } 218 219 public readonly void Dispose() { } 220 } 221 } 222 }