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