A game about forced loneliness, made by TACStudios
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}