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