using System; using Unity.Collections.LowLevel.Unsafe; namespace Unity.Collections { /// <summary> /// Provides extension methods for hash maps. /// </summary> [BurstCompatible] public static class NativeHashMapExtensions { /// <summary> /// Removes duplicate values from this sorted array and returns the number of values remaining. /// </summary> /// <remarks> /// Uses `Equals` to determine whether values are duplicates. /// /// Expects the array to already be sorted. /// /// The remaining elements will be tightly packed at the front of the array. /// </remarks> /// <typeparam name="T">The type of values in the array.</typeparam> /// <param name="array">The array from which to remove duplicates.</param> /// <returns>The number of unique elements in this array.</returns> [BurstCompatible(GenericTypeArguments = new [] { typeof(int) })] public static int Unique<T>(this NativeArray<T> array) where T : struct, IEquatable<T> { if (array.Length == 0) { return 0; } int first = 0; int last = array.Length; var result = first; while (++first != last) { if (!array[result].Equals(array[first])) { array[++result] = array[first]; } } return ++result; } #if !NET_DOTS // Tuple is not supported by TinyBCL /// <summary> /// Returns an array populated with the unique keys from this multi hash map. /// </summary> /// <typeparam name="TKey">The type of the keys.</typeparam> /// <typeparam name="TValue">The type of the values.</typeparam> /// <param name="container">The multi hash map.</param> /// <param name="allocator">The allocator to use.</param> /// <returns>An array populated with the unique keys from this multi hash map.</returns> [BurstCompatible(GenericTypeArguments = new[] { typeof(int), typeof(int) })] public static (NativeArray<TKey>, int) GetUniqueKeyArray<TKey, TValue>(this UnsafeMultiHashMap<TKey, TValue> container, AllocatorManager.AllocatorHandle allocator) where TKey : struct, IEquatable<TKey>, IComparable<TKey> where TValue : struct { var result = container.GetKeyArray(allocator); result.Sort(); int uniques = result.Unique(); return (result, uniques); } /// <summary> /// Returns an array populated with the unique keys from this multi hash map. /// </summary> /// <typeparam name="TKey">The type of the keys.</typeparam> /// <typeparam name="TValue">The type of the values.</typeparam> /// <param name="container">The multi hash map.</param> /// <param name="allocator">The allocator to use.</param> /// <returns>An array populated with the unique keys from this multi hash map.</returns> [BurstCompatible(GenericTypeArguments = new[] { typeof(int), typeof(int) })] public static (NativeArray<TKey>, int) GetUniqueKeyArray<TKey, TValue>(this NativeMultiHashMap<TKey, TValue> container, AllocatorManager.AllocatorHandle allocator) where TKey : struct, IEquatable<TKey>, IComparable<TKey> where TValue : struct { var result = container.GetKeyArray(allocator); result.Sort(); int uniques = result.Unique(); return (result, uniques); } #endif /// <summary> /// Returns a "bucket" view of this hash map. /// </summary> /// <remarks> /// Internally, the elements of a hash map are split into buckets of type <see cref="UnsafeHashMapBucketData"/>. /// /// With buckets, a job can safely access the elements of a hash map concurrently as long as each individual bucket is accessed /// only from an individual thread. Effectively, it is not safe to read elements of an individual bucket concurrently, /// but it is safe to read elements of separate buckets concurrently. /// </remarks> /// <typeparam name="TKey">The type of the keys.</typeparam> /// <typeparam name="TValue">The type of the values.</typeparam> /// <param name="container">The hash map.</param> /// <returns>A "bucket" view of this hash map.</returns> [Obsolete("GetBucketData is deprecated, please use GetUnsafeBucketData instead. (RemovedAfter 2021-07-08) (UnityUpgradable) -> GetUnsafeBucketData<TKey,TValue>(*)", false)] [BurstCompatible(GenericTypeArguments = new [] { typeof(int), typeof(int) })] public static unsafe UnsafeHashMapBucketData GetBucketData<TKey, TValue>(this NativeHashMap<TKey, TValue> container) where TKey : struct, IEquatable<TKey> where TValue : struct { return container.m_HashMapData.m_Buffer->GetBucketData(); } /// <summary> /// Returns a "bucket" view of this hash map. /// </summary> /// <remarks> /// Internally, the elements of a hash map are split into buckets of type <see cref="UnsafeHashMapBucketData"/>. /// /// With buckets, a job can safely access the elements of a hash map concurrently as long as each individual bucket is accessed /// only from an individual thread. Effectively, it is not safe to read elements of an individual bucket concurrently, /// but it is safe to read elements of separate buckets concurrently. /// </remarks> /// <typeparam name="TKey">The type of the keys.</typeparam> /// <typeparam name="TValue">The type of the values.</typeparam> /// <param name="container">The hash map.</param> /// <returns>A "bucket" view of this hash map.</returns> [BurstCompatible(GenericTypeArguments = new[] { typeof(int), typeof(int) })] public static unsafe UnsafeHashMapBucketData GetUnsafeBucketData<TKey, TValue>(this NativeHashMap<TKey, TValue> container) where TKey : struct, IEquatable<TKey> where TValue : struct { return container.m_HashMapData.m_Buffer->GetBucketData(); } /// <summary> /// Returns a "bucket" view of this multi hash map. /// </summary> /// <remarks> /// Internally, the elements of a hash map are split into buckets of type <see cref="UnsafeHashMapBucketData"/>. /// /// With buckets, a job can safely access the elements of a hash map concurrently as long as each individual bucket is accessed /// only from an individual thread. Effectively, it is not safe to read elements of an individual bucket concurrently, /// but it is safe to read elements of separate buckets concurrently. /// </remarks> /// <typeparam name="TKey">The type of the keys.</typeparam> /// <typeparam name="TValue">The type of the values.</typeparam> /// <param name="container">The multi hash map.</param> /// <returns>A "bucket" view of this multi hash map.</returns> [BurstCompatible(GenericTypeArguments = new [] { typeof(int), typeof(int) })] public static unsafe UnsafeHashMapBucketData GetUnsafeBucketData<TKey, TValue>(this NativeMultiHashMap<TKey, TValue> container) where TKey : struct, IEquatable<TKey> where TValue : struct { return container.m_MultiHashMapData.m_Buffer->GetBucketData(); } /// <summary> /// Removes all occurrences of a particular key-value pair. /// </summary> /// <remarks>Removes all key-value pairs which have a particular key and which *also have* a particular value. /// In other words: (key *AND* value) rather than (key *OR* value).</remarks> /// <typeparam name="TKey">The type of the keys.</typeparam> /// <typeparam name="TValue">The type of the values.</typeparam> /// <param name="container">The multi hash map.</param> /// <param name="key">The key of the key-value pairs to remove.</param> /// <param name="value">The value of the key-value pairs to remove.</param> [BurstCompatible(GenericTypeArguments = new [] { typeof(int), typeof(int) })] public static void Remove<TKey, TValue>(this NativeMultiHashMap<TKey, TValue> container, TKey key, TValue value) where TKey : struct, IEquatable<TKey> where TValue : struct, IEquatable<TValue> { #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(container.m_Safety); #endif container.m_MultiHashMapData.Remove(key, value); } } }