A game about forced loneliness, made by TACStudios
at master 843 lines 31 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.Burst; 8using Unity.Collections.LowLevel.Unsafe; 9using Unity.Jobs; 10 11namespace Unity.Collections 12{ 13 [NativeContainer] 14 [GenerateTestsForBurstCompatibility] 15 internal unsafe struct NativeHashMapDispose 16 { 17 [NativeDisableUnsafePtrRestriction] 18 internal UnsafeHashMap<int, int>* m_HashMapData; 19 internal AllocatorManager.AllocatorHandle m_Allocator; 20 21#if ENABLE_UNITY_COLLECTIONS_CHECKS 22 internal AtomicSafetyHandle m_Safety; 23#endif 24 25 internal void Dispose() 26 { 27 var hashMapData = (HashMapHelper<int>*)m_HashMapData; 28 HashMapHelper<int>.Free(hashMapData); 29 } 30 } 31 32 [BurstCompile] 33 internal unsafe struct NativeHashMapDisposeJob : IJob 34 { 35 internal NativeHashMapDispose Data; 36 37 public void Execute() 38 { 39 Data.Dispose(); 40 } 41 } 42 43 /// <summary> 44 /// A key-value pair. 45 /// </summary> 46 /// <remarks>Used for enumerators.</remarks> 47 /// <typeparam name="TKey">The type of the keys.</typeparam> 48 /// <typeparam name="TValue">The type of the values.</typeparam> 49 [DebuggerDisplay("Key = {Key}, Value = {Value}")] 50 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(int) })] 51 public unsafe struct KVPair<TKey, TValue> 52 where TKey : unmanaged, IEquatable<TKey> 53 where TValue : unmanaged 54 { 55 internal HashMapHelper<TKey>* m_Data; 56 internal int m_Index; 57 internal int m_Next; 58 59 /// <summary> 60 /// An invalid KeyValue. 61 /// </summary> 62 /// <value>In a hash map enumerator's initial state, its <see cref="UnsafeHashMap{TKey,TValue}.Enumerator.Current"/> value is Null.</value> 63 public static KVPair<TKey, TValue> Null => new KVPair<TKey, TValue> { m_Index = -1 }; 64 65 /// <summary> 66 /// The key. 67 /// </summary> 68 /// <value>The key. If this KeyValue is Null, returns the default of TKey.</value> 69 public TKey Key 70 { 71 get 72 { 73 if (m_Index != -1) 74 { 75 return m_Data->Keys[m_Index]; 76 } 77 78 return default; 79 } 80 } 81 82 /// <summary> 83 /// Value of key/value pair. 84 /// </summary> 85 public ref TValue Value 86 { 87 get 88 { 89#if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG 90 if (m_Index == -1) 91 throw new ArgumentException("must be valid"); 92#endif 93 94 return ref UnsafeUtility.AsRef<TValue>(m_Data->Ptr + sizeof(TValue) * m_Index); 95 } 96 } 97 98 /// <summary> 99 /// Gets the key and the value. 100 /// </summary> 101 /// <param name="key">Outputs the key. If this KeyValue is Null, outputs the default of TKey.</param> 102 /// <param name="value">Outputs the value. If this KeyValue is Null, outputs the default of TValue.</param> 103 /// <returns>True if the key-value pair is valid.</returns> 104 public bool GetKeyValue(out TKey key, out TValue value) 105 { 106 if (m_Index != -1) 107 { 108 key = m_Data->Keys[m_Index]; 109 value = UnsafeUtility.ReadArrayElement<TValue>(m_Data->Ptr, m_Index); 110 return true; 111 } 112 113 key = default; 114 value = default; 115 return false; 116 } 117 } 118 119 /// <summary> 120 /// An unordered, expandable associative array. 121 /// </summary> 122 /// <remarks> 123 /// Not suitable for parallel write access. Use <see cref="NativeParallelHashMap{TKey, TValue}"/> instead. 124 /// </remarks> 125 /// <typeparam name="TKey">The type of the keys.</typeparam> 126 /// <typeparam name="TValue">The type of the values.</typeparam> 127 [StructLayout(LayoutKind.Sequential)] 128 [NativeContainer] 129 [DebuggerTypeProxy(typeof(NativeHashMapDebuggerTypeProxy<,>))] 130 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(int) })] 131 public unsafe struct NativeHashMap<TKey, TValue> 132 : INativeDisposable 133 , IEnumerable<KVPair<TKey, TValue>> // Used by collection initializers. 134 where TKey : unmanaged, IEquatable<TKey> 135 where TValue : unmanaged 136 { 137 [NativeDisableUnsafePtrRestriction] 138 internal HashMapHelper<TKey>* m_Data; 139 140#if ENABLE_UNITY_COLLECTIONS_CHECKS 141 internal AtomicSafetyHandle m_Safety; 142 static readonly SharedStatic<int> s_staticSafetyId = SharedStatic<int>.GetOrCreate<NativeHashMap<TKey, TValue>>(); 143#endif 144 145 /// <summary> 146 /// Initializes and returns an instance of UnsafeHashMap. 147 /// </summary> 148 /// <param name="initialCapacity">The number of key-value pairs that should fit in the initial allocation.</param> 149 /// <param name="allocator">The allocator to use.</param> 150 public NativeHashMap(int initialCapacity, AllocatorManager.AllocatorHandle allocator) 151 { 152 m_Data = HashMapHelper<TKey>.Alloc(initialCapacity, sizeof(TValue), HashMapHelper<TKey>.kMinimumCapacity, allocator); 153 154#if ENABLE_UNITY_COLLECTIONS_CHECKS 155 m_Safety = CollectionHelper.CreateSafetyHandle(allocator); 156 157 if (UnsafeUtility.IsNativeContainerType<TKey>() || UnsafeUtility.IsNativeContainerType<TValue>()) 158 AtomicSafetyHandle.SetNestedContainer(m_Safety, true); 159 160 CollectionHelper.SetStaticSafetyId<NativeHashMap<TKey, TValue>>(ref m_Safety, ref s_staticSafetyId.Data); 161 AtomicSafetyHandle.SetBumpSecondaryVersionOnScheduleWrite(m_Safety, true); 162#endif 163 } 164 165 /// <summary> 166 /// Releases all resources (memory). 167 /// </summary> 168 public void Dispose() 169 { 170#if ENABLE_UNITY_COLLECTIONS_CHECKS 171 if (!AtomicSafetyHandle.IsDefaultValue(m_Safety)) 172 { 173 AtomicSafetyHandle.CheckExistsAndThrow(m_Safety); 174 } 175#endif 176 if (!IsCreated) 177 { 178 return; 179 } 180 181#if ENABLE_UNITY_COLLECTIONS_CHECKS 182 CollectionHelper.DisposeSafetyHandle(ref m_Safety); 183#endif 184 185 HashMapHelper<TKey>.Free(m_Data); 186 m_Data = null; 187 } 188 189 /// <summary> 190 /// Creates and schedules a job that will dispose this hash map. 191 /// </summary> 192 /// <param name="inputDeps">A job handle. The newly scheduled job will depend upon this handle.</param> 193 /// <returns>The handle of a new job that will dispose this hash map.</returns> 194 public JobHandle Dispose(JobHandle inputDeps) 195 { 196#if ENABLE_UNITY_COLLECTIONS_CHECKS 197 if (!AtomicSafetyHandle.IsDefaultValue(m_Safety)) 198 { 199 AtomicSafetyHandle.CheckExistsAndThrow(m_Safety); 200 } 201#endif 202 if (!IsCreated) 203 { 204 return inputDeps; 205 } 206 207#if ENABLE_UNITY_COLLECTIONS_CHECKS 208 var jobHandle = new NativeHashMapDisposeJob { Data = new NativeHashMapDispose { m_HashMapData = (UnsafeHashMap<int, int>*)m_Data, m_Safety = m_Safety } }.Schedule(inputDeps); 209 AtomicSafetyHandle.Release(m_Safety); 210#else 211 var jobHandle = new NativeHashMapDisposeJob { Data = new NativeHashMapDispose { m_HashMapData = (UnsafeHashMap<int, int>*)m_Data } }.Schedule(inputDeps); 212#endif 213 m_Data = null; 214 215 return jobHandle; 216 } 217 218 /// <summary> 219 /// Whether this hash map has been allocated (and not yet deallocated). 220 /// </summary> 221 /// <value>True if this hash map has been allocated (and not yet deallocated).</value> 222 public readonly bool IsCreated 223 { 224 [MethodImpl(MethodImplOptions.AggressiveInlining)] 225 get => m_Data != null && m_Data->IsCreated; 226 } 227 228 /// <summary> 229 /// Whether this hash map is empty. 230 /// </summary> 231 /// <value>True if this hash map is empty or if the map has not been constructed.</value> 232 public readonly bool IsEmpty 233 { 234 [MethodImpl(MethodImplOptions.AggressiveInlining)] 235 get 236 { 237 if (!IsCreated) 238 { 239 return true; 240 } 241 242 CheckRead(); 243 return m_Data->IsEmpty; 244 } 245 } 246 247 /// <summary> 248 /// The current number of key-value pairs in this hash map. 249 /// </summary> 250 /// <returns>The current number of key-value pairs in this hash map.</returns> 251 public readonly int Count 252 { 253 [MethodImpl(MethodImplOptions.AggressiveInlining)] 254 get 255 { 256 CheckRead(); 257 return m_Data->Count; 258 } 259 } 260 261 /// <summary> 262 /// The number of key-value pairs that fit in the current allocation. 263 /// </summary> 264 /// <value>The number of key-value pairs that fit in the current allocation.</value> 265 /// <param name="value">A new capacity. Must be larger than the current capacity.</param> 266 public int Capacity 267 { 268 [MethodImpl(MethodImplOptions.AggressiveInlining)] 269 readonly get 270 { 271 CheckRead(); 272 return m_Data->Capacity; 273 } 274 275 set 276 { 277 CheckWrite(); 278 m_Data->Resize(value); 279 } 280 } 281 282 /// <summary> 283 /// Removes all key-value pairs. 284 /// </summary> 285 /// <remarks>Does not change the capacity.</remarks> 286 public void Clear() 287 { 288 CheckWrite(); 289 m_Data->Clear(); 290 } 291 292 /// <summary> 293 /// Adds a new key-value pair. 294 /// </summary> 295 /// <remarks>If the key is already present, this method returns false without modifying the hash map.</remarks> 296 /// <param name="key">The key to add.</param> 297 /// <param name="item">The value to add.</param> 298 /// <returns>True if the key-value pair was added.</returns> 299 public bool TryAdd(TKey key, TValue item) 300 { 301 CheckWrite(); 302 303 var idx = m_Data->TryAdd(key); 304 if (-1 != idx) 305 { 306 UnsafeUtility.WriteArrayElement(m_Data->Ptr, idx, item); 307 return true; 308 } 309 310 return false; 311 } 312 313 /// <summary> 314 /// Adds a new key-value pair. 315 /// </summary> 316 /// <remarks>If the key is already present, this method throws without modifying the hash map.</remarks> 317 /// <param name="key">The key to add.</param> 318 /// <param name="item">The value to add.</param> 319 /// <exception cref="ArgumentException">Thrown if the key was already present.</exception> 320 public void Add(TKey key, TValue item) 321 { 322 var result = TryAdd(key, item); 323 324 if (!result) 325 { 326 ThrowKeyAlreadyAdded(key); 327 } 328 } 329 330 /// <summary> 331 /// Removes a key-value pair. 332 /// </summary> 333 /// <param name="key">The key to remove.</param> 334 /// <returns>True if a key-value pair was removed.</returns> 335 public bool Remove(TKey key) 336 { 337 CheckWrite(); 338 return -1 != m_Data->TryRemove(key); 339 } 340 341 /// <summary> 342 /// Returns the value associated with a key. 343 /// </summary> 344 /// <param name="key">The key to look up.</param> 345 /// <param name="item">Outputs the value associated with the key. Outputs default if the key was not present.</param> 346 /// <returns>True if the key was present.</returns> 347 public bool TryGetValue(TKey key, out TValue item) 348 { 349 CheckRead(); 350 return m_Data->TryGetValue(key, out item); 351 } 352 353 /// <summary> 354 /// Returns true if a given key is present in this hash map. 355 /// </summary> 356 /// <param name="key">The key to look up.</param> 357 /// <returns>True if the key was present.</returns> 358 public bool ContainsKey(TKey key) 359 { 360 CheckRead(); 361 return -1 != m_Data->Find(key); 362 } 363 364 /// <summary> 365 /// Sets the capacity to match what it would be if it had been originally initialized with all its entries. 366 /// </summary> 367 public void TrimExcess() 368 { 369 CheckWrite(); 370 m_Data->TrimExcess(); 371 } 372 373 /// <summary> 374 /// Gets and sets values by key. 375 /// </summary> 376 /// <remarks>Getting a key that is not present will throw. Setting a key that is not already present will add the key.</remarks> 377 /// <param name="key">The key to look up.</param> 378 /// <value>The value associated with the key.</value> 379 /// <exception cref="ArgumentException">For getting, thrown if the key was not present.</exception> 380 public TValue this[TKey key] 381 { 382 get 383 { 384 CheckRead(); 385 386 TValue result; 387 if (!m_Data->TryGetValue(key, out result)) 388 { 389 ThrowKeyNotPresent(key); 390 } 391 392 return result; 393 } 394 395 set 396 { 397 CheckWrite(); 398 399 var idx = m_Data->Find(key); 400 if (-1 == idx) 401 { 402 TryAdd(key, value); 403 return; 404 } 405 406 UnsafeUtility.WriteArrayElement(m_Data->Ptr, idx, value); 407 } 408 } 409 410 /// <summary> 411 /// Returns an array with a copy of all this hash map's keys (in no particular order). 412 /// </summary> 413 /// <param name="allocator">The allocator to use.</param> 414 /// <returns>An array with a copy of all this hash map's keys (in no particular order).</returns> 415 public NativeArray<TKey> GetKeyArray(AllocatorManager.AllocatorHandle allocator) 416 { 417 CheckRead(); 418 return m_Data->GetKeyArray(allocator); 419 } 420 421 /// <summary> 422 /// Returns an array with a copy of all this hash map's values (in no particular order). 423 /// </summary> 424 /// <param name="allocator">The allocator to use.</param> 425 /// <returns>An array with a copy of all this hash map's values (in no particular order).</returns> 426 public NativeArray<TValue> GetValueArray(AllocatorManager.AllocatorHandle allocator) 427 { 428 CheckRead(); 429 return m_Data->GetValueArray<TValue>(allocator); 430 } 431 432 /// <summary> 433 /// Returns a NativeKeyValueArrays with a copy of all this hash map's keys and values. 434 /// </summary> 435 /// <remarks>The key-value pairs are copied in no particular order. For all `i`, `Values[i]` will be the value associated with `Keys[i]`.</remarks> 436 /// <param name="allocator">The allocator to use.</param> 437 /// <returns>A NativeKeyValueArrays with a copy of all this hash map's keys and values.</returns> 438 public NativeKeyValueArrays<TKey, TValue> GetKeyValueArrays(AllocatorManager.AllocatorHandle allocator) 439 { 440 CheckRead(); 441 return m_Data->GetKeyValueArrays<TValue>(allocator); 442 } 443 444 /// <summary> 445 /// Returns an enumerator over the key-value pairs of this hash map. 446 /// </summary> 447 /// <returns>An enumerator over the key-value pairs of this hash map.</returns> 448 public Enumerator GetEnumerator() 449 { 450#if ENABLE_UNITY_COLLECTIONS_CHECKS 451 AtomicSafetyHandle.CheckGetSecondaryDataPointerAndThrow(m_Safety); 452 var ash = m_Safety; 453 AtomicSafetyHandle.UseSecondaryVersion(ref ash); 454#endif 455 return new Enumerator 456 { 457#if ENABLE_UNITY_COLLECTIONS_CHECKS 458 m_Safety = ash, 459#endif 460 m_Enumerator = new HashMapHelper<TKey>.Enumerator(m_Data), 461 }; 462 } 463 464 /// <summary> 465 /// This method is not implemented. Use <see cref="GetEnumerator"/> instead. 466 /// </summary> 467 /// <returns>Throws NotImplementedException.</returns> 468 /// <exception cref="NotImplementedException">Method is not implemented.</exception> 469 IEnumerator<KVPair<TKey, TValue>> IEnumerable<KVPair<TKey, TValue>>.GetEnumerator() 470 { 471 throw new NotImplementedException(); 472 } 473 474 /// <summary> 475 /// This method is not implemented. Use <see cref="GetEnumerator"/> instead. 476 /// </summary> 477 /// <returns>Throws NotImplementedException.</returns> 478 /// <exception cref="NotImplementedException">Method is not implemented.</exception> 479 IEnumerator IEnumerable.GetEnumerator() 480 { 481 throw new NotImplementedException(); 482 } 483 484 /// <summary> 485 /// An enumerator over the key-value pairs of a container. 486 /// </summary> 487 /// <remarks> 488 /// In an enumerator's initial state, <see cref="Current"/> is not valid to read. 489 /// From this state, the first <see cref="MoveNext"/> call advances the enumerator to the first key-value pair. 490 /// </remarks> 491 [NativeContainer] 492 [NativeContainerIsReadOnly] 493 public struct Enumerator : IEnumerator<KVPair<TKey, TValue>> 494 { 495 [NativeDisableUnsafePtrRestriction] 496 internal HashMapHelper<TKey>.Enumerator m_Enumerator; 497#if ENABLE_UNITY_COLLECTIONS_CHECKS 498 internal AtomicSafetyHandle m_Safety; 499#endif 500 501 /// <summary> 502 /// Does nothing. 503 /// </summary> 504 public void Dispose() { } 505 506 /// <summary> 507 /// Advances the enumerator to the next key-value pair. 508 /// </summary> 509 /// <returns>True if <see cref="Current"/> is valid to read after the call.</returns> 510 [MethodImpl(MethodImplOptions.AggressiveInlining)] 511 public bool MoveNext() 512 { 513#if ENABLE_UNITY_COLLECTIONS_CHECKS 514 AtomicSafetyHandle.CheckReadAndThrow(m_Safety); 515#endif 516 return m_Enumerator.MoveNext(); 517 } 518 519 /// <summary> 520 /// Resets the enumerator to its initial state. 521 /// </summary> 522 public void Reset() 523 { 524#if ENABLE_UNITY_COLLECTIONS_CHECKS 525 AtomicSafetyHandle.CheckReadAndThrow(m_Safety); 526#endif 527 m_Enumerator.Reset(); 528 } 529 530 /// <summary> 531 /// The current key-value pair. 532 /// </summary> 533 /// <value>The current key-value pair.</value> 534 public KVPair<TKey, TValue> Current 535 { 536 [MethodImpl(MethodImplOptions.AggressiveInlining)] 537 get => m_Enumerator.GetCurrent<TValue>(); 538 } 539 540 /// <summary> 541 /// Gets the element at the current position of the enumerator in the container. 542 /// </summary> 543 object IEnumerator.Current => Current; 544 } 545 546 /// <summary> 547 /// Returns a readonly version of this NativeHashMap instance. 548 /// </summary> 549 /// <remarks>ReadOnly containers point to the same underlying data as the NativeHashMap it is made from.</remarks> 550 /// <returns>ReadOnly instance for this.</returns> 551 public ReadOnly AsReadOnly() 552 { 553 return new ReadOnly(ref this); 554 } 555 556 /// <summary> 557 /// A read-only alias for the value of a NativeHashMap. Does not have its own allocated storage. 558 /// </summary> 559 [NativeContainer] 560 [NativeContainerIsReadOnly] 561 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(int) })] 562 public struct ReadOnly 563 : IEnumerable<KVPair<TKey, TValue>> 564 { 565 [NativeDisableUnsafePtrRestriction] 566 internal HashMapHelper<TKey>* m_Data; 567 568#if ENABLE_UNITY_COLLECTIONS_CHECKS 569 AtomicSafetyHandle m_Safety; 570 internal static readonly SharedStatic<int> s_staticSafetyId = SharedStatic<int>.GetOrCreate<ReadOnly>(); 571#endif 572 573 internal ReadOnly(ref NativeHashMap<TKey, TValue> data) 574 { 575 m_Data = data.m_Data; 576#if ENABLE_UNITY_COLLECTIONS_CHECKS 577 m_Safety = data.m_Safety; 578 CollectionHelper.SetStaticSafetyId<ReadOnly>(ref m_Safety, ref s_staticSafetyId.Data); 579#endif 580 } 581 582 /// <summary> 583 /// Whether this hash map has been allocated (and not yet deallocated). 584 /// </summary> 585 /// <value>True if this hash map has been allocated (and not yet deallocated).</value> 586 public readonly bool IsCreated 587 { 588 [MethodImpl(MethodImplOptions.AggressiveInlining)] 589 get => m_Data != null && m_Data->IsCreated; 590 } 591 592 /// <summary> 593 /// Whether this hash map is empty. 594 /// </summary> 595 /// <value>True if this hash map is empty or if the map has not been constructed.</value> 596 public readonly bool IsEmpty 597 { 598 [MethodImpl(MethodImplOptions.AggressiveInlining)] 599 get 600 { 601 if (!IsCreated) 602 { 603 return true; 604 } 605 606 CheckRead(); 607 return m_Data->IsEmpty; 608 } 609 } 610 611 /// <summary> 612 /// The current number of key-value pairs in this hash map. 613 /// </summary> 614 /// <returns>The current number of key-value pairs in this hash map.</returns> 615 public readonly int Count 616 { 617 [MethodImpl(MethodImplOptions.AggressiveInlining)] 618 get 619 { 620 CheckRead(); 621 return m_Data->Count; 622 } 623 } 624 625 /// <summary> 626 /// The number of key-value pairs that fit in the current allocation. 627 /// </summary> 628 /// <value>The number of key-value pairs that fit in the current allocation.</value> 629 public readonly int Capacity 630 { 631 [MethodImpl(MethodImplOptions.AggressiveInlining)] 632 get 633 { 634 CheckRead(); 635 return m_Data->Capacity; 636 } 637 } 638 639 /// <summary> 640 /// Returns the value associated with a key. 641 /// </summary> 642 /// <param name="key">The key to look up.</param> 643 /// <param name="item">Outputs the value associated with the key. Outputs default if the key was not present.</param> 644 /// <returns>True if the key was present.</returns> 645 public readonly bool TryGetValue(TKey key, out TValue item) 646 { 647 CheckRead(); 648 return m_Data->TryGetValue(key, out item); 649 } 650 651 /// <summary> 652 /// Returns true if a given key is present in this hash map. 653 /// </summary> 654 /// <param name="key">The key to look up.</param> 655 /// <returns>True if the key was present.</returns> 656 public readonly bool ContainsKey(TKey key) 657 { 658 CheckRead(); 659 return -1 != m_Data->Find(key); 660 } 661 662 /// <summary> 663 /// Gets values by key. 664 /// </summary> 665 /// <remarks>Getting a key that is not present will throw.</remarks> 666 /// <param name="key">The key to look up.</param> 667 /// <value>The value associated with the key.</value> 668 /// <exception cref="ArgumentException">For getting, thrown if the key was not present.</exception> 669 public readonly TValue this[TKey key] 670 { 671 get 672 { 673 CheckRead(); 674 675 TValue result; 676 if (!m_Data->TryGetValue(key, out result)) 677 { 678 ThrowKeyNotPresent(key); 679 } 680 681 return result; 682 } 683 } 684 685 /// <summary> 686 /// Returns an array with a copy of all this hash map's keys (in no particular order). 687 /// </summary> 688 /// <param name="allocator">The allocator to use.</param> 689 /// <returns>An array with a copy of all this hash map's keys (in no particular order).</returns> 690 public readonly NativeArray<TKey> GetKeyArray(AllocatorManager.AllocatorHandle allocator) 691 { 692 CheckRead(); 693 return m_Data->GetKeyArray(allocator); 694 } 695 696 /// <summary> 697 /// Returns an array with a copy of all this hash map's values (in no particular order). 698 /// </summary> 699 /// <param name="allocator">The allocator to use.</param> 700 /// <returns>An array with a copy of all this hash map's values (in no particular order).</returns> 701 public readonly NativeArray<TValue> GetValueArray(AllocatorManager.AllocatorHandle allocator) 702 { 703 CheckRead(); 704 return m_Data->GetValueArray<TValue>(allocator); 705 } 706 707 /// <summary> 708 /// Returns a NativeKeyValueArrays with a copy of all this hash map's keys and values. 709 /// </summary> 710 /// <remarks>The key-value pairs are copied in no particular order. For all `i`, `Values[i]` will be the value associated with `Keys[i]`.</remarks> 711 /// <param name="allocator">The allocator to use.</param> 712 /// <returns>A NativeKeyValueArrays with a copy of all this hash map's keys and values.</returns> 713 public readonly NativeKeyValueArrays<TKey, TValue> GetKeyValueArrays(AllocatorManager.AllocatorHandle allocator) 714 { 715 CheckRead(); 716 return m_Data->GetKeyValueArrays<TValue>(allocator); 717 } 718 719 /// <summary> 720 /// Returns an enumerator over the key-value pairs of this hash map. 721 /// </summary> 722 /// <returns>An enumerator over the key-value pairs of this hash map.</returns> 723 public readonly Enumerator GetEnumerator() 724 { 725#if ENABLE_UNITY_COLLECTIONS_CHECKS 726 AtomicSafetyHandle.CheckGetSecondaryDataPointerAndThrow(m_Safety); 727 var ash = m_Safety; 728 AtomicSafetyHandle.UseSecondaryVersion(ref ash); 729#endif 730 return new Enumerator 731 { 732#if ENABLE_UNITY_COLLECTIONS_CHECKS 733 m_Safety = ash, 734#endif 735 m_Enumerator = new HashMapHelper<TKey>.Enumerator(m_Data), 736 }; 737 } 738 739 /// <summary> 740 /// This method is not implemented. Use <see cref="GetEnumerator"/> instead. 741 /// </summary> 742 /// <returns>Throws NotImplementedException.</returns> 743 /// <exception cref="NotImplementedException">Method is not implemented.</exception> 744 IEnumerator<KVPair<TKey, TValue>> IEnumerable<KVPair<TKey, TValue>>.GetEnumerator() 745 { 746 throw new NotImplementedException(); 747 } 748 749 /// <summary> 750 /// This method is not implemented. Use <see cref="GetEnumerator"/> instead. 751 /// </summary> 752 /// <returns>Throws NotImplementedException.</returns> 753 /// <exception cref="NotImplementedException">Method is not implemented.</exception> 754 IEnumerator IEnumerable.GetEnumerator() 755 { 756 throw new NotImplementedException(); 757 } 758 759 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] 760 [MethodImpl(MethodImplOptions.AggressiveInlining)] 761 readonly void CheckRead() 762 { 763#if ENABLE_UNITY_COLLECTIONS_CHECKS 764 AtomicSafetyHandle.CheckReadAndThrow(m_Safety); 765#endif 766 } 767 768 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] 769 readonly void ThrowKeyNotPresent(TKey key) 770 { 771 throw new ArgumentException($"Key: {key} is not present."); 772 } 773 } 774 775 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] 776 [MethodImpl(MethodImplOptions.AggressiveInlining)] 777 readonly void CheckRead() 778 { 779#if ENABLE_UNITY_COLLECTIONS_CHECKS 780 AtomicSafetyHandle.CheckReadAndThrow(m_Safety); 781#endif 782 } 783 784 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] 785 [MethodImpl(MethodImplOptions.AggressiveInlining)] 786 void CheckWrite() 787 { 788#if ENABLE_UNITY_COLLECTIONS_CHECKS 789 AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety); 790#endif 791 } 792 793 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] 794 void ThrowKeyNotPresent(TKey key) 795 { 796 throw new ArgumentException($"Key: {key} is not present."); 797 } 798 799 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] 800 void ThrowKeyAlreadyAdded(TKey key) 801 { 802 throw new ArgumentException($"An item with the same key has already been added: {key}"); 803 } 804 } 805 806 internal unsafe sealed class NativeHashMapDebuggerTypeProxy<TKey, TValue> 807 where TKey : unmanaged, IEquatable<TKey> 808 where TValue : unmanaged 809 { 810 HashMapHelper<TKey>* Data; 811 812 public NativeHashMapDebuggerTypeProxy(NativeHashMap<TKey, TValue> target) 813 { 814 Data = target.m_Data; 815 } 816 817 public NativeHashMapDebuggerTypeProxy(NativeHashMap<TKey, TValue>.ReadOnly target) 818 { 819 Data = target.m_Data; 820 } 821 822 public List<Pair<TKey, TValue>> Items 823 { 824 get 825 { 826 if (Data == null) 827 { 828 return default; 829 } 830 831 var result = new List<Pair<TKey, TValue>>(); 832 using (var kva = Data->GetKeyValueArrays<TValue>(Allocator.Temp)) 833 { 834 for (var i = 0; i < kva.Length; ++i) 835 { 836 result.Add(new Pair<TKey, TValue>(kva.Keys[i], kva.Values[i])); 837 } 838 } 839 return result; 840 } 841 } 842 } 843}