Added 1.2 and 1.5 support, mod icon, DefaultValue/Key for FishSet/Table

This commit is contained in:
bbradson
2024-04-14 20:06:00 +02:00
parent c584f58dfd
commit 64152cef07
60 changed files with 643 additions and 251 deletions
+1 -1
View File
@@ -68,7 +68,7 @@ dotnet_naming_rule.public_or_internal_field_should_be_camel_case.style = camel_c
# Symbol specifications
dotnet_naming_symbols.constant.applicable_kinds = field, local
dotnet_naming_symbols.constant.applicable_accessibilities = *
dotnet_naming_symbols.constant.applicable_accessibilities = private, local
dotnet_naming_symbols.constant.required_modifiers = const
dotnet_naming_symbols.private_or_protected_field.applicable_kinds = field
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutDir>..\..\1.2\Assemblies\</OutDir>
<Copyright>Copyright (c) 2024 Bradson</Copyright>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DefineConstants>DEBUG;V1_2</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DefineConstants>V1_2</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.2.*" />
<PackageReference Include="Lib.Harmony" Version="2.2.*" ExcludeAssets="runtime" />
<PackageReference Include="JetBrains.Annotations" Version="2023.*" Aliases="nuget" ExcludeAssets="runtime" />
</ItemGroup>
</Project>
+17
View File
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutDir>..\..\1.3\Assemblies\</OutDir>
<Copyright>Copyright (c) 2022 Bradson</Copyright>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DefineConstants>DEBUG;V1_3</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DefineConstants>V1_3</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.3.*" />
<PackageReference Include="Lib.Harmony" Version="2.2.*" ExcludeAssets="runtime" />
</ItemGroup>
</Project>
+17
View File
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutDir>..\..\1.4\Assemblies\</OutDir>
<Copyright>Copyright (c) 2022 Bradson</Copyright>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DefineConstants>DEBUG;V1_4</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DefineConstants>V1_4</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.4.*" />
<PackageReference Include="Lib.Harmony" Version="2.2.*" ExcludeAssets="runtime" />
</ItemGroup>
</Project>
+17
View File
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutDir>..\..\1.5\Assemblies\</OutDir>
<Copyright>Copyright (c) 2024 Bradson</Copyright>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DefineConstants>DEBUG;V1_5</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DefineConstants>V1_5</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.5.*" />
<PackageReference Include="Lib.Harmony" Version="2.3.*" ExcludeAssets="runtime" />
</ItemGroup>
</Project>
-1
View File
@@ -5,7 +5,6 @@
global using static FisheryLib.Aliases;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib;
-1
View File
@@ -5,7 +5,6 @@
// https://github.com/CommunityToolkit/dotnet/blob/main/CommunityToolkit.HighPerformance/Extensions/BoolExtensions.cs
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib;
@@ -4,7 +4,6 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Diagnostics.CodeAnalysis;
using JetBrains.Annotations;
using Mono.Cecil;
using Mono.Collections.Generic;
+41 -41
View File
@@ -9,7 +9,6 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using FisheryLib.Collections;
using JetBrains.Annotations;
namespace FisheryLib;
@@ -77,8 +76,8 @@ public static class CollectionExtensions
return array;
}
public static Telement[] ToArray<Tenumerable, Telement>(this Tenumerable enumerable, Telement[] destination)
where Tenumerable : IList<Telement>
public static TElement[] ToArray<TEnumerable, TElement>(this TEnumerable enumerable, TElement[] destination)
where TEnumerable : IList<TElement>
{
Guard.IsNotNull(destination);
@@ -112,9 +111,9 @@ public static class CollectionExtensions
}
}
public static void Fill<Telement, Tcollection>(this Telement[] array, Tcollection collection, int startIndex = 0,
public static void Fill<TElement, TCollection>(this TElement[] array, TCollection collection, int startIndex = 0,
int count = -1)
where Tcollection : IList<Telement>
where TCollection : IList<TElement>
{
Guard.IsNotNull(collection);
@@ -269,13 +268,13 @@ public static class CollectionExtensions
return default;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TValue GetOrAddReference<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key)
where TValue : new()
{
var keyCode = HashCode.Get(key) & int.MaxValue;
StartOfLookup:
if (dictionary.buckets != null)
{
@@ -293,7 +292,7 @@ public static class CollectionExtensions
dictionary.Add(key, Reflection.New<TValue>());
goto StartOfLookup;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TValue GetOrAddReference<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, ref TKey key)
where TValue : new()
@@ -345,7 +344,7 @@ public static class CollectionExtensions
// dictionary.Add(key, Reflection.New<TValue>());
// goto StartOfLookup;
// }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TValue GetOrAdd<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key)
where TValue : new()
@@ -369,7 +368,7 @@ public static class CollectionExtensions
dictionary.Add(key, Reflection.New<TValue>());
goto StartOfLookup;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TValue GetOrAdd<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, ref TKey key)
where TValue : new()
@@ -393,7 +392,7 @@ public static class CollectionExtensions
dictionary.Add(key, Reflection.New<TValue>());
goto StartOfLookup;
}
public static bool TryGetOrAddValue<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key,
out TValue value)
{
@@ -431,10 +430,10 @@ public static class CollectionExtensions
var entries = dictionary.entries;
if (entries is null)
return 0;
var removedCount = 0;
var entriesCount = entries.Length;
for (var i = 0; i < entriesCount; i++)
{
ref var entry = ref entries[i];
@@ -442,8 +441,8 @@ public static class CollectionExtensions
continue;
// predicate might modify collection, turning a ref invalid
var keyValuePair = new KeyValuePair<TKey,TValue>(entry.key, entry.value);
var keyValuePair = new KeyValuePair<TKey, TValue>(entry.key, entry.value);
if (!predicate(keyValuePair))
continue;
@@ -468,7 +467,7 @@ public static class CollectionExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref int Version<T>(this List<T> list) => ref list._version;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AddRangeFast<T>(this List<T> list, List<T> range)
{
@@ -486,11 +485,11 @@ public static class CollectionExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void UnsafeCopyTo<T>(this List<T> source, List<T> destination, int destinationStartIndex)
=> UnsafeBlockCopy(ref source._items[0], ref destination._items[destinationStartIndex], source._size);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void UnsafeCopyTo<T>(this T[] source, T[] destination, int destinationStartIndex)
=> UnsafeBlockCopy(ref source[0], ref destination[destinationStartIndex], source.Length);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void UnsafeBlockCopy<T>(ref T source, ref T destination, int count)
=> Unsafe.CopyBlock(ref Unsafe.As<T, byte>(ref destination),
@@ -513,7 +512,7 @@ public static class CollectionExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<T> AsReadOnlySpan<T>(this List<T> list) => new(list._items, 0, list._size);
public static void ReplaceContentsWith<T>(this List<T> list, [AllowNull] List<T> collection)
{
if (ReferenceEquals(list, collection))
@@ -531,7 +530,7 @@ public static class CollectionExtensions
{
if (collectionCount < list._size)
list.RemoveRange(collectionCount, list._size - collectionCount);
collection.UnsafeCopyTo(list, 0);
}
else
@@ -544,11 +543,12 @@ public static class CollectionExtensions
list._version++;
}
public static void ReplaceContentsWith<T, V>(this List<T> list, [AllowNull] V collection) where V : ICollection<T>
public static void ReplaceContentsWith<TThis, TOther>(this List<TThis> list, [AllowNull] TOther collection)
where TOther : ICollection<TThis>
{
if (typeof(V) == typeof(List<T>))
list.ReplaceContentsWith(Unsafe.As<List<T>>(collection));
if (typeof(TOther) == typeof(List<TThis>))
list.ReplaceContentsWith(Unsafe.As<List<TThis>>(collection));
if (ReferenceEquals(list, collection))
return;
@@ -568,7 +568,7 @@ public static class CollectionExtensions
}
else
{
list._items = new T[collectionCount];
list._items = new TThis[collectionCount];
collection.CopyTo(list._items, 0);
list._size = collectionCount;
}
@@ -598,7 +598,7 @@ public static class CollectionExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Clear<T>(this T[] array) => Array.Clear(array, 0, array.Length);
// public static List<T> Copy<T>(this List<T> source)
// {
// Guard.IsNotNull(source);
@@ -618,30 +618,30 @@ public static class CollectionExtensions
//
// return destination;
// }
public static unsafe List<T> Copy<T>(this List<T> source)
{
Guard.IsNotNull(source);
var destination = new List<T>(source._size);
if (source._size == 0)
return destination;
fixed (void* destinationPointer = &destination._items[0], sourcePointer = &source._items[0])
Unsafe.CopyBlock(destinationPointer, sourcePointer, (uint)(source._size * sizeof(T)));
destination._size = source._size;
return destination;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AddRangeFast<T>(this List<T> list, T[] range)
{
Guard.IsNotNull(list);
Guard.IsNotNull(range);
if (range.Length < 1)
return;
@@ -651,13 +651,13 @@ public static class CollectionExtensions
list._size += range.Length;
list._version++;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Add(this List<char> list, string value)
{
Guard.IsNotNull(list);
Guard.IsNotNull(value);
if (value.Length < 1)
return;
@@ -702,7 +702,7 @@ public static class CollectionExtensions
=> GetListExtensionMethods(type).AddRange(list, collection);
public static Array TryToArray(this IList list, Type type) => GetListExtensionMethods(type).ToArray(list);
public static void RemoveAtFastUnordered<T>(this List<T> list, int index)
{
ref var lastBucket = ref list._items[list._size - 1];
@@ -710,33 +710,33 @@ public static class CollectionExtensions
lastBucket = default!;
list._size--;
}
public static bool RemoveFastUnordered<T>(this List<T> list, T item)
{
var index = list.IndexOf(item);
if (index < 0)
return false;
list.RemoveAtFastUnordered(index);
return true;
}
public static void InsertFastUnordered<T>(this List<T> list, int index, T item)
{
if ((uint) index > (uint) list._size)
if ((uint)index > (uint)list._size)
ThrowHelper.ThrowArgumentOutOfRangeException();
if (list._size == list._items.Length)
list.EnsureCapacity(list._size + 1);
ref var targetBucket = ref list._items[index];
list._items[list._size] = targetBucket;
targetBucket = item;
list._size++;
list._version++;
}
public static unsafe uint GetSizeEstimate<TKey, TValue>(this Dictionary<TKey, TValue> dictionary)
=> dictionary.GetType().ComputeManagedObjectSizeEstimate()
+ ((uint)sizeof(Dictionary<TKey, TValue>.Entry) * (uint)(dictionary.entries?.Length ?? 0))
+45 -4
View File
@@ -7,6 +7,9 @@
// which itself is based on
// https://probablydance.com/2018/05/28/a-new-fast-hash-table-in-response-to-googles-new-fast-hash-table/
#if V1_2
extern alias nuget;
#endif
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
@@ -14,7 +17,10 @@ using System.Runtime.InteropServices;
using FisheryLib.FunctionPointers;
using FisheryLib.Pools;
using FisheryLib.Utility;
using JetBrains.Annotations;
#if V1_2
using CollectionAccessType = nuget::JetBrains.Annotations.CollectionAccessType;
using CollectionAccessAttribute = nuget::JetBrains.Annotations.CollectionAccessAttribute;
#endif
namespace FisheryLib.Collections;
@@ -42,6 +48,8 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
private float _maxLoadFactor = 0.5f;
private T _defaultValue = Internal.KeyUtility<T>.Default;
public int Version
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -65,6 +73,22 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
public bool IsReadOnly => false;
/// <summary>
/// Empty FishSets have their entries set to a default value, causing fishSet.Contains(DefaultValue) to return
/// true. This property can be used for a check against this default, similar to a null. Unless set to a different
/// default through the FishSet constructor, for most reference types, the returned value is a static invalid
/// reference. Reference types implementing IEquatable or overriding Equals have null as default instead. Primitives
/// have all their bytes set to sbyte.MaxValue. Structs have their contents set according to previous rules.
/// </summary>
public T DefaultValue => _defaultValue;
public unsafe uint SizeEstimate
=> GetType().ComputeManagedObjectSizeEstimate()
+ ((uint)sizeof(T) * (uint)_buckets.Length)
+ ((_tails.Length + 1u) >> 1);
public uint CollisionCount => (uint)GetTailingEntriesCount();
public event Action<T>?
EntryAdded,
EntryRemoved;
@@ -112,6 +136,19 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
public FishSet(int minimumCapacity) => Initialize(minimumCapacity);
/// <summary>
/// Initialize a new FishSet
/// </summary>
/// <param name="minimumCapacity">The minimum capacity of the backing array</param>
/// <param name="defaultValue">
/// The default value for empty entries. Contains() returns true for this value when empty
/// </param>
public FishSet(int minimumCapacity, T defaultValue)
{
_defaultValue = defaultValue;
Initialize(minimumCapacity);
}
[MemberNotNull(nameof(_buckets))]
[MemberNotNull(nameof(_tails))]
private void Initialize(int minimumCapacity = 0)
@@ -120,6 +157,7 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
// Mathf.NextPowerOfTwo(minimumCapacity);
_buckets = new T?[minimumCapacity];
Array.Fill(_buckets, _defaultValue);
_tails = new((uint)minimumCapacity);
_bucketBitShift = 32 - FishMath.TrailingZeroCount((uint)_buckets.Length);
_wrapAroundMask = _buckets.Length - 1;
@@ -167,7 +205,7 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
private void SetBucketEmpty(int index)
{
_buckets[index] = default;
_buckets[index] = _defaultValue;
_tails.SetEmpty(index);
}
@@ -356,7 +394,7 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
{
ref var tailingBucket = ref _buckets[tailIndex];
tailingEntriesList.Add(tailingBucket!);
tailingBucket = default;
tailingBucket = _defaultValue;
if (HasTail(tailIndex))
{
@@ -409,6 +447,7 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
_tails = new((uint)_buckets.Length << 1);
_buckets = new T?[_buckets.Length << 1];
Array.Fill(_buckets, _defaultValue);
_bucketBitShift = 32 - FishMath.TrailingZeroCount((uint)_buckets.Length);
_wrapAroundMask = _buckets.Length - 1;
@@ -579,7 +618,7 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
if (_buckets is null)
Initialize();
Array.Clear(_buckets, 0, _buckets.Length);
Array.Fill(_buckets, Internal.KeyUtility<T>.Default);
_tails.Reset();
_count = 0;
@@ -660,6 +699,8 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
private NibbleArray _values = new(length);
public uint Length => _values.Length;
public uint this[uint index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+85 -10
View File
@@ -7,15 +7,22 @@
// which itself is based on
// https://probablydance.com/2018/05/28/a-new-fast-hash-table-in-response-to-googles-new-fast-hash-table/
#if V1_2
extern alias nuget;
#endif
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using FisheryLib.Collections.Internal;
using FisheryLib.FunctionPointers;
using FisheryLib.Pools;
using FisheryLib.Utility;
using JetBrains.Annotations;
#if V1_2
using CollectionAccessType = nuget::JetBrains.Annotations.CollectionAccessType;
using CollectionAccessAttribute = nuget::JetBrains.Annotations.CollectionAccessAttribute;
#endif
namespace FisheryLib.Collections;
@@ -45,6 +52,8 @@ public class FishTable<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, I
_version;
private float _maxLoadFactor = 0.5f;
private Entry _defaultEntry = Entry.Default;
public KeyCollection Keys => new(this);
@@ -65,6 +74,15 @@ public class FishTable<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, I
public bool IsReadOnly => false;
/// <summary>
/// Empty FishTables have their entries set to a default value, causing fishTable.ContainsKey(DefaultKey) to return
/// true. This property can be used for a check against this default, similar to a null. Unless set to a different
/// default through the FishTable constructor, for most reference types, the returned value is a static invalid
/// reference. Reference types implementing IEquatable or overriding Equals have null as default instead. Primitives
/// have all their bytes set to sbyte.MaxValue. Structs have their contents set according to previous rules.
/// </summary>
public TKey DefaultKey => _defaultEntry.Key;
public unsafe uint SizeEstimate
=> GetType().ComputeManagedObjectSizeEstimate()
+ ((uint)sizeof(Entry) * (uint)_buckets.Length)
@@ -227,6 +245,19 @@ public class FishTable<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, I
public FishTable(int minimumCapacity) => Initialize(minimumCapacity);
/// <summary>
/// Initialize a new FishTable
/// </summary>
/// <param name="minimumCapacity">The minimum capacity of the backing array</param>
/// <param name="defaultKey">
/// The default key for empty entries. ContainsKey() returns true for this when empty
/// </param>
public FishTable(int minimumCapacity, TKey defaultKey)
{
_defaultEntry.Key = defaultKey;
Initialize(minimumCapacity);
}
[MemberNotNull(nameof(_buckets))]
[MemberNotNull(nameof(_tails))]
private void Initialize(int minimumCapacity = 0)
@@ -235,6 +266,7 @@ public class FishTable<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, I
// Mathf.NextPowerOfTwo(minimumCapacity);
_buckets = new Entry[minimumCapacity];
Array.Fill(_buckets, _defaultEntry);
_tails = new((uint)minimumCapacity);
_bucketBitShift = 32 - FishMath.TrailingZeroCount((uint)_buckets.Length);
_wrapAroundMask = _buckets.Length - 1;
@@ -280,7 +312,7 @@ public class FishTable<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, I
private void SetBucketEmpty(int index)
{
_buckets[index] = default;
_buckets[index] = _defaultEntry;
_tails.SetEmpty(index);
}
@@ -400,8 +432,11 @@ public class FishTable<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, I
{
ref var bucket = ref buckets[bucketIndex];
if (!_keyEqualityComparer(bucket.Key, default))
if (!CompareBytes(bucket.Key, _defaultEntry.Key)
&& !_keyEqualityComparer(bucket.Key, _defaultEntry.Key))
{
ThrowHelper.ThrowEmptyBucketKeyInvalidOperationException(bucketIndex, bucket);
}
if (bucket.Value.Equals<TValue>(default))
return true;
@@ -412,6 +447,43 @@ public class FishTable<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, I
return false; // no good workaround without performance hit
}
private static unsafe bool CompareBytes(TKey a, TKey b)
{
var size = sizeof(TKey);
var iOffset = 0;
var i = 0;
do
{
var currentA = *((nint*)&a + iOffset);
var currentB = *((nint*)&b + iOffset);
if (size - i >= sizeof(nint))
{
if (currentA != currentB)
return false;
iOffset++;
i += sizeof(nint);
}
else
{
var remainingSize = size & sizeof(nint);
i = 0;
do
{
if ((byte)(currentA >> i) != (byte)(currentB >> i))
return false;
}
while (++i < remainingSize);
break;
}
}
while (i < size);
return size != 0;
}
private bool TryFindTailIndexAndReplace(in Entry entry, ref int bucketIndex, ReplaceBehaviour replaceBehaviour)
{
while (true)
@@ -498,7 +570,7 @@ public class FishTable<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, I
{
ref var tailingBucket = ref _buckets[tailIndex];
tailingEntriesList.Add(tailingBucket);
tailingBucket = default;
tailingBucket = _defaultEntry;
if (HasTail(tailIndex))
{
@@ -585,6 +657,7 @@ public class FishTable<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, I
_tails = new((uint)newSize);
_buckets = new Entry[newSize];
Array.Fill(_buckets, _defaultEntry);
_bucketBitShift = 32 - FishMath.TrailingZeroCount((uint)newSize);
_wrapAroundMask = _buckets.Length - 1;
@@ -1046,7 +1119,7 @@ public class FishTable<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, I
private void EmplaceWithTailsBackward(int entryIndex)
{
if (IsBucketEmpty(entryIndex))
Utility.Diagnostics.ThrowHelper.ThrowInvalidOperationException("Tried to emplace entry bucket");
Utility.Diagnostics.ThrowHelper.ThrowInvalidOperationException("Tried to emplace empty bucket");
while (true)
{
@@ -1225,7 +1298,7 @@ public class FishTable<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, I
if (_buckets is null)
Initialize();
Array.Clear(_buckets, 0, _buckets.Length);
Array.Fill(_buckets, _defaultEntry);
_tails.Reset();
_count = 0;
@@ -1360,6 +1433,8 @@ public class FishTable<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, I
.Append<TValue>(Value)
.Append(" }").ToString();
}
public static readonly Entry Default = new(KeyUtility<TKey>.Default, default);
}
internal struct Tails(uint length)
@@ -1688,11 +1763,11 @@ public class FishTable<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, I
internal static void ThrowKeyNotFoundException<T>(T key)
{
Guard.IsNotNull(key);
ThrowKeyNotFoundException((object)key);
ThrowKeyNotFoundException((object?)key);
}
[DoesNotReturn]
private static void ThrowKeyNotFoundException(object key)
private static void ThrowKeyNotFoundException(object? key)
=> throw new KeyNotFoundException($"The given key '{key}' was not present in the dictionary.");
[DoesNotReturn]
@@ -1715,7 +1790,7 @@ public class FishTable<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, I
internal static bool ThrowAddingDuplicateWithKeyArgumentException<T>(T key)
{
Guard.IsNotNull(key);
ThrowAddingDuplicateWithKeyArgumentException((object)key);
ThrowAddingDuplicateWithKeyArgumentException((object?)key);
return true;
}
@@ -1782,7 +1857,7 @@ public class FishTable<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, I
}
[DoesNotReturn]
private static void ThrowAddingDuplicateWithKeyArgumentException(object key)
private static void ThrowAddingDuplicateWithKeyArgumentException(object? key)
=> throw new ArgumentException($"An item with the same key has already been added. Key: {key}");
}
}
@@ -6,7 +6,6 @@
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib.Collections;
@@ -5,7 +5,6 @@
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using JetBrains.Annotations;
namespace FisheryLib.Collections;
@@ -0,0 +1,165 @@
// Copyright (c) 2022 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v.2.0.If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
namespace FisheryLib.Collections.Internal;
public static class KeyUtility<T>
{
public static readonly T Default = GetDefaultValue();
private static T GetDefaultValue()
{
var type = typeof(T);
var invalidKey = KeyUtility.GetDefaultValue(type);
if (!type.IsValueType)
return Unsafe.As<object?, T>(ref invalidKey);
return (T)invalidKey!;
}
}
public static class KeyUtility
{
public static readonly object InvalidObject = new();
public const ulong InvalidValue = 0x7F7F_7F7F_7F7F_7F7FUL;
public static readonly string InvalidString
= new(['\u7F7F', '\u7F7F', '\u7F7F', '\u7F7F', '\u7F7F', '\u7F7F', '\u7F7F', '\u7F7F']);
private static readonly ConcurrentDictionary<FieldInfo, Action<object>> _fieldSetters = [];
private static readonly ConcurrentDictionary<Type, object> _defaultValues = [];
private static readonly MethodInfo _genericMaxValueMethodDefinition
= methodof(GetMaxValue<ulong>).GetGenericMethodDefinition();
private static readonly FieldInfo _invalidFieldInfo = typeof(KeyUtility).GetField(nameof(InvalidObject));
public static object? GetDefaultValue(Type type)
{
if (type == typeof(string))
return InvalidString;
if (_defaultValues.TryGetValue(type, out var result))
return result;
if (!type.IsValueType)
{
return !type.IsAssignableTo(typeof(IEquatable<>), type)
&& type.GetMethod(nameof(object.Equals),
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, [typeof(object)],
Array.Empty<ParameterModifier>())?.DeclaringType
== typeof(object)
? InvalidObject
: null;
}
result = FormatterServices.GetUninitializedObject(type);
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
for (var i = 0; i < fields.Length; i++)
{
var field = fields[i];
var fieldType = field.FieldType;
if (fieldType.IsValueType)
{
if (AccessTools.IsValue(fieldType))
field.SetValue(result, GetMaxValue(fieldType));
else
GetFieldInvalidator(field)(result);
}
else
{
if (fieldType != typeof(string))
{
if (!fieldType.IsAssignableTo(typeof(IEquatable<>), fieldType))
GetFieldInvalidator(field)(result);
else
field.SetValue(result, null);
}
else
{
field.SetValue(result, InvalidString);
}
}
if (Unsafe.As<object, ulong>(ref result) > 0xFFFF_FFFF_FFFF)
Log.Error($"Detected invalid pointer with a value > 48 bit\n{new StackTrace(true)}");
}
_defaultValues.TryAdd(type, result);
return result;
}
private static object GetMaxValue(Type type)
{
if (!AccessTools.IsValue(type))
{
return ThrowHelper.ThrowArgumentException<object>($"Invalid type passed to KeyUtility.GetMaxValue: '{
type.FullDescription()}'. Only primitive structs are supported.");
}
var result = _genericMaxValueMethodDefinition.MakeGenericMethod(type).Invoke(null, null);
_defaultValues.TryAdd(type, result);
return result;
}
private static object GetMaxValue<T>() where T : struct
{
var maxValue = InvalidValue;
return Unsafe.As<ulong, T>(ref maxValue)!;
}
private static Action<object> GetFieldInvalidator(FieldInfo fieldInfo)
{
if (_fieldSetters.TryGetValue(fieldInfo, out var result))
return result;
var method = new DynamicMethod($"FisheryLib><::>UnsafeSetter<::><{fieldInfo.FullDescription()}", typeof(void),
[typeof(object)], typeof(KeyUtility), true);
var ilGenerator = method.GetILGenerator();
var fieldType = fieldInfo.FieldType;
var declaringType = fieldInfo.DeclaringType;
ilGenerator.Emit(FishTranspiler.Argument(0));
if (declaringType?.IsValueType ?? false)
ilGenerator.Emit(FishTranspiler.UnboxAddress(declaringType));
if (!fieldType.IsValueType)
{
ilGenerator.Emit(FishTranspiler.Field(_invalidFieldInfo));
ilGenerator.Emit(FishTranspiler.Call(methodof(Unsafe.As<object>).GetGenericMethodDefinition()
.MakeGenericMethod(fieldType)));
}
else
{
ilGenerator.Emit(FishTranspiler.Token(fieldType));
ilGenerator.Emit(FishTranspiler.Call(Type.GetTypeFromHandle));
ilGenerator.Emit(FishTranspiler.Call(AccessTools.IsValue(fieldType)
// ReSharper disable once RedundantCast
? (Func<Type, object?>)GetMaxValue
: GetDefaultValue));
ilGenerator.Emit(FishTranspiler.UnboxValue(fieldType));
}
ilGenerator.Emit(FishTranspiler.StoreField(fieldInfo));
ilGenerator.Emit(FishTranspiler.Return);
result = (Action<object>)method.CreateDelegate(typeof(Action<object>));
_fieldSetters.TryAdd(fieldInfo, result);
return result;
}
}
@@ -4,7 +4,6 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib.Collections;
@@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
#pragma warning disable CS8777
+2 -1
View File
@@ -4,8 +4,9 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;
#if V1_2
using JetBrains.Annotations;
using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute;
#endif
namespace FisheryLib.Utility.Diagnostics;
+1 -1
View File
@@ -7,7 +7,7 @@ using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using JetBrains.Annotations;
// ReSharper disable InconsistentNaming
namespace FisheryLib.Utility.Diagnostics;
+93
View File
@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<RootNamespace>FisheryLib</RootNamespace>
<AssemblyName>1Fishery</AssemblyName>
<Authors>bradson</Authors>
<TargetFramework>net48</TargetFramework>
<LangVersion>preview</LangVersion>
<PlatformTarget>AnyCPU</PlatformTarget>
<Optimize>true</Optimize>
<DebugSymbols>true</DebugSymbols>
<Nullable>enable</Nullable>
<EnableNETAnalyzers>True</EnableNETAnalyzers>
<AnalysisLevel>preview-recommended</AnalysisLevel>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>5</WarningLevel>
<DebugType>portable</DebugType>
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
<GenerateAssemblyInformationalVersionAttribute>false</GenerateAssemblyInformationalVersionAttribute>
<NoWarn>CA1000;CA1051;CA1305;CA1310;CA1707;CA1708;CA1710;CA1711;CA1716;CA1725;CA1852;CA2201;CS8500</NoWarn>
<BaseIntermediateOutputPath>obj\$(MSBuildProjectName)\$(Configuration)\</BaseIntermediateOutputPath>
<MSBuildProjectExtensionsPath>obj\$(MSBuildProjectName)\$(Configuration)\</MSBuildProjectExtensionsPath>
<DefaultItemExcludes>$(DefaultItemExcludes);obj/**/*.*</DefaultItemExcludes>
<PublicizerClearCacheOnClean>true</PublicizerClearCacheOnClean>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Zetrith.Prepatcher" Version="1.*" />
<PackageReference Include="PolySharp" Version="1.*" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.*" />
<PackageReference Include="Krafs.Publicizer" Version="2.*" />
<Publicize Include="Assembly-CSharp;0Harmony" IncludeVirtualMembers="false" />
<!-- DoNotPublicize Include="0Harmony:Mono.Collections.Generic.Collection`1" / -->
<DoNotPublicize Include="0Harmony:System.Memory`1" />
<DoNotPublicize Include="0Harmony:System.ReadOnlyMemory`1" />
<DoNotPublicize Include="0Harmony:System.Span`1" />
<DoNotPublicize Include="0Harmony:System.ReadOnlySpan`1" />
<DoNotPublicize Include="0Harmony:System.ThrowHelper" />
<DoNotPublicize Include="0Harmony:System.HashCode" />
<DoNotPublicize Include="0Harmony:System.StringExtensions" />
<DoNotPublicize Include="0Harmony:System.MemoryExtensions" />
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.CallerArgumentExpressionAttribute" />
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.DefaultInterpolatedStringHandler" />
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.IgnoresAccessChecksToAttribute" />
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute" />
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute" />
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.IsByRefLikeAttribute" />
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.IsExternalInit" />
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.IsReadOnlyAttribute" />
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.NativeIntegerAttribute" />
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.NullableAttribute" />
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.NullableContextAttribute" />
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.NullablePublicOnlyAttribute" />
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.RefSafetyRulesAttribute" />
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.ModuleInitializerAttribute" />
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.ITuple" />
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.Unsafe" />
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.AllowNullAttribute" />
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.DisallowNullAttribute" />
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute" />
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute" />
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute" />
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes" />
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.ExtraDynamicallyAccessedMemberTypes" />
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.MaybeNullAttribute" />
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute" />
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.MemberNotNullAttribute" />
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.MemberNotNullWhenAttribute" />
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.NotNullAttribute" />
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute" />
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute" />
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.UnscopedRefAttribute" />
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.buckets" />
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.entries" />
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.version" />
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2+Entry" />
<Publicize Include="mscorlib:System.Collections.Generic.List`1._items" />
<Publicize Include="mscorlib:System.Collections.Generic.List`1._version" />
<Publicize Include="mscorlib:System.Collections.Generic.List`1._size" />
<Publicize Include="mscorlib:System.Collections.Generic.List`1.EnsureCapacity" />
<Publicize Include="System.Core:System.Collections.Generic.HashSet`1._version" />
<Publicize Include="mscorlib:System.Runtime.CompilerServices.JitHelpers.UnsafeEnumCast" />
<Publicize Include="mscorlib:System.Runtime.CompilerServices.JitHelpers.UnsafeEnumCastLong" />
<Publicize Include="mscorlib:System.Runtime.CompilerServices.JitHelpers.UnsafeCast" />
<Publicize Include="mscorlib:System.Reflection.Emit.Label.label" />
<Publicize Include="mscorlib:System.String.m_firstChar" />
<None Include="..\..\LICENSE">
<Pack>True</Pack>
<PackagePath />
</None>
</ItemGroup>
</Project>
+97 -74
View File
@@ -9,8 +9,8 @@ using System.Linq;
using System.Linq.Expressions;
using System.Reflection.Emit;
using System.Security;
using JetBrains.Annotations;
using Verse;
// ReSharper disable PossibleMultipleEnumeration
namespace FisheryLib;
@@ -163,13 +163,19 @@ public static class FishTranspiler
: null;
public Type? GetTypeIn(MethodBase method)
=> OpCode.LoadsArgument() ? method.GetParameters()[GetIndex()].ParameterType
=> OpCode.LoadsArgument()
? method.GetParameters()[GetIndex()].ParameterType
: OpCode.LoadsLocalVariable() || OpCode.StoresLocalVariable()
? method.GetLocalVariables()[GetIndex()].LocalType
: OpCode.LoadsField() || OpCode.StoresField() ? (Operand as FieldInfo)?.FieldType
: OpCode == OpCodes.Call || OpCode == OpCodes.Callvirt ? (Operand as MethodInfo)?.ReturnType
: OpCode.LoadsConstant() ? Operand?.GetType()
: null;
: OpCode.LoadsField() || OpCode.StoresField()
? (Operand as FieldInfo)?.FieldType
: OpCode == OpCodes.Call || OpCode == OpCodes.Callvirt
? (Operand as MethodInfo)?.ReturnType
: OpCode.LoadsConstant()
? Operand?.GetType()
: null;
private bool Calls() => (OpCode == OpCodes.Call) | (OpCode == OpCodes.Callvirt);
public override bool Equals(object? obj)
=> obj is CodeInstruction code
@@ -186,13 +192,13 @@ public static class FishTranspiler
: unchecked((((1009 * 9176) + OpCode.GetHashCode()) * 9176) + Operand.GetHashCode());
public static bool operator ==(Container lhs, Container rhs)
=> lhs.OpCode == rhs.OpCode
&& CompareOperands(lhs.Operand, rhs.Operand);
=> OpCodeEquals(lhs.OpCode, rhs.OpCode) && CompareOperands(lhs.Operand, rhs.Operand);
public static bool operator !=(Container lhs, Container rhs) => !(lhs == rhs);
public static bool operator ==(Container helper, CodeInstruction code)
=> helper.OpCode == code.opcode
public static bool operator ==(Container helper, CodeInstruction? code)
=> code != null
&& OpCodeEquals(helper.OpCode, code.opcode)
&& CompareOperands(helper.Operand, code.operand);
public static bool operator !=(Container helper, CodeInstruction code) => !(helper == code);
@@ -201,6 +207,11 @@ public static class FishTranspiler
public static bool operator !=(CodeInstruction code, Container helper) => !(helper == code);
private static bool OpCodeEquals(in OpCode lhs, in OpCode rhs)
=> lhs == rhs || (IsCallOpcode(lhs) && IsCallOpcode(rhs));
private static bool IsCallOpcode(in OpCode opCode) => opCode == OpCodes.Call || opCode == OpCodes.Callvirt;
[SuppressMessage("Usage", "CA2225")]
public static implicit operator CodeInstruction(Container helper) => helper.ToInstruction();
@@ -506,7 +517,7 @@ public static class FishTranspiler
? locals.First()
: ThrowHelper.ThrowInvalidOperationException<Container>(
$"No local variable found for predicate {predicate.Method.FullDescription()} and method {
method.FullDescription()}");
method.FullDescription()}");
}
/// <summary>
@@ -782,7 +793,7 @@ public static class FishTranspiler
=> ThrowHelper.ThrowArgumentException<Container>(
$"Tried using Enum {e.GetType().FullDescription()} with underlying Type {
Enum.GetUnderlyingType(e.GetType()).FullDescription()} (TypeCode: {
typeCode}) for FishTranspiler.Constant.");
typeCode}) for FishTranspiler.Constant.");
/// <summary>
/// Calls the method indicated by the passed method group.
@@ -828,8 +839,7 @@ public static class FishTranspiler
Guard.IsNotNullOrEmpty(type);
Guard.IsNotNullOrEmpty(name);
return Call(
Type.GetType($"{type}, {assembly}")
return Call(Type.GetType($"{type}, {assembly}")
?? ThrowHelper.ThrowArgumentException<Type>($"No type named {type} found in assembly {assembly}"), name,
parameters, generics, forceNoCallvirt);
}
@@ -851,11 +861,10 @@ public static class FishTranspiler
Guard.IsNotNull(type);
Guard.IsNotNullOrEmpty(name);
return Call(
AccessTools.Method(type, name, parameters, generics)
return Call(AccessTools.Method(type, name, parameters, generics)
?? ThrowHelper.ThrowArgumentException<MethodInfo>($"No method found with type {type}, name {
name}{(parameters != null ? $", parameters: {parameters.ToStringSafeEnumerable()}" : "")}{
(generics != null ? $", generics: {generics.ToStringSafeEnumerable()}" : "")}"),
(generics != null ? $", generics: {generics.ToStringSafeEnumerable()}" : "")}"),
forceNoCallvirt);
}
@@ -869,10 +878,9 @@ public static class FishTranspiler
{
Guard.IsNotNull(method);
return new(
method.IsStatic || (method.DeclaringType?.IsValueType ?? false) || forceNoCallvirt
? OpCodes.Call
: OpCodes.Callvirt, method);
return new(method.IsStatic || (method.DeclaringType?.IsValueType ?? false) || forceNoCallvirt
? OpCodes.Call
: OpCodes.Callvirt, method);
}
/// <summary>
@@ -1488,7 +1496,6 @@ public static class FishTranspiler
: ThrowHelper.ThrowArgumentException<Container>(
$"Type ({type}) for FishTranspiler.Box must be a ValueType.", nameof(type));
}
/// <summary>
/// Converts the boxed representation of a value type to its unboxed form, pushing an address to the box object's
@@ -1983,9 +1990,9 @@ public static class FishTranspiler
return new(!type.IsPrimitive || type == typeof(char)
? ThrowHelper.ThrowArgumentException<OpCode>("Convert expects a non-character primitive type", nameof(type))
: ConvertOpCodesByType.TryGetValue(type, out var value) ? value
: ThrowHelper.ThrowArgumentException<OpCode>(
$"Type {type.FullDescription()} cannot be converted to"));
: ConvertOpCodesByType.TryGetValue(type, out var value)
? value
: ThrowHelper.ThrowArgumentException<OpCode>($"Type {type.FullDescription()} cannot be converted to"));
}
public static readonly Dictionary<Type, OpCode> ConvertOpCodesByType = new()
@@ -2034,12 +2041,13 @@ public static class FishTranspiler
: type == typeof(float)
? ThrowHelper.ThrowInvalidOperationException<OpCode>(
"There is no operation for converting to a float with overflow checking")
: type == typeof(double)
? ThrowHelper.ThrowInvalidOperationException<OpCode>(
"There is no operation for converting to a double with overflow checking")
: ConvertWithOverflowCheckOpCodesByType.TryGetValue(type, out var value) ? value
: ThrowHelper.ThrowArgumentException<OpCode>(
$"Type {type.FullDescription()} cannot be converted to"));
: type == typeof(double)
? ThrowHelper.ThrowInvalidOperationException<OpCode>(
"There is no operation for converting to a double with overflow checking")
: ConvertWithOverflowCheckOpCodesByType.TryGetValue(type, out var value)
? value
: ThrowHelper.ThrowArgumentException<OpCode>(
$"Type {type.FullDescription()} cannot be converted to"));
}
public static readonly Dictionary<Type, OpCode> ConvertWithOverflowCheckOpCodesByType = new()
@@ -2081,12 +2089,13 @@ public static class FishTranspiler
: type == typeof(float)
? ThrowHelper.ThrowInvalidOperationException<OpCode>(
"There is no operation for converting to a float with overflow checking")
: type == typeof(double)
? ThrowHelper.ThrowInvalidOperationException<OpCode>(
"There is no operation for converting to a double with overflow checking")
: ConvertUnsignedWithOverflowCheckOpCodesByType.TryGetValue(type, out var value) ? value
: ThrowHelper.ThrowArgumentException<OpCode>(
$"Type {type.FullDescription()} cannot be converted to"));
: type == typeof(double)
? ThrowHelper.ThrowInvalidOperationException<OpCode>(
"There is no operation for converting to a double with overflow checking")
: ConvertUnsignedWithOverflowCheckOpCodesByType.TryGetValue(type, out var value)
? value
: ThrowHelper.ThrowArgumentException<OpCode>(
$"Type {type.FullDescription()} cannot be converted to"));
}
public static readonly Dictionary<Type, OpCode> ConvertUnsignedWithOverflowCheckOpCodesByType = new()
@@ -2191,8 +2200,8 @@ public static class FishTranspiler
}
}
public static CodeInstructions MethodReplacer<T, V>(this CodeInstructions instructions, T from, V to,
bool throwOnFailure = true) where T : Delegate where V : Delegate
public static CodeInstructions MethodReplacer<TFrom, TTo>(this CodeInstructions instructions, TFrom from, TTo to,
bool throwOnFailure = true) where TFrom : Delegate where TTo : Delegate
{
Guard.IsNotNull(instructions);
Guard.IsNotNull(from);
@@ -2333,10 +2342,9 @@ public static class FishTranspiler
if (throwOnFailure && !success)
{
ThrowHelper.ThrowInvalidOperationException(
$"FishTranspiler.Replace couldn't find target instruction for {
predicate.Method.FullDescription()} of {UtilityF.GetCallingAssembly().GetName().Name}, Version {
UtilityF.GetCallingAssembly().GetName().Version}");
ThrowHelper.ThrowInvalidOperationException($"FishTranspiler.Replace couldn't find target instruction for {
predicate.Method.FullDescription()} of {UtilityF.GetCallingAssembly().GetName().Name}, Version {
UtilityF.GetCallingAssembly().GetName().Version}");
}
}
@@ -2377,9 +2385,8 @@ public static class FishTranspiler
if (throwOnFailure && !success)
{
ThrowHelper.ThrowInvalidOperationException(
$"FishTranspiler.ReplaceAt couldn't find target instruction for {
position.Method.FullDescription()} of {UtilityF.GetCallingAssembly().GetName().Name}, Version {
ThrowHelper.ThrowInvalidOperationException($"FishTranspiler.ReplaceAt couldn't find target instruction for {
position.Method.FullDescription()} of {UtilityF.GetCallingAssembly().GetName().Name}, Version {
UtilityF.GetCallingAssembly().GetName().Version}");
}
@@ -2406,8 +2413,13 @@ public static class FishTranspiler
return instruction;
}
public static bool LoadsLocalVariable(this OpCode opcode)
=> CodeInstructionExtensions.loadVarCodes.Contains(opcode);
public static bool LoadsLocalVariable(this OpCode opcode) => LoadLocalVariableOpCodes.Contains(opcode);
public static readonly HashSet<OpCode> LoadLocalVariableOpCodes =
[
OpCodes.Ldloca_S, OpCodes.Ldloca, OpCodes.Ldloc_0, OpCodes.Ldloc_1, OpCodes.Ldloc_2, OpCodes.Ldloc_3,
OpCodes.Ldloc_S, OpCodes.Ldloc
];
public static OpCode ToLoadLocalVariable(this OpCode opcode)
=> opcode.LoadsLocalVariable() && opcode != OpCodes.Ldarga && opcode != OpCodes.Ldarga_S ? opcode
@@ -2416,8 +2428,12 @@ public static class FishTranspiler
: opcode == OpCodes.Stloc ? OpCodes.Ldloc
: ThrowHelper.ThrowInvalidOperationException<OpCode>($"{opcode} cannot be cast to Ldloc.");
public static bool StoresLocalVariable(this OpCode opcode)
=> CodeInstructionExtensions.storeVarCodes.Contains(opcode);
public static bool StoresLocalVariable(this OpCode opcode) => StoreLocalVariableOpCodes.Contains(opcode);
public static readonly HashSet<OpCode> StoreLocalVariableOpCodes =
[
OpCodes.Stloc_0, OpCodes.Stloc_1, OpCodes.Stloc_2, OpCodes.Stloc_3, OpCodes.Stloc_S, OpCodes.Stloc
];
public static OpCode ToStoreLocalVariable(this OpCode opcode)
=> opcode.StoresLocalVariable() ? opcode
@@ -2498,9 +2514,16 @@ public static class FishTranspiler
public static bool LoadsString(this OpCode opcode) => opcode == OpCodes.Ldstr;
public static bool Branches(this OpCode opcode)
=> CodeInstructionExtensions.branchCodes.Contains(opcode)
|| opcode == OpCodes.Switch;
public static bool Branches(this OpCode opcode) => BranchingOpCodes.Contains(opcode);
public static readonly HashSet<OpCode> BranchingOpCodes =
[
OpCodes.Br_S, OpCodes.Brfalse_S, OpCodes.Brtrue_S, OpCodes.Beq_S, OpCodes.Bge_S, OpCodes.Bgt_S,
OpCodes.Ble_S, OpCodes.Blt_S, OpCodes.Bne_Un_S, OpCodes.Bge_Un_S, OpCodes.Bgt_Un_S, OpCodes.Ble_Un_S,
OpCodes.Blt_Un_S, OpCodes.Br, OpCodes.Brfalse, OpCodes.Brtrue, OpCodes.Beq, OpCodes.Bge, OpCodes.Bgt,
OpCodes.Ble, OpCodes.Blt, OpCodes.Bne_Un, OpCodes.Bge_Un, OpCodes.Bgt_Un, OpCodes.Ble_Un, OpCodes.Blt_Un,
OpCodes.Switch
];
public static bool Compares(this OpCode opcode) => CompareOpCodes.Contains(opcode);
@@ -2647,26 +2670,26 @@ public static class FishTranspiler
{
{ OpCodes.Ldarg_0, 0 },
{ OpCodes.Ldarg_1, 1 },
{ OpCodes.Ldarg_2, 2 },
{ OpCodes.Ldarg_3, 3 },
{ OpCodes.Ldloc_0, 0 },
{ OpCodes.Ldloc_1, 1 },
{ OpCodes.Ldloc_2, 2 },
{ OpCodes.Ldloc_3, 3 },
{ OpCodes.Stloc_0, 0 },
{ OpCodes.Stloc_1, 1 },
{ OpCodes.Stloc_2, 2 },
{ OpCodes.Stloc_3, 3 },
{ OpCodes.Ldc_I4_0, 0 },
{ OpCodes.Ldc_I4_1, 1 },
{ OpCodes.Ldc_I4_2, 2 },
{ OpCodes.Ldc_I4_3, 3 },
{ OpCodes.Ldc_I4_4, 4 },
{ OpCodes.Ldc_I4_5, 5 },
{ OpCodes.Ldc_I4_6, 6 },
{ OpCodes.Ldc_I4_7, 7 },
{ OpCodes.Ldc_I4_8, 8 },
{ OpCodes.Ldc_I4_M1, -1 }
{ OpCodes.Ldarg_2, 2 },
{ OpCodes.Ldarg_3, 3 },
{ OpCodes.Ldloc_0, 0 },
{ OpCodes.Ldloc_1, 1 },
{ OpCodes.Ldloc_2, 2 },
{ OpCodes.Ldloc_3, 3 },
{ OpCodes.Stloc_0, 0 },
{ OpCodes.Stloc_1, 1 },
{ OpCodes.Stloc_2, 2 },
{ OpCodes.Stloc_3, 3 },
{ OpCodes.Ldc_I4_0, 0 },
{ OpCodes.Ldc_I4_1, 1 },
{ OpCodes.Ldc_I4_2, 2 },
{ OpCodes.Ldc_I4_3, 3 },
{ OpCodes.Ldc_I4_4, 4 },
{ OpCodes.Ldc_I4_5, 5 },
{ OpCodes.Ldc_I4_6, 6 },
{ OpCodes.Ldc_I4_7, 7 },
{ OpCodes.Ldc_I4_8, 8 },
{ OpCodes.Ldc_I4_M1, -1 }
};
public static object? GetOperandFromIndex(int index) => index > 3 ? index : null;
@@ -2686,7 +2709,7 @@ public static class FishTranspiler
var methodBody = method.GetMethodBody();
if (methodBody is null)
ThrowHelper.ThrowArgumentException($"Method {method.FullDescription()} has no body.");
return methodBody.LocalVariables;
}
+4 -3
View File
@@ -10,11 +10,12 @@ global using System.Collections.Generic;
global using System.Reflection;
global using HarmonyLib;
global using FisheryLib.Utility.Diagnostics;
global using JetBrains.Annotations;
global using CodeInstructions = System.Collections.Generic.IEnumerable<HarmonyLib.CodeInstruction>;
global using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute;
using System.Security;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
[assembly: AllowPartiallyTrustedCallers]
[assembly: SecurityRules(SecurityRuleSet.Level2, SkipVerificationInFullTrust = true)]
@@ -28,11 +29,11 @@ namespace FisheryLib;
[PublicAPI]
public static class FisheryLib
{
public const decimal VERSION = 0.5M;
public const decimal VERSION = 0.51M;
public static decimal CurrentlyLoadedVersion { get; } = VERSION;
public const string VERSION_STRING = "0.5.0.0";
public const string VERSION_STRING = "0.5.1.0";
// public static string GetVersion()
// {
-81
View File
@@ -1,81 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>FisheryLib</RootNamespace>
<AssemblyName>1Fishery</AssemblyName>
<TargetFramework>net48</TargetFramework>
<LangVersion>preview</LangVersion>
<PlatformTarget>AnyCPU</PlatformTarget>
<Optimize>true</Optimize>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<Authors>bradson</Authors>
<Copyright>Copyright (c) 2022 bradson</Copyright>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<EnableNETAnalyzers>True</EnableNETAnalyzers>
<AnalysisLevel>preview-all</AnalysisLevel>
<DebugSymbols>true</DebugSymbols>
<DebugType>portable</DebugType>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>5</WarningLevel>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<Configurations>v1.4;v1.3</Configurations>
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
<GenerateAssemblyInformationalVersionAttribute>false</GenerateAssemblyInformationalVersionAttribute>
<!-- Version>0.3.800.85</Version -->
<!-- Deterministic>False</Deterministic -->
<NoWarn>
1701;1702;IL2121;CS8500;CS9082;CA1000;CA1002;CA1003;CA1707;CA1010;CA1715;CA1716;CA1724;CA1031;CA1033;
CA1034;CA1043;CA1051;CA1305;CA1310;CA1710;CA1711;CA1810;CA1812;CA1819;CA1851;CA2002;CA5394;CA1725;CA1062
</NoWarn>
</PropertyGroup>
<Choose>
<When Condition="'$(Configuration)' == 'v1.4'">
<PropertyGroup>
<OutputPath>..\..\1.4\Assemblies\</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.4.*" />
</ItemGroup>
</When>
<When Condition="'$(Configuration)' == 'v1.3'">
<PropertyGroup>
<OutputPath>..\..\1.3\Assemblies\</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.3.*" />
</ItemGroup>
</When>
</Choose>
<ItemGroup>
<PackageReference Include="Lib.Harmony" Version="2.2.*" ExcludeAssets="runtime" />
<PackageReference Include="PolySharp" Version="1.*" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.*" />
<PackageReference Include="Krafs.Publicizer" Version="2.*" />
<Publicize Include="Assembly-CSharp;0Harmony" IncludeVirtualMembers="false" />
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.IgnoresAccessChecksToAttribute" />
<DoNotPublicize Include="0Harmony:Mono.Collections.Generic.Collection`1" />
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.buckets" />
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.entries" />
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.version" />
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2+Entry" />
<Publicize Include="mscorlib:System.Collections.Generic.List`1._items" />
<Publicize Include="mscorlib:System.Collections.Generic.List`1._version" />
<Publicize Include="mscorlib:System.Collections.Generic.List`1._size" />
<Publicize Include="mscorlib:System.Collections.Generic.List`1.EnsureCapacity" />
<Publicize Include="System.Core:System.Collections.Generic.HashSet`1._version" />
<Publicize Include="mscorlib:System.Runtime.CompilerServices.JitHelpers.UnsafeEnumCast" />
<Publicize Include="mscorlib:System.Runtime.CompilerServices.JitHelpers.UnsafeEnumCastLong" />
<Publicize Include="mscorlib:System.Runtime.CompilerServices.JitHelpers.UnsafeCast" />
<Publicize Include="mscorlib:System.Reflection.Emit.Label.label" />
<Publicize Include="mscorlib:System.String.m_firstChar" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\LICENSE">
<Pack>True</Pack>
<PackagePath />
</None>
</ItemGroup>
</Project>
+25 -7
View File
@@ -3,18 +3,36 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.1.32120.378
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fishery", "Fishery.csproj", "{B5B38CBB-F8EE-451E-A05B-9ED9B03784D2}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "1.5", "1.5.csproj", "{B5B38CBB-F8EE-451E-A05B-9ED9B03784D2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "1.4", "1.4.csproj", "{BE85EB31-71CD-443C-8C93-20E2B7F3F549}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "1.3", "1.3.csproj", "{FC5FB1F4-626D-4895-AE5E-922DFE1AE120}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "1.2", "1.2.csproj", "{E4269847-7C6E-404E-93C0-E47CF7459E81}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
v1.3|Any CPU = v1.3|Any CPU
v1.4|Any CPU = v1.4|Any CPU
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B5B38CBB-F8EE-451E-A05B-9ED9B03784D2}.v1.3|Any CPU.ActiveCfg = v1.3|Any CPU
{B5B38CBB-F8EE-451E-A05B-9ED9B03784D2}.v1.3|Any CPU.Build.0 = v1.3|Any CPU
{B5B38CBB-F8EE-451E-A05B-9ED9B03784D2}.v1.4|Any CPU.ActiveCfg = v1.4|Any CPU
{B5B38CBB-F8EE-451E-A05B-9ED9B03784D2}.v1.4|Any CPU.Build.0 = v1.4|Any CPU
{B5B38CBB-F8EE-451E-A05B-9ED9B03784D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B5B38CBB-F8EE-451E-A05B-9ED9B03784D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B5B38CBB-F8EE-451E-A05B-9ED9B03784D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B5B38CBB-F8EE-451E-A05B-9ED9B03784D2}.Release|Any CPU.Build.0 = Release|Any CPU
{BE85EB31-71CD-443C-8C93-20E2B7F3F549}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BE85EB31-71CD-443C-8C93-20E2B7F3F549}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BE85EB31-71CD-443C-8C93-20E2B7F3F549}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BE85EB31-71CD-443C-8C93-20E2B7F3F549}.Release|Any CPU.Build.0 = Release|Any CPU
{FC5FB1F4-626D-4895-AE5E-922DFE1AE120}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FC5FB1F4-626D-4895-AE5E-922DFE1AE120}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FC5FB1F4-626D-4895-AE5E-922DFE1AE120}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FC5FB1F4-626D-4895-AE5E-922DFE1AE120}.Release|Any CPU.Build.0 = Release|Any CPU
{E4269847-7C6E-404E-93C0-E47CF7459E81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E4269847-7C6E-404E-93C0-E47CF7459E81}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E4269847-7C6E-404E-93C0-E47CF7459E81}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E4269847-7C6E-404E-93C0-E47CF7459E81}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -7,7 +7,6 @@ using System.Diagnostics.CodeAnalysis;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Security;
using JetBrains.Annotations;
namespace FisheryLib.FunctionPointers;
@@ -7,7 +7,6 @@ using System.Diagnostics.CodeAnalysis;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Security;
using JetBrains.Annotations;
namespace FisheryLib.FunctionPointers;
@@ -5,7 +5,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib.FunctionPointers;
@@ -5,7 +5,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib.FunctionPointers;
+12 -3
View File
@@ -4,7 +4,6 @@
// You can obtain one at https://opensource.org/licenses/MIT/.
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
using UnityEngine;
using Verse;
@@ -15,9 +14,19 @@ public static class GUIScope
{
public readonly record struct WidgetGroup : IDisposable
{
public WidgetGroup(in Rect rect) => Widgets.BeginGroup(rect);
public WidgetGroup(in Rect rect)
#if !V1_2
=> Widgets.BeginGroup(rect);
#else
=> GUI.BeginGroup(rect);
#endif
public void Dispose() => Widgets.EndGroup();
public void Dispose()
#if !V1_2
=> Widgets.EndGroup();
#else
=> GUI.EndGroup();
#endif
}
public readonly record struct ScrollView : IDisposable
-2
View File
@@ -3,8 +3,6 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using JetBrains.Annotations;
namespace FisheryLib;
[PublicAPI]
@@ -5,7 +5,6 @@
using System.Security;
using System.Text;
using JetBrains.Annotations;
namespace FisheryLib.Pools;
@@ -5,7 +5,6 @@
using System.Runtime.CompilerServices;
using System.Text;
using JetBrains.Annotations;
namespace FisheryLib.Pools;
@@ -4,7 +4,6 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib.Pools;
@@ -6,7 +6,6 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib;
@@ -8,7 +8,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib;
-1
View File
@@ -13,7 +13,6 @@ using System.Security;
using System.Text;
using System.Threading.Tasks;
using FisheryLib.Pools;
using JetBrains.Annotations;
using Verse;
namespace FisheryLib;
-1
View File
@@ -4,7 +4,6 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Collections.Concurrent;
using JetBrains.Annotations;
namespace FisheryLib;
-1
View File
@@ -6,7 +6,6 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib;
-1
View File
@@ -8,7 +8,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib;
-1
View File
@@ -4,7 +4,6 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib;
-1
View File
@@ -4,7 +4,6 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Diagnostics;
using JetBrains.Annotations;
namespace FisheryLib;
-1
View File
@@ -7,7 +7,6 @@ using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib;