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.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}