Нет описания
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

NativeHashMap.cs 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Runtime.InteropServices;
  6. using Unity.Burst;
  7. using Unity.Collections.LowLevel.Unsafe;
  8. using Unity.Jobs;
  9. namespace Unity.Collections
  10. {
  11. /// <summary>
  12. /// The keys and values of a hash map copied into two parallel arrays.
  13. /// </summary>
  14. /// <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`).
  15. ///
  16. /// NativeKeyValueArrays is not actually itself a native collection: it contains a NativeArray for the keys and a NativeArray for the values,
  17. /// but a NativeKeyValueArrays does not have its own safety handles.</remarks>
  18. /// <typeparam name="TKey">The type of the keys.</typeparam>
  19. /// <typeparam name="TValue">The type of the values.</typeparam>
  20. [BurstCompatible(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
  21. public struct NativeKeyValueArrays<TKey, TValue>
  22. : INativeDisposable
  23. where TKey : struct
  24. where TValue : struct
  25. {
  26. /// <summary>
  27. /// The keys.
  28. /// </summary>
  29. /// <value>The keys. The key at `Keys[i]` is paired with the value at `Values[i]`.</value>
  30. public NativeArray<TKey> Keys;
  31. /// <summary>
  32. /// The values.
  33. /// </summary>
  34. /// <value>The values. The value at `Values[i]` is paired with the key at `Keys[i]`.</value>
  35. public NativeArray<TValue> Values;
  36. /// <summary>
  37. /// The number of key-value pairs.
  38. /// </summary>
  39. /// <value>The number of key-value pairs.</value>
  40. public int Length => Keys.Length;
  41. /// <summary>
  42. /// Initializes and returns an instance of NativeKeyValueArrays.
  43. /// </summary>
  44. /// <param name="length">The number of keys-value pairs.</param>
  45. /// <param name="allocator">The allocator to use.</param>
  46. /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
  47. public NativeKeyValueArrays(int length, AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options)
  48. {
  49. Keys = CollectionHelper.CreateNativeArray<TKey>(length, allocator, options);
  50. Values = CollectionHelper.CreateNativeArray<TValue>(length, allocator, options);
  51. }
  52. /// <summary>
  53. /// Releases all resources (memory and safety handles).
  54. /// </summary>
  55. public void Dispose()
  56. {
  57. Keys.Dispose();
  58. Values.Dispose();
  59. }
  60. /// <summary>
  61. /// Creates and schedules a job that will dispose this collection's key and value arrays.
  62. /// </summary>
  63. /// <param name="inputDeps">A job handle. The newly scheduled job will depend upon this handle.</param>
  64. /// <returns>The handle of a new job that will dispose this collection's key and value arrays.</returns>
  65. [NotBurstCompatible /* This is not burst compatible because of IJob's use of a static IntPtr. Should switch to IJobBurstSchedulable in the future */]
  66. public JobHandle Dispose(JobHandle inputDeps)
  67. {
  68. return Keys.Dispose(Values.Dispose(inputDeps));
  69. }
  70. }
  71. /// <summary>
  72. /// An unordered, expandable associative array.
  73. /// </summary>
  74. /// <typeparam name="TKey">The type of the keys.</typeparam>
  75. /// <typeparam name="TValue">The type of the values.</typeparam>
  76. [StructLayout(LayoutKind.Sequential)]
  77. [NativeContainer]
  78. [DebuggerDisplay("Count = {m_HashMapData.Count()}, Capacity = {m_HashMapData.Capacity}, IsCreated = {m_HashMapData.IsCreated}, IsEmpty = {IsEmpty}")]
  79. [DebuggerTypeProxy(typeof(NativeHashMapDebuggerTypeProxy<,>))]
  80. [BurstCompatible(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
  81. public unsafe struct NativeHashMap<TKey, TValue>
  82. : INativeDisposable
  83. , IEnumerable<KeyValue<TKey, TValue>> // Used by collection initializers.
  84. where TKey : struct, IEquatable<TKey>
  85. where TValue : struct
  86. {
  87. internal UnsafeHashMap<TKey, TValue> m_HashMapData;
  88. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  89. internal AtomicSafetyHandle m_Safety;
  90. static readonly SharedStatic<int> s_staticSafetyId = SharedStatic<int>.GetOrCreate<NativeHashMap<TKey, TValue>>();
  91. #if REMOVE_DISPOSE_SENTINEL
  92. #else
  93. [NativeSetClassTypeToNullOnSchedule]
  94. DisposeSentinel m_DisposeSentinel;
  95. #endif
  96. #endif
  97. /// <summary>
  98. /// Initializes and returns an instance of NativeHashMap.
  99. /// </summary>
  100. /// <param name="capacity">The number of key-value pairs that should fit in the initial allocation.</param>
  101. /// <param name="allocator">The allocator to use.</param>
  102. public NativeHashMap(int capacity, AllocatorManager.AllocatorHandle allocator)
  103. : this(capacity, allocator, 2)
  104. {
  105. }
  106. NativeHashMap(int capacity, AllocatorManager.AllocatorHandle allocator, int disposeSentinelStackDepth)
  107. {
  108. m_HashMapData = new UnsafeHashMap<TKey, TValue>(capacity, allocator);
  109. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  110. #if REMOVE_DISPOSE_SENTINEL
  111. m_Safety = CollectionHelper.CreateSafetyHandle(allocator);
  112. #else
  113. if (AllocatorManager.IsCustomAllocator(allocator.ToAllocator))
  114. {
  115. m_Safety = AtomicSafetyHandle.Create();
  116. m_DisposeSentinel = null;
  117. }
  118. else
  119. {
  120. DisposeSentinel.Create(out m_Safety, out m_DisposeSentinel, disposeSentinelStackDepth, allocator.ToAllocator);
  121. }
  122. #endif
  123. CollectionHelper.SetStaticSafetyId<NativeHashMap<TKey, TValue>>(ref m_Safety, ref s_staticSafetyId.Data);
  124. AtomicSafetyHandle.SetBumpSecondaryVersionOnScheduleWrite(m_Safety, true);
  125. #endif
  126. }
  127. /// <summary>
  128. /// Whether this hash map is empty.
  129. /// </summary>
  130. /// <value>True if this hash map is empty or if the map has not been constructed.</value>
  131. public bool IsEmpty
  132. {
  133. get
  134. {
  135. if (!IsCreated)
  136. {
  137. return true;
  138. }
  139. CheckRead();
  140. return m_HashMapData.IsEmpty;
  141. }
  142. }
  143. /// <summary>
  144. /// The current number of key-value pairs in this hash map.
  145. /// </summary>
  146. /// <returns>The current number of key-value pairs in this hash map.</returns>
  147. public int Count()
  148. {
  149. CheckRead();
  150. return m_HashMapData.Count();
  151. }
  152. /// <summary>
  153. /// The number of key-value pairs that fit in the current allocation.
  154. /// </summary>
  155. /// <value>The number of key-value pairs that fit in the current allocation.</value>
  156. /// <param name="value">A new capacity. Must be larger than the current capacity.</param>
  157. /// <exception cref="Exception">Thrown if `value` is less than the current capacity.</exception>
  158. public int Capacity
  159. {
  160. get
  161. {
  162. CheckRead();
  163. return m_HashMapData.Capacity;
  164. }
  165. set
  166. {
  167. CheckWrite();
  168. m_HashMapData.Capacity = value;
  169. }
  170. }
  171. /// <summary>
  172. /// Removes all key-value pairs.
  173. /// </summary>
  174. /// <remarks>Does not change the capacity.</remarks>
  175. public void Clear()
  176. {
  177. CheckWrite();
  178. m_HashMapData.Clear();
  179. }
  180. /// <summary>
  181. /// Adds a new key-value pair.
  182. /// </summary>
  183. /// <remarks>If the key is already present, this method returns false without modifying the hash map.</remarks>
  184. /// <param name="key">The key to add.</param>
  185. /// <param name="item">The value to add.</param>
  186. /// <returns>True if the key-value pair was added.</returns>
  187. public bool TryAdd(TKey key, TValue item)
  188. {
  189. CheckWrite();
  190. return m_HashMapData.TryAdd(key, item);
  191. }
  192. /// <summary>
  193. /// Adds a new key-value pair.
  194. /// </summary>
  195. /// <remarks>If the key is already present, this method throws without modifying the hash map.</remarks>
  196. /// <param name="key">The key to add.</param>
  197. /// <param name="item">The value to add.</param>
  198. /// <exception cref="ArgumentException">Thrown if the key was already present.</exception>
  199. public void Add(TKey key, TValue item)
  200. {
  201. var added = TryAdd(key, item);
  202. if (!added)
  203. {
  204. ThrowKeyAlreadyAdded(key);
  205. }
  206. }
  207. /// <summary>
  208. /// Removes a key-value pair.
  209. /// </summary>
  210. /// <param name="key">The key to remove.</param>
  211. /// <returns>True if a key-value pair was removed.</returns>
  212. public bool Remove(TKey key)
  213. {
  214. CheckWrite();
  215. return m_HashMapData.Remove(key);
  216. }
  217. /// <summary>
  218. /// Returns the value associated with a key.
  219. /// </summary>
  220. /// <param name="key">The key to look up.</param>
  221. /// <param name="item">Outputs the value associated with the key. Outputs default if the key was not present.</param>
  222. /// <returns>True if the key was present.</returns>
  223. public bool TryGetValue(TKey key, out TValue item)
  224. {
  225. CheckRead();
  226. return m_HashMapData.TryGetValue(key, out item);
  227. }
  228. /// <summary>
  229. /// Returns true if a given key is present in this hash map.
  230. /// </summary>
  231. /// <param name="key">The key to look up.</param>
  232. /// <returns>True if the key was present.</returns>
  233. public bool ContainsKey(TKey key)
  234. {
  235. CheckRead();
  236. return m_HashMapData.ContainsKey(key);
  237. }
  238. /// <summary>
  239. /// Gets and sets values by key.
  240. /// </summary>
  241. /// <remarks>Getting a key that is not present will throw. Setting a key that is not already present will add the key.</remarks>
  242. /// <param name="key">The key to look up.</param>
  243. /// <value>The value associated with the key.</value>
  244. /// <exception cref="ArgumentException">For getting, thrown if the key was not present.</exception>
  245. public TValue this[TKey key]
  246. {
  247. get
  248. {
  249. CheckRead();
  250. TValue res;
  251. if (m_HashMapData.TryGetValue(key, out res))
  252. {
  253. return res;
  254. }
  255. ThrowKeyNotPresent(key);
  256. return default;
  257. }
  258. set
  259. {
  260. CheckWrite();
  261. m_HashMapData[key] = value;
  262. }
  263. }
  264. /// <summary>
  265. /// Whether this hash map has been allocated (and not yet deallocated).
  266. /// </summary>
  267. /// <value>True if this hash map has been allocated (and not yet deallocated).</value>
  268. public bool IsCreated => m_HashMapData.IsCreated;
  269. /// <summary>
  270. /// Releases all resources (memory and safety handles).
  271. /// </summary>
  272. public void Dispose()
  273. {
  274. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  275. #if REMOVE_DISPOSE_SENTINEL
  276. CollectionHelper.DisposeSafetyHandle(ref m_Safety);
  277. #else
  278. DisposeSentinel.Dispose(ref m_Safety, ref m_DisposeSentinel);
  279. #endif
  280. #endif
  281. m_HashMapData.Dispose();
  282. }
  283. /// <summary>
  284. /// Creates and schedules a job that will dispose this hash map.
  285. /// </summary>
  286. /// <param name="inputDeps">A job handle. The newly scheduled job will depend upon this handle.</param>
  287. /// <returns>The handle of a new job that will dispose this hash map.</returns>
  288. [NotBurstCompatible /* This is not burst compatible because of IJob's use of a static IntPtr. Should switch to IJobBurstSchedulable in the future */]
  289. public JobHandle Dispose(JobHandle inputDeps)
  290. {
  291. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  292. #if REMOVE_DISPOSE_SENTINEL
  293. #else
  294. // [DeallocateOnJobCompletion] is not supported, but we want the deallocation
  295. // to happen in a thread. DisposeSentinel needs to be cleared on main thread.
  296. // AtomicSafetyHandle can be destroyed after the job was scheduled (Job scheduling
  297. // will check that no jobs are writing to the container).
  298. DisposeSentinel.Clear(ref m_DisposeSentinel);
  299. #endif
  300. var jobHandle = new UnsafeHashMapDataDisposeJob { Data = new UnsafeHashMapDataDispose { m_Buffer = m_HashMapData.m_Buffer, m_AllocatorLabel = m_HashMapData.m_AllocatorLabel, m_Safety = m_Safety } }.Schedule(inputDeps);
  301. AtomicSafetyHandle.Release(m_Safety);
  302. #else
  303. var jobHandle = new UnsafeHashMapDataDisposeJob { Data = new UnsafeHashMapDataDispose { m_Buffer = m_HashMapData.m_Buffer, m_AllocatorLabel = m_HashMapData.m_AllocatorLabel } }.Schedule(inputDeps);
  304. #endif
  305. m_HashMapData.m_Buffer = null;
  306. return jobHandle;
  307. }
  308. /// <summary>
  309. /// Returns an array with a copy of all this hash map's keys (in no particular order).
  310. /// </summary>
  311. /// <param name="allocator">The allocator to use.</param>
  312. /// <returns>An array with a copy of all this hash map's keys (in no particular order).</returns>
  313. public NativeArray<TKey> GetKeyArray(AllocatorManager.AllocatorHandle allocator)
  314. {
  315. CheckRead();
  316. return m_HashMapData.GetKeyArray(allocator);
  317. }
  318. /// <summary>
  319. /// Returns an array with a copy of all this hash map's values (in no particular order).
  320. /// </summary>
  321. /// <param name="allocator">The allocator to use.</param>
  322. /// <returns>An array with a copy of all this hash map's values (in no particular order).</returns>
  323. public NativeArray<TValue> GetValueArray(AllocatorManager.AllocatorHandle allocator)
  324. {
  325. CheckRead();
  326. return m_HashMapData.GetValueArray(allocator);
  327. }
  328. /// <summary>
  329. /// Returns a NativeKeyValueArrays with a copy of all this hash map's keys and values.
  330. /// </summary>
  331. /// <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>
  332. /// <param name="allocator">The allocator to use.</param>
  333. /// <returns>A NativeKeyValueArrays with a copy of all this hash map's keys and values.</returns>
  334. public NativeKeyValueArrays<TKey, TValue> GetKeyValueArrays(AllocatorManager.AllocatorHandle allocator)
  335. {
  336. CheckRead();
  337. return m_HashMapData.GetKeyValueArrays(allocator);
  338. }
  339. /// <summary>
  340. /// Returns a parallel writer for this hash map.
  341. /// </summary>
  342. /// <returns>A parallel writer for this hash map.</returns>
  343. public ParallelWriter AsParallelWriter()
  344. {
  345. ParallelWriter writer;
  346. writer.m_Writer = m_HashMapData.AsParallelWriter();
  347. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  348. writer.m_Safety = m_Safety;
  349. CollectionHelper.SetStaticSafetyId<ParallelWriter>(ref writer.m_Safety, ref ParallelWriter.s_staticSafetyId.Data);
  350. #endif
  351. return writer;
  352. }
  353. /// <summary>
  354. /// A parallel writer for a NativeHashMap.
  355. /// </summary>
  356. /// <remarks>
  357. /// Use <see cref="AsParallelWriter"/> to create a parallel writer for a NativeHashMap.
  358. /// </remarks>
  359. [NativeContainer]
  360. [NativeContainerIsAtomicWriteOnly]
  361. [DebuggerDisplay("Capacity = {m_Writer.Capacity}")]
  362. [BurstCompatible(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
  363. public unsafe struct ParallelWriter
  364. {
  365. internal UnsafeHashMap<TKey, TValue>.ParallelWriter m_Writer;
  366. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  367. internal AtomicSafetyHandle m_Safety;
  368. internal static readonly SharedStatic<int> s_staticSafetyId = SharedStatic<int>.GetOrCreate<ParallelWriter>();
  369. #endif
  370. /// <summary>
  371. /// Returns the index of the current thread.
  372. /// </summary>
  373. /// <remarks>In a job, each thread gets its own copy of the ParallelWriter struct, and the job system assigns
  374. /// each copy the index of its thread.</remarks>
  375. /// <value>The index of the current thread.</value>
  376. public int m_ThreadIndex => m_Writer.m_ThreadIndex;
  377. /// <summary>
  378. /// The number of key-value pairs that fit in the current allocation.
  379. /// </summary>
  380. /// <value>The number of key-value pairs that fit in the current allocation.</value>
  381. public int Capacity
  382. {
  383. get
  384. {
  385. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  386. AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
  387. #endif
  388. return m_Writer.Capacity;
  389. }
  390. }
  391. /// <summary>
  392. /// Adds a new key-value pair.
  393. /// </summary>
  394. /// <remarks>If the key is already present, this method returns false without modifying this hash map.</remarks>
  395. /// <param name="key">The key to add.</param>
  396. /// <param name="item">The value to add.</param>
  397. /// <returns>True if the key-value pair was added.</returns>
  398. public bool TryAdd(TKey key, TValue item)
  399. {
  400. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  401. AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety);
  402. #endif
  403. return m_Writer.TryAdd(key, item);
  404. }
  405. }
  406. /// <summary>
  407. /// Returns an enumerator over the key-value pairs of this hash map.
  408. /// </summary>
  409. /// <returns>An enumerator over the key-value pairs of this hash map.</returns>
  410. public Enumerator GetEnumerator()
  411. {
  412. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  413. AtomicSafetyHandle.CheckGetSecondaryDataPointerAndThrow(m_Safety);
  414. var ash = m_Safety;
  415. AtomicSafetyHandle.UseSecondaryVersion(ref ash);
  416. #endif
  417. return new Enumerator
  418. {
  419. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  420. m_Safety = ash,
  421. #endif
  422. m_Enumerator = new UnsafeHashMapDataEnumerator(m_HashMapData.m_Buffer),
  423. };
  424. }
  425. /// <summary>
  426. /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
  427. /// </summary>
  428. /// <returns>Throws NotImplementedException.</returns>
  429. /// <exception cref="NotImplementedException">Method is not implemented.</exception>
  430. IEnumerator<KeyValue<TKey, TValue>> IEnumerable<KeyValue<TKey, TValue>>.GetEnumerator()
  431. {
  432. throw new NotImplementedException();
  433. }
  434. /// <summary>
  435. /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
  436. /// </summary>
  437. /// <returns>Throws NotImplementedException.</returns>
  438. /// <exception cref="NotImplementedException">Method is not implemented.</exception>
  439. IEnumerator IEnumerable.GetEnumerator()
  440. {
  441. throw new NotImplementedException();
  442. }
  443. /// <summary>
  444. /// An enumerator over the key-value pairs of a hash map.
  445. /// </summary>
  446. /// <remarks>
  447. /// In an enumerator's initial state, <see cref="Current"/> is not valid to read.
  448. /// From this state, the first <see cref="MoveNext"/> call advances the enumerator to the first key-value pair.
  449. /// </remarks>
  450. [NativeContainer]
  451. [NativeContainerIsReadOnly]
  452. public struct Enumerator : IEnumerator<KeyValue<TKey, TValue>>
  453. {
  454. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  455. internal AtomicSafetyHandle m_Safety;
  456. #endif
  457. internal UnsafeHashMapDataEnumerator m_Enumerator;
  458. /// <summary>
  459. /// Does nothing.
  460. /// </summary>
  461. public void Dispose() { }
  462. /// <summary>
  463. /// Advances the enumerator to the next key-value pair.
  464. /// </summary>
  465. /// <returns>True if <see cref="Current"/> is valid to read after the call.</returns>
  466. public bool MoveNext()
  467. {
  468. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  469. AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
  470. #endif
  471. return m_Enumerator.MoveNext();
  472. }
  473. /// <summary>
  474. /// Resets the enumerator to its initial state.
  475. /// </summary>
  476. public void Reset()
  477. {
  478. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  479. AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
  480. #endif
  481. m_Enumerator.Reset();
  482. }
  483. /// <summary>
  484. /// The current key-value pair.
  485. /// </summary>
  486. /// <value>The current key-value pair.</value>
  487. public KeyValue<TKey, TValue> Current => m_Enumerator.GetCurrent<TKey, TValue>();
  488. object IEnumerator.Current => Current;
  489. }
  490. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  491. void CheckRead()
  492. {
  493. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  494. AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
  495. #endif
  496. }
  497. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  498. void CheckWrite()
  499. {
  500. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  501. AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety);
  502. #endif
  503. }
  504. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  505. void ThrowKeyNotPresent(TKey key)
  506. {
  507. throw new ArgumentException($"Key: {key} is not present in the NativeHashMap.");
  508. }
  509. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  510. void ThrowKeyAlreadyAdded(TKey key)
  511. {
  512. throw new ArgumentException("An item with the same key has already been added", nameof(key));
  513. }
  514. }
  515. internal sealed class NativeHashMapDebuggerTypeProxy<TKey, TValue>
  516. where TKey : struct, IEquatable<TKey>
  517. where TValue : struct
  518. {
  519. #if !NET_DOTS
  520. UnsafeHashMap<TKey, TValue> m_Target;
  521. public NativeHashMapDebuggerTypeProxy(NativeHashMap<TKey, TValue> target)
  522. {
  523. m_Target = target.m_HashMapData;
  524. }
  525. public List<Pair<TKey, TValue>> Items
  526. {
  527. get
  528. {
  529. var result = new List<Pair<TKey, TValue>>();
  530. using (var kva = m_Target.GetKeyValueArrays(Allocator.Temp))
  531. {
  532. for (var i = 0; i < kva.Length; ++i)
  533. {
  534. result.Add(new Pair<TKey, TValue>(kva.Keys[i], kva.Values[i]));
  535. }
  536. }
  537. return result;
  538. }
  539. }
  540. #endif
  541. }
  542. }