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

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