using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Unity.Jobs; namespace Unity.Collections.LowLevel.Unsafe { /// /// An unordered, expandable set of unique values. /// /// The type of the values. [StructLayout(LayoutKind.Sequential)] [DebuggerTypeProxy(typeof(UnsafeParallelHashSetDebuggerTypeProxy<>))] [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })] public unsafe struct UnsafeParallelHashSet : INativeDisposable , IEnumerable // Used by collection initializers. where T : unmanaged, IEquatable { internal UnsafeParallelHashMap m_Data; /// /// Initializes and returns an instance of UnsafeParallelHashSet. /// /// The number of values that should fit in the initial allocation. /// The allocator to use. public UnsafeParallelHashSet(int capacity, AllocatorManager.AllocatorHandle allocator) { m_Data = new UnsafeParallelHashMap(capacity, allocator); } /// /// Whether this set is empty. /// /// True if this set is empty. public readonly bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => m_Data.IsEmpty; } /// /// Returns the current number of values in this set. /// /// The current number of values in this set. public int Count() => m_Data.Count(); /// /// The number of values that fit in the current allocation. /// /// The number of values that fit in the current allocation. /// A new capacity. Must be larger than current capacity. /// Thrown if `value` is less than the current capacity. public int Capacity { [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get => m_Data.Capacity; set => m_Data.Capacity = value; } /// /// Whether this set has been allocated (and not yet deallocated). /// /// True if this set has been allocated (and not yet deallocated). public readonly bool IsCreated { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => m_Data.IsCreated; } /// /// Releases all resources (memory). /// public void Dispose() => m_Data.Dispose(); /// /// Creates and schedules a job that will dispose this set. /// /// A job handle. The newly scheduled job will depend upon this handle. /// The handle of a new job that will dispose this set. public JobHandle Dispose(JobHandle inputDeps) => m_Data.Dispose(inputDeps); /// /// Removes all values. /// /// Does not change the capacity. public void Clear() => m_Data.Clear(); /// /// Adds a new value (unless it is already present). /// /// The value to add. /// True if the value was not already present. public bool Add(T item) => m_Data.TryAdd(item, false); /// /// Removes a particular value. /// /// The value to remove. /// True if the value was present. public bool Remove(T item) => m_Data.Remove(item); /// /// Returns true if a particular value is present. /// /// The value to check for. /// True if the value was present. public bool Contains(T item) => m_Data.ContainsKey(item); /// /// Returns an array with a copy of this set's values (in no particular order). /// /// The allocator to use. /// An array with a copy of the set's values. public NativeArray ToNativeArray(AllocatorManager.AllocatorHandle allocator) => m_Data.GetKeyArray(allocator); /// /// Returns a parallel writer. /// /// A parallel writer. public ParallelWriter AsParallelWriter() { return new ParallelWriter { m_Data = m_Data.AsParallelWriter() }; } /// /// A parallel writer for an UnsafeParallelHashSet. /// /// /// Use to create a parallel writer for a set. /// [NativeContainerIsAtomicWriteOnly] [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })] public struct ParallelWriter { internal UnsafeParallelHashMap.ParallelWriter m_Data; /// /// The number of values that fit in the current allocation. /// /// The number of values that fit in the current allocation. public readonly int Capacity { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => m_Data.Capacity; } /// /// Adds a new value (unless it is already present). /// /// The value to add. /// True if the value is not already present. public bool Add(T item) => m_Data.TryAdd(item, false); /// /// Adds a new value (unless it is already present). /// /// The value to add. /// The thread index which must be set by a field from a job struct with the attribute. /// True if the value is not already present. internal bool Add(T item, int threadIndexOverride) => m_Data.TryAdd(item, false, threadIndexOverride); } /// /// Returns an enumerator over the values of this set. /// /// An enumerator over the values of this set. public Enumerator GetEnumerator() { return new Enumerator { m_Enumerator = new UnsafeParallelHashMapDataEnumerator(m_Data.m_Buffer) }; } /// /// This method is not implemented. Use instead. /// /// Throws NotImplementedException. /// Method is not implemented. IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } /// /// This method is not implemented. Use instead. /// /// Throws NotImplementedException. /// Method is not implemented. IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } /// /// An enumerator over the values of a set. /// /// /// In an enumerator's initial state, is invalid. /// The first call advances the enumerator to the first value. /// public struct Enumerator : IEnumerator { internal UnsafeParallelHashMapDataEnumerator m_Enumerator; /// /// Does nothing. /// public void Dispose() { } /// /// Advances the enumerator to the next value. /// /// True if `Current` is valid to read after the call. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() => m_Enumerator.MoveNext(); /// /// Resets the enumerator to its initial state. /// public void Reset() => m_Enumerator.Reset(); /// /// The current value. /// /// The current value. public T Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => m_Enumerator.GetCurrentKey(); } object IEnumerator.Current => Current; } /// /// Returns a readonly version of this UnsafeParallelHashSet instance. /// /// ReadOnly containers point to the same underlying data as the UnsafeParallelHashSet it is made from. /// ReadOnly instance for this. public ReadOnly AsReadOnly() { return new ReadOnly(ref this); } /// /// A read-only alias for the value of a UnsafeParallelHashSet. Does not have its own allocated storage. /// [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })] public struct ReadOnly : IEnumerable { internal UnsafeParallelHashMap m_Data; internal ReadOnly(ref UnsafeParallelHashSet data) { m_Data = data.m_Data; } /// /// Whether this hash set has been allocated (and not yet deallocated). /// /// True if this hash set has been allocated (and not yet deallocated). public readonly bool IsCreated { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => m_Data.IsCreated; } /// /// Whether this hash set is empty. /// /// True if this hash set is empty or if the map has not been constructed. public readonly bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => !m_Data.IsCreated || m_Data.IsEmpty; } /// /// The current number of items in this hash set. /// /// The current number of items in this hash set. public readonly int Count() => m_Data.Count(); /// /// The number of items that fit in the current allocation. /// /// The number of items that fit in the current allocation. public readonly int Capacity { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => m_Data.Capacity; } /// /// Returns true if a given item is present in this hash set. /// /// The item to look up. /// True if the item was present. public readonly bool Contains(T item) { return m_Data.ContainsKey(item); } /// /// Returns an array with a copy of all this hash set's items (in no particular order). /// /// The allocator to use. /// An array with a copy of all this hash set's items (in no particular order). public readonly NativeArray ToNativeArray(AllocatorManager.AllocatorHandle allocator) { return m_Data.GetKeyArray(allocator); } /// /// Returns an enumerator over the items of this hash set. /// /// An enumerator over the items of this hash set. public readonly Enumerator GetEnumerator() { return new Enumerator { m_Enumerator = new UnsafeParallelHashMapDataEnumerator(m_Data.m_Buffer), }; } /// /// This method is not implemented. Use instead. /// /// Throws NotImplementedException. /// Method is not implemented. IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } /// /// This method is not implemented. Use instead. /// /// Throws NotImplementedException. /// Method is not implemented. IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } } } sealed internal class UnsafeParallelHashSetDebuggerTypeProxy where T : unmanaged, IEquatable { UnsafeParallelHashSet Data; public UnsafeParallelHashSetDebuggerTypeProxy(UnsafeParallelHashSet data) { Data = data; } public List Items { get { var result = new List(); using (var item = Data.ToNativeArray(Allocator.Temp)) { for (var k = 0; k < item.Length; ++k) { result.Add(item[k]); } } return result; } } } }