mirror of
https://gitee.com/csharpui/CPF.git
synced 2026-02-27 06:03:10 +08:00
初始化
This commit is contained in:
28
ConsoleApp1/InvokeST/ConvertMap/ConvertItem.cs
Normal file
28
ConsoleApp1/InvokeST/ConvertMap/ConvertItem.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace Raindrops.Shared.InvokeST
|
||||
{
|
||||
public class ConvertItem
|
||||
{
|
||||
public ConvertItem(Action<ILGenerator> action, int lostWeight = 0, int consumptionWeight = 0, int order = 0)
|
||||
{
|
||||
Order = order;
|
||||
Action = action;
|
||||
LostWeight = lostWeight;
|
||||
ConsumptionWeight = consumptionWeight;
|
||||
}
|
||||
public ConvertItem(OpCode opCode, int lostWeight = 0, int consumptionWeight = 0, int order = 0)
|
||||
{
|
||||
Order = order;
|
||||
OpCode = opCode;
|
||||
LostWeight = lostWeight;
|
||||
ConsumptionWeight = consumptionWeight;
|
||||
}
|
||||
internal int Order { get; set; }
|
||||
public int ConsumptionWeight { get; }
|
||||
public int LostWeight { get; }
|
||||
public Action<ILGenerator> Action { get; }
|
||||
public OpCode? OpCode { get; }
|
||||
}
|
||||
}
|
||||
319
ConsoleApp1/InvokeST/ConvertMap/EmitConvertMap.cs
Normal file
319
ConsoleApp1/InvokeST/ConvertMap/EmitConvertMap.cs
Normal file
@@ -0,0 +1,319 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Raindrops.Shared.InvokeST.ConvertMap
|
||||
{
|
||||
public static class EmitConvertMap
|
||||
{
|
||||
internal static readonly ConcurrentDictionary<Type, ConcurrentDictionary<Type, ConvertItem>> s_map;
|
||||
internal static readonly Type[] s_numbers;
|
||||
static EmitConvertMap()
|
||||
{
|
||||
s_map = new ConcurrentDictionary<Type, ConcurrentDictionary<Type, ConvertItem>>();
|
||||
s_numbers = new Type[10] {
|
||||
typeof(sbyte),
|
||||
typeof(short),
|
||||
typeof(int),
|
||||
typeof(long),
|
||||
typeof(byte),
|
||||
typeof(ushort),
|
||||
typeof(uint),
|
||||
typeof(ulong),
|
||||
typeof(float),
|
||||
typeof(double),
|
||||
};
|
||||
Init();
|
||||
}
|
||||
private static bool IsUnsignedNumber(Type type)
|
||||
{
|
||||
return type == typeof(byte)
|
||||
|| type == typeof(ushort)
|
||||
|| type == typeof(uint)
|
||||
|| type == typeof(ulong);
|
||||
}
|
||||
private static bool IsSignedNumber(Type type)
|
||||
{
|
||||
return type == typeof(sbyte)
|
||||
|| type == typeof(short)
|
||||
|| type == typeof(int)
|
||||
|| type == typeof(long);
|
||||
}
|
||||
private static void Init()
|
||||
{
|
||||
OpCode[] numberConverts = new OpCode[10] {
|
||||
OpCodes.Conv_I1,
|
||||
OpCodes.Conv_I2,
|
||||
OpCodes.Conv_I4,
|
||||
OpCodes.Conv_I8,
|
||||
OpCodes.Conv_U1,
|
||||
OpCodes.Conv_U2,
|
||||
OpCodes.Conv_U4,
|
||||
OpCodes.Conv_U8,
|
||||
OpCodes.Conv_R4,
|
||||
OpCodes.Conv_R8,
|
||||
};
|
||||
ConcurrentDictionary<Type, ConvertItem> dic;
|
||||
//Single
|
||||
MethodInfo cucMethodInfo = typeof(CultureInfo).GetProperty(nameof(CultureInfo.CurrentUICulture)).GetGetMethod();
|
||||
for (int i = 0; i < s_numbers.Length; i++)
|
||||
{
|
||||
int sSize = Marshal.SizeOf(s_numbers[i]) * 8;
|
||||
dic = s_map.GetOrAdd(s_numbers[i], new ConcurrentDictionary<Type, ConvertItem>());
|
||||
//Number to Number
|
||||
for (int j = 0; j < s_numbers.Length; j++)
|
||||
{
|
||||
int k = (i + j) % s_numbers.Length;
|
||||
if (s_numbers[i] == s_numbers[k])
|
||||
continue;
|
||||
int lost = GetLost(s_numbers[i], s_numbers[k]);
|
||||
if (s_numbers[k] == typeof(double) && IsUnsignedNumber(s_numbers[i]))
|
||||
continue;
|
||||
if (s_numbers[k] == typeof(float) && IsUnsignedNumber(s_numbers[i]))
|
||||
{
|
||||
dic.TryAdd(s_numbers[k], new ConvertItem(OpCodes.Conv_R_Un, lost, 1, k));
|
||||
continue;
|
||||
}
|
||||
dic.TryAdd(s_numbers[k], new ConvertItem(numberConverts[k], lost, 1, k));
|
||||
}
|
||||
//Number to Boolen
|
||||
dic.TryAdd(typeof(bool), new ConvertItem((il) =>
|
||||
{
|
||||
il.Emit(OpCodes.Call, cucMethodInfo);
|
||||
il.Emit(OpCodes.Callvirt, typeof(IConvertible).GetMethod(nameof(IConvertible.ToBoolean)));
|
||||
}, sSize - 1, 4, s_numbers.Length));
|
||||
AnalyzeConverter(s_numbers[i]);
|
||||
}
|
||||
//Decimal <-> Number
|
||||
AnalyzeConverter(typeof(decimal));
|
||||
AnalyzeConverter(typeof(IntPtr));
|
||||
AnalyzeConverter(typeof(UIntPtr));
|
||||
|
||||
//DateTime
|
||||
AnalyzeConverter(typeof(DateTime));
|
||||
|
||||
//String
|
||||
AnalyzeConverter(typeof(string));
|
||||
}
|
||||
private static void AddConverter(Type sourceType, Type targetType, ConvertItem convertItem)
|
||||
{
|
||||
ConcurrentDictionary<Type, ConvertItem> dic = s_map.GetOrAdd(sourceType, new ConcurrentDictionary<Type, ConvertItem>());
|
||||
convertItem.Order = dic.Count;
|
||||
dic.TryAdd(targetType, convertItem);
|
||||
}
|
||||
private static void UpdateConverter(Type sourceType, Type targetType, ConvertItem convertItem)
|
||||
{
|
||||
ConcurrentDictionary<Type, ConvertItem> dic = s_map.GetOrAdd(sourceType, new ConcurrentDictionary<Type, ConvertItem>());
|
||||
convertItem.Order = dic.Count;
|
||||
dic.AddOrUpdate(targetType, convertItem, (k, v) => convertItem);
|
||||
}
|
||||
private static int GetSize(Type type)
|
||||
{
|
||||
return type == typeof(bool) ? 1 : Marshal.SizeOf(type) * 8;
|
||||
}
|
||||
private static int GetLost(Type source, Type target)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (source.IsValueType && target.IsValueType)
|
||||
{
|
||||
int sSize = GetSize(source);
|
||||
int tSize = GetSize(target);
|
||||
int rSize = tSize / 2 < sSize && (IsUnsignedNumber(source) && IsSignedNumber(target)) || (IsSignedNumber(source) && IsUnsignedNumber(target)) ? 1 : 0;
|
||||
return (tSize >= sSize ? 0 : sSize - tSize) + rSize;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public static bool SearchConvertItem(Type source, Type target, out ConvertItem convertItem)
|
||||
{
|
||||
convertItem = default;
|
||||
if (s_map.TryGetValue(source, out ConcurrentDictionary<Type, ConvertItem> dic))
|
||||
{
|
||||
return dic.TryGetValue(target, out convertItem);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public static void AnalyzeConverter(Type type)
|
||||
{
|
||||
if (type.IsByRef) throw new ArgumentException(nameof(type));
|
||||
|
||||
foreach (MethodInfo methodInfo in type.GetMethods(BindingFlags.Public | BindingFlags.Static))
|
||||
{
|
||||
//implicit
|
||||
if (methodInfo.IsSpecialName)
|
||||
{
|
||||
ParameterInfo[] parameterInfos = methodInfo.GetParameters();
|
||||
if (parameterInfos.Length == 1)
|
||||
{
|
||||
Type cType = parameterInfos[0].ParameterType;
|
||||
if (cType.IsByRef)
|
||||
continue;
|
||||
if (methodInfo.ReturnType == type && cType != type)
|
||||
{
|
||||
AddConverter(cType, type, new ConvertItem((il) => il.Emit(OpCodes.Call, methodInfo), GetLost(cType, type), 2));
|
||||
}
|
||||
else if (methodInfo.ReturnType != type && cType == type)
|
||||
{
|
||||
AddConverter(type, methodInfo.ReturnType, new ConvertItem((il) => il.Emit(OpCodes.Call, methodInfo), GetLost(type, methodInfo.ReturnType), 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
//Parse
|
||||
else if(methodInfo.Name == "Parse" && methodInfo.ReturnType == type)
|
||||
{
|
||||
ParameterInfo[] parameterInfos = methodInfo.GetParameters();
|
||||
Type cType = parameterInfos[0].ParameterType;
|
||||
if (parameterInfos.Length == 1 && !cType.IsByRef)
|
||||
{
|
||||
AddConverter(cType, type, new ConvertItem((il) => il.Emit(OpCodes.Call, methodInfo), GetLost(cType, type), 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IConvertible
|
||||
if (typeof(IConvertible).IsAssignableFrom(type))
|
||||
{
|
||||
Type[] numbers = new Type[10] {
|
||||
typeof(sbyte),
|
||||
typeof(short),
|
||||
typeof(int),
|
||||
typeof(long),
|
||||
typeof(byte),
|
||||
typeof(ushort),
|
||||
typeof(uint),
|
||||
typeof(ulong),
|
||||
typeof(bool),
|
||||
typeof(string)
|
||||
};
|
||||
MethodInfo[] funcs = new MethodInfo[10] {
|
||||
typeof(IConvertible).GetMethod(nameof(IConvertible.ToSByte)),
|
||||
typeof(IConvertible).GetMethod(nameof(IConvertible.ToInt16)),
|
||||
typeof(IConvertible).GetMethod(nameof(IConvertible.ToInt32)),
|
||||
typeof(IConvertible).GetMethod(nameof(IConvertible.ToInt64)),
|
||||
typeof(IConvertible).GetMethod(nameof(IConvertible.ToByte)),
|
||||
typeof(IConvertible).GetMethod(nameof(IConvertible.ToUInt16)),
|
||||
typeof(IConvertible).GetMethod(nameof(IConvertible.ToUInt32)),
|
||||
typeof(IConvertible).GetMethod(nameof(IConvertible.ToUInt64)),
|
||||
typeof(IConvertible).GetMethod(nameof(IConvertible.ToBoolean)),
|
||||
typeof(IConvertible).GetMethod(nameof(IConvertible.ToString))
|
||||
};
|
||||
|
||||
MethodInfo cucMethodInfo = typeof(CultureInfo).GetProperty(nameof(CultureInfo.CurrentUICulture)).GetGetMethod();
|
||||
for (int i = 0; i < numbers.Length; i++)
|
||||
{
|
||||
if (type == numbers[i])
|
||||
continue;
|
||||
MethodInfo methodInfo = funcs[i];
|
||||
AddConverter(type, numbers[i], new ConvertItem((il) =>
|
||||
{
|
||||
il.Emit(OpCodes.Call, cucMethodInfo);
|
||||
il.Emit(OpCodes.Callvirt, methodInfo);
|
||||
}, GetLost(type, numbers[i]), 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
public static bool SearchConvertPath(Type source, Type target, out SearchResult searchResult)
|
||||
{
|
||||
searchResult = default;
|
||||
if (!s_map.TryGetValue(source, out ConcurrentDictionary<Type, ConvertItem> value) || value.Keys.Count == 0)
|
||||
return false;
|
||||
|
||||
List<SearchNode> nodes = new List<SearchNode>();
|
||||
List<Type> logs = new List<Type>();
|
||||
foreach (KeyValuePair<Type, ConvertItem> kv in value.ToArray().OrderByDescending(a => a.Value.Order))
|
||||
{
|
||||
int lost = GetLost(source, kv.Key);
|
||||
nodes.Add(new SearchNode()
|
||||
{
|
||||
ParentIndex = -1,
|
||||
Target = kv.Key,
|
||||
ConvertItem = kv.Value,
|
||||
ConsumptionWeight = kv.Value.ConsumptionWeight,
|
||||
Inherit = lost > 0 ? kv.Key : source,
|
||||
LostWeight = lost,
|
||||
Deep = 1
|
||||
});
|
||||
}
|
||||
int currentDeep = 1;
|
||||
for (int i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
SearchNode current = nodes[i];
|
||||
|
||||
if (searchResult != null)
|
||||
{
|
||||
if (searchResult.LostWeight < current.LostWeight)
|
||||
continue;
|
||||
if (searchResult.LostWeight == current.LostWeight)
|
||||
{
|
||||
if (searchResult.Length < current.Deep)
|
||||
continue;
|
||||
if (searchResult.Length == current.Deep)
|
||||
{
|
||||
if (searchResult.ConsumptionWeight <= current.ConsumptionWeight)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (current.Target == target)
|
||||
{
|
||||
KeyValuePair<Type, ConvertItem>[] convertItems = new KeyValuePair<Type, ConvertItem>[current.Deep];
|
||||
SearchNode t = current;
|
||||
for (int k = 0; k < convertItems.Length; k++)
|
||||
{
|
||||
convertItems[convertItems.Length - k - 1] = new KeyValuePair<Type, ConvertItem>(t.Target, t.ConvertItem);
|
||||
if (t.ParentIndex >= 0) t = nodes[t.ParentIndex];
|
||||
}
|
||||
|
||||
searchResult = new SearchResult()
|
||||
{
|
||||
ConsumptionWeight = current.ConsumptionWeight,
|
||||
LostWeight = current.LostWeight,
|
||||
Items = convertItems
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s_map.TryGetValue(current.Target, out ConcurrentDictionary<Type, ConvertItem> cur))
|
||||
{
|
||||
int deep = current.Deep + 1;
|
||||
if (currentDeep < deep)
|
||||
{
|
||||
logs.AddRange(nodes.Where(x => x.Deep == currentDeep).Select(y => y.Target));
|
||||
currentDeep = deep;
|
||||
}
|
||||
foreach (KeyValuePair<Type, ConvertItem> kv in cur.ToArray().OrderByDescending(a => a.Value.Order))
|
||||
{
|
||||
if (logs.Contains(kv.Key))
|
||||
continue;
|
||||
int lost = GetLost(current.Inherit, kv.Key);
|
||||
nodes.Add(new SearchNode()
|
||||
{
|
||||
ParentIndex = i,
|
||||
Target = kv.Key,
|
||||
ConvertItem = kv.Value,
|
||||
ConsumptionWeight = current.ConsumptionWeight + kv.Value.ConsumptionWeight,
|
||||
Inherit = lost > 0 ? current.Inherit : kv.Key,
|
||||
LostWeight = current.LostWeight + lost,
|
||||
Deep = deep
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return searchResult != default;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
ConsoleApp1/InvokeST/ConvertMap/SearchNode.cs
Normal file
18
ConsoleApp1/InvokeST/ConvertMap/SearchNode.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Raindrops.Shared.InvokeST.ConvertMap
|
||||
{
|
||||
public struct SearchNode
|
||||
{
|
||||
public int Deep;
|
||||
public int ParentIndex;
|
||||
public int LostWeight;
|
||||
public int ConsumptionWeight;
|
||||
public Type Inherit;
|
||||
public Type Target;
|
||||
public ConvertItem ConvertItem;
|
||||
}
|
||||
}
|
||||
13
ConsoleApp1/InvokeST/ConvertMap/SearchResult.cs
Normal file
13
ConsoleApp1/InvokeST/ConvertMap/SearchResult.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Raindrops.Shared.InvokeST.ConvertMap
|
||||
{
|
||||
public class SearchResult
|
||||
{
|
||||
public int Length => Items?.Length ?? 0;
|
||||
public int ConsumptionWeight { get; set; }
|
||||
public int LostWeight { get; set; }
|
||||
public KeyValuePair<Type, ConvertItem>[] Items { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user