Added 1.2 and 1.5 support, mod icon, DefaultValue/Key for FishSet/Table
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
global using static FisheryLib.Aliases;
|
||||
using System.Runtime.CompilerServices;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace FisheryLib;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
// {
|
||||
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ using System.Security;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using FisheryLib.Pools;
|
||||
using JetBrains.Annotations;
|
||||
using Verse;
|
||||
|
||||
namespace FisheryLib;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
using System.Diagnostics;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace FisheryLib;
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace FisheryLib;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user