A game about forced loneliness, made by TACStudios
at master 378 lines 15 kB view raw
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}