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