A game about forced loneliness, made by TACStudios
at master 479 lines 18 kB view raw
1using System; 2using System.Collections; 3using System.Collections.Generic; 4using System.Diagnostics; 5using System.Runtime.InteropServices; 6using Unity.Collections.LowLevel.Unsafe; 7using Unity.Jobs; 8using UnityEngine.Internal; 9using Unity.Burst; 10using System.Runtime.CompilerServices; 11 12namespace Unity.Collections 13{ 14 /// <summary> 15 /// An unordered, expandable set of unique values. 16 /// </summary> 17 /// <typeparam name="T">The type of the values.</typeparam> 18 [StructLayout(LayoutKind.Sequential)] 19 [DebuggerTypeProxy(typeof(NativeParallelHashSetDebuggerTypeProxy<>))] 20 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })] 21 public unsafe struct NativeParallelHashSet<T> 22 : INativeDisposable 23 , IEnumerable<T> // Used by collection initializers. 24 where T : unmanaged, IEquatable<T> 25 { 26#if ENABLE_UNITY_COLLECTIONS_CHECKS 27 internal static readonly SharedStatic<int> s_staticSafetyId = SharedStatic<int>.GetOrCreate<NativeParallelHashSet<T>>(); 28#endif 29 30 internal NativeParallelHashMap<T, bool> m_Data; 31 32 /// <summary> 33 /// Initializes and returns an instance of NativeParallelHashSet. 34 /// </summary> 35 /// <param name="capacity">The number of values that should fit in the initial allocation.</param> 36 /// <param name="allocator">The allocator to use.</param> 37 public NativeParallelHashSet(int capacity, AllocatorManager.AllocatorHandle allocator) 38 { 39 m_Data = new NativeParallelHashMap<T, bool>(capacity, allocator); 40#if ENABLE_UNITY_COLLECTIONS_CHECKS 41 CollectionHelper.SetStaticSafetyId<NativeParallelHashSet<T>>(ref m_Data.m_Safety, ref s_staticSafetyId.Data); 42#endif 43 } 44 45 /// <summary> 46 /// Whether this set is empty. 47 /// </summary> 48 /// <value>True if this set is empty or if the set has not been constructed.</value> 49 public readonly bool IsEmpty 50 { 51 [MethodImpl(MethodImplOptions.AggressiveInlining)] 52 get => m_Data.IsEmpty; 53 } 54 55 /// <summary> 56 /// Returns the current number of values in this set. 57 /// </summary> 58 /// <returns>The current number of values in this set.</returns> 59 public int Count() => m_Data.Count(); 60 61 /// <summary> 62 /// The number of values that fit in the current allocation. 63 /// </summary> 64 /// <value>The number of values that fit in the current allocation.</value> 65 /// <param name="value">A new capacity. Must be larger than current capacity.</param> 66 /// <exception cref="InvalidOperationException">Thrown if `value` is less than the current capacity.</exception> 67 public int Capacity 68 { 69 [MethodImpl(MethodImplOptions.AggressiveInlining)] 70 readonly get => m_Data.Capacity; 71 set => m_Data.Capacity = value; 72 } 73 74 /// <summary> 75 /// Whether this set has been allocated (and not yet deallocated). 76 /// </summary> 77 /// <value>True if this set has been allocated (and not yet deallocated).</value> 78 public readonly bool IsCreated 79 { 80 [MethodImpl(MethodImplOptions.AggressiveInlining)] 81 get => m_Data.IsCreated; 82 } 83 84 /// <summary> 85 /// Releases all resources (memory and safety handles). 86 /// </summary> 87 public void Dispose() => m_Data.Dispose(); 88 89 /// <summary> 90 /// Creates and schedules a job that will dispose this set. 91 /// </summary> 92 /// <param name="inputDeps">A job handle. The newly scheduled job will depend upon this handle.</param> 93 /// <returns>The handle of a new job that will dispose this set.</returns> 94 public JobHandle Dispose(JobHandle inputDeps) => m_Data.Dispose(inputDeps); 95 96 /// <summary> 97 /// Removes all values. 98 /// </summary> 99 /// <remarks>Does not change the capacity.</remarks> 100 public void Clear() => m_Data.Clear(); 101 102 /// <summary> 103 /// Adds a new value (unless it is already present). 104 /// </summary> 105 /// <param name="item">The value to add.</param> 106 /// <returns>True if the value was not already present.</returns> 107 public bool Add(T item) => m_Data.TryAdd(item, false); 108 109 /// <summary> 110 /// Removes a particular value. 111 /// </summary> 112 /// <param name="item">The value to remove.</param> 113 /// <returns>True if the value was present.</returns> 114 public bool Remove(T item) => m_Data.Remove(item); 115 116 /// <summary> 117 /// Returns true if a particular value is present. 118 /// </summary> 119 /// <param name="item">The value to check for.</param> 120 /// <returns>True if the value was present.</returns> 121 public bool Contains(T item) => m_Data.ContainsKey(item); 122 123 /// <summary> 124 /// Returns an array with a copy of this set's values (in no particular order). 125 /// </summary> 126 /// <param name="allocator">The allocator to use.</param> 127 /// <returns>An array with a copy of the set's values.</returns> 128 public NativeArray<T> ToNativeArray(AllocatorManager.AllocatorHandle allocator) => m_Data.GetKeyArray(allocator); 129 130 /// <summary> 131 /// Returns a parallel writer. 132 /// </summary> 133 /// <returns>A parallel writer.</returns> 134 public ParallelWriter AsParallelWriter() 135 { 136 ParallelWriter writer; 137 writer.m_Data = m_Data.AsParallelWriter(); 138#if ENABLE_UNITY_COLLECTIONS_CHECKS 139 CollectionHelper.SetStaticSafetyId<ParallelWriter>(ref writer.m_Data.m_Safety, ref ParallelWriter.s_staticSafetyId.Data); 140#endif 141 return writer; 142 } 143 144 /// <summary> 145 /// A parallel writer for a NativeParallelHashSet. 146 /// </summary> 147 /// <remarks> 148 /// Use <see cref="AsParallelWriter"/> to create a parallel writer for a set. 149 /// </remarks> 150 [NativeContainerIsAtomicWriteOnly] 151 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })] 152 public struct ParallelWriter 153 { 154#if ENABLE_UNITY_COLLECTIONS_CHECKS 155 internal static readonly SharedStatic<int> s_staticSafetyId = SharedStatic<int>.GetOrCreate<ParallelWriter>(); 156#endif 157 internal NativeParallelHashMap<T, bool>.ParallelWriter m_Data; 158 159 /// <summary> 160 /// The number of values that fit in the current allocation. 161 /// </summary> 162 /// <value>The number of values that fit in the current allocation.</value> 163 public readonly int Capacity 164 { 165 [MethodImpl(MethodImplOptions.AggressiveInlining)] 166 get => m_Data.Capacity; 167 } 168 169 /// <summary> 170 /// Adds a new value (unless it is already present). 171 /// </summary> 172 /// <param name="item">The value to add.</param> 173 /// <returns>True if the value is not already present.</returns> 174 public bool Add(T item) => m_Data.TryAdd(item, false); 175 176 /// <summary> 177 /// Adds a new value (unless it is already present). 178 /// </summary> 179 /// <param name="item">The value to add.</param> 180 /// <param name="threadIndexOverride">The thread index which must be set by a field from a job struct with the <see cref="NativeSetThreadIndexAttribute"/> attribute.</param> 181 /// <returns>True if the value is not already present.</returns> 182 internal bool Add(T item, int threadIndexOverride) => m_Data.TryAdd(item, false, threadIndexOverride); 183 } 184 185 /// <summary> 186 /// Returns an enumerator over the values of this set. 187 /// </summary> 188 /// <returns>An enumerator over the values of this set.</returns> 189 public Enumerator GetEnumerator() 190 { 191#if ENABLE_UNITY_COLLECTIONS_CHECKS 192 AtomicSafetyHandle.CheckGetSecondaryDataPointerAndThrow(m_Data.m_Safety); 193 var ash = m_Data.m_Safety; 194 AtomicSafetyHandle.UseSecondaryVersion(ref ash); 195#endif 196 return new Enumerator 197 { 198#if ENABLE_UNITY_COLLECTIONS_CHECKS 199 m_Safety = ash, 200#endif 201 m_Enumerator = new UnsafeParallelHashMapDataEnumerator(m_Data.m_HashMapData.m_Buffer), 202 }; 203 } 204 205 /// <summary> 206 /// This method is not implemented. Use <see cref="GetEnumerator"/> instead. 207 /// </summary> 208 /// <returns>Throws NotImplementedException.</returns> 209 /// <exception cref="NotImplementedException">Method is not implemented.</exception> 210 IEnumerator<T> IEnumerable<T>.GetEnumerator() 211 { 212 throw new NotImplementedException(); 213 } 214 215 /// <summary> 216 /// This method is not implemented. Use <see cref="GetEnumerator"/> instead. 217 /// </summary> 218 /// <returns>Throws NotImplementedException.</returns> 219 /// <exception cref="NotImplementedException">Method is not implemented.</exception> 220 IEnumerator IEnumerable.GetEnumerator() 221 { 222 throw new NotImplementedException(); 223 } 224 225 /// <summary> 226 /// An enumerator over the values of a set. 227 /// </summary> 228 /// <remarks> 229 /// In an enumerator's initial state, <see cref="Current"/> is invalid. 230 /// The first <see cref="MoveNext"/> call advances the enumerator to the first value. 231 /// </remarks> 232 [NativeContainer] 233 [NativeContainerIsReadOnly] 234 public struct Enumerator : IEnumerator<T> 235 { 236#if ENABLE_UNITY_COLLECTIONS_CHECKS 237 internal AtomicSafetyHandle m_Safety; 238#endif 239 internal UnsafeParallelHashMapDataEnumerator m_Enumerator; 240 241 /// <summary> 242 /// Does nothing. 243 /// </summary> 244 public void Dispose() { } 245 246 /// <summary> 247 /// Advances the enumerator to the next value. 248 /// </summary> 249 /// <returns>True if `Current` is valid to read after the call.</returns> 250 [MethodImpl(MethodImplOptions.AggressiveInlining)] 251 public bool MoveNext() 252 { 253#if ENABLE_UNITY_COLLECTIONS_CHECKS 254 AtomicSafetyHandle.CheckReadAndThrow(m_Safety); 255#endif 256 return m_Enumerator.MoveNext(); 257 } 258 259 /// <summary> 260 /// Resets the enumerator to its initial state. 261 /// </summary> 262 public void Reset() 263 { 264#if ENABLE_UNITY_COLLECTIONS_CHECKS 265 AtomicSafetyHandle.CheckReadAndThrow(m_Safety); 266#endif 267 m_Enumerator.Reset(); 268 } 269 270 /// <summary> 271 /// The current value. 272 /// </summary> 273 /// <value>The current value.</value> 274 public T Current 275 { 276 [MethodImpl(MethodImplOptions.AggressiveInlining)] 277 get 278 { 279#if ENABLE_UNITY_COLLECTIONS_CHECKS 280 AtomicSafetyHandle.CheckReadAndThrow(m_Safety); 281#endif 282 return m_Enumerator.GetCurrentKey<T>(); 283 } 284 } 285 286 /// <summary> 287 /// Gets the element at the current position of the enumerator in the container. 288 /// </summary> 289 object IEnumerator.Current => Current; 290 } 291 292 /// <summary> 293 /// Returns a readonly version of this NativeParallelHashSet instance. 294 /// </summary> 295 /// <remarks>ReadOnly containers point to the same underlying data as the NativeParallelHashSet it is made from.</remarks> 296 /// <returns>ReadOnly instance for this.</returns> 297 public ReadOnly AsReadOnly() 298 { 299 return new ReadOnly(ref this); 300 } 301 302 /// <summary> 303 /// A read-only alias for the value of a NativeParallelHashSet. Does not have its own allocated storage. 304 /// </summary> 305 [NativeContainer] 306 [NativeContainerIsReadOnly] 307 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })] 308 public struct ReadOnly 309 : IEnumerable<T> 310 { 311 internal UnsafeParallelHashMap<T, bool> m_Data; 312 313#if ENABLE_UNITY_COLLECTIONS_CHECKS 314 AtomicSafetyHandle m_Safety; 315 internal static readonly SharedStatic<int> s_staticSafetyId = SharedStatic<int>.GetOrCreate<ReadOnly>(); 316#endif 317 318 internal ReadOnly(ref NativeParallelHashSet<T> data) 319 { 320 m_Data = data.m_Data.m_HashMapData; 321#if ENABLE_UNITY_COLLECTIONS_CHECKS 322 m_Safety = data.m_Data.m_Safety; 323 CollectionHelper.SetStaticSafetyId<ReadOnly>(ref m_Safety, ref s_staticSafetyId.Data); 324#endif 325 } 326 327 /// <summary> 328 /// Whether this hash set has been allocated (and not yet deallocated). 329 /// </summary> 330 /// <value>True if this hash set has been allocated (and not yet deallocated).</value> 331 public readonly bool IsCreated 332 { 333 [MethodImpl(MethodImplOptions.AggressiveInlining)] 334 get => m_Data.IsCreated; 335 } 336 337 /// <summary> 338 /// Whether this hash set is empty. 339 /// </summary> 340 /// <value>True if this hash set is empty or if the map has not been constructed.</value> 341 public readonly bool IsEmpty 342 { 343 [MethodImpl(MethodImplOptions.AggressiveInlining)] 344 get 345 { 346 if (!IsCreated) 347 { 348 return true; 349 } 350 351 CheckRead(); 352 return m_Data.IsEmpty; 353 } 354 } 355 356 /// <summary> 357 /// The current number of items in this hash set. 358 /// </summary> 359 /// <returns>The current number of items in this hash set.</returns> 360 public readonly int Count() 361 { 362 CheckRead(); 363 return m_Data.Count(); 364 } 365 366 /// <summary> 367 /// The number of items that fit in the current allocation. 368 /// </summary> 369 /// <value>The number of items that fit in the current allocation.</value> 370 public readonly int Capacity 371 { 372 [MethodImpl(MethodImplOptions.AggressiveInlining)] 373 get 374 { 375 CheckRead(); 376 return m_Data.Capacity; 377 } 378 } 379 380 /// <summary> 381 /// Returns true if a given item is present in this hash set. 382 /// </summary> 383 /// <param name="item">The item to look up.</param> 384 /// <returns>True if the item was present.</returns> 385 public readonly bool Contains(T item) 386 { 387 CheckRead(); 388 return m_Data.ContainsKey(item); 389 } 390 391 /// <summary> 392 /// Returns an array with a copy of all this hash set's items (in no particular order). 393 /// </summary> 394 /// <param name="allocator">The allocator to use.</param> 395 /// <returns>An array with a copy of all this hash set's items (in no particular order).</returns> 396 public readonly NativeArray<T> ToNativeArray(AllocatorManager.AllocatorHandle allocator) 397 { 398 CheckRead(); 399 return m_Data.GetKeyArray(allocator); 400 } 401 402 /// <summary> 403 /// Returns an enumerator over the items of this hash set. 404 /// </summary> 405 /// <returns>An enumerator over the items of this hash set.</returns> 406 public readonly Enumerator GetEnumerator() 407 { 408#if ENABLE_UNITY_COLLECTIONS_CHECKS 409 AtomicSafetyHandle.CheckGetSecondaryDataPointerAndThrow(m_Safety); 410 var ash = m_Safety; 411 AtomicSafetyHandle.UseSecondaryVersion(ref ash); 412#endif 413 return new Enumerator 414 { 415#if ENABLE_UNITY_COLLECTIONS_CHECKS 416 m_Safety = ash, 417#endif 418 m_Enumerator = new UnsafeParallelHashMapDataEnumerator(m_Data.m_Buffer), 419 }; 420 } 421 422 /// <summary> 423 /// This method is not implemented. Use <see cref="GetEnumerator"/> instead. 424 /// </summary> 425 /// <returns>Throws NotImplementedException.</returns> 426 /// <exception cref="NotImplementedException">Method is not implemented.</exception> 427 IEnumerator<T> IEnumerable<T>.GetEnumerator() 428 { 429 throw new NotImplementedException(); 430 } 431 432 /// <summary> 433 /// This method is not implemented. Use <see cref="GetEnumerator"/> instead. 434 /// </summary> 435 /// <returns>Throws NotImplementedException.</returns> 436 /// <exception cref="NotImplementedException">Method is not implemented.</exception> 437 IEnumerator IEnumerable.GetEnumerator() 438 { 439 throw new NotImplementedException(); 440 } 441 442 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] 443 readonly void CheckRead() 444 { 445#if ENABLE_UNITY_COLLECTIONS_CHECKS 446 AtomicSafetyHandle.CheckReadAndThrow(m_Safety); 447#endif 448 } 449 } 450 } 451 452 sealed internal class NativeParallelHashSetDebuggerTypeProxy<T> 453 where T : unmanaged, IEquatable<T> 454 { 455 NativeParallelHashSet<T> Data; 456 457 public NativeParallelHashSetDebuggerTypeProxy(NativeParallelHashSet<T> data) 458 { 459 Data = data; 460 } 461 462 public List<T> Items 463 { 464 get 465 { 466 var result = new List<T>(); 467 using (var keys = Data.ToNativeArray(Allocator.Temp)) 468 { 469 for (var k = 0; k < keys.Length; ++k) 470 { 471 result.Add(keys[k]); 472 } 473 } 474 475 return result; 476 } 477 } 478 } 479}