Added pools, IndexedFishMap, IndexedFishSet, fixed bugs related to FishTable.Remove, added Preview.png, various smaller additions, some refactoring too
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 512 KiB |
@@ -1,4 +1,6 @@
|
|||||||
# Fishery
|
# Fishery
|
||||||
|

|
||||||
|
|
||||||
Modding Library for RimWorld. Used by Performance Fish.
|
Modding Library for RimWorld. Used by Performance Fish.
|
||||||
Useful functions include various abstractions around IL in FishTranspiler, to make writing of transpilers easier and more robust. A couple collections like Dictionary and Span have extension methods for fast and convenient lookups. Some functionalities of Microsoft's Community Toolkit are included, expanded upon and modified to better fit Unity. Equality includes generic extension methods to use in place of Equals on structs without memory allocations. Convert provides a faster, generic alternative to System.Convert.ToType. FisheryLib.Reflection provides a couple reflection helpers, Aliases has a methodof function intended to be utilized with `global using static Aliases` to directly retrieve method infos from static method groups.
|
Useful functions include various abstractions around IL in FishTranspiler, to make writing of transpilers easier and more robust. A couple collections like Dictionary and Span have extension methods for fast and convenient lookups. Some functionalities of Microsoft's Community Toolkit are included, expanded upon and modified to better fit Unity. Equality includes generic extension methods to use in place of Equals on structs without memory allocations. Convert provides a faster, generic alternative to System.Convert.ToType. FisheryLib.Reflection provides a couple reflection helpers, Aliases has a methodof function intended to be utilized with `global using static Aliases` to directly retrieve method infos from static method groups.
|
||||||
Additionally this includes a fast and much more versatile System.Collections.Generic.Dictionary replacement in FisheryLib.Collections.FishTable, loosely based on Malte Skarupke's ska::bytell_hash_map, as well as several extensions and helpers for Mono.Cecil, which is utilized by Prepatcher.
|
Additionally this includes a fast and much more versatile System.Collections.Generic.Dictionary replacement in FisheryLib.Collections.FishTable, loosely based on Malte Skarupke's ska::bytell_hash_map, as well as several extensions and helpers for Mono.Cecil, which is utilized by Prepatcher.
|
||||||
@@ -3,9 +3,12 @@
|
|||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using Mono.Cecil;
|
using Mono.Cecil;
|
||||||
using Mono.Cecil.Cil;
|
using Mono.Cecil.Cil;
|
||||||
|
using Mono.Cecil.Rocks;
|
||||||
using Mono.Collections.Generic;
|
using Mono.Collections.Generic;
|
||||||
using Verse;
|
using Verse;
|
||||||
using CallSite = Mono.Cecil.CallSite;
|
using CallSite = Mono.Cecil.CallSite;
|
||||||
@@ -334,8 +337,8 @@ public static class CecilExtensions
|
|||||||
|
|
||||||
public static void ReplaceBodyWith(this ILProcessor ilProcessor, MethodInfo replacement)
|
public static void ReplaceBodyWith(this ILProcessor ilProcessor, MethodInfo replacement)
|
||||||
{
|
{
|
||||||
var oldBody = ilProcessor.Body;
|
var targetBody = ilProcessor.Body;
|
||||||
var module = oldBody.Method.Module;
|
var module = targetBody.Method.Module;
|
||||||
|
|
||||||
if (replacement.IsGenericMethod)
|
if (replacement.IsGenericMethod)
|
||||||
replacement = replacement.GetGenericMethodDefinition();
|
replacement = replacement.GetGenericMethodDefinition();
|
||||||
@@ -349,25 +352,39 @@ public static class CecilExtensions
|
|||||||
var newMethodReference = module.ImportReference(replacement);
|
var newMethodReference = module.ImportReference(replacement);
|
||||||
var newBody = newMethodReference.Resolve().Body;
|
var newBody = newMethodReference.Resolve().Body;
|
||||||
|
|
||||||
oldBody.Instructions.Clear();
|
newBody.SimplifyMacros();
|
||||||
|
MethodBodyRocks.ComputeOffsets(newBody);
|
||||||
|
|
||||||
|
var targetInstructions = targetBody.instructions;
|
||||||
|
targetInstructions.Clear();
|
||||||
|
|
||||||
foreach (var instruction in newBody.instructions)
|
foreach (var instruction in newBody.instructions)
|
||||||
{
|
{
|
||||||
oldBody.Instructions.Add(new(instruction.OpCode, instruction.Operand switch
|
targetInstructions.Add(new(instruction.OpCode, instruction.Operand switch
|
||||||
{
|
{
|
||||||
FieldReference field => module.ImportReference(field, newMethodReference),
|
FieldReference field => module.ImportReference(field, newMethodReference),
|
||||||
MethodReference method => module.ImportReference(method, newMethodReference),
|
MethodReference method => module.ImportReference(method, newMethodReference),
|
||||||
TypeReference type => module.ImportReference(type, newMethodReference),
|
TypeReference type => module.ImportReference(type, newMethodReference),
|
||||||
|
ParameterReference parameter => targetBody.GetParameter(parameter.Index),
|
||||||
_ => instruction.Operand
|
_ => instruction.Operand
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
oldBody.Variables.Clear();
|
MethodBodyRocks.ComputeOffsets(targetBody);
|
||||||
|
|
||||||
|
foreach (var instruction in targetInstructions)
|
||||||
|
{
|
||||||
|
if (instruction.Operand is Instruction operand)
|
||||||
|
instruction.Operand = targetInstructions.First(ti => ti.Offset == operand.Offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
targetBody.Variables.Clear();
|
||||||
foreach (var variable in newBody.Variables)
|
foreach (var variable in newBody.Variables)
|
||||||
oldBody.Variables.Add(new(module.ImportReference(variable.VariableType, newMethodReference)));
|
targetBody.Variables.Add(new(module.ImportReference(variable.VariableType, newMethodReference)));
|
||||||
|
|
||||||
oldBody.MaxStackSize = newBody.MaxStackSize;
|
targetBody.MaxStackSize = newBody.MaxStackSize;
|
||||||
|
|
||||||
oldBody.ExceptionHandlers.Clear();
|
targetBody.ExceptionHandlers.Clear();
|
||||||
foreach (var exceptionHandler in newBody.ExceptionHandlers)
|
foreach (var exceptionHandler in newBody.ExceptionHandlers)
|
||||||
{
|
{
|
||||||
var handler = new ExceptionHandler(exceptionHandler.HandlerType)
|
var handler = new ExceptionHandler(exceptionHandler.HandlerType)
|
||||||
@@ -382,10 +399,13 @@ public static class CecilExtensions
|
|||||||
if (exceptionHandler.CatchType is { } catchType)
|
if (exceptionHandler.CatchType is { } catchType)
|
||||||
handler.CatchType = module.ImportReference(catchType, newMethodReference);
|
handler.CatchType = module.ImportReference(catchType, newMethodReference);
|
||||||
|
|
||||||
oldBody.ExceptionHandlers.Add(handler);
|
targetBody.ExceptionHandlers.Add(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
oldBody.Method.ImplAttributes = newBody.Method.ImplAttributes;
|
targetBody.Method.ImplAttributes = newBody.Method.ImplAttributes;
|
||||||
|
|
||||||
|
targetBody.OptimizeMacros();
|
||||||
|
newBody.OptimizeMacros();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MethodDefinition GetMethod(this ILProcessor ilProcessor) => ilProcessor.Body.Method;
|
public static MethodDefinition GetMethod(this ILProcessor ilProcessor) => ilProcessor.Body.Method;
|
||||||
@@ -533,6 +553,7 @@ public static class CecilExtensions
|
|||||||
// ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
|
// ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
|
||||||
$"Argument is of invalid type: {instruction?.GetType().FullName ?? "NULL"}");
|
$"Argument is of invalid type: {instruction?.GetType().FullName ?? "NULL"}");
|
||||||
|
|
||||||
|
[SuppressMessage("Maintainability", "CA1508")]
|
||||||
private static Instruction CreateInstruction(OpCode opCode, object operand)
|
private static Instruction CreateInstruction(OpCode opCode, object operand)
|
||||||
=> operand switch
|
=> operand switch
|
||||||
{
|
{
|
||||||
@@ -554,6 +575,7 @@ public static class CecilExtensions
|
|||||||
ParameterDefinition parameter => Instruction.Create(opCode, parameter),
|
ParameterDefinition parameter => Instruction.Create(opCode, parameter),
|
||||||
_ => ThrowHelper.ThrowArgumentException<Instruction>(nameof(operand),
|
_ => ThrowHelper.ThrowArgumentException<Instruction>(nameof(operand),
|
||||||
// ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
|
// ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
|
||||||
$"Operand for OpCode {opCode} is of invalid type: {operand?.GetType().FullName ?? "NULL"}")
|
$"Operand for OpCode {opCode} is of invalid type: {
|
||||||
|
operand?.GetType().FullName ?? operand?.GetType().Name ?? "NULL"}")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// 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 FisheryLib.Collections;
|
||||||
|
using Mono.Cecil;
|
||||||
|
using Mono.Cecil.Cil;
|
||||||
|
|
||||||
|
namespace FisheryLib.Cecil;
|
||||||
|
|
||||||
|
public static class InstructionExtensions
|
||||||
|
{
|
||||||
|
public static bool LoadsArgument(this Instruction instruction, int index)
|
||||||
|
=> instruction.LoadsArgument() && GetLoadArgumentIndex(instruction) == index;
|
||||||
|
|
||||||
|
public static bool LoadsArgument(this Instruction instruction) => LoadArgumentOpcodes.Contains(instruction.OpCode);
|
||||||
|
|
||||||
|
private static int GetLoadArgumentIndex(Instruction instruction)
|
||||||
|
{
|
||||||
|
var opCode = instruction.OpCode;
|
||||||
|
return opCode == OpCodes.Ldarg_0 ? 0
|
||||||
|
: opCode == OpCodes.Ldarg_1 ? 1
|
||||||
|
: opCode == OpCodes.Ldarg_2 ? 2
|
||||||
|
: opCode == OpCodes.Ldarg_3 ? 3
|
||||||
|
: instruction.Operand is ParameterDefinition parameterDefinition ? parameterDefinition.Index
|
||||||
|
: Convert.Type<object, int>(instruction.Operand);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly FishSet<OpCode> LoadArgumentOpcodes =
|
||||||
|
[
|
||||||
|
OpCodes.Ldarg, OpCodes.Ldarg_0, OpCodes.Ldarg_1, OpCodes.Ldarg_2, OpCodes.Ldarg_3, OpCodes.Ldarg_S,
|
||||||
|
OpCodes.Ldarga, OpCodes.Ldarga_S
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -58,6 +58,8 @@ public static class ReflectionExtensions
|
|||||||
private static T? TryGetNamed<T>(this IList<T> memberInfos, string name, Func<T, string> nameGetter)
|
private static T? TryGetNamed<T>(this IList<T> memberInfos, string name, Func<T, string> nameGetter)
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
|
Guard.IsNotNull(memberInfos);
|
||||||
|
|
||||||
for (var i = 0; i < memberInfos.Count; i++)
|
for (var i = 0; i < memberInfos.Count; i++)
|
||||||
{
|
{
|
||||||
if (nameGetter(memberInfos[i]) == name)
|
if (nameGetter(memberInfos[i]) == name)
|
||||||
@@ -69,6 +71,8 @@ public static class ReflectionExtensions
|
|||||||
|
|
||||||
public static TypeReference[] GetTypeReferences(this Type[] types, ModuleDefinition module)
|
public static TypeReference[] GetTypeReferences(this Type[] types, ModuleDefinition module)
|
||||||
{
|
{
|
||||||
|
Guard.IsNotNull(types);
|
||||||
|
|
||||||
var result = new TypeReference[types.Length];
|
var result = new TypeReference[types.Length];
|
||||||
for (var i = types.Length; i-- > 0;)
|
for (var i = types.Length; i-- > 0;)
|
||||||
result[i] = module.ImportReference(types[i]);
|
result[i] = module.ImportReference(types[i]);
|
||||||
@@ -77,8 +81,14 @@ public static class ReflectionExtensions
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsGeneric(this TypeReference type)
|
public static bool IsGeneric(this TypeReference type)
|
||||||
=> type.ContainsGenericParameter || type.HasGenericParameters || type.IsGenericInstance;
|
{
|
||||||
|
Guard.IsNotNull(type);
|
||||||
|
return type.ContainsGenericParameter || type.HasGenericParameters || type.IsGenericInstance;
|
||||||
|
}
|
||||||
|
|
||||||
public static bool IsGeneric(this Type type)
|
public static bool IsGeneric(this Type type)
|
||||||
=> type.ContainsGenericParameters || type.IsGenericType || type.IsConstructedGenericType;
|
{
|
||||||
|
Guard.IsNotNull(type);
|
||||||
|
return type.ContainsGenericParameters || type.IsGenericType || type.IsConstructedGenericType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -145,7 +145,8 @@ public static class CollectionExtensions
|
|||||||
{
|
{
|
||||||
if (dictionary.buckets != null)
|
if (dictionary.buckets != null)
|
||||||
{
|
{
|
||||||
var keyCode = HashCode.Get(key) & 0x7FFFFFFF; // HashCode.Get can handle null, & 0x7FFFFFFF removes any sign
|
var keyCode = HashCode.Get(key) & int.MaxValue;
|
||||||
|
// HashCode.Get can handle null, & int.MaxValue removes any sign
|
||||||
|
|
||||||
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
|
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
|
||||||
while (bucket >= 0)
|
while (bucket >= 0)
|
||||||
@@ -165,7 +166,7 @@ public static class CollectionExtensions
|
|||||||
{
|
{
|
||||||
if (dictionary.buckets != null)
|
if (dictionary.buckets != null)
|
||||||
{
|
{
|
||||||
var keyCode = HashCode.Get(ref key) & 0x7FFFFFFF;
|
var keyCode = HashCode.Get(ref key) & int.MaxValue;
|
||||||
|
|
||||||
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
|
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
|
||||||
while (bucket >= 0)
|
while (bucket >= 0)
|
||||||
@@ -192,7 +193,7 @@ public static class CollectionExtensions
|
|||||||
{
|
{
|
||||||
if (dictionary.buckets != null)
|
if (dictionary.buckets != null)
|
||||||
{
|
{
|
||||||
var keyCode = HashCode.Get(key) & 0x7FFFFFFF;
|
var keyCode = HashCode.Get(key) & int.MaxValue;
|
||||||
|
|
||||||
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
|
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
|
||||||
while (bucket >= 0)
|
while (bucket >= 0)
|
||||||
@@ -212,7 +213,7 @@ public static class CollectionExtensions
|
|||||||
{
|
{
|
||||||
if (dictionary.buckets != null)
|
if (dictionary.buckets != null)
|
||||||
{
|
{
|
||||||
var keyCode = HashCode.Get(ref key) & 0x7FFFFFFF;
|
var keyCode = HashCode.Get(ref key) & int.MaxValue;
|
||||||
|
|
||||||
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
|
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
|
||||||
while (bucket >= 0)
|
while (bucket >= 0)
|
||||||
@@ -232,7 +233,7 @@ public static class CollectionExtensions
|
|||||||
{
|
{
|
||||||
if (dictionary.buckets != null)
|
if (dictionary.buckets != null)
|
||||||
{
|
{
|
||||||
var keyCode = HashCode.Get(ref key) & 0x7FFFFFFF;
|
var keyCode = HashCode.Get(ref key) & int.MaxValue;
|
||||||
|
|
||||||
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
|
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
|
||||||
while (bucket >= 0)
|
while (bucket >= 0)
|
||||||
@@ -253,7 +254,7 @@ public static class CollectionExtensions
|
|||||||
{
|
{
|
||||||
if (dictionary.buckets != null)
|
if (dictionary.buckets != null)
|
||||||
{
|
{
|
||||||
var keyCode = HashCode.Get(key) & 0x7FFFFFFF;
|
var keyCode = HashCode.Get(key) & int.MaxValue;
|
||||||
|
|
||||||
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
|
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
|
||||||
while (bucket >= 0)
|
while (bucket >= 0)
|
||||||
@@ -273,7 +274,7 @@ public static class CollectionExtensions
|
|||||||
public static ref TValue GetOrAddReference<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key)
|
public static ref TValue GetOrAddReference<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key)
|
||||||
where TValue : new()
|
where TValue : new()
|
||||||
{
|
{
|
||||||
var keyCode = HashCode.Get(key) & 0x7FFFFFFF;
|
var keyCode = HashCode.Get(key) & int.MaxValue;
|
||||||
|
|
||||||
StartOfLookup:
|
StartOfLookup:
|
||||||
if (dictionary.buckets != null)
|
if (dictionary.buckets != null)
|
||||||
@@ -297,7 +298,7 @@ public static class CollectionExtensions
|
|||||||
public static ref TValue GetOrAddReference<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, ref TKey key)
|
public static ref TValue GetOrAddReference<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, ref TKey key)
|
||||||
where TValue : new()
|
where TValue : new()
|
||||||
{
|
{
|
||||||
var keyCode = HashCode.Get(ref key) & 0x7FFFFFFF;
|
var keyCode = HashCode.Get(ref key) & int.MaxValue;
|
||||||
|
|
||||||
StartOfLookup:
|
StartOfLookup:
|
||||||
if (dictionary.buckets != null)
|
if (dictionary.buckets != null)
|
||||||
@@ -320,7 +321,7 @@ public static class CollectionExtensions
|
|||||||
// public static bool TryGetOrAddReference<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key,
|
// public static bool TryGetOrAddReference<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key,
|
||||||
// ref TValue reference)
|
// ref TValue reference)
|
||||||
// {
|
// {
|
||||||
// var keyCode = HashCode.Get(ref key) & 0x7FFFFFFF;
|
// var keyCode = HashCode.Get(ref key) & int.MaxValue;
|
||||||
// var result = true;
|
// var result = true;
|
||||||
//
|
//
|
||||||
// StartOfLookup:
|
// StartOfLookup:
|
||||||
@@ -349,7 +350,7 @@ public static class CollectionExtensions
|
|||||||
public static TValue GetOrAdd<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key)
|
public static TValue GetOrAdd<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key)
|
||||||
where TValue : new()
|
where TValue : new()
|
||||||
{
|
{
|
||||||
var keyCode = HashCode.Get(key) & 0x7FFFFFFF;
|
var keyCode = HashCode.Get(key) & int.MaxValue;
|
||||||
|
|
||||||
StartOfLookup:
|
StartOfLookup:
|
||||||
if (dictionary.buckets != null)
|
if (dictionary.buckets != null)
|
||||||
@@ -373,7 +374,7 @@ public static class CollectionExtensions
|
|||||||
public static TValue GetOrAdd<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, ref TKey key)
|
public static TValue GetOrAdd<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, ref TKey key)
|
||||||
where TValue : new()
|
where TValue : new()
|
||||||
{
|
{
|
||||||
var keyCode = HashCode.Get(ref key) & 0x7FFFFFFF;
|
var keyCode = HashCode.Get(ref key) & int.MaxValue;
|
||||||
|
|
||||||
StartOfLookup:
|
StartOfLookup:
|
||||||
if (dictionary.buckets != null)
|
if (dictionary.buckets != null)
|
||||||
@@ -396,7 +397,7 @@ public static class CollectionExtensions
|
|||||||
public static bool TryGetOrAddValue<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key,
|
public static bool TryGetOrAddValue<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key,
|
||||||
out TValue value)
|
out TValue value)
|
||||||
{
|
{
|
||||||
var keyCode = HashCode.Get(ref key) & 0x7FFFFFFF;
|
var keyCode = HashCode.Get(ref key) & int.MaxValue;
|
||||||
var result = true;
|
var result = true;
|
||||||
|
|
||||||
StartOfLookup:
|
StartOfLookup:
|
||||||
@@ -421,6 +422,38 @@ public static class CollectionExtensions
|
|||||||
goto StartOfLookup;
|
goto StartOfLookup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int RemoveWhere<TKey, TValue>(this Dictionary<TKey, TValue> dictionary,
|
||||||
|
Predicate<KeyValuePair<TKey, TValue>> predicate)
|
||||||
|
{
|
||||||
|
Guard.IsNotNull(dictionary);
|
||||||
|
Guard.IsNotNull(predicate);
|
||||||
|
|
||||||
|
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];
|
||||||
|
if (entry.hashCode < 0) // valid entries are stored with & int.MaxValue on hashCode
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// predicate might modify collection, turning a ref invalid
|
||||||
|
var keyValuePair = new KeyValuePair<TKey,TValue>(entry.key, entry.value);
|
||||||
|
|
||||||
|
if (!predicate(keyValuePair))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (dictionary.Remove(keyValuePair.Key))
|
||||||
|
removedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return removedCount;
|
||||||
|
}
|
||||||
|
|
||||||
[DoesNotReturn]
|
[DoesNotReturn]
|
||||||
private static ref V ThrowKeyNotFoundException<T, V>(T key)
|
private static ref V ThrowKeyNotFoundException<T, V>(T key)
|
||||||
{
|
{
|
||||||
@@ -640,7 +673,28 @@ public static class CollectionExtensions
|
|||||||
public static StringBuilder Append(this StringBuilder builder, List<char> value)
|
public static StringBuilder Append(this StringBuilder builder, List<char> value)
|
||||||
=> builder.Append(value._items, 0, value._size);
|
=> builder.Append(value._items, 0, value._size);
|
||||||
|
|
||||||
public static ref T GetReferenceUnverifiable<T>(this List<T> list, int index) => ref list._items[index];
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static StringBuilder Append<T>(this StringBuilder builder, T? value)
|
||||||
|
// ReSharper disable once RedundantToStringCallForValueType
|
||||||
|
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||||
|
// ReSharper disable once HeapView.PossibleBoxingAllocation
|
||||||
|
=> value == null ? builder : builder.Append(value.ToString());
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static ref T GetReference<T>(this List<T> list, int index)
|
||||||
|
{
|
||||||
|
Guard.IsInRange(index, 0, list.Count);
|
||||||
|
|
||||||
|
list._version++;
|
||||||
|
return ref list._items[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("Renamed to GetReferenceUnsafe")]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static ref T GetReferenceUnverifiable<T>(this List<T> list, int index) => ref list.GetReferenceUnsafe(index);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static ref T GetReferenceUnsafe<T>(this List<T> list, int index) => ref list._items[index];
|
||||||
|
|
||||||
public static IList TryNew(IEnumerable enumerable, Type type) => GetListExtensionMethods(type).New(enumerable);
|
public static IList TryNew(IEnumerable enumerable, Type type) => GetListExtensionMethods(type).New(enumerable);
|
||||||
|
|
||||||
@@ -649,6 +703,40 @@ public static class CollectionExtensions
|
|||||||
|
|
||||||
public static Array TryToArray(this IList list, Type type) => GetListExtensionMethods(type).ToArray(list);
|
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];
|
||||||
|
list[index] = lastBucket;
|
||||||
|
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)
|
||||||
|
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)
|
public static unsafe uint GetSizeEstimate<TKey, TValue>(this Dictionary<TKey, TValue> dictionary)
|
||||||
=> dictionary.GetType().ComputeManagedObjectSizeEstimate()
|
=> dictionary.GetType().ComputeManagedObjectSizeEstimate()
|
||||||
+ ((uint)sizeof(Dictionary<TKey, TValue>.Entry) * (uint)(dictionary.entries?.Length ?? 0))
|
+ ((uint)sizeof(Dictionary<TKey, TValue>.Entry) * (uint)(dictionary.entries?.Length ?? 0))
|
||||||
@@ -657,7 +745,8 @@ public static class CollectionExtensions
|
|||||||
internal static unsafe uint ComputeManagedObjectSizeEstimate(this Type type)
|
internal static unsafe uint ComputeManagedObjectSizeEstimate(this Type type)
|
||||||
{
|
{
|
||||||
var objectHeader = (uint)sizeof(void*) * 2u;
|
var objectHeader = (uint)sizeof(void*) * 2u;
|
||||||
ref var cachedSize = ref CachedManagedObjectSizes.GetOrAddReference(type.TypeHandle.Value);
|
var cachedSize = CachedManagedObjectSizes.GetOrAdd(type.TypeHandle.Value);
|
||||||
|
// can't use GetOrAddReference here due to recursion
|
||||||
|
|
||||||
if (cachedSize.Size == uint.MaxValue)
|
if (cachedSize.Size == uint.MaxValue)
|
||||||
UpdateCachedManagedObjectSize(type, ref cachedSize);
|
UpdateCachedManagedObjectSize(type, ref cachedSize);
|
||||||
@@ -689,6 +778,8 @@ public static class CollectionExtensions
|
|||||||
var remainder = cachedSize.Size % (uint)IntPtr.Size;
|
var remainder = cachedSize.Size % (uint)IntPtr.Size;
|
||||||
if (remainder != 0u)
|
if (remainder != 0u)
|
||||||
cachedSize.Size += (uint)IntPtr.Size - remainder;
|
cachedSize.Size += (uint)IntPtr.Size - remainder;
|
||||||
|
|
||||||
|
CachedManagedObjectSizes[type.TypeHandle.Value] = cachedSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static FishTable<IntPtr, ManagedObjectSize> CachedManagedObjectSizes => _cachedManagedObjectSizes ??= new();
|
private static FishTable<IntPtr, ManagedObjectSize> CachedManagedObjectSizes => _cachedManagedObjectSizes ??= new();
|
||||||
|
|||||||
@@ -58,9 +58,9 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
|
|||||||
get => _count;
|
get => _count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public object SyncRoot => throw new NotImplementedException();
|
object ICollection.SyncRoot => this;
|
||||||
|
|
||||||
public bool IsSynchronized => throw new NotImplementedException();
|
bool ICollection.IsSynchronized => false;
|
||||||
|
|
||||||
public bool IsReadOnly => false;
|
public bool IsReadOnly => false;
|
||||||
|
|
||||||
@@ -96,7 +96,13 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
|
|||||||
get => Contains(key);
|
get => Contains(key);
|
||||||
|
|
||||||
[CollectionAccess(CollectionAccessType.UpdatedContent)]
|
[CollectionAccess(CollectionAccessType.UpdatedContent)]
|
||||||
set => InsertEntry(key, value);
|
set
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
InsertEntry(key, ReplaceBehaviour.Fail);
|
||||||
|
else
|
||||||
|
Remove(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FishSet() : this(0)
|
public FishSet() : this(0)
|
||||||
@@ -121,7 +127,7 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
|
|||||||
public FishSet(IEnumerable<T> entries) : this(0)
|
public FishSet(IEnumerable<T> entries) : this(0)
|
||||||
{
|
{
|
||||||
foreach (var entry in entries)
|
foreach (var entry in entries)
|
||||||
InsertEntry(entry, false);
|
InsertEntry(entry, ReplaceBehaviour.Throw);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetTailIndex(int entryIndex) => GetIndexForTail(entryIndex, _tails[(uint)entryIndex]);
|
private int GetTailIndex(int entryIndex) => GetIndexForTail(entryIndex, _tails[(uint)entryIndex]);
|
||||||
@@ -129,56 +135,6 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private int GetIndexForTail(int entryIndex, uint tail) => (entryIndex + GetJumpDistance(tail)) & _wrapAroundMask;
|
private int GetIndexForTail(int entryIndex, uint tail) => (entryIndex + GetJumpDistance(tail)) & _wrapAroundMask;
|
||||||
|
|
||||||
#region Junk
|
|
||||||
|
|
||||||
// private static long GetJumpDistanceLong(byte index)
|
|
||||||
// => index switch
|
|
||||||
// {
|
|
||||||
// 0 => 0, 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 7 => 7,
|
|
||||||
// 8 => 8, 9 => 9, 10 => 10, 11 => 11, 12 => 12, 13 => 13, 14 => 14,
|
|
||||||
// 15 => 15, 16 => 21, 17 => 28, 18 => 36, 19 => 45, 20 => 55, 21 => 66,
|
|
||||||
// 22 => 78, 23 => 91, 24 => 105, 25 => 120, 26 => 136, 27 => 153,
|
|
||||||
// 28 => 171, 29 => 190, 30 => 210, 31 => 231, 32 => 253, 33 => 276,
|
|
||||||
// 34 => 300, 35 => 325, 36 => 351, 37 => 378, 38 => 406, 39 => 435,
|
|
||||||
// 40 => 465, 41 => 496, 42 => 528, 43 => 561, 44 => 595, 45 => 630,
|
|
||||||
// 46 => 666, 47 => 703, 48 => 741, 49 => 780, 50 => 820, 51 => 861,
|
|
||||||
// 52 => 903, 53 => 946, 54 => 990, 55 => 1035, 56 => 1081, 57 => 1128,
|
|
||||||
// 58 => 1176, 59 => 1225, 60 => 1275, 61 => 1326, 62 => 1378, 63 => 1431,
|
|
||||||
// 64 => 1485, 65 => 1540, 66 => 1596, 67 => 1653, 68 => 1711, 69 => 1770,
|
|
||||||
// 70 => 1830, 71 => 1891, 72 => 1953, 73 => 2016, 74 => 2080, 75 => 2145,
|
|
||||||
// 76 => 2211, 77 => 2278, 78 => 2346, 79 => 2415, 80 => 2485, 81 => 2556,
|
|
||||||
// 82 => 3741, 83 => 8385, 84 => 18915, 85 => 42486, 86 => 95703, 87 => 215496,
|
|
||||||
// 88 => 485605, 89 => 1091503, 90 => 2456436, 91 => 5529475, 92 => 12437578,
|
|
||||||
// 93 => 27986421, 94 => 62972253, 95 => 141700195, 96 => 318819126, 97 => 717314626,
|
|
||||||
// 98 => 1614000520, 99 => 3631437253, 100 => 8170829695, 101 => 18384318876, 102 => 41364501751,
|
|
||||||
// 103 => 93070021080, 104 => 209407709220, 105 => 471167588430, 106 => 1060127437995, 107 => 2385287281530,
|
|
||||||
// 108 => 5366895564381, 109 => 12075513791265, 110 => 27169907873235, 111 => 61132301007778,
|
|
||||||
// 112 => 137547673121001, 113 => 309482258302503, 114 => 696335090510256, 115 => 1566753939653640,
|
|
||||||
// 116 => 3525196427195653, 117 => 7931691866727775, 118 => 17846306747368716,
|
|
||||||
// 119 => 40154190394120111, 120 => 90346928493040500, 121 => 203280588949935750,
|
|
||||||
// 122 => 457381324898247375, 123 => 1029107980662394500, 124 => 2315492957028380766,
|
|
||||||
// 125 => 5209859150892887590, > 125 => throw new()
|
|
||||||
// };
|
|
||||||
|
|
||||||
// private static int GetJumpDistance(byte index)
|
|
||||||
// => index switch
|
|
||||||
// {
|
|
||||||
// 0 => 0, 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 7 => 7, 8 => 8, 9 => 9, 10 => 10, 11 => 11,
|
|
||||||
// 12 => 12, 13 => 13, 14 => 14, 15 => 15, 16 => 21, 17 => 28, 18 => 36, 19 => 45, 20 => 55, 21 => 66,
|
|
||||||
// 22 => 78, 23 => 91, 24 => 105, 25 => 120, 26 => 136, 27 => 153, 28 => 171, 29 => 190, 30 => 210, 31 => 231,
|
|
||||||
// 32 => 253, 33 => 276, 34 => 300, 35 => 325, 36 => 351, 37 => 378, 38 => 406, 39 => 435, 40 => 465,
|
|
||||||
// 41 => 496, 42 => 528, 43 => 561, 44 => 595, 45 => 630, 46 => 666, 47 => 703, 48 => 741, 49 => 780,
|
|
||||||
// 50 => 820, 51 => 861, 52 => 903, 53 => 946, 54 => 990, 55 => 1035, 56 => 1081, 57 => 1128, 58 => 1176,
|
|
||||||
// 59 => 1225, 60 => 1275, 61 => 1326, 62 => 1378, 63 => 1431, 64 => 1485, 65 => 1540, 66 => 1596, 67 => 1653,
|
|
||||||
// 68 => 1711, 69 => 1770, 70 => 1830, 71 => 1891, 72 => 1953, 73 => 2016, 74 => 2080, 75 => 2145, 76 => 2211,
|
|
||||||
// 77 => 2278, 78 => 2346, 79 => 2415, 80 => 2485, 81 => 2556, 82 => 3741, 83 => 8385, 84 => 18915,
|
|
||||||
// 85 => 42486, 86 => 95703, 87 => 215496, 88 => 485605, 89 => 1091503, 90 => 2456436, 91 => 5529475,
|
|
||||||
// 92 => 12437578, 93 => 27986421, 94 => 62972253, 95 => 141700195, 96 => 318819126, 97 => 717314626,
|
|
||||||
// 98 => 1614000520, 255 => int.MaxValue, > 98 => ThrowForInvalidIndex(index)
|
|
||||||
// };
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
private static int GetJumpDistance(uint index)
|
private static int GetJumpDistance(uint index)
|
||||||
=> index switch
|
=> index switch
|
||||||
{
|
{
|
||||||
@@ -191,9 +147,6 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
|
|||||||
private static int MultiplyWithPhi(uint index)
|
private static int MultiplyWithPhi(uint index)
|
||||||
=> throw new NotImplementedException(); // (int)(uint)Math.Round(index * FishMath.PHI);
|
=> throw new NotImplementedException(); // (int)(uint)Math.Round(index * FishMath.PHI);
|
||||||
|
|
||||||
// private static int ThrowForInvalidIndex(byte index)
|
|
||||||
// => throw new($"Jump distance index for FishTable too large. Should be < 99, got {index} instead.");
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private bool IsTail(int bucketIndex)
|
private bool IsTail(int bucketIndex)
|
||||||
=> bucketIndex != GetBucketIndexForKeyAt(bucketIndex);
|
=> bucketIndex != GetBucketIndexForKeyAt(bucketIndex);
|
||||||
@@ -211,6 +164,12 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
|
|||||||
_tails.SetSolo(bucketIndex);
|
_tails.SetSolo(bucketIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SetBucketEmpty(int index)
|
||||||
|
{
|
||||||
|
_buckets[index] = default;
|
||||||
|
_tails.SetEmpty(index);
|
||||||
|
}
|
||||||
|
|
||||||
private void SetEntryAsTail(int bucketIndex, T entry, int parentIndex, uint offset)
|
private void SetEntryAsTail(int bucketIndex, T entry, int parentIndex, uint offset)
|
||||||
{
|
{
|
||||||
SetEntryWithoutTail(bucketIndex, entry);
|
SetEntryWithoutTail(bucketIndex, entry);
|
||||||
@@ -220,9 +179,9 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
|
|||||||
private void SetParentTail(int entryIndex, uint newTail)
|
private void SetParentTail(int entryIndex, uint newTail)
|
||||||
=> _tails[(uint)GetParentBucketIndex(entryIndex)] = newTail;
|
=> _tails[(uint)GetParentBucketIndex(entryIndex)] = newTail;
|
||||||
|
|
||||||
private bool InsertEntry(T entry, bool allowReplace = true, bool shifting = false)
|
private bool InsertEntry(T entry, ReplaceBehaviour replaceBehaviour, bool shifting = false)
|
||||||
{
|
{
|
||||||
var addedNewEntry = InsertEntryInternal(entry, allowReplace);
|
var addedNewEntry = InsertEntryInternal(entry, replaceBehaviour);
|
||||||
|
|
||||||
if (shifting)
|
if (shifting)
|
||||||
return addedNewEntry;
|
return addedNewEntry;
|
||||||
@@ -247,7 +206,7 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
|
|||||||
/// Returns true when adding a new entry, false for replacing an existing entry. AllowReplace: false causes throwing
|
/// Returns true when adding a new entry, false for replacing an existing entry. AllowReplace: false causes throwing
|
||||||
/// instead. Does not adjust Count, Version or invoke EntryAdded.
|
/// instead. Does not adjust Count, Version or invoke EntryAdded.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private bool InsertEntryInternal(T entry, bool allowReplace = true)
|
private bool InsertEntryInternal(T entry, ReplaceBehaviour replaceBehaviour)
|
||||||
{
|
{
|
||||||
StartOfMethod:
|
StartOfMethod:
|
||||||
var bucketIndex = GetBucketIndex(entry);
|
var bucketIndex = GetBucketIndex(entry);
|
||||||
@@ -259,7 +218,7 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TryReplaceValueOfMatchingKey(entry, allowReplace, ref bucket!))
|
if (TryReplaceValueOfMatchingKey(entry, replaceBehaviour, ref bucket!))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (CheckResize())
|
if (CheckResize())
|
||||||
@@ -268,53 +227,30 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
|
|||||||
if (IsTail(bucket, bucketIndex))
|
if (IsTail(bucket, bucketIndex))
|
||||||
{
|
{
|
||||||
var previousEntry = bucket;
|
var previousEntry = bucket;
|
||||||
var hasTail = !_tails.IsSolo(bucketIndex);
|
var tailingEntries = TryGetAndClearTailingEntries(bucketIndex);
|
||||||
var tailingEntries = hasTail ? new T[GetTailCount(bucketIndex)] : null;
|
|
||||||
|
|
||||||
if (hasTail)
|
|
||||||
{
|
|
||||||
var i = 0;
|
|
||||||
var tailIndex = GetTailIndex(bucketIndex);
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
ref var tailingBucket = ref _buckets[tailIndex];
|
|
||||||
tailingEntries![i++] = tailingBucket!;
|
|
||||||
tailingBucket = default;
|
|
||||||
|
|
||||||
if (!_tails.IsSoloOrEmpty(tailIndex))
|
|
||||||
{
|
|
||||||
var nextTailIndex = GetTailIndex(tailIndex);
|
|
||||||
_tails.SetEmpty(tailIndex);
|
|
||||||
tailIndex = nextTailIndex;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_tails.SetEmpty(tailIndex);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SetParentTail(bucketIndex, Tails.SOLO);
|
SetParentTail(bucketIndex, Tails.SOLO);
|
||||||
SetEntryWithoutTail(bucketIndex, entry);
|
SetEntryWithoutTail(bucketIndex, entry);
|
||||||
|
InsertEntry(previousEntry, ReplaceBehaviour.Throw, true);
|
||||||
|
|
||||||
InsertEntry(previousEntry, shifting: true);
|
if (tailingEntries != null)
|
||||||
if (hasTail)
|
InsertEntries(tailingEntries, ReplaceBehaviour.Throw, true);
|
||||||
{
|
|
||||||
for (var i = 0; i < tailingEntries!.Length; i++)
|
|
||||||
InsertEntry(tailingEntries[i], shifting: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return !TryFindTailIndexAndReplace(entry, ref bucketIndex, allowReplace)
|
return !TryFindTailIndexAndReplace(entry, ref bucketIndex, replaceBehaviour)
|
||||||
&& InsertAsTail(entry, bucketIndex);
|
&& InsertAsTail(entry, bucketIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void InsertEntries(T[] tailingEntries, ReplaceBehaviour replaceBehaviour, bool shifting = false)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < tailingEntries.Length; i++)
|
||||||
|
InsertEntry(tailingEntries[i], replaceBehaviour, shifting);
|
||||||
|
}
|
||||||
|
|
||||||
private int GetTailCount(int index)
|
private int GetTailCount(int index)
|
||||||
{
|
{
|
||||||
var tailCount = 0;
|
var tailCount = 0;
|
||||||
@@ -331,31 +267,37 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
|
|||||||
private bool IsBucketEmpty(int bucketIndex) => IsBucketEmpty(bucketIndex, _tails, _buckets);
|
private bool IsBucketEmpty(int bucketIndex) => IsBucketEmpty(bucketIndex, _tails, _buckets);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private bool IsBucketEmpty(int bucketIndex, Tails tails, T?[] buckets) => tails.IsEmpty(bucketIndex);
|
private static bool IsBucketEmpty(int bucketIndex, Tails tails, T?[] buckets) => tails.IsEmpty(bucketIndex);
|
||||||
|
|
||||||
private bool TryFindTailIndexAndReplace(T entry, ref int bucketIndex, bool allowReplace)
|
private bool TryFindTailIndexAndReplace(T entry, ref int bucketIndex, ReplaceBehaviour replaceBehaviour)
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (!TryContinueWithTail(ref bucketIndex))
|
if (!TryContinueWithTail(ref bucketIndex))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (TryReplaceValueOfMatchingKey(entry, allowReplace, ref _buckets[bucketIndex]!))
|
if (TryReplaceValueOfMatchingKey(entry, replaceBehaviour, ref _buckets[bucketIndex]!))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe bool TryReplaceValueOfMatchingKey(T entry, bool allowReplace, ref T bucket)
|
private unsafe bool TryReplaceValueOfMatchingKey(T entry, ReplaceBehaviour replaceBehaviour, ref T bucket)
|
||||||
{
|
{
|
||||||
if (!_equalityComparer(bucket, entry))
|
if (!_equalityComparer(bucket, entry))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!allowReplace)
|
switch (replaceBehaviour)
|
||||||
ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(entry);
|
{
|
||||||
|
case ReplaceBehaviour.Throw:
|
||||||
|
return ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(entry);
|
||||||
|
case ReplaceBehaviour.Replace:
|
||||||
bucket = entry;
|
bucket = entry;
|
||||||
|
goto default;
|
||||||
|
case ReplaceBehaviour.Fail:
|
||||||
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool InsertAsTail(T entry, int bucketIndex)
|
private bool InsertAsTail(T entry, int bucketIndex)
|
||||||
{
|
{
|
||||||
@@ -382,29 +324,40 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
|
|||||||
WarnForPossiblyExcessiveResizing(entry);
|
WarnForPossiblyExcessiveResizing(entry);
|
||||||
|
|
||||||
Resize();
|
Resize();
|
||||||
InsertEntry(entry, false, true);
|
InsertEntry(entry, ReplaceBehaviour.Throw, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var previousEntry = _buckets[bucketIndex];
|
var previousEntry = _buckets[bucketIndex];
|
||||||
var hasTail = !_tails.IsSolo(bucketIndex);
|
var tailingEntries = TryGetAndClearTailingEntries(bucketIndex);
|
||||||
|
|
||||||
var tailingEntries = hasTail ? new T?[GetTailCount(bucketIndex)] : null;
|
SetParentTail(bucketIndex, Tails.SOLO);
|
||||||
|
SetEntryAsTail(bucketIndex, entry, parentIndex, offset);
|
||||||
|
|
||||||
if (hasTail)
|
InsertEntry(previousEntry!, ReplaceBehaviour.Throw, true);
|
||||||
|
|
||||||
|
if (tailingEntries != null)
|
||||||
|
InsertEntries(tailingEntries, ReplaceBehaviour.Throw, true);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private T[]? TryGetAndClearTailingEntries(int bucketIndex)
|
||||||
{
|
{
|
||||||
var k = 0;
|
if (!HasTail(bucketIndex))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var tailingEntries = new T[GetTailCount(bucketIndex)];
|
||||||
|
var i = 0;
|
||||||
var tailIndex = GetTailIndex(bucketIndex);
|
var tailIndex = GetTailIndex(bucketIndex);
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
ref var tailingBucket = ref _buckets[tailIndex];
|
ref var tailingBucket = ref _buckets[tailIndex];
|
||||||
tailingEntries![k] = tailingBucket;
|
tailingEntries[i++] = tailingBucket!;
|
||||||
|
|
||||||
k++;
|
|
||||||
tailingBucket = default;
|
tailingBucket = default;
|
||||||
|
|
||||||
if (!_tails.IsSoloOrEmpty(tailIndex))
|
if (HasTail(tailIndex))
|
||||||
{
|
{
|
||||||
var nextTailIndex = GetTailIndex(tailIndex);
|
var nextTailIndex = GetTailIndex(tailIndex);
|
||||||
_tails.SetEmpty(tailIndex);
|
_tails.SetEmpty(tailIndex);
|
||||||
@@ -416,20 +369,8 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
SetParentTail(bucketIndex, Tails.SOLO);
|
return tailingEntries;
|
||||||
SetEntryAsTail(bucketIndex, entry, parentIndex, offset);
|
|
||||||
|
|
||||||
InsertEntry(previousEntry!, shifting: true);
|
|
||||||
|
|
||||||
if (hasTail)
|
|
||||||
{
|
|
||||||
for (var k = 0; k < tailingEntries!.Length; k++)
|
|
||||||
InsertEntry(tailingEntries[k]!, shifting: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
@@ -474,7 +415,7 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
|
|||||||
for (var i = 0; i < oldBuckets.Length; i++)
|
for (var i = 0; i < oldBuckets.Length; i++)
|
||||||
{
|
{
|
||||||
if (!IsBucketEmpty(i, oldTails, oldBuckets))
|
if (!IsBucketEmpty(i, oldTails, oldBuckets))
|
||||||
InsertEntry(oldBuckets[i]!, false, true);
|
InsertEntry(oldBuckets[i]!, ReplaceBehaviour.Throw, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,7 +486,7 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
|
|||||||
public bool Add(T key)
|
public bool Add(T key)
|
||||||
{
|
{
|
||||||
Guard.IsNotNull(key);
|
Guard.IsNotNull(key);
|
||||||
return InsertEntry(key, false);
|
return InsertEntry(key, ReplaceBehaviour.Fail);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UnionWith(IEnumerable<T> other) => throw new NotImplementedException();
|
public void UnionWith(IEnumerable<T> other) => throw new NotImplementedException();
|
||||||
@@ -578,36 +519,23 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EmplaceWithTails(int entryIndex)
|
private void EmplaceWithTailsBackward(int entryIndex)
|
||||||
{
|
{
|
||||||
ref var bucket = ref _buckets[entryIndex];
|
if (IsBucketEmpty(entryIndex))
|
||||||
var entry = bucket;
|
Utility.Diagnostics.ThrowHelper.ThrowInvalidOperationException("Tried to emplace entry bucket");
|
||||||
|
|
||||||
Span<int> tailIndices = stackalloc int[GetTailCount(entryIndex)];
|
while (true)
|
||||||
|
|
||||||
if (HasTail(entryIndex))
|
|
||||||
{
|
{
|
||||||
tailIndices[0] = GetTailIndex(entryIndex);
|
var tailIndex = HasTail(entryIndex) ? GetTailIndex(entryIndex) : -1;
|
||||||
for (var i = 1; i < tailIndices.Length; i++)
|
var entry = _buckets[entryIndex];
|
||||||
tailIndices[i] = GetTailIndex(tailIndices[i - 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsTail(entry!, entryIndex))
|
SetBucketEmpty(entryIndex);
|
||||||
SetParentTail(entryIndex, Tails.SOLO);
|
InsertEntry(entry!, ReplaceBehaviour.Throw, true);
|
||||||
|
|
||||||
bucket = default;
|
if (tailIndex < 0)
|
||||||
_tails.SetEmpty(entryIndex);
|
return;
|
||||||
InsertEntry(entry!, shifting: true);
|
|
||||||
|
|
||||||
for (var i = 1; i < tailIndices.Length; i++)
|
entryIndex = tailIndex;
|
||||||
{
|
|
||||||
var tailIndex = tailIndices[i];
|
|
||||||
ref var tailBucket = ref _buckets[tailIndex];
|
|
||||||
var tailEntry = tailBucket;
|
|
||||||
|
|
||||||
tailBucket = default;
|
|
||||||
_tails.SetEmpty(tailIndex);
|
|
||||||
InsertEntry(tailEntry!, shifting: true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -621,20 +549,15 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
|
|||||||
|
|
||||||
if (_equalityComparer(key, entry))
|
if (_equalityComparer(key, entry))
|
||||||
{
|
{
|
||||||
var tailIndex = -1;
|
var tailIndex = HasTail(bucketIndex) ? GetTailIndex(bucketIndex) : -1;
|
||||||
if (IsTail(entry!, bucketIndex))
|
|
||||||
{
|
|
||||||
if (HasTail(bucketIndex))
|
|
||||||
tailIndex = GetTailIndex(bucketIndex);
|
|
||||||
else
|
|
||||||
SetParentTail(bucketIndex, Tails.SOLO);
|
|
||||||
}
|
|
||||||
|
|
||||||
entry = default;
|
if (IsTail(entry!, bucketIndex))
|
||||||
_tails.SetEmpty(bucketIndex);
|
SetParentTail(bucketIndex, Tails.SOLO);
|
||||||
|
|
||||||
|
SetBucketEmpty(bucketIndex);
|
||||||
|
|
||||||
if (tailIndex != -1)
|
if (tailIndex != -1)
|
||||||
EmplaceWithTails(tailIndex);
|
EmplaceWithTailsBackward(tailIndex);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -714,17 +637,27 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region enums
|
||||||
|
|
||||||
|
private enum ReplaceBehaviour
|
||||||
|
{
|
||||||
|
Replace,
|
||||||
|
Throw,
|
||||||
|
Fail
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
#region Structs
|
#region Structs
|
||||||
|
|
||||||
internal struct Tails
|
internal struct Tails(uint length)
|
||||||
{
|
{
|
||||||
public const uint
|
public const uint
|
||||||
EMPTY = 0u,
|
EMPTY = 0u,
|
||||||
SOLO = 1u;
|
SOLO = 1u;
|
||||||
|
|
||||||
private NibbleArray _values;
|
private NibbleArray _values = new(length);
|
||||||
|
|
||||||
public Tails(uint length) => _values = new(length);
|
|
||||||
|
|
||||||
public uint this[uint index]
|
public uint this[uint index]
|
||||||
{
|
{
|
||||||
@@ -837,10 +770,11 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
|
|||||||
}
|
}
|
||||||
|
|
||||||
[DoesNotReturn]
|
[DoesNotReturn]
|
||||||
internal static void ThrowAddingDuplicateWithKeyArgumentException(T key)
|
internal static bool ThrowAddingDuplicateWithKeyArgumentException(T key)
|
||||||
{
|
{
|
||||||
Guard.IsNotNull(key);
|
Guard.IsNotNull(key);
|
||||||
ThrowAddingDuplicateWithKeyArgumentException((object)key);
|
ThrowAddingDuplicateWithKeyArgumentException((object)key);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[DoesNotReturn]
|
[DoesNotReturn]
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,260 @@
|
|||||||
|
// 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;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
|
namespace FisheryLib.Collections;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indexable collection similar to a list, but with constant time Contains, Remove, Insert and IndexOf methods for any
|
||||||
|
/// argument type. Order of elements is not preserved on inserting and removing however. Displacement happens between
|
||||||
|
/// the target index and the last slot. X and Y are individually enforced to be unique
|
||||||
|
/// </summary>
|
||||||
|
[PublicAPI]
|
||||||
|
public class IndexedFishMap<TX, TY> : IList<(TX X, TY Y)>, IList, IReadOnlyList<(TX X, TY Y)>
|
||||||
|
// IDictionary<TX, TY>, IDictionary<TY, TX>
|
||||||
|
{
|
||||||
|
private readonly List<(TX X, TY Y)> _items = [];
|
||||||
|
private readonly FishTable<TX, int> _xIndices = new();
|
||||||
|
private readonly FishTable<TY, int> _yIndices = new();
|
||||||
|
|
||||||
|
public (TX X, TY Y) this[int index]
|
||||||
|
{
|
||||||
|
get => _items[index];
|
||||||
|
set
|
||||||
|
{
|
||||||
|
var previousValue = _items[index];
|
||||||
|
_items[index] = value;
|
||||||
|
|
||||||
|
_xIndices.Remove(previousValue.X);
|
||||||
|
_xIndices.Add(value.X, index);
|
||||||
|
|
||||||
|
_yIndices.Remove(previousValue.Y);
|
||||||
|
_yIndices.Add(value.Y, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TY this[TX x]
|
||||||
|
{
|
||||||
|
get => _items[_xIndices[x]].Y;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Start:
|
||||||
|
if (!_xIndices.TryGetValue(x, out var index))
|
||||||
|
index = _items.Count;
|
||||||
|
|
||||||
|
if (!_yIndices.TryAdd(value, index))
|
||||||
|
{
|
||||||
|
if (RemoveIfNotEqual(_yIndices, value, index))
|
||||||
|
goto Start;
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == _items.Count)
|
||||||
|
{
|
||||||
|
_xIndices[x] = index;
|
||||||
|
_items.Add((x, value));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_items[index] = (x, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TX this[TY y]
|
||||||
|
{
|
||||||
|
get => _items[_yIndices[y]].X;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Start:
|
||||||
|
if (!_yIndices.TryGetValue(y, out var index))
|
||||||
|
index = _items.Count;
|
||||||
|
|
||||||
|
if (!_xIndices.TryAdd(value, index))
|
||||||
|
{
|
||||||
|
if (RemoveIfNotEqual(_xIndices, value, index))
|
||||||
|
goto Start;
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == _items.Count)
|
||||||
|
{
|
||||||
|
_yIndices[y] = index;
|
||||||
|
_items.Add((value, y));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_items[index] = (value, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
private static bool RemoveIfNotEqual<TKey>(FishTable<TKey, int> fishTable, TKey key, int index)
|
||||||
|
=> fishTable[key] != index && fishTable.Remove(key);
|
||||||
|
|
||||||
|
public List<(TX X, TY Y)> ReadOnlyList => _items;
|
||||||
|
|
||||||
|
public List<(TX X, TY Y)>.Enumerator GetEnumerator() => _items.GetEnumerator();
|
||||||
|
|
||||||
|
IEnumerator<(TX X, TY Y)> IEnumerable<(TX X, TY Y)>.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
|
public void Add((TX X, TY Y) item)
|
||||||
|
{
|
||||||
|
if (_xIndices.ContainsKey(item.X) || !_yIndices.TryAdd(item.Y, _items.Count))
|
||||||
|
ThrowHelper.ThrowInvalidOperationException();
|
||||||
|
|
||||||
|
_xIndices.Add(item.X, _items.Count);
|
||||||
|
|
||||||
|
_items.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Add(object? value)
|
||||||
|
{
|
||||||
|
Add(EnsureCorrectType(value));
|
||||||
|
return _items.Count - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryAdd((TX X, TY Y) item)
|
||||||
|
{
|
||||||
|
if (_xIndices.ContainsKey(item.X) || !_yIndices.TryAdd(item.Y, _items.Count))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_xIndices.Add(item.X, _items.Count);
|
||||||
|
|
||||||
|
_items.Add(item);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(object? value) => IsCompatibleObject(value) && Contains(((TX X, TY Y))value);
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
_xIndices.Clear();
|
||||||
|
_items.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int IndexOf(object value) => IsCompatibleObject(value) ? IndexOf(((TX X, TY Y))value) : -1;
|
||||||
|
|
||||||
|
public void Insert(int index, object value) => Insert(index, EnsureCorrectType(value));
|
||||||
|
|
||||||
|
public void Remove(object value)
|
||||||
|
{
|
||||||
|
if (IsCompatibleObject(value))
|
||||||
|
Remove(((TX X, TY Y))value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains((TX X, TY Y) item) => _xIndices.ContainsKey(item.X);
|
||||||
|
|
||||||
|
public bool Contains(TX x) => _xIndices.ContainsKey(x);
|
||||||
|
|
||||||
|
public bool Contains(TY y) => _yIndices.ContainsKey(y);
|
||||||
|
|
||||||
|
public void CopyTo((TX X, TY Y)[] array, int arrayIndex) => _items.CopyTo(array, arrayIndex);
|
||||||
|
|
||||||
|
public bool Remove((TX X, TY Y) item)
|
||||||
|
{
|
||||||
|
if (!_xIndices.ContainsKey(item.X) || !_yIndices.Remove(item.Y))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_xIndices.Remove(item.X, out var index);
|
||||||
|
|
||||||
|
_items.RemoveAtFastUnordered(index);
|
||||||
|
if (index < _items.Count)
|
||||||
|
_xIndices[_items[index].X] = _yIndices[_items[index].Y] = index;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(TX x)
|
||||||
|
{
|
||||||
|
if (!_xIndices.Remove(x, out var index))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_yIndices.Remove(_items[index].Y);
|
||||||
|
|
||||||
|
_items.RemoveAtFastUnordered(index);
|
||||||
|
if (index < _items.Count)
|
||||||
|
_xIndices[_items[index].X] = _yIndices[_items[index].Y] = index;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(TY y)
|
||||||
|
{
|
||||||
|
if (!_yIndices.Remove(y, out var index))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_xIndices.Remove(_items[index].X);
|
||||||
|
|
||||||
|
_items.RemoveAtFastUnordered(index);
|
||||||
|
if (index < _items.Count)
|
||||||
|
_xIndices[_items[index].X] = _yIndices[_items[index].Y] = index;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyTo(Array array, int index) => ((IList)_items).CopyTo(array, index);
|
||||||
|
|
||||||
|
public int Count => _items.Count;
|
||||||
|
|
||||||
|
public object SyncRoot => this;
|
||||||
|
|
||||||
|
public bool IsSynchronized => false;
|
||||||
|
|
||||||
|
public bool IsReadOnly => false;
|
||||||
|
|
||||||
|
public bool IsFixedSize => false;
|
||||||
|
|
||||||
|
public int IndexOf((TX X, TY Y) item) => _xIndices.TryGetValue(item.X, out var index) ? index : -1;
|
||||||
|
|
||||||
|
public int IndexOf(TX x) => _xIndices.TryGetValue(x, out var index) ? index : -1;
|
||||||
|
|
||||||
|
public int IndexOf(TY y) => _yIndices.TryGetValue(y, out var index) ? index : -1;
|
||||||
|
|
||||||
|
public void Insert(int index, (TX X, TY Y) item)
|
||||||
|
{
|
||||||
|
if (_xIndices.ContainsKey(item.X) || !_yIndices.TryAdd(item.Y, index))
|
||||||
|
ThrowHelper.ThrowInvalidOperationException();
|
||||||
|
|
||||||
|
_xIndices.Add(item.X, index);
|
||||||
|
|
||||||
|
_items.InsertFastUnordered(index, item);
|
||||||
|
|
||||||
|
var lastIndex = _items.Count - 1;
|
||||||
|
_xIndices[_items[lastIndex].X] = _yIndices[_items[lastIndex].Y] = lastIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAt(int index)
|
||||||
|
{
|
||||||
|
var item = _items[index];
|
||||||
|
_items.RemoveAtFastUnordered(index);
|
||||||
|
_xIndices.Remove(item.X);
|
||||||
|
_yIndices.Remove(item.Y);
|
||||||
|
|
||||||
|
if (index < _items.Count)
|
||||||
|
_xIndices[_items[index].X] = _yIndices[_items[index].Y] = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
object IList.this[int index]
|
||||||
|
{
|
||||||
|
get => this[index];
|
||||||
|
set => this[index] = EnsureCorrectType(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsCompatibleObject([NotNullWhen(true)] object? value)
|
||||||
|
=> value is (TX, TY) || (value == null) & (default(TX) == null) & (default(TY) == null);
|
||||||
|
|
||||||
|
private static (TX X, TY Y) EnsureCorrectType([NotNullWhen(true)] object? item)
|
||||||
|
=> item is ValueTuple<TX, TY> t ? t : ThrowHelper.ThrowArgumentException<(TX X, TY Y)>();
|
||||||
|
}
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
// 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;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
|
namespace FisheryLib.Collections;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indexable collection similar to a list, but with constant time Contains, Remove, Insert and IndexOf methods. Order
|
||||||
|
/// of elements is not preserved on inserting and removing however. Displacement happens between the target index and
|
||||||
|
/// the last slot
|
||||||
|
/// </summary>
|
||||||
|
[PublicAPI]
|
||||||
|
public class IndexedFishSet<T> : IList<T>, IList, IReadOnlyList<T>
|
||||||
|
{
|
||||||
|
private readonly List<T> _items = [];
|
||||||
|
private readonly FishTable<T, int> _indices = new();
|
||||||
|
|
||||||
|
public T this[int index]
|
||||||
|
{
|
||||||
|
get => _items[index];
|
||||||
|
set
|
||||||
|
{
|
||||||
|
var previousValue = _items[index];
|
||||||
|
_items[index] = value;
|
||||||
|
_indices.Remove(previousValue);
|
||||||
|
_indices.Add(value, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<T> ReadOnlyList => _items;
|
||||||
|
|
||||||
|
public List<T>.Enumerator GetEnumerator() => _items.GetEnumerator();
|
||||||
|
|
||||||
|
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
|
public void Add(T item)
|
||||||
|
{
|
||||||
|
if (!_indices.TryAdd(item, _items.Count))
|
||||||
|
ThrowHelper.ThrowInvalidOperationException();
|
||||||
|
|
||||||
|
_items.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Add(object? value)
|
||||||
|
{
|
||||||
|
Add(EnsureCorrectType(value));
|
||||||
|
return _items.Count - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryAdd(T item)
|
||||||
|
{
|
||||||
|
if (!_indices.TryAdd(item, _items.Count))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_items.Add(item);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(object? value) => IsCompatibleObject(value) && Contains((T)value);
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
_indices.Clear();
|
||||||
|
_items.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int IndexOf(object value) => IsCompatibleObject(value) ? IndexOf((T)value) : -1;
|
||||||
|
|
||||||
|
public void Insert(int index, object value) => Insert(index, EnsureCorrectType(value));
|
||||||
|
|
||||||
|
public void Remove(object value)
|
||||||
|
{
|
||||||
|
if (IsCompatibleObject(value))
|
||||||
|
Remove((T)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(T item) => _indices.ContainsKey(item);
|
||||||
|
|
||||||
|
public void CopyTo(T[] array, int arrayIndex) => _items.CopyTo(array, arrayIndex);
|
||||||
|
|
||||||
|
public bool Remove(T item)
|
||||||
|
{
|
||||||
|
if (!_indices.Remove(item, out var index))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_items.RemoveAtFastUnordered(index);
|
||||||
|
if (index < _items.Count)
|
||||||
|
_indices[_items[index]] = index;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyTo(Array array, int index) => ((IList)_items).CopyTo(array, index);
|
||||||
|
|
||||||
|
public int Count => _items.Count;
|
||||||
|
|
||||||
|
public object SyncRoot => this;
|
||||||
|
|
||||||
|
public bool IsSynchronized => false;
|
||||||
|
|
||||||
|
public bool IsReadOnly => false;
|
||||||
|
|
||||||
|
public bool IsFixedSize => false;
|
||||||
|
|
||||||
|
public int IndexOf(T item) => _indices.TryGetValue(item, out var index) ? index : -1;
|
||||||
|
|
||||||
|
public void Insert(int index, T item)
|
||||||
|
{
|
||||||
|
if (!_indices.TryAdd(item, index))
|
||||||
|
ThrowHelper.ThrowInvalidOperationException();
|
||||||
|
|
||||||
|
_items.InsertFastUnordered(index, item);
|
||||||
|
|
||||||
|
var lastIndex = _items.Count - 1;
|
||||||
|
_indices[_items[lastIndex]] = lastIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAt(int index)
|
||||||
|
{
|
||||||
|
var item = _items[index];
|
||||||
|
_items.RemoveAtFastUnordered(index);
|
||||||
|
_indices.Remove(item);
|
||||||
|
|
||||||
|
if (index < _items.Count)
|
||||||
|
_indices[_items[index]] = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
object IList.this[int index]
|
||||||
|
{
|
||||||
|
get => this[index]!;
|
||||||
|
set => this[index] = EnsureCorrectType(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsCompatibleObject([NotNullWhen(true)] object? value)
|
||||||
|
=> value is T || (value == null && default(T) == null);
|
||||||
|
|
||||||
|
private static T EnsureCorrectType([NotNullWhen(true)] object? item)
|
||||||
|
=> item is T t ? t : ThrowHelper.ThrowArgumentException<T>();
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ using JetBrains.Annotations;
|
|||||||
namespace FisheryLib.Collections;
|
namespace FisheryLib.Collections;
|
||||||
|
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public struct NibbleArray
|
public record struct NibbleArray
|
||||||
{
|
{
|
||||||
private uint _length;
|
private uint _length;
|
||||||
private byte[] _data;
|
private byte[] _data;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
@@ -15,7 +16,8 @@ public static class TypeExtensions
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The mapping of built-in types to their simple representation.
|
/// The mapping of built-in types to their simple representation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly IReadOnlyDictionary<Type, string> BuiltInTypesMap = new Dictionary<Type, string>
|
[SuppressMessage("Performance", "CA1859")]
|
||||||
|
private static readonly IReadOnlyDictionary<Type, string> _builtInTypesMap = new Dictionary<Type, string>
|
||||||
{
|
{
|
||||||
[typeof(bool)] = "bool",
|
[typeof(bool)] = "bool",
|
||||||
[typeof(byte)] = "byte",
|
[typeof(byte)] = "byte",
|
||||||
@@ -38,7 +40,7 @@ public static class TypeExtensions
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A thread-safe mapping of precomputed string representation of types.
|
/// A thread-safe mapping of precomputed string representation of types.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly ConditionalWeakTable<Type, string> DisplayNames = new();
|
private static readonly ConditionalWeakTable<Type, string> _displayNames = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a simple string representation of a type.
|
/// Returns a simple string representation of a type.
|
||||||
@@ -51,7 +53,7 @@ public static class TypeExtensions
|
|||||||
static string FormatDisplayString(Type type, int genericTypeOffset, ReadOnlySpan<Type> typeArguments)
|
static string FormatDisplayString(Type type, int genericTypeOffset, ReadOnlySpan<Type> typeArguments)
|
||||||
{
|
{
|
||||||
// Primitive types use the keyword name
|
// Primitive types use the keyword name
|
||||||
if (BuiltInTypesMap.TryGetValue(type, out var typeName))
|
if (_builtInTypesMap.TryGetValue(type, out var typeName))
|
||||||
return typeName!;
|
return typeName!;
|
||||||
|
|
||||||
// Array types are displayed as Foo[]
|
// Array types are displayed as Foo[]
|
||||||
@@ -131,7 +133,7 @@ public static class TypeExtensions
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Atomically get or build the display string for the current type.
|
// Atomically get or build the display string for the current type.
|
||||||
return DisplayNames.GetValue(type, static t =>
|
return _displayNames.GetValue(type, static t =>
|
||||||
{
|
{
|
||||||
// By-ref types are displayed as T&
|
// By-ref types are displayed as T&
|
||||||
if (t.IsByRef)
|
if (t.IsByRef)
|
||||||
|
|||||||
@@ -7,12 +7,16 @@ using System.Diagnostics.CodeAnalysis;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
namespace FisheryLib.Utility.Diagnostics;
|
namespace FisheryLib.Utility.Diagnostics;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper methods to efficiently throw exceptions.
|
/// Helper methods to efficiently throw exceptions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[PublicAPI]
|
||||||
|
[SuppressMessage("Usage", "CA2201")]
|
||||||
public static partial class ThrowHelper
|
public static partial class ThrowHelper
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
@@ -200,6 +201,7 @@ public static class FishTranspiler
|
|||||||
|
|
||||||
public static bool operator !=(CodeInstruction code, Container helper) => !(helper == code);
|
public static bool operator !=(CodeInstruction code, Container helper) => !(helper == code);
|
||||||
|
|
||||||
|
[SuppressMessage("Usage", "CA2225")]
|
||||||
public static implicit operator CodeInstruction(Container helper) => helper.ToInstruction();
|
public static implicit operator CodeInstruction(Container helper) => helper.ToInstruction();
|
||||||
|
|
||||||
//public static implicit operator Container(CodeInstruction instruction)
|
//public static implicit operator Container(CodeInstruction instruction)
|
||||||
@@ -701,13 +703,15 @@ public static class FishTranspiler
|
|||||||
/// Pushes a supplied value of type int32 onto the evaluation stack as an int32.
|
/// Pushes a supplied value of type int32 onto the evaluation stack as an int32.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="integer">The value to push onto the evaluation stack</param>
|
/// <param name="integer">The value to push onto the evaluation stack</param>
|
||||||
public static Container Constant(int integer) => new(GetLoadConstantOpCode(integer), GetOperandOfConstant(integer));
|
public static Container Constant([SuppressMessage("Naming", "CA1720")] int integer)
|
||||||
|
=> new(GetLoadConstantOpCode(integer), GetOperandOfConstant(integer));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Pushes a supplied value of type int64 onto the evaluation stack as an int64.
|
/// Pushes a supplied value of type int64 onto the evaluation stack as an int64.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="integer">The value to push onto the evaluation stack</param>
|
/// <param name="integer">The value to push onto the evaluation stack</param>
|
||||||
public static Container Constant(long integer) => new(OpCodes.Ldc_I8, integer);
|
public static Container Constant([SuppressMessage("Naming", "CA1720")] long integer)
|
||||||
|
=> new(OpCodes.Ldc_I8, integer);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Pushes a supplied value of type float32 onto the evaluation stack as type F (float).
|
/// Pushes a supplied value of type float32 onto the evaluation stack as type F (float).
|
||||||
@@ -1129,6 +1133,7 @@ public static class FishTranspiler
|
|||||||
/// Pushes a new object reference to a string literal stored in the metadata.
|
/// Pushes a new object reference to a string literal stored in the metadata.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="text">The string literal</param>
|
/// <param name="text">The string literal</param>
|
||||||
|
[SuppressMessage("Naming", "CA1720")]
|
||||||
public static Container String(string text)
|
public static Container String(string text)
|
||||||
{
|
{
|
||||||
Guard.IsNotNull(text);
|
Guard.IsNotNull(text);
|
||||||
@@ -2425,72 +2430,34 @@ public static class FishTranspiler
|
|||||||
|
|
||||||
public static bool StoresElement(this OpCode opcode) => StoreElementOpCodes.Contains(opcode);
|
public static bool StoresElement(this OpCode opcode) => StoreElementOpCodes.Contains(opcode);
|
||||||
|
|
||||||
public static readonly HashSet<OpCode> LoadElementOpCodes
|
public static readonly HashSet<OpCode> LoadElementOpCodes =
|
||||||
= new()
|
[
|
||||||
{
|
OpCodes.Ldelem, OpCodes.Ldelema, OpCodes.Ldelem_I, OpCodes.Ldelem_I1, OpCodes.Ldelem_I2, OpCodes.Ldelem_I4,
|
||||||
OpCodes.Ldelem,
|
OpCodes.Ldelem_I8, OpCodes.Ldelem_R4, OpCodes.Ldelem_R8, OpCodes.Ldelem_Ref, OpCodes.Ldelem_U1,
|
||||||
OpCodes.Ldelema,
|
OpCodes.Ldelem_U2, OpCodes.Ldelem_U4
|
||||||
OpCodes.Ldelem_I,
|
];
|
||||||
OpCodes.Ldelem_I1,
|
|
||||||
OpCodes.Ldelem_I2,
|
|
||||||
OpCodes.Ldelem_I4,
|
|
||||||
OpCodes.Ldelem_I8,
|
|
||||||
OpCodes.Ldelem_R4,
|
|
||||||
OpCodes.Ldelem_R8,
|
|
||||||
OpCodes.Ldelem_Ref,
|
|
||||||
OpCodes.Ldelem_U1,
|
|
||||||
OpCodes.Ldelem_U2,
|
|
||||||
OpCodes.Ldelem_U4
|
|
||||||
};
|
|
||||||
|
|
||||||
public static readonly HashSet<OpCode> StoreElementOpCodes
|
public static readonly HashSet<OpCode> StoreElementOpCodes =
|
||||||
= new()
|
[
|
||||||
{
|
OpCodes.Stelem, OpCodes.Stelem_I, OpCodes.Stelem_I1, OpCodes.Stelem_I2, OpCodes.Stelem_I4,
|
||||||
OpCodes.Stelem,
|
OpCodes.Stelem_I8, OpCodes.Stelem_R4, OpCodes.Stelem_R8, OpCodes.Stelem_Ref
|
||||||
OpCodes.Stelem_I,
|
];
|
||||||
OpCodes.Stelem_I1,
|
|
||||||
OpCodes.Stelem_I2,
|
|
||||||
OpCodes.Stelem_I4,
|
|
||||||
OpCodes.Stelem_I8,
|
|
||||||
OpCodes.Stelem_R4,
|
|
||||||
OpCodes.Stelem_R8,
|
|
||||||
OpCodes.Stelem_Ref
|
|
||||||
};
|
|
||||||
|
|
||||||
public static bool LoadsByRef(this OpCode opcode) => LoadByRefOpCodes.Contains(opcode);
|
public static bool LoadsByRef(this OpCode opcode) => LoadByRefOpCodes.Contains(opcode);
|
||||||
|
|
||||||
public static bool StoresByRef(this OpCode opcode) => StoreByRefOpCodes.Contains(opcode);
|
public static bool StoresByRef(this OpCode opcode) => StoreByRefOpCodes.Contains(opcode);
|
||||||
|
|
||||||
public static readonly HashSet<OpCode> LoadByRefOpCodes
|
public static readonly HashSet<OpCode> LoadByRefOpCodes =
|
||||||
= new()
|
[
|
||||||
{
|
OpCodes.Ldobj, OpCodes.Ldind_I, OpCodes.Ldind_I1, OpCodes.Ldind_I2, OpCodes.Ldind_I4, OpCodes.Ldind_I8,
|
||||||
OpCodes.Ldobj,
|
OpCodes.Ldind_R4, OpCodes.Ldind_R8, OpCodes.Ldind_Ref, OpCodes.Ldind_U1, OpCodes.Ldind_U2, OpCodes.Ldind_U4
|
||||||
OpCodes.Ldind_I,
|
];
|
||||||
OpCodes.Ldind_I1,
|
|
||||||
OpCodes.Ldind_I2,
|
|
||||||
OpCodes.Ldind_I4,
|
|
||||||
OpCodes.Ldind_I8,
|
|
||||||
OpCodes.Ldind_R4,
|
|
||||||
OpCodes.Ldind_R8,
|
|
||||||
OpCodes.Ldind_Ref,
|
|
||||||
OpCodes.Ldind_U1,
|
|
||||||
OpCodes.Ldind_U2,
|
|
||||||
OpCodes.Ldind_U4
|
|
||||||
};
|
|
||||||
|
|
||||||
public static readonly HashSet<OpCode> StoreByRefOpCodes
|
public static readonly HashSet<OpCode> StoreByRefOpCodes =
|
||||||
= new()
|
[
|
||||||
{
|
OpCodes.Stobj, OpCodes.Stind_I, OpCodes.Stind_I1, OpCodes.Stind_I2, OpCodes.Stind_I4, OpCodes.Stind_I8,
|
||||||
OpCodes.Stobj,
|
OpCodes.Stind_R4, OpCodes.Stind_R8, OpCodes.Stind_Ref
|
||||||
OpCodes.Stind_I,
|
];
|
||||||
OpCodes.Stind_I1,
|
|
||||||
OpCodes.Stind_I2,
|
|
||||||
OpCodes.Stind_I4,
|
|
||||||
OpCodes.Stind_I8,
|
|
||||||
OpCodes.Stind_R4,
|
|
||||||
OpCodes.Stind_R8,
|
|
||||||
OpCodes.Stind_Ref
|
|
||||||
};
|
|
||||||
|
|
||||||
public static bool LoadsField(this OpCode opcode) => opcode.LoadsInstanceField() || opcode.LoadsStaticField();
|
public static bool LoadsField(this OpCode opcode) => opcode.LoadsInstanceField() || opcode.LoadsStaticField();
|
||||||
|
|
||||||
@@ -2538,100 +2505,38 @@ public static class FishTranspiler
|
|||||||
public static bool Compares(this OpCode opcode) => CompareOpCodes.Contains(opcode);
|
public static bool Compares(this OpCode opcode) => CompareOpCodes.Contains(opcode);
|
||||||
|
|
||||||
public static readonly HashSet<OpCode> CompareOpCodes
|
public static readonly HashSet<OpCode> CompareOpCodes
|
||||||
= new()
|
= [OpCodes.Ceq, OpCodes.Cgt, OpCodes.Cgt_Un, OpCodes.Clt, OpCodes.Clt_Un];
|
||||||
{
|
|
||||||
OpCodes.Ceq,
|
|
||||||
OpCodes.Cgt,
|
|
||||||
OpCodes.Cgt_Un,
|
|
||||||
OpCodes.Clt,
|
|
||||||
OpCodes.Clt_Un
|
|
||||||
};
|
|
||||||
|
|
||||||
public static bool Computes(this OpCode opcode) => ComputeOpCodes.Contains(opcode);
|
public static bool Computes(this OpCode opcode) => ComputeOpCodes.Contains(opcode);
|
||||||
|
|
||||||
public static readonly HashSet<OpCode> ComputeOpCodes
|
public static readonly HashSet<OpCode> ComputeOpCodes =
|
||||||
= new()
|
[
|
||||||
{
|
OpCodes.Add, OpCodes.Add_Ovf, OpCodes.Add_Ovf_Un, OpCodes.Sub, OpCodes.Sub_Ovf, OpCodes.Sub_Ovf_Un,
|
||||||
OpCodes.Add,
|
OpCodes.Mul, OpCodes.Mul_Ovf, OpCodes.Mul_Ovf_Un, OpCodes.Div, OpCodes.Div_Un, OpCodes.Rem, OpCodes.Rem_Un,
|
||||||
OpCodes.Add_Ovf,
|
OpCodes.And, OpCodes.Or, OpCodes.Xor, OpCodes.Shl, OpCodes.Shr, OpCodes.Shr_Un, OpCodes.Not, OpCodes.Neg
|
||||||
OpCodes.Add_Ovf_Un,
|
];
|
||||||
OpCodes.Sub,
|
|
||||||
OpCodes.Sub_Ovf,
|
|
||||||
OpCodes.Sub_Ovf_Un,
|
|
||||||
OpCodes.Mul,
|
|
||||||
OpCodes.Mul_Ovf,
|
|
||||||
OpCodes.Mul_Ovf_Un,
|
|
||||||
OpCodes.Div,
|
|
||||||
OpCodes.Div_Un,
|
|
||||||
OpCodes.Rem,
|
|
||||||
OpCodes.Rem_Un,
|
|
||||||
OpCodes.And,
|
|
||||||
OpCodes.Or,
|
|
||||||
OpCodes.Xor,
|
|
||||||
OpCodes.Shl,
|
|
||||||
OpCodes.Shr,
|
|
||||||
OpCodes.Shr_Un,
|
|
||||||
OpCodes.Not,
|
|
||||||
OpCodes.Neg
|
|
||||||
};
|
|
||||||
|
|
||||||
public static bool Converts(this OpCode opcode) => ConvertOpCodes.Contains(opcode);
|
public static bool Converts(this OpCode opcode) => ConvertOpCodes.Contains(opcode);
|
||||||
|
|
||||||
public static readonly HashSet<OpCode> ConvertOpCodes
|
public static readonly HashSet<OpCode> ConvertOpCodes =
|
||||||
= new()
|
[
|
||||||
{
|
OpCodes.Conv_I, OpCodes.Conv_I1, OpCodes.Conv_I2, OpCodes.Conv_I4, OpCodes.Conv_I8, OpCodes.Conv_Ovf_I,
|
||||||
OpCodes.Conv_I,
|
OpCodes.Conv_Ovf_I1, OpCodes.Conv_Ovf_I1_Un, OpCodes.Conv_Ovf_I2, OpCodes.Conv_Ovf_I2_Un,
|
||||||
OpCodes.Conv_I1,
|
OpCodes.Conv_Ovf_I4, OpCodes.Conv_Ovf_I4_Un, OpCodes.Conv_Ovf_I8, OpCodes.Conv_Ovf_I8_Un,
|
||||||
OpCodes.Conv_I2,
|
OpCodes.Conv_Ovf_I_Un, OpCodes.Conv_Ovf_U, OpCodes.Conv_Ovf_U1, OpCodes.Conv_Ovf_U1_Un, OpCodes.Conv_Ovf_U2,
|
||||||
OpCodes.Conv_I4,
|
OpCodes.Conv_Ovf_U2_Un, OpCodes.Conv_Ovf_U4, OpCodes.Conv_Ovf_U4_Un, OpCodes.Conv_Ovf_U8,
|
||||||
OpCodes.Conv_I8,
|
OpCodes.Conv_Ovf_U8_Un, OpCodes.Conv_Ovf_U_Un, OpCodes.Conv_R4, OpCodes.Conv_R8, OpCodes.Conv_R_Un,
|
||||||
OpCodes.Conv_Ovf_I,
|
OpCodes.Conv_U, OpCodes.Conv_U1, OpCodes.Conv_U2, OpCodes.Conv_U4, OpCodes.Conv_U8, OpCodes.Box,
|
||||||
OpCodes.Conv_Ovf_I1,
|
OpCodes.Unbox, OpCodes.Unbox_Any
|
||||||
OpCodes.Conv_Ovf_I1_Un,
|
];
|
||||||
OpCodes.Conv_Ovf_I2,
|
|
||||||
OpCodes.Conv_Ovf_I2_Un,
|
|
||||||
OpCodes.Conv_Ovf_I4,
|
|
||||||
OpCodes.Conv_Ovf_I4_Un,
|
|
||||||
OpCodes.Conv_Ovf_I8,
|
|
||||||
OpCodes.Conv_Ovf_I8_Un,
|
|
||||||
OpCodes.Conv_Ovf_I_Un,
|
|
||||||
OpCodes.Conv_Ovf_U,
|
|
||||||
OpCodes.Conv_Ovf_U1,
|
|
||||||
OpCodes.Conv_Ovf_U1_Un,
|
|
||||||
OpCodes.Conv_Ovf_U2,
|
|
||||||
OpCodes.Conv_Ovf_U2_Un,
|
|
||||||
OpCodes.Conv_Ovf_U4,
|
|
||||||
OpCodes.Conv_Ovf_U4_Un,
|
|
||||||
OpCodes.Conv_Ovf_U8,
|
|
||||||
OpCodes.Conv_Ovf_U8_Un,
|
|
||||||
OpCodes.Conv_Ovf_U_Un,
|
|
||||||
OpCodes.Conv_R4,
|
|
||||||
OpCodes.Conv_R8,
|
|
||||||
OpCodes.Conv_R_Un,
|
|
||||||
OpCodes.Conv_U,
|
|
||||||
OpCodes.Conv_U1,
|
|
||||||
OpCodes.Conv_U2,
|
|
||||||
OpCodes.Conv_U4,
|
|
||||||
OpCodes.Conv_U8,
|
|
||||||
OpCodes.Box,
|
|
||||||
OpCodes.Unbox,
|
|
||||||
OpCodes.Unbox_Any
|
|
||||||
};
|
|
||||||
|
|
||||||
public static bool LoadsArgument(this OpCode opcode) => LoadArgumentOpCodes.Contains(opcode);
|
public static bool LoadsArgument(this OpCode opcode) => LoadArgumentOpCodes.Contains(opcode);
|
||||||
|
|
||||||
public static readonly HashSet<OpCode> LoadArgumentOpCodes
|
public static readonly HashSet<OpCode> LoadArgumentOpCodes =
|
||||||
= new()
|
[
|
||||||
{
|
OpCodes.Ldarg, OpCodes.Ldarga, OpCodes.Ldarga_S, OpCodes.Ldarg_0, OpCodes.Ldarg_1, OpCodes.Ldarg_2,
|
||||||
OpCodes.Ldarg,
|
OpCodes.Ldarg_3, OpCodes.Ldarg_S
|
||||||
OpCodes.Ldarga,
|
];
|
||||||
OpCodes.Ldarga_S,
|
|
||||||
OpCodes.Ldarg_0,
|
|
||||||
OpCodes.Ldarg_1,
|
|
||||||
OpCodes.Ldarg_2,
|
|
||||||
OpCodes.Ldarg_3,
|
|
||||||
OpCodes.Ldarg_S
|
|
||||||
};
|
|
||||||
|
|
||||||
public static OpCode ToStoreArgument(this OpCode opcode)
|
public static OpCode ToStoreArgument(this OpCode opcode)
|
||||||
=> opcode.StoresArgument() ? opcode
|
=> opcode.StoresArgument() ? opcode
|
||||||
@@ -2766,7 +2671,8 @@ public static class FishTranspiler
|
|||||||
|
|
||||||
public static object? GetOperandFromIndex(int index) => index > 3 ? index : null;
|
public static object? GetOperandFromIndex(int index) => index > 3 ? index : null;
|
||||||
|
|
||||||
public static object? GetOperandOfConstant(int integer) => integer is < 9 and > -2 ? null : integer;
|
public static object? GetOperandOfConstant([SuppressMessage("Naming", "CA1720")] int integer)
|
||||||
|
=> integer is < 9 and > -2 ? null : integer;
|
||||||
|
|
||||||
public static object? GetOperandFromBuilder(LocalBuilder builder)
|
public static object? GetOperandFromBuilder(LocalBuilder builder)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,20 +14,38 @@ global using CodeInstructions = System.Collections.Generic.IEnumerable<HarmonyLi
|
|||||||
using System.Security;
|
using System.Security;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
[assembly: AllowPartiallyTrustedCallers]
|
[assembly: AllowPartiallyTrustedCallers]
|
||||||
[assembly: SecurityRules(SecurityRuleSet.Level2, SkipVerificationInFullTrust = true)]
|
[assembly: SecurityRules(SecurityRuleSet.Level2, SkipVerificationInFullTrust = true)]
|
||||||
[assembly: Debuggable(false, false)]
|
[assembly: Debuggable(false, false)]
|
||||||
|
[assembly: AssemblyVersion(FisheryLib.FisheryLib.VERSION_STRING)]
|
||||||
|
|
||||||
[module: SkipLocalsInit]
|
[module: SkipLocalsInit]
|
||||||
|
|
||||||
namespace FisheryLib;
|
namespace FisheryLib;
|
||||||
|
|
||||||
|
[PublicAPI]
|
||||||
public static class FisheryLib
|
public static class FisheryLib
|
||||||
{
|
{
|
||||||
public const decimal VERSION = 0.380085M;
|
public const decimal VERSION = 0.39995M;
|
||||||
|
|
||||||
public static decimal CurrentlyLoadedVersion { get; } = VERSION;
|
public static decimal CurrentlyLoadedVersion { get; } = VERSION;
|
||||||
|
|
||||||
|
public const string VERSION_STRING = "0.3.999.5";
|
||||||
|
|
||||||
|
// public static string GetVersion()
|
||||||
|
// {
|
||||||
|
// var text = File.ReadAllText("Fishery.cs");
|
||||||
|
// const string VERSION_FIELD_NAME = "VERSION_STRING = \"";
|
||||||
|
// var versionIndex = text.IndexOf(VERSION_FIELD_NAME, StringComparison.InvariantCulture);
|
||||||
|
// versionIndex += VERSION_FIELD_NAME.Length;
|
||||||
|
// var versionEndIndex = versionIndex;
|
||||||
|
// while (text[versionEndIndex] != '"')
|
||||||
|
// versionEndIndex++;
|
||||||
|
//
|
||||||
|
// return text.Substring(versionIndex, versionEndIndex - versionIndex);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
/*namespace FisheryLib;
|
/*namespace FisheryLib;
|
||||||
|
|||||||
@@ -20,8 +20,15 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
<Configurations>v1.4;v1.3</Configurations>
|
<Configurations>v1.4;v1.3</Configurations>
|
||||||
<Version>0.3.800.85</Version>
|
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
|
||||||
<NoWarn>1701;1702;IL2121;CS8500;CS9082</NoWarn>
|
<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>
|
</PropertyGroup>
|
||||||
<Choose>
|
<Choose>
|
||||||
<When Condition="'$(Configuration)' == 'v1.4'">
|
<When Condition="'$(Configuration)' == 'v1.4'">
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ internal static class Convert
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var dm = new DynamicMethod($"ObjectConverter_{from}_{to}", to,
|
var dm = new DynamicMethod($"ObjectConverter_{from}_{to}", to,
|
||||||
new[] { typeof(object), typeof(IFormatProvider) }, typeof(FromObjectTo<TTo>), true);
|
[typeof(object), typeof(IFormatProvider)], typeof(FromObjectTo<TTo>), true);
|
||||||
var il = dm.GetILGenerator();
|
var il = dm.GetILGenerator();
|
||||||
|
|
||||||
il.Emit(FishTranspiler.This);
|
il.Emit(FishTranspiler.This);
|
||||||
@@ -59,7 +59,7 @@ internal static class Convert
|
|||||||
if (from.IsValueType)
|
if (from.IsValueType)
|
||||||
{
|
{
|
||||||
il.Emit(FishTranspiler.Call(typeof(FromObjectTo<TTo>), nameof(UnboxSafely),
|
il.Emit(FishTranspiler.Call(typeof(FromObjectTo<TTo>), nameof(UnboxSafely),
|
||||||
generics: new[] { from }));
|
generics: [from]));
|
||||||
}
|
}
|
||||||
|
|
||||||
il.Emit(FishTranspiler.Argument(1));
|
il.Emit(FishTranspiler.Argument(1));
|
||||||
@@ -72,7 +72,8 @@ internal static class Convert
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
throw new($"Failed to compile object converter from {from.FullDescription()} to {
|
return ThrowHelper.ThrowInvalidOperationException<Func<object, IFormatProvider, TTo>>(
|
||||||
|
$"Failed to compile object converter from {from.FullDescription()} to {
|
||||||
to.FullDescription()}\n{ex}");
|
to.FullDescription()}\n{ex}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,7 +84,7 @@ internal static class Convert
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
[SecuritySafeCritical]
|
[SecuritySafeCritical]
|
||||||
private static ref T UnboxSafely<T>(object obj) where T : struct
|
private static ref T UnboxSafely<T>(object obj) where T : struct
|
||||||
=> ref Unsafe.Unbox<T>(obj is T ? obj : default);
|
=> ref Unsafe.Unbox<T>(obj is T ? obj : default(T));
|
||||||
|
|
||||||
private record struct FuncWrapper
|
private record struct FuncWrapper
|
||||||
{
|
{
|
||||||
@@ -163,7 +164,7 @@ internal static class Convert
|
|||||||
private static MethodInfo GenerateWrapperMethod(Type from, Type to, MethodBase method)
|
private static MethodInfo GenerateWrapperMethod(Type from, Type to, MethodBase method)
|
||||||
{
|
{
|
||||||
var dm = new DynamicMethod($"Wrapper_op_{from.Name}_{to.Name}", to,
|
var dm = new DynamicMethod($"Wrapper_op_{from.Name}_{to.Name}", to,
|
||||||
new[] { from.IsValueType ? from.MakeByRefType() : from, typeof(IFormatProvider) }, from, true);
|
[from.IsValueType ? from.MakeByRefType() : from, typeof(IFormatProvider)], from, true);
|
||||||
var il = dm.GetILGenerator();
|
var il = dm.GetILGenerator();
|
||||||
|
|
||||||
il.Emit(FishTranspiler.Argument(0));
|
il.Emit(FishTranspiler.Argument(0));
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Reflection.Emit;
|
using System.Reflection.Emit;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Security;
|
using System.Security;
|
||||||
@@ -16,6 +17,7 @@ public static class Equals<T>
|
|||||||
= (delegate*<T, T, bool>)FunctionPointers.Equals.EqualsMethods.GetFunctionPointer(typeof(T));
|
= (delegate*<T, T, bool>)FunctionPointers.Equals.EqualsMethods.GetFunctionPointer(typeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("Naming", "CA1720")]
|
||||||
public static class Equals
|
public static class Equals
|
||||||
{
|
{
|
||||||
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
|
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
|
||||||
@@ -37,6 +39,8 @@ public static class Equals
|
|||||||
public static bool IntPtr(IntPtr x, IntPtr y) => x == y;
|
public static bool IntPtr(IntPtr x, IntPtr y) => x == y;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
[SuppressMessage("Globalization", "CA1307")]
|
||||||
|
[SuppressMessage("Globalization", "CA1309")]
|
||||||
public static bool String(string x, string y) => string.Equals(x, y);
|
public static bool String(string x, string y) => string.Equals(x, y);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Reflection.Emit;
|
using System.Reflection.Emit;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Security;
|
using System.Security;
|
||||||
@@ -16,6 +17,7 @@ public static class EqualsByRef<T>
|
|||||||
= (delegate*<ref T, ref T, bool>)EqualsByRef.EqualsMethods.GetFunctionPointer(typeof(T));
|
= (delegate*<ref T, ref T, bool>)EqualsByRef.EqualsMethods.GetFunctionPointer(typeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("Naming", "CA1720")]
|
||||||
public static class EqualsByRef
|
public static class EqualsByRef
|
||||||
{
|
{
|
||||||
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
|
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
|
||||||
@@ -37,6 +39,8 @@ public static class EqualsByRef
|
|||||||
public static bool IntPtr(ref IntPtr x, ref IntPtr y) => x == y;
|
public static bool IntPtr(ref IntPtr x, ref IntPtr y) => x == y;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
[SuppressMessage("Globalization", "CA1307")]
|
||||||
|
[SuppressMessage("Globalization", "CA1309")]
|
||||||
public static bool String(ref string x, ref string y) => string.Equals(x, y);
|
public static bool String(ref string x, ref string y) => string.Equals(x, y);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
@@ -14,6 +15,7 @@ public static class GetHashCode<T>
|
|||||||
= (delegate*<T, int>)FunctionPointers.GetHashCode.HashCodeMethods.GetFunctionPointer(typeof(T));
|
= (delegate*<T, int>)FunctionPointers.GetHashCode.HashCodeMethods.GetFunctionPointer(typeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("Naming", "CA1720")]
|
||||||
public static class GetHashCode
|
public static class GetHashCode
|
||||||
{
|
{
|
||||||
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
|
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
@@ -14,6 +15,7 @@ public static class GetHashCodeByRef<T>
|
|||||||
= (delegate*<ref T, int>)GetHashCodeByRef.HashCodeMethods.GetFunctionPointer(typeof(T));
|
= (delegate*<ref T, int>)GetHashCodeByRef.HashCodeMethods.GetFunctionPointer(typeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("Naming", "CA1720")]
|
||||||
public static class GetHashCodeByRef
|
public static class GetHashCodeByRef
|
||||||
{
|
{
|
||||||
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
|
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
|
||||||
|
|||||||
@@ -11,15 +11,15 @@ using System.Security;
|
|||||||
|
|
||||||
namespace FisheryLib.FunctionPointers;
|
namespace FisheryLib.FunctionPointers;
|
||||||
|
|
||||||
public abstract class MethodFactory
|
public abstract class MethodFactory(MethodInfo[] methods, string namePrefix, bool byRef)
|
||||||
{
|
{
|
||||||
protected readonly MethodInfo[] _methods;
|
protected readonly MethodInfo[] _methods = methods;
|
||||||
|
|
||||||
protected readonly ConcurrentDictionary<Type, MethodInfo> _methodsByType = new();
|
protected readonly ConcurrentDictionary<Type, MethodInfo> _methodsByType = new();
|
||||||
|
|
||||||
protected readonly string _namePrefix;
|
protected readonly string _namePrefix = namePrefix;
|
||||||
|
|
||||||
protected readonly bool _byRef;
|
protected readonly bool _byRef = byRef;
|
||||||
|
|
||||||
public MethodInfo GetGeneric(string name, params Type[] typeArguments)
|
public MethodInfo GetGeneric(string name, params Type[] typeArguments)
|
||||||
=> GetNamed(name).MakeGenericMethod(typeArguments);
|
=> GetNamed(name).MakeGenericMethod(typeArguments);
|
||||||
@@ -64,14 +64,8 @@ public abstract class MethodFactory
|
|||||||
=> type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly)
|
=> type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly)
|
||||||
.Where(predicate).ToArray();
|
.Where(predicate).ToArray();
|
||||||
|
|
||||||
protected MethodFactory(MethodInfo[] methods, string namePrefix, bool byRef)
|
public new class Equals(Type type, string dynamicMethodNamePrefix, bool byRef)
|
||||||
{
|
: MethodFactory(GetMethodInfoArraySafely(type), dynamicMethodNamePrefix, byRef)
|
||||||
_byRef = byRef;
|
|
||||||
_methods = methods;
|
|
||||||
_namePrefix = namePrefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
public new class Equals : MethodFactory
|
|
||||||
{
|
{
|
||||||
protected override MethodInfo GetOrMakeMethodForType(Type type)
|
protected override MethodInfo GetOrMakeMethodForType(Type type)
|
||||||
=> GetSpecializedMethod(type)
|
=> GetSpecializedMethod(type)
|
||||||
@@ -160,7 +154,7 @@ public abstract class MethodFactory
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var dm = new DynamicMethod($"{_namePrefix}{type.Name}", typeof(bool),
|
var dm = new DynamicMethod($"{_namePrefix}{type.Name}", typeof(bool),
|
||||||
_byRef ? new[] { type.MakeByRefType(), type.MakeByRefType() } : new[] { type, type }, type, true);
|
_byRef ? [type.MakeByRefType(), type.MakeByRefType()] : [type, type], type, true);
|
||||||
var il = dm.GetILGenerator();
|
var il = dm.GetILGenerator();
|
||||||
|
|
||||||
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
@@ -233,15 +227,11 @@ public abstract class MethodFactory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Equals(Type type, string dynamicMethodNamePrefix, bool byRef)
|
|
||||||
: base(GetMethodInfoArraySafely(type), dynamicMethodNamePrefix, byRef)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private delegate TResult ByRefFunc<T1, T2, out TResult>(ref T1 arg1, ref T2 arg2);
|
private delegate TResult ByRefFunc<T1, T2, out TResult>(ref T1 arg1, ref T2 arg2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class HashCode : MethodFactory
|
public class HashCode(Type type, string dynamicMethodNamePrefix, bool byRef)
|
||||||
|
: MethodFactory(GetMethodInfoArraySafely(type), dynamicMethodNamePrefix, byRef)
|
||||||
{
|
{
|
||||||
protected override MethodInfo GetOrMakeMethodForType(Type type)
|
protected override MethodInfo GetOrMakeMethodForType(Type type)
|
||||||
=> GetSpecializedMethod(type)
|
=> GetSpecializedMethod(type)
|
||||||
@@ -325,7 +315,7 @@ public abstract class MethodFactory
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var dm = new DynamicMethod($"{_namePrefix}{type.Name}", typeof(int),
|
var dm = new DynamicMethod($"{_namePrefix}{type.Name}", typeof(int),
|
||||||
_byRef ? new[] { type.MakeByRefType() } : new[] { type }, type, true);
|
_byRef ? [type.MakeByRefType()] : [type], type, true);
|
||||||
var il = dm.GetILGenerator();
|
var il = dm.GetILGenerator();
|
||||||
|
|
||||||
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
@@ -382,11 +372,6 @@ public abstract class MethodFactory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public HashCode(Type type, string dynamicMethodNamePrefix, bool byRef)
|
|
||||||
: base(GetMethodInfoArraySafely(type), dynamicMethodNamePrefix, byRef)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private delegate int ByRefHashCodeGetter<T>(ref T arg);
|
private delegate int ByRefHashCodeGetter<T>(ref T arg);
|
||||||
private delegate int HashCodeGetter<in T>(T arg);
|
private delegate int HashCodeGetter<in T>(T arg);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,201 @@
|
|||||||
|
// Copyright (c) 2023 bradson
|
||||||
|
// This Source Code Form is subject to the terms of the MIT license.
|
||||||
|
// If a copy of the license was not distributed with this file,
|
||||||
|
// You can obtain one at https://opensource.org/licenses/MIT/.
|
||||||
|
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using UnityEngine;
|
||||||
|
using Verse;
|
||||||
|
|
||||||
|
namespace FisheryLib;
|
||||||
|
|
||||||
|
[PublicAPI]
|
||||||
|
public static class GUIScope
|
||||||
|
{
|
||||||
|
public readonly record struct WidgetGroup : IDisposable
|
||||||
|
{
|
||||||
|
public WidgetGroup(in Rect rect) => Widgets.BeginGroup(rect);
|
||||||
|
|
||||||
|
public void Dispose() => Widgets.EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly record struct ScrollView : IDisposable
|
||||||
|
{
|
||||||
|
private const float SCROLL_BAR_WIDTH = 16f;
|
||||||
|
|
||||||
|
private readonly ScrollViewStatus _scrollViewStatus;
|
||||||
|
|
||||||
|
private readonly float _outRectHeight;
|
||||||
|
|
||||||
|
public readonly Vector2 ViewSize;
|
||||||
|
|
||||||
|
public ref float Height
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => ref _scrollViewStatus.Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2 Position
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => _scrollViewStatus.Position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScrollView(in Rect outRect, ScrollViewStatus scrollViewStatus, bool showScrollbars = true)
|
||||||
|
{
|
||||||
|
_scrollViewStatus = scrollViewStatus;
|
||||||
|
_outRectHeight = outRect.height;
|
||||||
|
var viewRect = new Rect(0f, 0f, outRect.width, Math.Max(Height, _outRectHeight));
|
||||||
|
if (Height - 0.1f >= outRect.height)
|
||||||
|
viewRect.width -= SCROLL_BAR_WIDTH;
|
||||||
|
|
||||||
|
ViewSize = viewRect.size;
|
||||||
|
Height = 0f;
|
||||||
|
Widgets.BeginScrollView(outRect, ref _scrollViewStatus.Position, viewRect, showScrollbars);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() => Widgets.EndScrollView();
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool CanCull(float entryHeight, float entryY)
|
||||||
|
=> entryY + entryHeight < Position.y
|
||||||
|
|| entryY > Position.y + _outRectHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ScrollViewStatus
|
||||||
|
{
|
||||||
|
public Vector2 Position;
|
||||||
|
public float Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly record struct ListingStandard : IDisposable
|
||||||
|
{
|
||||||
|
public Listing_Standard Listing
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListingStandard(in Rect rect, Listing_Standard? listing = null)
|
||||||
|
=> (Listing = listing ?? new()).Begin(rect);
|
||||||
|
|
||||||
|
public void Dispose() => Listing.End();
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly record struct ScrollableListingStandard : IDisposable
|
||||||
|
{
|
||||||
|
private readonly ListingStandard _listingStandard;
|
||||||
|
|
||||||
|
private readonly ScrollView _scrollView;
|
||||||
|
|
||||||
|
public Listing_Standard Listing
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => _listingStandard.Listing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2 ScrollViewSize
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => _scrollView.ViewSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2 ScrollViewPosition
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => _scrollView.Position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScrollableListingStandard(Rect outRect, ScrollViewStatus scrollViewStatus,
|
||||||
|
Listing_Standard? listing = null, bool showScrollbars = true)
|
||||||
|
{
|
||||||
|
_scrollView = new(outRect, scrollViewStatus, showScrollbars);
|
||||||
|
_listingStandard = new(new(0f, 0f, ScrollViewSize.x, float.PositiveInfinity), listing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_scrollView.Height = Listing.CurHeight + 12f;
|
||||||
|
_listingStandard.Dispose();
|
||||||
|
_scrollView.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool TryCull(float entryHeight)
|
||||||
|
{
|
||||||
|
var canCull = _scrollView.CanCull(entryHeight, Listing.curY);
|
||||||
|
if (canCull)
|
||||||
|
Listing.curY += entryHeight;
|
||||||
|
|
||||||
|
return canCull;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly record struct TextAnchor : IDisposable
|
||||||
|
{
|
||||||
|
private readonly UnityEngine.TextAnchor _default;
|
||||||
|
|
||||||
|
public TextAnchor(UnityEngine.TextAnchor anchor)
|
||||||
|
{
|
||||||
|
_default = Text.Anchor;
|
||||||
|
Text.Anchor = anchor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() => Text.Anchor = _default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly record struct WordWrap : IDisposable
|
||||||
|
{
|
||||||
|
private readonly bool _default;
|
||||||
|
|
||||||
|
public WordWrap(bool wordWrap)
|
||||||
|
{
|
||||||
|
_default = Text.WordWrap;
|
||||||
|
Text.WordWrap = wordWrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() => Text.WordWrap = _default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly record struct Color : IDisposable
|
||||||
|
{
|
||||||
|
private readonly UnityEngine.Color _default;
|
||||||
|
|
||||||
|
public Color(UnityEngine.Color color)
|
||||||
|
{
|
||||||
|
_default = GUI.color;
|
||||||
|
GUI.color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() => GUI.color = _default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly record struct Font : IDisposable
|
||||||
|
{
|
||||||
|
private readonly GameFont _default;
|
||||||
|
|
||||||
|
public Font(GameFont font)
|
||||||
|
{
|
||||||
|
_default = Text.Font;
|
||||||
|
Text.Font = font;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() => Text.Font = _default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly record struct FontSize : IDisposable
|
||||||
|
{
|
||||||
|
private readonly int _default;
|
||||||
|
|
||||||
|
public FontSize(int size)
|
||||||
|
{
|
||||||
|
var curFontStyle = Text.CurFontStyle;
|
||||||
|
|
||||||
|
_default = curFontStyle.fontSize;
|
||||||
|
curFontStyle.fontSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() => Text.CurFontStyle.fontSize = _default;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
// Copyright (c) 2023 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.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace FisheryLib.Pools;
|
||||||
|
|
||||||
|
public static class FishPool<T> where T : IFishPoolable
|
||||||
|
{
|
||||||
|
private static T?[] _freeItems = new T[4];
|
||||||
|
private static int _freeItemCount;
|
||||||
|
|
||||||
|
public static T Get()
|
||||||
|
{
|
||||||
|
if (_freeItemCount == 0)
|
||||||
|
return Reflection.New<T>();
|
||||||
|
|
||||||
|
var freeItem = _freeItems[--_freeItemCount];
|
||||||
|
_freeItems[_freeItemCount] = default;
|
||||||
|
return freeItem!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Return(T item)
|
||||||
|
{
|
||||||
|
item.Reset();
|
||||||
|
|
||||||
|
if (++_freeItemCount >= _freeItems.Length)
|
||||||
|
ExpandFreeItems();
|
||||||
|
|
||||||
|
_freeItems[_freeItemCount - 1] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
private static void ExpandFreeItems() => Array.Resize(ref _freeItems, _freeItems.Length << 1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// 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/.
|
||||||
|
|
||||||
|
namespace FisheryLib.Pools;
|
||||||
|
|
||||||
|
public interface IFishPoolable
|
||||||
|
{
|
||||||
|
public void Reset();
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright (c) 2023 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;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace FisheryLib.Pools;
|
||||||
|
|
||||||
|
public record struct PooledIList<T>() : IDisposable where T : IList, new()
|
||||||
|
{
|
||||||
|
public T List = Get();
|
||||||
|
|
||||||
|
private static T?[] _freeItems = new T[4];
|
||||||
|
private static int _freeItemCount;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (List == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Return(List);
|
||||||
|
List = default!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T Get()
|
||||||
|
{
|
||||||
|
if (_freeItemCount == 0)
|
||||||
|
return Reflection.New<T>();
|
||||||
|
|
||||||
|
var freeItem = _freeItems[--_freeItemCount];
|
||||||
|
_freeItems[_freeItemCount] = default;
|
||||||
|
return freeItem!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Return(T item)
|
||||||
|
{
|
||||||
|
item.Clear();
|
||||||
|
|
||||||
|
if (++_freeItemCount >= _freeItems.Length)
|
||||||
|
ExpandFreeItems();
|
||||||
|
|
||||||
|
_freeItems[_freeItemCount - 1] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
private static void ExpandFreeItems() => Array.Resize(ref _freeItems, _freeItems.Length << 1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
// Copyright (c) 2023 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.Security;
|
||||||
|
using System.Text;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
|
namespace FisheryLib.Pools;
|
||||||
|
|
||||||
|
public record struct PooledStringBuilder : IDisposable
|
||||||
|
{
|
||||||
|
private FishPoolable _fishPoolable;
|
||||||
|
|
||||||
|
public StringBuilder Builder => _fishPoolable.Builder;
|
||||||
|
|
||||||
|
public StringBuilder Append(string? value) => Builder.Append(value);
|
||||||
|
public StringBuilder Append(char value) => Builder.Append(value);
|
||||||
|
public StringBuilder Append(char[] value) => Builder.Append(value);
|
||||||
|
public StringBuilder Append(PooledStringBuilderHandler value) => value.AppendToStringBuilder(Builder);
|
||||||
|
|
||||||
|
public StringBuilder AppendLine(string? value) => Builder.AppendLine(value);
|
||||||
|
|
||||||
|
public StringBuilder Insert(int index, string? value) => Builder.Insert(index, value);
|
||||||
|
public StringBuilder Insert(int index, char value) => Builder.Insert(index, value);
|
||||||
|
|
||||||
|
public StringBuilder Remove(int startIndex, int length) => Builder.Remove(startIndex, length);
|
||||||
|
|
||||||
|
public void Clear() => Builder.Clear();
|
||||||
|
|
||||||
|
public override string ToString() => Builder.ToString();
|
||||||
|
|
||||||
|
[SecuritySafeCritical]
|
||||||
|
public PooledStringBuilder() => _fishPoolable = FishPool<FishPoolable>.Get();
|
||||||
|
|
||||||
|
public PooledStringBuilder(int minimumSize) : this()
|
||||||
|
{
|
||||||
|
if (_fishPoolable.Builder.Capacity < minimumSize)
|
||||||
|
_fishPoolable.Builder.Capacity = minimumSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SecuritySafeCritical]
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_fishPoolable.Builder == null!)
|
||||||
|
return;
|
||||||
|
|
||||||
|
FishPool<FishPoolable>.Return(_fishPoolable);
|
||||||
|
_fishPoolable.Builder = null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct FishPoolable : IFishPoolable
|
||||||
|
{
|
||||||
|
public StringBuilder Builder = new();
|
||||||
|
|
||||||
|
public void Reset() => Builder.Clear();
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
public FishPoolable() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// Copyright (c) 2023 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.Runtime.CompilerServices;
|
||||||
|
using System.Text;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
|
namespace FisheryLib.Pools;
|
||||||
|
|
||||||
|
[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
|
||||||
|
[InterpolatedStringHandler]
|
||||||
|
public record struct PooledStringBuilderHandler : IDisposable
|
||||||
|
{
|
||||||
|
private PooledIList<List<char>> _chars;
|
||||||
|
|
||||||
|
public PooledStringBuilderHandler(int literalLength, int formattedCount)
|
||||||
|
{
|
||||||
|
_chars = new();
|
||||||
|
_chars.List.EnsureCapacity(literalLength + (formattedCount * 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AppendLiteral(string s) => _chars.List.Add(s);
|
||||||
|
|
||||||
|
public void AppendFormatted<T>(T t)
|
||||||
|
{
|
||||||
|
if (t != null)
|
||||||
|
_chars.List.Add(t.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringBuilder AppendToStringBuilder(StringBuilder destination)
|
||||||
|
{
|
||||||
|
destination.Append(_chars.List);
|
||||||
|
Dispose();
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() => _chars.Dispose();
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (c) 2023 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.Runtime.CompilerServices;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
|
namespace FisheryLib.Pools;
|
||||||
|
|
||||||
|
[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
|
||||||
|
[InterpolatedStringHandler]
|
||||||
|
public record struct PooledStringHandler : IDisposable
|
||||||
|
{
|
||||||
|
private PooledStringBuilder _builder;
|
||||||
|
|
||||||
|
public PooledStringHandler(int literalLength, int formattedCount)
|
||||||
|
=> _builder = new(literalLength + (formattedCount * 8));
|
||||||
|
|
||||||
|
public void AppendLiteral(string s) => _builder.Append(s);
|
||||||
|
|
||||||
|
public void AppendFormatted<T>(T t)
|
||||||
|
{
|
||||||
|
if (typeof(T) == typeof(PooledStringBuilderHandler))
|
||||||
|
_builder.Append(Unsafe.As<T, PooledStringBuilderHandler>(ref t));
|
||||||
|
else
|
||||||
|
_builder.Append(t?.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetFormattedText()
|
||||||
|
{
|
||||||
|
var result = _builder.ToString();
|
||||||
|
Dispose();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() => _builder.Dispose();
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ using System.Runtime.CompilerServices;
|
|||||||
using System.Security;
|
using System.Security;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using FisheryLib.Pools;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Verse;
|
using Verse;
|
||||||
|
|
||||||
@@ -20,6 +21,9 @@ namespace FisheryLib;
|
|||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public static class Reflection
|
public static class Reflection
|
||||||
{
|
{
|
||||||
|
private static Assembly[] _allAssemblies = Array.Empty<Assembly>();
|
||||||
|
private static object _allAssembliesLock = new();
|
||||||
|
|
||||||
public static CodeInstructions GetCodeInstructions(Delegate method, ILGenerator? generator = null)
|
public static CodeInstructions GetCodeInstructions(Delegate method, ILGenerator? generator = null)
|
||||||
=> GetCodeInstructions(method.Method, generator);
|
=> GetCodeInstructions(method.Method, generator);
|
||||||
|
|
||||||
@@ -43,7 +47,37 @@ public static class Reflection
|
|||||||
yield return FishTranspiler.Return;
|
yield return FishTranspiler.Return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Type? Type(string assembly, string type) => System.Type.GetType($"{type}, {assembly}");
|
public static Assembly[] AllAssemblies
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (_allAssembliesLock)
|
||||||
|
{
|
||||||
|
return _allAssemblies.Length == AppDomain.CurrentDomain.GetAssemblies().Length
|
||||||
|
? _allAssemblies
|
||||||
|
: _allAssemblies = AccessTools.AllAssemblies().ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Type? Type(string assembly, string typeFullName)
|
||||||
|
=> System.Type.GetType(string.Concat(typeFullName, ", ", assembly));
|
||||||
|
|
||||||
|
public static Type? Type(string assembly, string @namespace, string type)
|
||||||
|
=> System.Type.GetType(StringHelper.Resolve($"{@namespace}.{type}, {assembly}"));
|
||||||
|
|
||||||
|
public static Type? Type(string fullName)
|
||||||
|
{
|
||||||
|
var assemblies = AllAssemblies;
|
||||||
|
|
||||||
|
for (var i = assemblies.Length; i-- > 0;)
|
||||||
|
{
|
||||||
|
if (assemblies[i].GetType(fullName) is { } type)
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public static MethodInfo MethodInfo<T>(T method) where T : Delegate => method.Method;
|
public static MethodInfo MethodInfo<T>(T method) where T : Delegate => method.Method;
|
||||||
|
|
||||||
@@ -323,7 +357,8 @@ public static class Reflection
|
|||||||
if (memberInfo is null)
|
if (memberInfo is null)
|
||||||
return "NULL";
|
return "NULL";
|
||||||
|
|
||||||
var stringBuilder = new StringBuilder();
|
using var pooledStringBuilder = new PooledStringBuilder();
|
||||||
|
var stringBuilder = pooledStringBuilder.Builder;
|
||||||
|
|
||||||
switch (memberInfo)
|
switch (memberInfo)
|
||||||
{
|
{
|
||||||
@@ -346,7 +381,7 @@ public static class Reflection
|
|||||||
if (memberInfo is PropertyInfo propertyInfo1)
|
if (memberInfo is PropertyInfo propertyInfo1)
|
||||||
AppendPropertyPostfix(stringBuilder, propertyInfo1);
|
AppendPropertyPostfix(stringBuilder, propertyInfo1);
|
||||||
|
|
||||||
return stringBuilder.ToString();
|
return pooledStringBuilder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AppendFieldPrefix(FieldInfo fieldInfo, StringBuilder stringBuilder)
|
private static void AppendFieldPrefix(FieldInfo fieldInfo, StringBuilder stringBuilder)
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// 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 FisheryLib.Pools;
|
||||||
|
|
||||||
|
namespace FisheryLib;
|
||||||
|
|
||||||
|
public static class StringHelper
|
||||||
|
{
|
||||||
|
public static string Resolve(PooledStringHandler interpolation) => interpolation.GetFormattedText();
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ internal static class UtilityF
|
|||||||
}
|
}
|
||||||
|
|
||||||
[DoesNotReturn]
|
[DoesNotReturn]
|
||||||
|
[SuppressMessage("Usage", "CA2201")]
|
||||||
internal static void Throw(string message) => throw new(message);
|
internal static void Throw(string message) => throw new(message);
|
||||||
|
|
||||||
[DoesNotReturn]
|
[DoesNotReturn]
|
||||||
@@ -37,10 +38,10 @@ internal static class UtilityF
|
|||||||
|
|
||||||
for (var i = 0; i < stacktrace.FrameCount; i++)
|
for (var i = 0; i < stacktrace.FrameCount; i++)
|
||||||
{
|
{
|
||||||
var assembly = stacktrace.GetFrame(i).GetMethod().ReflectedType.Assembly;
|
var assembly = stacktrace.GetFrame(i).GetMethod().ReflectedType?.Assembly;
|
||||||
fishAssembly ??= assembly;
|
fishAssembly ??= assembly;
|
||||||
|
|
||||||
if (assembly != fishAssembly)
|
if (assembly != fishAssembly && assembly != null)
|
||||||
return assembly;
|
return assembly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,10 +35,11 @@ public static class FishMath
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] _trailingZeroCountDeBruijn
|
private static byte[] _trailingZeroCountDeBruijn
|
||||||
= {
|
=
|
||||||
|
[
|
||||||
0, 1, 28, 2, 29, 14, 24, 3, 30, 22,
|
0, 1, 28, 2, 29, 14, 24, 3, 30, 22,
|
||||||
20, 15, 25, 17, 4, 8, 31, 27, 13, 23,
|
20, 15, 25, 17, 4, 8, 31, 27, 13, 23,
|
||||||
21, 19, 16, 7, 26, 12, 18, 6, 11, 5,
|
21, 19, 16, 7, 26, 12, 18, 6, 11, 5,
|
||||||
10, 9
|
10, 9
|
||||||
};
|
];
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user