No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

UnsafeParallelMultiHashMap.cs 30KB

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