diff --git a/1.2/Assemblies/1Fishery.dll b/1.2/Assemblies/1Fishery.dll index 1f2a4a2..12560b0 100644 Binary files a/1.2/Assemblies/1Fishery.dll and b/1.2/Assemblies/1Fishery.dll differ diff --git a/1.2/Assemblies/1Fishery.pdb b/1.2/Assemblies/1Fishery.pdb index cfe540b..1e840e8 100644 Binary files a/1.2/Assemblies/1Fishery.pdb and b/1.2/Assemblies/1Fishery.pdb differ diff --git a/1.3/Assemblies/1Fishery.dll b/1.3/Assemblies/1Fishery.dll index e365ffd..1a95631 100644 Binary files a/1.3/Assemblies/1Fishery.dll and b/1.3/Assemblies/1Fishery.dll differ diff --git a/1.3/Assemblies/1Fishery.pdb b/1.3/Assemblies/1Fishery.pdb index b18519c..cf8368f 100644 Binary files a/1.3/Assemblies/1Fishery.pdb and b/1.3/Assemblies/1Fishery.pdb differ diff --git a/1.4/Assemblies/1Fishery.dll b/1.4/Assemblies/1Fishery.dll index b5c540b..1fe91b2 100644 Binary files a/1.4/Assemblies/1Fishery.dll and b/1.4/Assemblies/1Fishery.dll differ diff --git a/1.4/Assemblies/1Fishery.pdb b/1.4/Assemblies/1Fishery.pdb index 9a18dd3..7b727fa 100644 Binary files a/1.4/Assemblies/1Fishery.pdb and b/1.4/Assemblies/1Fishery.pdb differ diff --git a/1.5/Assemblies/1Fishery.dll b/1.5/Assemblies/1Fishery.dll index 2b39b51..6e2b833 100644 Binary files a/1.5/Assemblies/1Fishery.dll and b/1.5/Assemblies/1Fishery.dll differ diff --git a/1.5/Assemblies/1Fishery.pdb b/1.5/Assemblies/1Fishery.pdb index e235daa..3611429 100644 Binary files a/1.5/Assemblies/1Fishery.pdb and b/1.5/Assemblies/1Fishery.pdb differ diff --git a/Source/FisheryLib/CollectionExtensions.cs b/Source/FisheryLib/CollectionExtensions.cs index f586d3f..4c8b7ed 100644 --- a/Source/FisheryLib/CollectionExtensions.cs +++ b/Source/FisheryLib/CollectionExtensions.cs @@ -636,24 +636,31 @@ public static class CollectionExtensions return destination; } - public static unsafe void Fill(this T[] array, T value) + public static void Fill(this T[] array, T value) { Guard.IsNotNull(array); + + var i = array.Length - 1; - if (array.Length == 0) + if (i < 0) return; - - fixed (T* startAddress = &array[0]) + + do { - var i = array.Length - 1; - do - { - startAddress[i] = value; - } - while (--i >= 0); + array.UnsafeStore(i, value); } + while (--i >= 0); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T UnsafeLoad(this T[] array, int index) => Array.UnsafeLoad(array, index); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void UnsafeStore(this T[] array, int index, T value) => Array.UnsafeStore(array, index, value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TTo UnsafeCast(this TFrom obj) => Array.UnsafeMov(obj); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void AddRangeFast(this List list, T[] range) { diff --git a/Source/FisheryLib/Collections/FishSet.cs b/Source/FisheryLib/Collections/FishSet.cs index e88216c..778b2f4 100644 --- a/Source/FisheryLib/Collections/FishSet.cs +++ b/Source/FisheryLib/Collections/FishSet.cs @@ -205,7 +205,11 @@ public class FishSet : ISet, IReadOnlyCollection, ICollection private void SetBucketEmpty(int index) { - _buckets[index] = _defaultValue; + var buckets = _buckets; + if (buckets.Length <= index) + ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported(); + + buckets.UnsafeStore(index, _defaultValue); _tails.SetEmpty(index); } @@ -303,10 +307,10 @@ public class FishSet : ISet, IReadOnlyCollection, ICollection return tailCount; } - private bool IsBucketEmpty(int bucketIndex) => IsBucketEmpty(bucketIndex, _tails, _buckets); + private bool IsBucketEmpty(int bucketIndex) => IsBucketEmpty(bucketIndex, _tails); [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool IsBucketEmpty(int bucketIndex, Tails tails, T?[] buckets) => tails.IsEmpty(bucketIndex); + private static bool IsBucketEmpty(int bucketIndex, Tails tails) => tails.IsEmpty(bucketIndex); private bool TryFindTailIndexAndReplace(T entry, ref int bucketIndex, ReplaceBehaviour replaceBehaviour) { @@ -389,12 +393,15 @@ public class FishSet : ISet, IReadOnlyCollection, ICollection var tailingEntries = new PooledIList>(); var tailingEntriesList = tailingEntries.List; var tailIndex = GetTailIndex(bucketIndex); + var buckets = _buckets; while (true) { - ref var tailingBucket = ref _buckets[tailIndex]; - tailingEntriesList.Add(tailingBucket!); - tailingBucket = _defaultValue; + if (tailIndex >= buckets.Length) + ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported(); + + tailingEntriesList.Add(buckets.UnsafeLoad(tailIndex)!); + buckets.UnsafeStore(tailIndex, _defaultValue); if (HasTail(tailIndex)) { @@ -454,7 +461,7 @@ public class FishSet : ISet, IReadOnlyCollection, ICollection for (var i = 0; i < oldBuckets.Length; i++) { - if (!IsBucketEmpty(i, oldTails, oldBuckets)) + if (!IsBucketEmpty(i, oldTails)) InsertEntry(oldBuckets[i]!, ReplaceBehaviour.Throw, true); } } @@ -802,6 +809,13 @@ public class FishSet : ISet, IReadOnlyCollection, ICollection internal static void ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen() => throw new InvalidOperationException("Enumeration has either not started or has already finished."); + [DoesNotReturn] + internal static void ThrowInvalidOperationException_ConcurrentOperationsNotSupported() + => throw new InvalidOperationException( + "Operations that change non-concurrent collections must have exclusive access. A concurrent " + + "update was performed on this collection and corrupted its state. The collection's state is no " + + "longer correct."); + [DoesNotReturn] internal static void ThrowWrongKeyTypeArgumentException(object? key) { diff --git a/Source/FisheryLib/Collections/FishTable.cs b/Source/FisheryLib/Collections/FishTable.cs index 01a08a9..12a8b40 100644 --- a/Source/FisheryLib/Collections/FishTable.cs +++ b/Source/FisheryLib/Collections/FishTable.cs @@ -312,7 +312,11 @@ public class FishTable : IDictionary, IDictionary, I private void SetBucketEmpty(int index) { - _buckets[index] = _defaultEntry; + var buckets = _buckets; + if (buckets.Length <= index) + ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported(); + + buckets.UnsafeStore(index, _defaultEntry); _tails.SetEmpty(index); } @@ -565,12 +569,15 @@ public class FishTable : IDictionary, IDictionary, I var tailingEntries = new PooledIList>(); var tailingEntriesList = tailingEntries.List; var tailIndex = GetTailIndex(bucketIndex); + var buckets = _buckets; while (true) { - ref var tailingBucket = ref _buckets[tailIndex]; - tailingEntriesList.Add(tailingBucket); - tailingBucket = _defaultEntry; + if (tailIndex >= buckets.Length) + ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported(); + + tailingEntriesList.Add(buckets.UnsafeLoad(tailIndex)); + buckets.UnsafeStore(tailIndex, _defaultEntry); if (HasTail(tailIndex)) { @@ -1759,6 +1766,13 @@ public class FishTable : IDictionary, IDictionary, I internal static void ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen() => throw new InvalidOperationException("Enumeration has either not started or has already finished."); + [DoesNotReturn] + internal static void ThrowInvalidOperationException_ConcurrentOperationsNotSupported() + => throw new InvalidOperationException( + "Operations that change non-concurrent collections must have exclusive access. A concurrent " + + "update was performed on this collection and corrupted its state. The collection's state is no " + + "longer correct."); + [DoesNotReturn] internal static void ThrowKeyNotFoundException(T key) { diff --git a/Source/FisheryLib/Fishery.cs b/Source/FisheryLib/Fishery.cs index 0d3b073..c9576b7 100644 --- a/Source/FisheryLib/Fishery.cs +++ b/Source/FisheryLib/Fishery.cs @@ -29,11 +29,11 @@ namespace FisheryLib; [PublicAPI] public static class FisheryLib { - public const decimal VERSION = 0.511M; + public const decimal VERSION = 0.512M; public static decimal CurrentlyLoadedVersion { get; } = VERSION; - public const string VERSION_STRING = "0.5.1.1"; + public const string VERSION_STRING = "0.5.1.2"; // public static string GetVersion() // {