A game about forced loneliness, made by TACStudios
at master 384 lines 14 kB view raw
1using System; 2using System.Collections; 3using System.Collections.Generic; 4using System.Diagnostics; 5using System.Runtime.CompilerServices; 6using System.Runtime.InteropServices; 7using Unity.Jobs; 8 9namespace Unity.Collections.LowLevel.Unsafe 10{ 11 /// <summary> 12 /// An unordered, expandable set of unique values. 13 /// </summary> 14 /// <typeparam name="T">The type of the values.</typeparam> 15 [StructLayout(LayoutKind.Sequential)] 16 [DebuggerTypeProxy(typeof(UnsafeHashSetDebuggerTypeProxy<>))] 17 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })] 18 public unsafe struct UnsafeHashSet<T> 19 : INativeDisposable 20 , IEnumerable<T> // Used by collection initializers. 21 where T : unmanaged, IEquatable<T> 22 { 23 internal HashMapHelper<T> m_Data; 24 25 /// <summary> 26 /// Initializes and returns an instance of NativeParallelHashSet. 27 /// </summary> 28 /// <param name="initialCapacity">The number of values that should fit in the initial allocation.</param> 29 /// <param name="allocator">The allocator to use.</param> 30 public UnsafeHashSet(int initialCapacity, AllocatorManager.AllocatorHandle allocator) 31 { 32 m_Data = default; 33 m_Data.Init(initialCapacity, 0, HashMapHelper<T>.kMinimumCapacity, allocator); 34 } 35 36 /// <summary> 37 /// Whether this set is empty. 38 /// </summary> 39 /// <value>True if this set is empty or if the set has not been constructed.</value> 40 public readonly bool IsEmpty 41 { 42 [MethodImpl(MethodImplOptions.AggressiveInlining)] 43 get => !IsCreated || m_Data.IsEmpty; 44 } 45 46 /// <summary> 47 /// Returns the current number of values in this set. 48 /// </summary> 49 /// <returns>The current number of values in this set.</returns> 50 public readonly int Count 51 { 52 [MethodImpl(MethodImplOptions.AggressiveInlining)] 53 get => m_Data.Count; 54 } 55 56 /// <summary> 57 /// The number of values that fit in the current allocation. 58 /// </summary> 59 /// <value>The number of values that fit in the current allocation.</value> 60 /// <param name="value">A new capacity. Must be larger than current capacity.</param> 61 public int Capacity 62 { 63 [MethodImpl(MethodImplOptions.AggressiveInlining)] 64 readonly get => m_Data.Capacity; 65 set => m_Data.Resize(value); 66 } 67 68 /// <summary> 69 /// Whether this set has been allocated (and not yet deallocated). 70 /// </summary> 71 /// <value>True if this set has been allocated (and not yet deallocated).</value> 72 public readonly bool IsCreated 73 { 74 [MethodImpl(MethodImplOptions.AggressiveInlining)] 75 get => m_Data.IsCreated; 76 } 77 78 /// <summary> 79 /// Releases all resources (memory and safety handles). 80 /// </summary> 81 public void Dispose() 82 { 83 if (!IsCreated) 84 { 85 return; 86 } 87 88 m_Data.Dispose(); 89 } 90 91 /// <summary> 92 /// Creates and schedules a job that will dispose this set. 93 /// </summary> 94 /// <param name="inputDeps">A job handle. The newly scheduled job will depend upon this handle.</param> 95 /// <returns>The handle of a new job that will dispose this set.</returns> 96 public JobHandle Dispose(JobHandle inputDeps) 97 { 98 if (!IsCreated) 99 { 100 return inputDeps; 101 } 102 103 var jobHandle = new UnsafeDisposeJob { Ptr = m_Data.Ptr, Allocator = m_Data.Allocator }.Schedule(inputDeps); 104 m_Data.Ptr = null; 105 106 return jobHandle; 107 } 108 109 /// <summary> 110 /// Removes all values. 111 /// </summary> 112 /// <remarks>Does not change the capacity.</remarks> 113 public void Clear() 114 { 115 m_Data.Clear(); 116 } 117 118 /// <summary> 119 /// Adds a new value (unless it is already present). 120 /// </summary> 121 /// <param name="item">The value to add.</param> 122 /// <returns>True if the value was not already present.</returns> 123 public bool Add(T item) 124 { 125 return -1 != m_Data.TryAdd(item); 126 } 127 128 /// <summary> 129 /// Removes a particular value. 130 /// </summary> 131 /// <param name="item">The value to remove.</param> 132 /// <returns>True if the value was present.</returns> 133 public bool Remove(T item) 134 { 135 return -1 != m_Data.TryRemove(item); 136 } 137 138 /// <summary> 139 /// Returns true if a particular value is present. 140 /// </summary> 141 /// <param name="item">The value to check for.</param> 142 /// <returns>True if the value was present.</returns> 143 public bool Contains(T item) 144 { 145 return -1 != m_Data.Find(item); 146 } 147 148 /// <summary> 149 /// Sets the capacity to match what it would be if it had been originally initialized with all its entries. 150 /// </summary> 151 public void TrimExcess() => m_Data.TrimExcess(); 152 153 /// <summary> 154 /// Returns an array with a copy of this set's values (in no particular order). 155 /// </summary> 156 /// <param name="allocator">The allocator to use.</param> 157 /// <returns>An array with a copy of the set's values.</returns> 158 public NativeArray<T> ToNativeArray(AllocatorManager.AllocatorHandle allocator) 159 { 160 return m_Data.GetKeyArray(allocator); 161 } 162 163 /// <summary> 164 /// Returns an enumerator over the values of this set. 165 /// </summary> 166 /// <returns>An enumerator over the values of this set.</returns> 167 public Enumerator GetEnumerator() 168 { 169 fixed (HashMapHelper<T>* data = &m_Data) 170 { 171 return new Enumerator { m_Enumerator = new HashMapHelper<T>.Enumerator(data) }; 172 } 173 } 174 175 /// <summary> 176 /// This method is not implemented. Use <see cref="GetEnumerator"/> instead. 177 /// </summary> 178 /// <returns>Throws NotImplementedException.</returns> 179 /// <exception cref="NotImplementedException">Method is not implemented.</exception> 180 IEnumerator<T> IEnumerable<T>.GetEnumerator() 181 { 182 throw new NotImplementedException(); 183 } 184 185 /// <summary> 186 /// This method is not implemented. Use <see cref="GetEnumerator"/> instead. 187 /// </summary> 188 /// <returns>Throws NotImplementedException.</returns> 189 /// <exception cref="NotImplementedException">Method is not implemented.</exception> 190 IEnumerator IEnumerable.GetEnumerator() 191 { 192 throw new NotImplementedException(); 193 } 194 195 /// <summary> 196 /// An enumerator over the values of a set. 197 /// </summary> 198 /// <remarks> 199 /// In an enumerator's initial state, <see cref="Current"/> is invalid. 200 /// The first <see cref="MoveNext"/> call advances the enumerator to the first value. 201 /// </remarks> 202 public struct Enumerator : IEnumerator<T> 203 { 204 internal HashMapHelper<T>.Enumerator m_Enumerator; 205 206 /// <summary> 207 /// Does nothing. 208 /// </summary> 209 public void Dispose() { } 210 211 /// <summary> 212 /// Advances the enumerator to the next value. 213 /// </summary> 214 /// <returns>True if `Current` is valid to read after the call.</returns> 215 [MethodImpl(MethodImplOptions.AggressiveInlining)] 216 public bool MoveNext() => m_Enumerator.MoveNext(); 217 218 /// <summary> 219 /// Resets the enumerator to its initial state. 220 /// </summary> 221 public void Reset() => m_Enumerator.Reset(); 222 223 /// <summary> 224 /// The current value. 225 /// </summary> 226 /// <value>The current value.</value> 227 public T Current 228 { 229 [MethodImpl(MethodImplOptions.AggressiveInlining)] 230 get => m_Enumerator.m_Data->Keys[m_Enumerator.m_Index]; 231 } 232 233 /// <summary> 234 /// Gets the element at the current position of the enumerator in the container. 235 /// </summary> 236 object IEnumerator.Current => Current; 237 } 238 239 /// <summary> 240 /// Returns a readonly version of this UnsafeHashMap instance. 241 /// </summary> 242 /// <remarks>ReadOnly containers point to the same underlying data as the UnsafeHashMap it is made from.</remarks> 243 /// <returns>ReadOnly instance for this.</returns> 244 public ReadOnly AsReadOnly() 245 { 246 return new ReadOnly(ref m_Data); 247 } 248 249 /// <summary> 250 /// A read-only alias for the value of a UnsafeHashSet. Does not have its own allocated storage. 251 /// </summary> 252 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })] 253 public struct ReadOnly 254 : IEnumerable<T> 255 { 256 internal HashMapHelper<T> m_Data; 257 258 internal ReadOnly(ref HashMapHelper<T> data) 259 { 260 m_Data = data; 261 } 262 263 /// <summary> 264 /// Whether this hash map has been allocated (and not yet deallocated). 265 /// </summary> 266 /// <value>True if this hash map has been allocated (and not yet deallocated).</value> 267 public readonly bool IsCreated 268 { 269 [MethodImpl(MethodImplOptions.AggressiveInlining)] 270 get => m_Data.IsCreated; 271 } 272 273 /// <summary> 274 /// Whether this hash set is empty. 275 /// </summary> 276 /// <value>True if this hash set is empty or if the set has not been constructed.</value> 277 public readonly bool IsEmpty 278 { 279 [MethodImpl(MethodImplOptions.AggressiveInlining)] 280 get => m_Data.IsEmpty; 281 } 282 283 /// <summary> 284 /// The current number of key-value pairs in this hash map. 285 /// </summary> 286 /// <returns>The current number of key-value pairs in this hash map.</returns> 287 public readonly int Count 288 { 289 [MethodImpl(MethodImplOptions.AggressiveInlining)] 290 get => m_Data.Count; 291 } 292 293 /// <summary> 294 /// The number of key-value pairs that fit in the current allocation. 295 /// </summary> 296 /// <value>The number of key-value pairs that fit in the current allocation.</value> 297 public readonly int Capacity 298 { 299 [MethodImpl(MethodImplOptions.AggressiveInlining)] 300 get => m_Data.Capacity; 301 } 302 303 /// <summary> 304 /// Returns true if a particular value is present. 305 /// </summary> 306 /// <param name="item">The item to look up.</param> 307 /// <returns>True if the item was present.</returns> 308 public readonly bool Contains(T item) 309 { 310 return -1 != m_Data.Find(item); 311 } 312 313 /// <summary> 314 /// Returns an array with a copy of this set's values (in no particular order). 315 /// </summary> 316 /// <param name="allocator">The allocator to use.</param> 317 /// <returns>An array with a copy of the set's values.</returns> 318 public readonly NativeArray<T> ToNativeArray(AllocatorManager.AllocatorHandle allocator) 319 { 320 return m_Data.GetKeyArray(allocator); 321 } 322 323 /// <summary> 324 /// Returns an enumerator over the key-value pairs of this hash map. 325 /// </summary> 326 /// <returns>An enumerator over the key-value pairs of this hash map.</returns> 327 public readonly Enumerator GetEnumerator() 328 { 329 fixed (HashMapHelper<T>* data = &m_Data) 330 { 331 return new Enumerator { m_Enumerator = new HashMapHelper<T>.Enumerator(data) }; 332 } 333 } 334 335 /// <summary> 336 /// This method is not implemented. Use <see cref="GetEnumerator"/> instead. 337 /// </summary> 338 /// <returns>Throws NotImplementedException.</returns> 339 /// <exception cref="NotImplementedException">Method is not implemented.</exception> 340 IEnumerator<T> IEnumerable<T>.GetEnumerator() 341 { 342 throw new NotImplementedException(); 343 } 344 345 /// <summary> 346 /// This method is not implemented. Use <see cref="GetEnumerator"/> instead. 347 /// </summary> 348 /// <returns>Throws NotImplementedException.</returns> 349 /// <exception cref="NotImplementedException">Method is not implemented.</exception> 350 IEnumerator IEnumerable.GetEnumerator() 351 { 352 throw new NotImplementedException(); 353 } 354 } 355 } 356 357 sealed internal class UnsafeHashSetDebuggerTypeProxy<T> 358 where T : unmanaged, IEquatable<T> 359 { 360 HashMapHelper<T> Data; 361 362 public UnsafeHashSetDebuggerTypeProxy(UnsafeHashSet<T> data) 363 { 364 Data = data.m_Data; 365 } 366 367 public List<T> Items 368 { 369 get 370 { 371 var result = new List<T>(); 372 using (var keys = Data.GetKeyArray(Allocator.Temp)) 373 { 374 for (var k = 0; k < keys.Length; ++k) 375 { 376 result.Add(keys[k]); 377 } 378 } 379 380 return result; 381 } 382 } 383 } 384}