diff --git a/Misaki.HighPerformance.Image/ImageResultFloat.cs b/Misaki.HighPerformance.Image/ImageResultFloat.cs index c623825..31695fd 100644 --- a/Misaki.HighPerformance.Image/ImageResultFloat.cs +++ b/Misaki.HighPerformance.Image/ImageResultFloat.cs @@ -88,14 +88,14 @@ public unsafe readonly struct ImageResultFloat : IDisposable return FromStream(stream, requiredComponents); } - public Span AsSpan() + public Span AsSpan() { if (Data == null) { - return Span.Empty; + return Span.Empty; } - return new Span(Data, (int)Size); + return new Span(Data, (int)Size); } public void Dispose() diff --git a/Misaki.HighPerformance.Image/Misaki.HighPerformance.Image.csproj b/Misaki.HighPerformance.Image/Misaki.HighPerformance.Image.csproj index bfb9281..27004fe 100644 --- a/Misaki.HighPerformance.Image/Misaki.HighPerformance.Image.csproj +++ b/Misaki.HighPerformance.Image/Misaki.HighPerformance.Image.csproj @@ -6,7 +6,7 @@ true net10.0 Misaki - 1.1.0 + 1.1.1 $(AssemblyVersion) https://git.personalnas.com/Misaki/Misaki.HighPerformance.git https://git.personalnas.com/Misaki/Misaki.HighPerformance.git diff --git a/Misaki.HighPerformance.LowLevel/Buffer/FreeList.cs b/Misaki.HighPerformance.LowLevel/Buffer/FreeList.cs index 8aa2af0..aca13f6 100644 --- a/Misaki.HighPerformance.LowLevel/Buffer/FreeList.cs +++ b/Misaki.HighPerformance.LowLevel/Buffer/FreeList.cs @@ -1,4 +1,4 @@ -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Misaki.HighPerformance.LowLevel.Buffer; @@ -291,6 +291,8 @@ public unsafe struct FreeList : IDisposable header->magicNumber = 0; } + // FIX: This may introduce ABA problem. Consider adding a version counter to the free list nodes if this becomes an issue. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private readonly void* TryPopFromBucket(int bucketIndex) { diff --git a/Misaki.HighPerformance.LowLevel/Collections/HashMapHelper.cs b/Misaki.HighPerformance.LowLevel/Collections/HashMapHelper.cs index 6cecfa2..515eead 100644 --- a/Misaki.HighPerformance.LowLevel/Collections/HashMapHelper.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/HashMapHelper.cs @@ -200,7 +200,7 @@ public unsafe struct HashMapHelper : IDisposable } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private readonly int GetBucket(in TKey key) + private readonly int GetBucket(scoped in TKey key) { var h = key.GetHashCode(); return GetBucket(h); @@ -216,7 +216,7 @@ public unsafe struct HashMapHelper : IDisposable } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int AllocateEntry(in TKey key) + private int AllocateEntry(scoped in TKey key) { int idx; @@ -323,7 +323,7 @@ public unsafe struct HashMapHelper : IDisposable ResizeExact(capacity, capacity * 2); } - public int Find(in TKey key) + public int Find(scoped in TKey key) { ThrowIfNotCreated(); @@ -354,7 +354,7 @@ public unsafe struct HashMapHelper : IDisposable return -1; } - public int TryAdd(in TKey key) + public int TryAdd(scoped in TKey key) { ThrowIfNotCreated(); @@ -366,14 +366,14 @@ public unsafe struct HashMapHelper : IDisposable return AllocateEntry(key); } - public int Add(in TKey key) + public int Add(scoped in TKey key) { ThrowIfNotCreated(); return AllocateEntry(key); } - public int TryRemove(in TKey key) + public int TryRemove(scoped in TKey key) { ThrowIfNotCreated(); @@ -425,7 +425,7 @@ public unsafe struct HashMapHelper : IDisposable return 0 != removed ? removed : -1; } - public int RemoveAll(in TKey key) + public int RemoveAll(scoped in TKey key) { ThrowIfNotCreated(); @@ -469,7 +469,7 @@ public unsafe struct HashMapHelper : IDisposable return removed; } - public bool TryGetValue(in TKey key, out TValue item) + public bool TryGetValue(scoped in TKey key, out TValue item) where TValue : unmanaged { ThrowIfNotCreated(); @@ -486,7 +486,7 @@ public unsafe struct HashMapHelper : IDisposable return false; } - public int FindNext(int entryIdx, in TKey key) + public int FindNext(int entryIdx, scoped in TKey key) { ThrowIfNotCreated(); @@ -509,7 +509,7 @@ public unsafe struct HashMapHelper : IDisposable return -1; } - public int CountValuesForKey(in TKey key) + public int CountValuesForKey(scoped in TKey key) { ThrowIfNotCreated(); @@ -522,8 +522,7 @@ public unsafe struct HashMapHelper : IDisposable return count; } - [UnscopedRef] - public ref TValue GetValueRef(in TKey key, out bool exists) + public ref TValue GetValueRef(scoped in TKey key, out bool exists) where TValue : unmanaged { ThrowIfNotCreated(); @@ -539,8 +538,7 @@ public unsafe struct HashMapHelper : IDisposable return ref Unsafe.NullRef(); } - [UnscopedRef] - public ref TValue GetValueRefOrAddDefault(in TKey key, out bool exists) + public ref TValue GetValueRefOrAddDefault(scoped in TKey key, out bool exists) where TValue : unmanaged { ThrowIfNotCreated(); diff --git a/Misaki.HighPerformance.LowLevel/Collections/UnsafeHashMap.cs b/Misaki.HighPerformance.LowLevel/Collections/UnsafeHashMap.cs index 11e4613..6884917 100644 --- a/Misaki.HighPerformance.LowLevel/Collections/UnsafeHashMap.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/UnsafeHashMap.cs @@ -116,7 +116,8 @@ public unsafe struct UnsafeHashMap : IUnsafeHashCollectionThe key to add. /// The value to add. /// True if the key-value pair was added. - public bool TryAdd(in TKey key, TValue item) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryAdd(scoped in TKey key, TValue item) { var idx = _helper.TryAdd(key); if (idx != -1) @@ -135,7 +136,8 @@ public unsafe struct UnsafeHashMap : IUnsafeHashCollectionThe key to add. /// The value to add. /// Thrown if the key was already present. - public void Add(in TKey key, TValue item) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(scoped in TKey key, TValue item) { var result = TryAdd(key, item); if (!result) @@ -149,7 +151,8 @@ public unsafe struct UnsafeHashMap : IUnsafeHashCollection /// The value to remove. /// True if the value was present. - public bool Remove(in TKey key) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Remove(scoped in TKey key) { return -1 != _helper.TryRemove(key); } @@ -160,7 +163,8 @@ public unsafe struct UnsafeHashMap : IUnsafeHashCollectionThe key to look up. /// Outputs the value associated with the key. Outputs default if the key was not present. /// True if the key was present. - public bool TryGetValue(in TKey key, out TValue item) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryGetValue(scoped in TKey key, out TValue item) { return _helper.TryGetValue(key, out item); } @@ -171,7 +175,8 @@ public unsafe struct UnsafeHashMap : IUnsafeHashCollectionThe key whose value to retrieve. /// The value to return if the specified key does not exist. If not specified, the default value for the type is used. /// The value associated with the specified key if the key is found; otherwise, the specified default value. - public TValue GetValueOrDefault(in TKey key, TValue defaultValue = default) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public TValue GetValueOrDefault(scoped in TKey key, TValue defaultValue = default) { if (_helper.TryGetValue(key, out var value)) { @@ -181,14 +186,26 @@ public unsafe struct UnsafeHashMap : IUnsafeHashCollection + /// Returns a reference to the value associated with the specified key, and a boolean indicating whether the key exists in the hash map. + /// + /// The key whose value to retrieve. + /// Outputs true if the key exists in the hash map; otherwise, false. + /// A reference to the value associated with the specified key. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref TValue GetValueRef(scoped in TKey key, out bool exists) { return ref _helper.GetValueRef(key, out exists); } - [UnscopedRef] - public ref TValue GetValueRefOrAddDefault(in TKey key, out bool exists) + /// + /// Returns a reference to the value associated with the specified key if it exists; otherwise, adds a new key with a default value and returns a reference to that value. The method also outputs a boolean indicating whether the key already existed in the hash map. + /// + /// The key whose value to retrieve or add. + /// Outputs true if the key already existed in the hash map; otherwise, false. + /// A reference to the value associated with the specified key. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref TValue GetValueRefOrAddDefault(scoped in TKey key, out bool exists) { return ref _helper.GetValueRefOrAddDefault(key, out exists); } @@ -198,7 +215,8 @@ public unsafe struct UnsafeHashMap : IUnsafeHashCollection /// The key to look up. /// True if the key was present. - public bool ContainsKey(in TKey key) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool ContainsKey(scoped in TKey key) { return -1 != _helper.Find(key); } @@ -206,16 +224,19 @@ public unsafe struct UnsafeHashMap : IUnsafeHashCollection /// Sets the capacity to match what it would be if it had been originally initialized with all its entries. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void TrimExcess() { _helper.TrimExcess(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Resize(int newSize, AllocationOption option = AllocationOption.None) { _helper.Resize(newSize); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear() { _helper.Clear(); @@ -225,6 +246,7 @@ public unsafe struct UnsafeHashMap : IUnsafeHashCollection /// An array containing the keys stored in the hash map. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public UnsafeArray GetKeyArray(Allocator allocator) { return _helper.GetKeyArray(allocator); @@ -234,6 +256,7 @@ public unsafe struct UnsafeHashMap : IUnsafeHashCollection /// An UnsafeArray containing the values stored in the hash map. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public UnsafeArray GetValueArray(Allocator allocator) { return _helper.GetValueArray(allocator); @@ -244,6 +267,7 @@ public unsafe struct UnsafeHashMap : IUnsafeHashCollection /// Returns an UnsafeArray containing KeyValuePair objects. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public UnsafeArray> GetKeyValueArrays(Allocator allocator) { return _helper.GetKeyValueArrays(allocator); diff --git a/Misaki.HighPerformance.LowLevel/Collections/UnsafeHashSet.cs b/Misaki.HighPerformance.LowLevel/Collections/UnsafeHashSet.cs index 1a31809..bdf53ef 100644 --- a/Misaki.HighPerformance.LowLevel/Collections/UnsafeHashSet.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/UnsafeHashSet.cs @@ -86,6 +86,7 @@ public unsafe struct UnsafeHashSet : IUnsafeHashCollection, IEnumerable /// /// The value to add. /// True if the value was not already present. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Add(T item) { return -1 != _helper.TryAdd(item); @@ -96,6 +97,7 @@ public unsafe struct UnsafeHashSet : IUnsafeHashCollection, IEnumerable /// /// The value to remove. /// True if the value was present. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Remove(T item) { return -1 != _helper.TryRemove(item); @@ -106,6 +108,7 @@ public unsafe struct UnsafeHashSet : IUnsafeHashCollection, IEnumerable /// /// The value to check for. /// True if the value was present. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(T item) { return -1 != _helper.Find(item); @@ -114,6 +117,7 @@ public unsafe struct UnsafeHashSet : IUnsafeHashCollection, IEnumerable /// /// Sets the capacity to match what it would be if it had been originally initialized with all its entries. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void TrimExcess() { _helper.TrimExcess(); @@ -124,16 +128,19 @@ public unsafe struct UnsafeHashSet : IUnsafeHashCollection, IEnumerable /// /// The allocator to use. /// An array with a copy of the set's values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public UnsafeArray ToNativeArray(Allocator allocator) { return _helper.GetKeyArray(allocator); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Resize(int newSize, AllocationOption option = AllocationOption.None) { _helper.Resize(newSize); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear() { _helper.Clear(); diff --git a/Misaki.HighPerformance.LowLevel/Collections/UnsafeMultiHashMap.cs b/Misaki.HighPerformance.LowLevel/Collections/UnsafeMultiHashMap.cs index d6e6d62..56a34ee 100644 --- a/Misaki.HighPerformance.LowLevel/Collections/UnsafeMultiHashMap.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/UnsafeMultiHashMap.cs @@ -43,7 +43,7 @@ public unsafe struct UnsafeMultiHashMap : IUnsafeHashCollection : IUnsafeHashCollection* _data; private readonly TKey _key; - internal ValueEnumerable(HashMapHelper* data, in TKey key) + internal ValueEnumerable(HashMapHelper* data, scoped in TKey key) { _data = data; _key = key; @@ -77,7 +77,7 @@ public unsafe struct UnsafeMultiHashMap : IUnsafeHashCollection UnsafeUtility.ReadArrayElement(_data->Buffer, _entryIndex); readonly object IEnumerator.Current => Current; - internal ValueEnumerator(HashMapHelper* data, in TKey key) + internal ValueEnumerator(HashMapHelper* data, scoped in TKey key) { _data = data; _key = key; @@ -151,18 +151,18 @@ public unsafe struct UnsafeMultiHashMap : IUnsafeHashCollection : IUnsafeHashCollection(key, out var value)) { @@ -213,17 +213,17 @@ public unsafe struct UnsafeMultiHashMap : IUnsafeHashCollection*)UnsafeUtility.AddressOf(ref this), key); } - public int CountValuesForKey(in TKey key) + public int CountValuesForKey(scoped in TKey key) { return _helper.CountValuesForKey(key); } - public bool ContainsKey(in TKey key) + public bool ContainsKey(scoped in TKey key) { return _helper.Find(key) != -1; } diff --git a/Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj b/Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj index 2cb9d67..7f0d5e5 100644 --- a/Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj +++ b/Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj @@ -7,7 +7,7 @@ true true Misaki - 1.4.3 + 1.4.4 $(AssemblyVersion) https://git.personalnas.com/Misaki/Misaki.HighPerformance.git https://git.personalnas.com/Misaki/Misaki.HighPerformance.git diff --git a/Misaki.HighPerformance.LowLevel/Ptr.cs b/Misaki.HighPerformance.LowLevel/Ptr.cs index 4c6628f..a7cab01 100644 --- a/Misaki.HighPerformance.LowLevel/Ptr.cs +++ b/Misaki.HighPerformance.LowLevel/Ptr.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using System.Runtime.CompilerServices; namespace Misaki.HighPerformance.LowLevel; @@ -86,7 +87,7 @@ public unsafe struct UniquePtr : IEquatable> _value = value; } - public T* Get() + public readonly T* Get() { return _value; } @@ -101,6 +102,12 @@ public unsafe struct UniquePtr : IEquatable> return new SharedPtr(_value); } + public void Attach(T* value) + { + Debug.Assert(_value == null); + _value = value; + } + public T* Detach() { var temp = _value; @@ -146,6 +153,67 @@ public unsafe struct UniquePtr : IEquatable> } } +[NonCopyable] +public unsafe struct DisposablePtr : IDisposable, IEquatable> + where T : unmanaged, IDisposable +{ + private T* _value; + + public DisposablePtr(T* value) + { + _value = value; + } + + public readonly T* Get() + { + return _value; + } + + public ref T GetRef() + { + return ref *_value; + } + + public readonly SharedPtr Share() + { + return new SharedPtr(_value); + } + + public void Dispose() + { + if (_value != null) + { + _value->Dispose(); + _value = null; + } + } + + public readonly bool Equals(DisposablePtr other) + { + return other._value == _value; + } + + public override readonly bool Equals(object? obj) + { + return obj is DisposablePtr ptr && Equals(ptr); + } + + public override readonly int GetHashCode() + { + return ((nint)_value).GetHashCode(); + } + + public static bool operator ==(DisposablePtr left, DisposablePtr right) + { + return left.Equals(right); + } + + public static bool operator !=(DisposablePtr left, DisposablePtr right) + { + return !(left == right); + } +} + public ref struct Ref : IEquatable> { private ref T _value; @@ -193,23 +261,23 @@ public ref struct Ref : IEquatable> /// /// Provides a wrapper for a value type that implements , ensuring proper disposal of the contained value. /// -/// The class manages the lifetime of the contained value by calling its +/// The class manages the lifetime of the contained value by calling its /// method when the wrapper is disposed or finalized. After disposal, accessing the value /// will throw an . /// The value type to wrap. Must be a struct that implements . -public class Owner : IDisposable +public class Wrapper : IDisposable where T : struct, IDisposable { private T _value; private bool _disposed; - public Owner(T value) + public Wrapper(T value) { _value = value; } - ~Owner() + ~Wrapper() { Dispose(); } diff --git a/Misaki.HighPerformance.Test/Program.cs b/Misaki.HighPerformance.Test/Program.cs index cb8e681..c99fd74 100644 --- a/Misaki.HighPerformance.Test/Program.cs +++ b/Misaki.HighPerformance.Test/Program.cs @@ -4,19 +4,35 @@ using Misaki.HighPerformance.LowLevel.Utilities; using Misaki.HighPerformance.Mathematics.SPMD; using Misaki.HighPerformance.Test.Benchmark; using Misaki.HighPerformance.Test.Jobs; +using System.Runtime.CompilerServices; -//BenchmarkRunner.Run(); -var hashMap = new UnsafeHashMap(10, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent); -hashMap[0] = 5; -hashMap[1] = 6; +BenchmarkRunner.Run(); +//var hashMap = new UnsafeHashMap(10, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent); +//hashMap[0] = 5; +//hashMap[1] = 6; -Console.WriteLine(hashMap[1]); +//Console.WriteLine(hashMap[1]); -ref var v = ref hashMap.GetValueRefOrAddDefault(1, out var exists); +//ref var v = ref hashMap.GetValueRefOrAddDefault(1, out var exists); -Console.WriteLine(exists); +//Console.WriteLine(exists); -v = 10; -Console.WriteLine(hashMap[1]); +//v = 10; +//Console.WriteLine(hashMap[1]); -hashMap.Dispose(); \ No newline at end of file +//hashMap.Dispose(); + +//class TestClass +//{ +// private UnsafeHashMap _map; +// ref int Test() +// { +// ref var x = ref _map.GetValueRef(9, out var exists); +// if (exists) +// { +// return ref x; +// } + +// return ref Unsafe.NullRef(); +// } +//} \ No newline at end of file