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