Ingen beskrivning
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.

UnsafeHashSet.cs 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  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. namespace Unity.Collections.LowLevel.Unsafe
  9. {
  10. /// <summary>
  11. /// An unordered, expandable set of unique values.
  12. /// </summary>
  13. /// <typeparam name="T">The type of the values.</typeparam>
  14. [StructLayout(LayoutKind.Sequential)]
  15. [DebuggerTypeProxy(typeof(UnsafeHashSetDebuggerTypeProxy<>))]
  16. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
  17. public unsafe struct UnsafeHashSet<T>
  18. : INativeDisposable
  19. , IEnumerable<T> // Used by collection initializers.
  20. where T : unmanaged, IEquatable<T>
  21. {
  22. internal HashMapHelper<T> m_Data;
  23. /// <summary>
  24. /// Initializes and returns an instance of NativeParallelHashSet.
  25. /// </summary>
  26. /// <param name="initialCapacity">The number of values that should fit in the initial allocation.</param>
  27. /// <param name="allocator">The allocator to use.</param>
  28. public UnsafeHashSet(int initialCapacity, AllocatorManager.AllocatorHandle allocator)
  29. {
  30. m_Data = default;
  31. m_Data.Init(initialCapacity, 0, HashMapHelper<T>.kMinimumCapacity, allocator);
  32. }
  33. /// <summary>
  34. /// Whether this set is empty.
  35. /// </summary>
  36. /// <value>True if this set is empty or if the set has not been constructed.</value>
  37. public readonly bool IsEmpty
  38. {
  39. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  40. get => !IsCreated || m_Data.IsEmpty;
  41. }
  42. /// <summary>
  43. /// Returns the current number of values in this set.
  44. /// </summary>
  45. /// <returns>The current number of values in this set.</returns>
  46. public readonly int Count
  47. {
  48. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  49. get => m_Data.Count;
  50. }
  51. /// <summary>
  52. /// The number of values that fit in the current allocation.
  53. /// </summary>
  54. /// <value>The number of values that fit in the current allocation.</value>
  55. /// <param name="value">A new capacity. Must be larger than current capacity.</param>
  56. public int Capacity
  57. {
  58. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  59. readonly get => m_Data.Capacity;
  60. set => m_Data.Resize(value);
  61. }
  62. /// <summary>
  63. /// Whether this set has been allocated (and not yet deallocated).
  64. /// </summary>
  65. /// <value>True if this set has been allocated (and not yet deallocated).</value>
  66. public readonly bool IsCreated
  67. {
  68. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  69. get => m_Data.IsCreated;
  70. }
  71. /// <summary>
  72. /// Releases all resources (memory and safety handles).
  73. /// </summary>
  74. public void Dispose()
  75. {
  76. if (!IsCreated)
  77. {
  78. return;
  79. }
  80. m_Data.Dispose();
  81. }
  82. /// <summary>
  83. /// Creates and schedules a job that will dispose this set.
  84. /// </summary>
  85. /// <param name="inputDeps">A job handle. The newly scheduled job will depend upon this handle.</param>
  86. /// <returns>The handle of a new job that will dispose this set.</returns>
  87. public JobHandle Dispose(JobHandle inputDeps)
  88. {
  89. if (!IsCreated)
  90. {
  91. return inputDeps;
  92. }
  93. var jobHandle = new UnsafeDisposeJob { Ptr = m_Data.Ptr, Allocator = m_Data.Allocator }.Schedule(inputDeps);
  94. m_Data.Ptr = null;
  95. return jobHandle;
  96. }
  97. /// <summary>
  98. /// Removes all values.
  99. /// </summary>
  100. /// <remarks>Does not change the capacity.</remarks>
  101. public void Clear()
  102. {
  103. m_Data.Clear();
  104. }
  105. /// <summary>
  106. /// Adds a new value (unless it is already present).
  107. /// </summary>
  108. /// <param name="item">The value to add.</param>
  109. /// <returns>True if the value was not already present.</returns>
  110. public bool Add(T item)
  111. {
  112. return -1 != m_Data.TryAdd(item);
  113. }
  114. /// <summary>
  115. /// Removes a particular value.
  116. /// </summary>
  117. /// <param name="item">The value to remove.</param>
  118. /// <returns>True if the value was present.</returns>
  119. public bool Remove(T item)
  120. {
  121. return -1 != m_Data.TryRemove(item);
  122. }
  123. /// <summary>
  124. /// Returns true if a particular value is present.
  125. /// </summary>
  126. /// <param name="item">The value to check for.</param>
  127. /// <returns>True if the value was present.</returns>
  128. public bool Contains(T item)
  129. {
  130. return -1 != m_Data.Find(item);
  131. }
  132. /// <summary>
  133. /// Sets the capacity to match what it would be if it had been originally initialized with all its entries.
  134. /// </summary>
  135. public void TrimExcess() => m_Data.TrimExcess();
  136. /// <summary>
  137. /// Returns an array with a copy of this set's values (in no particular order).
  138. /// </summary>
  139. /// <param name="allocator">The allocator to use.</param>
  140. /// <returns>An array with a copy of the set's values.</returns>
  141. public NativeArray<T> ToNativeArray(AllocatorManager.AllocatorHandle allocator)
  142. {
  143. return m_Data.GetKeyArray(allocator);
  144. }
  145. /// <summary>
  146. /// Returns an enumerator over the values of this set.
  147. /// </summary>
  148. /// <returns>An enumerator over the values of this set.</returns>
  149. public Enumerator GetEnumerator()
  150. {
  151. fixed (HashMapHelper<T>* data = &m_Data)
  152. {
  153. return new Enumerator { m_Enumerator = new HashMapHelper<T>.Enumerator(data) };
  154. }
  155. }
  156. /// <summary>
  157. /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
  158. /// </summary>
  159. /// <returns>Throws NotImplementedException.</returns>
  160. /// <exception cref="NotImplementedException">Method is not implemented.</exception>
  161. IEnumerator<T> IEnumerable<T>.GetEnumerator()
  162. {
  163. throw new NotImplementedException();
  164. }
  165. /// <summary>
  166. /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
  167. /// </summary>
  168. /// <returns>Throws NotImplementedException.</returns>
  169. /// <exception cref="NotImplementedException">Method is not implemented.</exception>
  170. IEnumerator IEnumerable.GetEnumerator()
  171. {
  172. throw new NotImplementedException();
  173. }
  174. /// <summary>
  175. /// An enumerator over the values of a set.
  176. /// </summary>
  177. /// <remarks>
  178. /// In an enumerator's initial state, <see cref="Current"/> is invalid.
  179. /// The first <see cref="MoveNext"/> call advances the enumerator to the first value.
  180. /// </remarks>
  181. public struct Enumerator : IEnumerator<T>
  182. {
  183. internal HashMapHelper<T>.Enumerator m_Enumerator;
  184. /// <summary>
  185. /// Does nothing.
  186. /// </summary>
  187. public void Dispose() { }
  188. /// <summary>
  189. /// Advances the enumerator to the next value.
  190. /// </summary>
  191. /// <returns>True if `Current` is valid to read after the call.</returns>
  192. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  193. public bool MoveNext() => m_Enumerator.MoveNext();
  194. /// <summary>
  195. /// Resets the enumerator to its initial state.
  196. /// </summary>
  197. public void Reset() => m_Enumerator.Reset();
  198. /// <summary>
  199. /// The current value.
  200. /// </summary>
  201. /// <value>The current value.</value>
  202. public T Current
  203. {
  204. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  205. get => m_Enumerator.m_Data->Keys[m_Enumerator.m_Index];
  206. }
  207. /// <summary>
  208. /// Gets the element at the current position of the enumerator in the container.
  209. /// </summary>
  210. object IEnumerator.Current => Current;
  211. }
  212. /// <summary>
  213. /// Returns a readonly version of this UnsafeHashMap instance.
  214. /// </summary>
  215. /// <remarks>ReadOnly containers point to the same underlying data as the UnsafeHashMap it is made from.</remarks>
  216. /// <returns>ReadOnly instance for this.</returns>
  217. public ReadOnly AsReadOnly()
  218. {
  219. return new ReadOnly(ref m_Data);
  220. }
  221. /// <summary>
  222. /// A read-only alias for the value of a UnsafeHashSet. Does not have its own allocated storage.
  223. /// </summary>
  224. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
  225. public struct ReadOnly
  226. : IEnumerable<T>
  227. {
  228. internal HashMapHelper<T> m_Data;
  229. internal ReadOnly(ref HashMapHelper<T> data)
  230. {
  231. m_Data = data;
  232. }
  233. /// <summary>
  234. /// Whether this hash map has been allocated (and not yet deallocated).
  235. /// </summary>
  236. /// <value>True if this hash map has been allocated (and not yet deallocated).</value>
  237. public readonly bool IsCreated
  238. {
  239. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  240. get => m_Data.IsCreated;
  241. }
  242. /// <summary>
  243. /// Whether this hash set is empty.
  244. /// </summary>
  245. /// <value>True if this hash set is empty or if the set has not been constructed.</value>
  246. public readonly bool IsEmpty
  247. {
  248. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  249. get => m_Data.IsEmpty;
  250. }
  251. /// <summary>
  252. /// The current number of key-value pairs in this hash map.
  253. /// </summary>
  254. /// <returns>The current number of key-value pairs in this hash map.</returns>
  255. public readonly int Count
  256. {
  257. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  258. get => m_Data.Count;
  259. }
  260. /// <summary>
  261. /// The number of key-value pairs that fit in the current allocation.
  262. /// </summary>
  263. /// <value>The number of key-value pairs that fit in the current allocation.</value>
  264. public readonly int Capacity
  265. {
  266. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  267. get => m_Data.Capacity;
  268. }
  269. /// <summary>
  270. /// Returns true if a particular value is present.
  271. /// </summary>
  272. /// <param name="item">The item to look up.</param>
  273. /// <returns>True if the item was present.</returns>
  274. public readonly bool Contains(T item)
  275. {
  276. return -1 != m_Data.Find(item);
  277. }
  278. /// <summary>
  279. /// Returns an array with a copy of this set's values (in no particular order).
  280. /// </summary>
  281. /// <param name="allocator">The allocator to use.</param>
  282. /// <returns>An array with a copy of the set's values.</returns>
  283. public readonly NativeArray<T> ToNativeArray(AllocatorManager.AllocatorHandle allocator)
  284. {
  285. return m_Data.GetKeyArray(allocator);
  286. }
  287. /// <summary>
  288. /// Returns an enumerator over the key-value pairs of this hash map.
  289. /// </summary>
  290. /// <returns>An enumerator over the key-value pairs of this hash map.</returns>
  291. public readonly Enumerator GetEnumerator()
  292. {
  293. fixed (HashMapHelper<T>* data = &m_Data)
  294. {
  295. return new Enumerator { m_Enumerator = new HashMapHelper<T>.Enumerator(data) };
  296. }
  297. }
  298. /// <summary>
  299. /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
  300. /// </summary>
  301. /// <returns>Throws NotImplementedException.</returns>
  302. /// <exception cref="NotImplementedException">Method is not implemented.</exception>
  303. IEnumerator<T> IEnumerable<T>.GetEnumerator()
  304. {
  305. throw new NotImplementedException();
  306. }
  307. /// <summary>
  308. /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
  309. /// </summary>
  310. /// <returns>Throws NotImplementedException.</returns>
  311. /// <exception cref="NotImplementedException">Method is not implemented.</exception>
  312. IEnumerator IEnumerable.GetEnumerator()
  313. {
  314. throw new NotImplementedException();
  315. }
  316. }
  317. }
  318. sealed internal class UnsafeHashSetDebuggerTypeProxy<T>
  319. where T : unmanaged, IEquatable<T>
  320. {
  321. HashMapHelper<T> Data;
  322. public UnsafeHashSetDebuggerTypeProxy(UnsafeHashSet<T> data)
  323. {
  324. Data = data.m_Data;
  325. }
  326. public List<T> Items
  327. {
  328. get
  329. {
  330. var result = new List<T>();
  331. using (var keys = Data.GetKeyArray(Allocator.Temp))
  332. {
  333. for (var k = 0; k < keys.Length; ++k)
  334. {
  335. result.Add(keys[k]);
  336. }
  337. }
  338. return result;
  339. }
  340. }
  341. }
  342. }