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