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