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.Jobs;
8using UnityEngine.Assertions;
9
10namespace Unity.Collections.LowLevel.Unsafe
11{
12 /// <summary>
13 /// An unordered, expandable associative array. Each key can have more than one associated value.
14 /// </summary>
15 /// <remarks>
16 /// Unlike a regular UnsafeParallelHashMap, an UnsafeParallelMultiHashMap can store multiple key-value pairs with the same key.
17 ///
18 /// The keys are not deduplicated: two key-value pairs with the same key are stored as fully separate key-value pairs.
19 /// </remarks>
20 /// <typeparam name="TKey">The type of the keys.</typeparam>
21 /// <typeparam name="TValue">The type of the values.</typeparam>
22 [StructLayout(LayoutKind.Sequential)]
23 [DebuggerTypeProxy(typeof(UnsafeParallelMultiHashMapDebuggerTypeProxy<,>))]
24 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
25 public unsafe struct UnsafeParallelMultiHashMap<TKey, TValue>
26 : INativeDisposable
27 , IEnumerable<KeyValue<TKey, TValue>> // Used by collection initializers.
28 where TKey : unmanaged, IEquatable<TKey>
29 where TValue : unmanaged
30 {
31 [NativeDisableUnsafePtrRestriction]
32 internal UnsafeParallelHashMapData* m_Buffer;
33 internal AllocatorManager.AllocatorHandle m_AllocatorLabel;
34
35 /// <summary>
36 /// Initializes and returns an instance of UnsafeParallelMultiHashMap.
37 /// </summary>
38 /// <param name="capacity">The number of key-value pairs that should fit in the initial allocation.</param>
39 /// <param name="allocator">The allocator to use.</param>
40 public UnsafeParallelMultiHashMap(int capacity, AllocatorManager.AllocatorHandle allocator)
41 {
42 m_AllocatorLabel = allocator;
43 // Bucket size if bigger to reduce collisions
44 UnsafeParallelHashMapData.AllocateHashMap<TKey, TValue>(capacity, capacity * 2, allocator, out m_Buffer);
45 Clear();
46 }
47
48 /// <summary>
49 /// Whether this hash map is empty.
50 /// </summary>
51 /// <value>True if this hash map is empty or the hash map has not been constructed.</value>
52 public readonly bool IsEmpty
53 {
54 [MethodImpl(MethodImplOptions.AggressiveInlining)]
55 get => !IsCreated || UnsafeParallelHashMapData.IsEmpty(m_Buffer);
56 }
57
58 /// <summary>
59 /// Returns the current number of key-value pairs in this hash map.
60 /// </summary>
61 /// <remarks>Key-value pairs with matching keys are counted as separate, individual pairs.</remarks>
62 /// <returns>The current number of key-value pairs in this hash map.</returns>
63 public readonly int Count()
64 {
65 if (m_Buffer->allocatedIndexLength <= 0)
66 {
67 return 0;
68 }
69
70 return UnsafeParallelHashMapData.GetCount(m_Buffer);
71 }
72
73 /// <summary>
74 /// Returns the number of key-value pairs that fit in the current allocation.
75 /// </summary>
76 /// <value>The number of key-value pairs that fit in the current allocation.</value>
77 /// <param name="value">A new capacity. Must be larger than the current capacity.</param>
78 /// <exception cref="InvalidOperationException">Thrown if `value` is less than the current capacity.</exception>
79 public int Capacity
80 {
81 [MethodImpl(MethodImplOptions.AggressiveInlining)]
82 readonly get
83 {
84 UnsafeParallelHashMapData* data = m_Buffer;
85 return data->keyCapacity;
86 }
87
88 set
89 {
90 UnsafeParallelHashMapData* data = m_Buffer;
91 UnsafeParallelHashMapData.ReallocateHashMap<TKey, TValue>(data, value, UnsafeParallelHashMapData.GetBucketSize(value), m_AllocatorLabel);
92 }
93 }
94
95 /// <summary>
96 /// Removes all key-value pairs.
97 /// </summary>
98 /// <remarks>Does not change the capacity.</remarks>
99 public void Clear()
100 {
101 UnsafeParallelHashMapBase<TKey, TValue>.Clear(m_Buffer);
102 }
103
104 /// <summary>
105 /// Adds a new key-value pair.
106 /// </summary>
107 /// <remarks>
108 /// If a key-value pair with this key is already present, an additional separate key-value pair is added.
109 /// </remarks>
110 /// <param name="key">The key to add.</param>
111 /// <param name="item">The value to add.</param>
112 public void Add(TKey key, TValue item)
113 {
114 UnsafeParallelHashMapBase<TKey, TValue>.TryAdd(m_Buffer, key, item, true, m_AllocatorLabel);
115 }
116
117 /// <summary>
118 /// Removes a key and its associated value(s).
119 /// </summary>
120 /// <param name="key">The key to remove.</param>
121 /// <returns>The number of removed key-value pairs. If the key was not present, returns 0.</returns>
122 public int Remove(TKey key)
123 {
124 return UnsafeParallelHashMapBase<TKey, TValue>.Remove(m_Buffer, key, true);
125 }
126
127 /// <summary>
128 /// Removes all key-value pairs with a particular key and a particular value.
129 /// </summary>
130 /// <remarks>Removes all key-value pairs which have a particular key and which *also have* a particular value.
131 /// In other words: (key *AND* value) rather than (key *OR* value).</remarks>
132 /// <typeparam name="TValueEQ">The type of the value.</typeparam>
133 /// <param name="key">The key of the key-value pairs to remove.</param>
134 /// <param name="value">The value of the key-value pairs to remove.</param>
135 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
136 public void Remove<TValueEQ>(TKey key, TValueEQ value)
137 where TValueEQ : unmanaged, IEquatable<TValueEQ>
138 {
139 UnsafeParallelHashMapBase<TKey, TValueEQ>.RemoveKeyValue(m_Buffer, key, value);
140 }
141
142 /// <summary>
143 /// Removes a single key-value pair.
144 /// </summary>
145 /// <param name="it">An iterator representing the key-value pair to remove.</param>
146 /// <exception cref="InvalidOperationException">Thrown if the iterator is invalid.</exception>
147 public void Remove(NativeParallelMultiHashMapIterator<TKey> it)
148 {
149 UnsafeParallelHashMapBase<TKey, TValue>.Remove(m_Buffer, it);
150 }
151
152 /// <summary>
153 /// Gets an iterator for a key.
154 /// </summary>
155 /// <param name="key">The key.</param>
156 /// <param name="item">Outputs the associated value represented by the iterator.</param>
157 /// <param name="it">Outputs an iterator.</param>
158 /// <returns>True if the key was present.</returns>
159 public readonly bool TryGetFirstValue(TKey key, out TValue item, out NativeParallelMultiHashMapIterator<TKey> it)
160 {
161 return UnsafeParallelHashMapBase<TKey, TValue>.TryGetFirstValueAtomic(m_Buffer, key, out item, out it);
162 }
163
164 /// <summary>
165 /// Advances an iterator to the next value associated with its key.
166 /// </summary>
167 /// <param name="item">Outputs the next value.</param>
168 /// <param name="it">A reference to the iterator to advance.</param>
169 /// <returns>True if the key was present and had another value.</returns>
170 public readonly bool TryGetNextValue(out TValue item, ref NativeParallelMultiHashMapIterator<TKey> it)
171 {
172 return UnsafeParallelHashMapBase<TKey, TValue>.TryGetNextValueAtomic(m_Buffer, out item, ref it);
173 }
174
175 /// <summary>
176 /// Returns true if a given key is present in this hash map.
177 /// </summary>
178 /// <param name="key">The key to look up.</param>
179 /// <returns>True if the key was present in this hash map.</returns>
180 public readonly bool ContainsKey(TKey key)
181 {
182 return TryGetFirstValue(key, out var temp0, out var temp1);
183 }
184
185 /// <summary>
186 /// Returns the number of values associated with a given key.
187 /// </summary>
188 /// <param name="key">The key to look up.</param>
189 /// <returns>The number of values associated with the key. Returns 0 if the key was not present.</returns>
190 public readonly int CountValuesForKey(TKey key)
191 {
192 if (!TryGetFirstValue(key, out var value, out var iterator))
193 {
194 return 0;
195 }
196
197 var count = 1;
198 while (TryGetNextValue(out value, ref iterator))
199 {
200 count++;
201 }
202
203 return count;
204 }
205
206 /// <summary>
207 /// Sets a new value for an existing key-value pair.
208 /// </summary>
209 /// <param name="item">The new value.</param>
210 /// <param name="it">The iterator representing a key-value pair.</param>
211 /// <returns>True if a value was overwritten.</returns>
212 public bool SetValue(TValue item, NativeParallelMultiHashMapIterator<TKey> it)
213 {
214 return UnsafeParallelHashMapBase<TKey, TValue>.SetValue(m_Buffer, ref it, ref item);
215 }
216
217 /// <summary>
218 /// Whether this hash map has been allocated (and not yet deallocated).
219 /// </summary>
220 /// <value>True if this hash map has been allocated (and not yet deallocated).</value>
221 public readonly bool IsCreated
222 {
223 [MethodImpl(MethodImplOptions.AggressiveInlining)]
224 get => m_Buffer != null;
225 }
226
227 /// <summary>
228 /// Releases all resources (memory and safety handles).
229 /// </summary>
230 public void Dispose()
231 {
232 if (!IsCreated)
233 {
234 return;
235 }
236
237 UnsafeParallelHashMapData.DeallocateHashMap(m_Buffer, m_AllocatorLabel);
238 m_Buffer = null;
239 }
240
241 /// <summary>
242 /// Creates and schedules a job that will dispose this hash map.
243 /// </summary>
244 /// <param name="inputDeps">A job handle. The newly scheduled job will depend upon this handle.</param>
245 /// <returns>The handle of a new job that will dispose this hash map.</returns>
246 public JobHandle Dispose(JobHandle inputDeps)
247 {
248 if (!IsCreated)
249 {
250 return inputDeps;
251 }
252
253 var jobHandle = new UnsafeParallelHashMapDisposeJob { Data = m_Buffer, Allocator = m_AllocatorLabel }.Schedule(inputDeps);
254 m_Buffer = null;
255 return jobHandle;
256 }
257
258 /// <summary>
259 /// Returns an array with a copy of all the keys (in no particular order).
260 /// </summary>
261 /// <remarks>A key with *N* values is included *N* times in the array.
262 ///
263 /// Use `GetUniqueKeyArray` of <see cref="Unity.Collections.NativeParallelHashMapExtensions"/> instead if you only want one occurrence of each key.</remarks>
264 /// <param name="allocator">The allocator to use.</param>
265 /// <returns>An array with a copy of all the keys (in no particular order).</returns>
266 public readonly NativeArray<TKey> GetKeyArray(AllocatorManager.AllocatorHandle allocator)
267 {
268 var result = CollectionHelper.CreateNativeArray<TKey>(Count(), allocator, NativeArrayOptions.UninitializedMemory);
269 UnsafeParallelHashMapData.GetKeyArray(m_Buffer, result);
270 return result;
271 }
272
273 /// <summary>
274 /// Returns an array with a copy of all the values (in no particular order).
275 /// </summary>
276 /// <remarks>The values are not deduplicated. If you sort the returned array,
277 /// you can use <see cref="Unity.Collections.NativeParallelHashMapExtensions.Unique{T}"/> to remove duplicate values.</remarks>
278 /// <param name="allocator">The allocator to use.</param>
279 /// <returns>An array with a copy of all the values (in no particular order).</returns>
280 public readonly NativeArray<TValue> GetValueArray(AllocatorManager.AllocatorHandle allocator)
281 {
282 var result = CollectionHelper.CreateNativeArray<TValue>(Count(), allocator, NativeArrayOptions.UninitializedMemory);
283 UnsafeParallelHashMapData.GetValueArray(m_Buffer, result);
284 return result;
285 }
286
287 /// <summary>
288 /// Returns a NativeKeyValueArrays with a copy of all the keys and values (in no particular order).
289 /// </summary>
290 /// <remarks>A key with *N* values is included *N* times in the array.
291 /// </remarks>
292 /// <param name="allocator">The allocator to use.</param>
293 /// <returns>A NativeKeyValueArrays with a copy of all the keys and values (in no particular order).</returns>
294 public readonly NativeKeyValueArrays<TKey, TValue> GetKeyValueArrays(AllocatorManager.AllocatorHandle allocator)
295 {
296 var result = new NativeKeyValueArrays<TKey, TValue>(Count(), allocator, NativeArrayOptions.UninitializedMemory);
297 UnsafeParallelHashMapData.GetKeyValueArrays(m_Buffer, result);
298 return result;
299 }
300
301 /// <summary>
302 /// Returns an enumerator over the values of an individual key.
303 /// </summary>
304 /// <param name="key">The key to get an enumerator for.</param>
305 /// <returns>An enumerator over the values of a key.</returns>
306 public Enumerator GetValuesForKey(TKey key)
307 {
308 return new Enumerator { hashmap = this, key = key, isFirst = true };
309 }
310
311 /// <summary>
312 /// An enumerator over the values of an individual key in a multi hash map.
313 /// </summary>
314 /// <remarks>
315 /// In an enumerator's initial state, <see cref="Current"/> is not valid to read.
316 /// The first <see cref="MoveNext"/> call advances the enumerator to the first value of the key.
317 /// </remarks>
318 public struct Enumerator : IEnumerator<TValue>
319 {
320 internal UnsafeParallelMultiHashMap<TKey, TValue> hashmap;
321 internal TKey key;
322 internal bool isFirst;
323
324 TValue value;
325 NativeParallelMultiHashMapIterator<TKey> iterator;
326
327 /// <summary>
328 /// Does nothing.
329 /// </summary>
330 public void Dispose() { }
331
332 /// <summary>
333 /// Advances the enumerator to the next value of the key.
334 /// </summary>
335 /// <returns>True if <see cref="Current"/> is valid to read after the call.</returns>
336 [MethodImpl(MethodImplOptions.AggressiveInlining)]
337 public bool MoveNext()
338 {
339 //Avoids going beyond the end of the collection.
340 if (isFirst)
341 {
342 isFirst = false;
343 return hashmap.TryGetFirstValue(key, out value, out iterator);
344 }
345
346 return hashmap.TryGetNextValue(out value, ref iterator);
347 }
348
349 /// <summary>
350 /// Resets the enumerator to its initial state.
351 /// </summary>
352 public void Reset() => isFirst = true;
353
354 /// <summary>
355 /// The current value.
356 /// </summary>
357 /// <value>The current value.</value>
358 public TValue Current
359 {
360 [MethodImpl(MethodImplOptions.AggressiveInlining)]
361 get => value;
362 }
363
364 object IEnumerator.Current => Current;
365
366 /// <summary>
367 /// Returns this enumerator.
368 /// </summary>
369 /// <returns>This enumerator.</returns>
370 public Enumerator GetEnumerator() { return this; }
371 }
372
373 /// <summary>
374 /// Returns a parallel writer for this hash map.
375 /// </summary>
376 /// <returns>A parallel writer for this hash map.</returns>
377 public ParallelWriter AsParallelWriter()
378 {
379 ParallelWriter writer;
380
381 writer.m_ThreadIndex = 0;
382 writer.m_Buffer = m_Buffer;
383
384 return writer;
385 }
386
387 /// <summary>
388 /// A parallel writer for an UnsafeParallelMultiHashMap.
389 /// </summary>
390 /// <remarks>
391 /// Use <see cref="AsParallelWriter"/> to create a parallel writer for a NativeParallelMultiHashMap.
392 /// </remarks>
393 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
394 public unsafe struct ParallelWriter
395 {
396 [NativeDisableUnsafePtrRestriction]
397 internal UnsafeParallelHashMapData* m_Buffer;
398
399 [NativeSetThreadIndex]
400 internal int m_ThreadIndex;
401
402 /// <summary>
403 /// Returns the number of key-value pairs that fit in the current allocation.
404 /// </summary>
405 /// <value>The number of key-value pairs that fit in the current allocation.</value>
406 public readonly int Capacity
407 {
408 [MethodImpl(MethodImplOptions.AggressiveInlining)]
409 get => m_Buffer->keyCapacity;
410 }
411
412 /// <summary>
413 /// Adds a new key-value pair.
414 /// </summary>
415 /// <remarks>
416 /// If a key-value pair with this key is already present, an additional separate key-value pair is added.
417 /// </remarks>
418 /// <param name="key">The key to add.</param>
419 /// <param name="item">The value to add.</param>
420 public void Add(TKey key, TValue item)
421 {
422 Assert.IsTrue(m_ThreadIndex >= 0);
423 UnsafeParallelHashMapBase<TKey, TValue>.AddAtomicMulti(m_Buffer, key, item, m_ThreadIndex);
424 }
425 }
426
427 /// <summary>
428 /// Returns an enumerator over the key-value pairs of this hash map.
429 /// </summary>
430 /// <remarks>A key with *N* values is visited by the enumerator *N* times.</remarks>
431 /// <returns>An enumerator over the key-value pairs of this hash map.</returns>
432 public KeyValueEnumerator GetEnumerator()
433 {
434 return new KeyValueEnumerator { m_Enumerator = new UnsafeParallelHashMapDataEnumerator(m_Buffer) };
435 }
436
437 /// <summary>
438 /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
439 /// </summary>
440 /// <returns>Throws NotImplementedException.</returns>
441 /// <exception cref="NotImplementedException">Method is not implemented.</exception>
442 IEnumerator<KeyValue<TKey, TValue>> IEnumerable<KeyValue<TKey, TValue>>.GetEnumerator()
443 {
444 throw new NotImplementedException();
445 }
446
447 /// <summary>
448 /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
449 /// </summary>
450 /// <returns>Throws NotImplementedException.</returns>
451 /// <exception cref="NotImplementedException">Method is not implemented.</exception>
452 IEnumerator IEnumerable.GetEnumerator()
453 {
454 throw new NotImplementedException();
455 }
456
457 /// <summary>
458 /// An enumerator over the key-value pairs of a multi hash map.
459 /// </summary>
460 /// <remarks>A key with *N* values is visited by the enumerator *N* times.
461 ///
462 /// In an enumerator's initial state, <see cref="Current"/> is not valid to read.
463 /// The first <see cref="MoveNext"/> call advances the enumerator to the first key-value pair.
464 /// </remarks>
465 public struct KeyValueEnumerator : IEnumerator<KeyValue<TKey, TValue>>
466 {
467 internal UnsafeParallelHashMapDataEnumerator m_Enumerator;
468
469 /// <summary>
470 /// Does nothing.
471 /// </summary>
472 public void Dispose() { }
473
474 /// <summary>
475 /// Advances the enumerator to the next key-value pair.
476 /// </summary>
477 /// <returns>True if <see cref="Current"/> is valid to read after the call.</returns>
478 [MethodImpl(MethodImplOptions.AggressiveInlining)]
479 public bool MoveNext() => m_Enumerator.MoveNext();
480
481 /// <summary>
482 /// Resets the enumerator to its initial state.
483 /// </summary>
484 public void Reset() => m_Enumerator.Reset();
485
486 /// <summary>
487 /// The current key-value pair.
488 /// </summary>
489 /// <value>The current key-value pair.</value>
490 public KeyValue<TKey, TValue> Current
491 {
492 [MethodImpl(MethodImplOptions.AggressiveInlining)]
493 get => m_Enumerator.GetCurrent<TKey, TValue>();
494 }
495
496 object IEnumerator.Current => Current;
497 }
498
499 /// <summary>
500 /// Returns a readonly version of this NativeParallelHashMap instance.
501 /// </summary>
502 /// <remarks>ReadOnly containers point to the same underlying data as the NativeParallelHashMap it is made from.</remarks>
503 /// <returns>ReadOnly instance for this.</returns>
504 public ReadOnly AsReadOnly()
505 {
506 return new ReadOnly(this);
507 }
508
509 /// <summary>
510 /// A read-only alias for the value of a UnsafeParallelHashMap. Does not have its own allocated storage.
511 /// </summary>
512 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(int) })]
513 public struct ReadOnly
514 : IEnumerable<KeyValue<TKey, TValue>>
515 {
516 internal UnsafeParallelMultiHashMap<TKey, TValue> m_MultiHashMapData;
517
518 internal ReadOnly(UnsafeParallelMultiHashMap<TKey, TValue> container)
519 {
520 m_MultiHashMapData = container;
521 }
522
523 /// <summary>
524 /// Whether this hash map has been allocated (and not yet deallocated).
525 /// </summary>
526 /// <value>True if this hash map has been allocated (and not yet deallocated).</value>
527 public readonly bool IsCreated
528 {
529 [MethodImpl(MethodImplOptions.AggressiveInlining)]
530 get => m_MultiHashMapData.IsCreated;
531 }
532
533 /// <summary>
534 /// Whether this hash map is empty.
535 /// </summary>
536 /// <value>True if this hash map is empty or if the map has not been constructed.</value>
537 public readonly bool IsEmpty
538 {
539 [MethodImpl(MethodImplOptions.AggressiveInlining)]
540 get
541 {
542 if (!IsCreated)
543 {
544 return true;
545 }
546
547 return m_MultiHashMapData.IsEmpty;
548 }
549 }
550
551 /// <summary>
552 /// The current number of key-value pairs in this hash map.
553 /// </summary>
554 /// <returns>The current number of key-value pairs in this hash map.</returns>
555 public readonly int Count()
556 {
557 return m_MultiHashMapData.Count();
558 }
559
560 /// <summary>
561 /// The number of key-value pairs that fit in the current allocation.
562 /// </summary>
563 /// <value>The number of key-value pairs that fit in the current allocation.</value>
564 public readonly int Capacity
565 {
566 [MethodImpl(MethodImplOptions.AggressiveInlining)]
567 get
568 {
569 return m_MultiHashMapData.Capacity;
570 }
571 }
572
573 /// <summary>
574 /// Gets an iterator for a key.
575 /// </summary>
576 /// <param name="key">The key.</param>
577 /// <param name="item">Outputs the associated value represented by the iterator.</param>
578 /// <param name="it">Outputs an iterator.</param>
579 /// <returns>True if the key was present.</returns>
580 public readonly bool TryGetFirstValue(TKey key, out TValue item, out NativeParallelMultiHashMapIterator<TKey> it)
581 {
582 return m_MultiHashMapData.TryGetFirstValue(key, out item, out it);
583 }
584
585 /// <summary>
586 /// Advances an iterator to the next value associated with its key.
587 /// </summary>
588 /// <param name="item">Outputs the next value.</param>
589 /// <param name="it">A reference to the iterator to advance.</param>
590 /// <returns>True if the key was present and had another value.</returns>
591 public readonly bool TryGetNextValue(out TValue item, ref NativeParallelMultiHashMapIterator<TKey> it)
592 {
593 return m_MultiHashMapData.TryGetNextValue(out item, ref it);
594 }
595
596 /// <summary>
597 /// Returns true if a given key is present in this hash map.
598 /// </summary>
599 /// <param name="key">The key to look up.</param>
600 /// <returns>True if the key was present.</returns>
601 public readonly bool ContainsKey(TKey key)
602 {
603 return m_MultiHashMapData.ContainsKey(key);
604 }
605
606 /// <summary>
607 /// Returns an array with a copy of all this hash map's keys (in no particular order).
608 /// </summary>
609 /// <param name="allocator">The allocator to use.</param>
610 /// <returns>An array with a copy of all this hash map's keys (in no particular order).</returns>
611 public readonly NativeArray<TKey> GetKeyArray(AllocatorManager.AllocatorHandle allocator)
612 {
613 return m_MultiHashMapData.GetKeyArray(allocator);
614 }
615
616 /// <summary>
617 /// Returns an array with a copy of all this hash map's values (in no particular order).
618 /// </summary>
619 /// <param name="allocator">The allocator to use.</param>
620 /// <returns>An array with a copy of all this hash map's values (in no particular order).</returns>
621 public readonly NativeArray<TValue> GetValueArray(AllocatorManager.AllocatorHandle allocator)
622 {
623 return m_MultiHashMapData.GetValueArray(allocator);
624 }
625
626 /// <summary>
627 /// Returns a NativeKeyValueArrays with a copy of all this hash map's keys and values.
628 /// </summary>
629 /// <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>
630 /// <param name="allocator">The allocator to use.</param>
631 /// <returns>A NativeKeyValueArrays with a copy of all this hash map's keys and values.</returns>
632 public readonly NativeKeyValueArrays<TKey, TValue> GetKeyValueArrays(AllocatorManager.AllocatorHandle allocator)
633 {
634 return m_MultiHashMapData.GetKeyValueArrays(allocator);
635 }
636
637 /// <summary>
638 /// Returns an enumerator over the key-value pairs of this hash map.
639 /// </summary>
640 /// <remarks>A key with *N* values is visited by the enumerator *N* times.</remarks>
641 /// <returns>An enumerator over the key-value pairs of this hash map.</returns>
642 public KeyValueEnumerator GetEnumerator()
643 {
644 return new KeyValueEnumerator
645 {
646 m_Enumerator = new UnsafeParallelHashMapDataEnumerator(m_MultiHashMapData.m_Buffer),
647 };
648 }
649
650 /// <summary>
651 /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
652 /// </summary>
653 /// <returns>Throws NotImplementedException.</returns>
654 /// <exception cref="NotImplementedException">Method is not implemented.</exception>
655 IEnumerator<KeyValue<TKey, TValue>> IEnumerable<KeyValue<TKey, TValue>>.GetEnumerator()
656 {
657 throw new NotImplementedException();
658 }
659
660 /// <summary>
661 /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
662 /// </summary>
663 /// <returns>Throws NotImplementedException.</returns>
664 /// <exception cref="NotImplementedException">Method is not implemented.</exception>
665 IEnumerator IEnumerable.GetEnumerator()
666 {
667 throw new NotImplementedException();
668 }
669
670 }
671 }
672
673 internal sealed class UnsafeParallelMultiHashMapDebuggerTypeProxy<TKey, TValue>
674 where TKey : unmanaged, IEquatable<TKey>, IComparable<TKey>
675 where TValue : unmanaged
676 {
677 UnsafeParallelMultiHashMap<TKey, TValue> m_Target;
678
679 public UnsafeParallelMultiHashMapDebuggerTypeProxy(UnsafeParallelMultiHashMap<TKey, TValue> target)
680 {
681 m_Target = target;
682 }
683
684 public static (NativeArray<TKey>, int) GetUniqueKeyArray(ref UnsafeParallelMultiHashMap<TKey, TValue> hashMap, AllocatorManager.AllocatorHandle allocator)
685 {
686 var withDuplicates = hashMap.GetKeyArray(allocator);
687 withDuplicates.Sort();
688 int uniques = withDuplicates.Unique();
689 return (withDuplicates, uniques);
690 }
691
692 public List<ListPair<TKey, List<TValue>>> Items
693 {
694 get
695 {
696 var result = new List<ListPair<TKey, List<TValue>>>();
697 var keys = GetUniqueKeyArray(ref m_Target, Allocator.Temp);
698
699 using (keys.Item1)
700 {
701 for (var k = 0; k < keys.Item2; ++k)
702 {
703 var values = new List<TValue>();
704 if (m_Target.TryGetFirstValue(keys.Item1[k], out var value, out var iterator))
705 {
706 do
707 {
708 values.Add(value);
709 }
710 while (m_Target.TryGetNextValue(out value, ref iterator));
711 }
712
713 result.Add(new ListPair<TKey, List<TValue>>(keys.Item1[k], values));
714 }
715 }
716
717 return result;
718 }
719 }
720 }
721}