diff --git a/src/libs/FastEnum.Core/Internals/EnumInfo.cs b/src/libs/FastEnum.Core/Internals/EnumInfo.cs index 0031d4f..1550251 100644 --- a/src/libs/FastEnum.Core/Internals/EnumInfo.cs +++ b/src/libs/FastEnum.Core/Internals/EnumInfo.cs @@ -23,7 +23,7 @@ internal static class EnumInfo public static readonly Member[] s_orderedMembers; public static readonly CaseSensitiveStringDictionary> s_memberByNameCaseSensitive; public static readonly CaseInsensitiveStringDictionary> s_memberByNameCaseInsensitive; - public static readonly FastReadOnlyDictionary> s_memberByValue; + public static readonly FastReadOnlyDictionary>? s_memberByValue; public static readonly T s_minValue; public static readonly T s_maxValue; public static readonly bool s_isContinuous; @@ -41,52 +41,20 @@ static EnumInfo() s_names = Enum.GetNames(s_type); s_values = (T[])Enum.GetValues(s_type); s_members = s_names.Select(static x => new Member(x)).ToArray(); - s_orderedMembers - = s_members - .OrderBy(static x => x.Value) - .DistinctBy(static x => x.Value) - .ToArray(); - s_memberByNameCaseSensitive = s_members.ToCaseSensitiveStringDictionary(static x => x.Name); - s_memberByNameCaseInsensitive - = s_members - .DistinctBy(static x => x.Name, StringComparer.OrdinalIgnoreCase) - .ToCaseInsensitiveStringDictionary(static x => x.Name); - s_memberByValue = s_orderedMembers.ToFastReadOnlyDictionary(static x => x.Value); - s_minValue = s_values.DefaultIfEmpty().Min(); - s_maxValue = s_values.DefaultIfEmpty().Max(); - s_isContinuous = isContinuous(s_memberByValue.Count, s_maxValue, s_minValue); - s_isEmpty = s_values.Length is 0; - s_isFlags = s_type.IsDefined(typeof(FlagsAttribute), true); + Func, string> nameSelector = x => x.Name; + Func, T> valueSelector = x => x.Value; - #region Local Functions - static bool isContinuous(int uniqueCount, T max, T min) - { - if (uniqueCount <= 0) - return false; + var provider = EnumInfoProvider.Create(s_type, s_typeCode, s_members, Unsafe.As>(nameSelector), valueSelector); + s_orderedMembers = Unsafe.As[]>(provider.OrderedMembers)!; + s_memberByNameCaseSensitive = Unsafe.As>>(provider.MemberByNameCaseSensitive)!; + s_memberByNameCaseInsensitive = Unsafe.As>>(provider.MemberByNameCaseInsensitive)!; - var length = toUInt64(max) - toUInt64(min); - var count = (ulong)uniqueCount - 1; - return length == count; - } - - - static ulong toUInt64(T value) - { - return s_typeCode switch - { - TypeCode.SByte => (ulong)Unsafe.BitCast(value), - TypeCode.Byte => Unsafe.BitCast(value), - TypeCode.Int16 => (ulong)Unsafe.BitCast(value), - TypeCode.UInt16 => Unsafe.BitCast(value), - TypeCode.Int32 => (ulong)Unsafe.BitCast(value), - TypeCode.UInt32 => Unsafe.BitCast(value), - TypeCode.Int64 => (ulong)Unsafe.BitCast(value), - TypeCode.UInt64 => Unsafe.BitCast(value), - _ => throw new InvalidOperationException(), - }; - } - #endregion + (s_minValue, s_maxValue) = provider.GetMinMax(); + s_isContinuous = provider.IsContinuous; + s_memberByValue = Unsafe.As>?>(provider.MemberByValue); + s_isEmpty = provider.IsEmpty; + s_isFlags = provider.IsFlags; } #endregion } diff --git a/src/libs/FastEnum.Core/Internals/EnumInfoProvider.cs b/src/libs/FastEnum.Core/Internals/EnumInfoProvider.cs new file mode 100644 index 0000000..b8ebf27 --- /dev/null +++ b/src/libs/FastEnum.Core/Internals/EnumInfoProvider.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Immutable; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; + +namespace FastEnumUtility.Internals; + +/// +/// +/// +internal abstract class EnumInfoProvider +{ + #region Fields + + public object[]? OrderedMembers; + public CaseSensitiveStringDictionary? MemberByNameCaseSensitive; + public CaseInsensitiveStringDictionary? MemberByNameCaseInsensitive; + public bool IsContinuous; + public bool IsEmpty; + public bool IsFlags; + public object? MemberByValue; + + #endregion + + public (T, T) GetMinMax() where T : struct + { + return (Unsafe.As>(this).MinValue, Unsafe.As>(this).MaxValue); + } + + + public static EnumInfoProvider Create(Type type, TypeCode typeCode, object[] members, Func nameSelector, object valueSelector) + { + switch (typeCode) + { + case TypeCode.SByte: + return new GenericEnumInfoProvider(type, members, nameSelector, Unsafe.As>(valueSelector)); + case TypeCode.Byte: + return new GenericEnumInfoProvider(type, members, nameSelector, Unsafe.As>(valueSelector)); + case TypeCode.Int16: + return new GenericEnumInfoProvider(type, members, nameSelector, Unsafe.As>(valueSelector)); + case TypeCode.UInt16: + return new GenericEnumInfoProvider(type, members, nameSelector, Unsafe.As>(valueSelector)); + case TypeCode.Int32: + return new GenericEnumInfoProvider(type, members, nameSelector, Unsafe.As>(valueSelector)); + case TypeCode.UInt32: + return new GenericEnumInfoProvider(type, members, nameSelector, Unsafe.As>(valueSelector)); + case TypeCode.Int64: + return new GenericEnumInfoProvider(type, members, nameSelector, Unsafe.As>(valueSelector)); + case TypeCode.UInt64: + return new GenericEnumInfoProvider(type, members, nameSelector, Unsafe.As>(valueSelector)); + default: + throw new InvalidOperationException(); + } + } + + + public sealed class GenericEnumInfoProvider : EnumInfoProvider where T : struct + { + public T MinValue; + public T MaxValue; + + #region Constructor + + public GenericEnumInfoProvider(Type type, object[] members, Func nameSelector, Func valueSelector) + { + OrderedMembers + = members + .OrderBy(valueSelector) + .DistinctBy(valueSelector) + .ToArray(); + MemberByNameCaseSensitive = members.ToCaseSensitiveStringDictionary(nameSelector); + MemberByNameCaseInsensitive + = members + .DistinctBy(nameSelector, StringComparer.OrdinalIgnoreCase) + .ToCaseInsensitiveStringDictionary(nameSelector); + if (OrderedMembers.Length != 0) + { + MinValue = valueSelector(OrderedMembers[0]); + MaxValue = valueSelector(OrderedMembers[^1]); + } + + IsContinuous = isContinuous(OrderedMembers.Length, MaxValue, MinValue); + if (!IsContinuous) + MemberByValue = OrderedMembers.ToFastReadOnlyDictionary(valueSelector); + IsEmpty = members.Length == 0; + IsFlags = type.IsDefined(typeof(FlagsAttribute), true); + + #region Local Functions + + static bool isContinuous(int uniqueCount, T max, T min) + { + if (uniqueCount <= 0) + return false; + + var length = toUInt64(max) - toUInt64(min); + var count = (ulong)uniqueCount - 1; + return length == count; + } + + static ulong toUInt64(T value) + { + if (typeof(T) == typeof(sbyte)) + return (ulong)Unsafe.BitCast(value); + if (typeof(T) == typeof(byte)) + return Unsafe.BitCast(value); + if (typeof(T) == typeof(short)) + return (ulong)Unsafe.BitCast(value); + if (typeof(T) == typeof(ushort)) + return Unsafe.BitCast(value); + if (typeof(T) == typeof(int)) + return (ulong)Unsafe.BitCast(value); + if (typeof(T) == typeof(uint)) + return Unsafe.BitCast(value); + if (typeof(T) == typeof(long)) + return (ulong)Unsafe.BitCast(value); + if (typeof(T) == typeof(ulong)) + return Unsafe.BitCast(value); + throw new InvalidOperationException(); + } + + #endregion + } + + #endregion + } +} diff --git a/src/libs/FastEnum.Core/Internals/UnderlyingOperation.cs b/src/libs/FastEnum.Core/Internals/UnderlyingOperation.cs index 45a3fde..98b1867 100644 --- a/src/libs/FastEnum.Core/Internals/UnderlyingOperation.cs +++ b/src/libs/FastEnum.Core/Internals/UnderlyingOperation.cs @@ -165,7 +165,7 @@ public static bool IsDefined(T value) } else { - return EnumInfo.s_memberByValue.ContainsKey(value); + return EnumInfo.s_memberByValue!.ContainsKey(value); } @@ -219,7 +219,7 @@ public static bool TryGetMember(T value, [NotNullWhen(true)] out Member? resu } else { - return EnumInfo.s_memberByValue.TryGetValue(value, out result); + return EnumInfo.s_memberByValue!.TryGetValue(value, out result); } @@ -255,7 +255,7 @@ public static bool IsDefined(T value) } else { - return EnumInfo.s_memberByValue.ContainsKey(value); + return EnumInfo.s_memberByValue!.ContainsKey(value); } @@ -309,7 +309,7 @@ public static bool TryGetMember(T value, [NotNullWhen(true)] out Member? resu } else { - return EnumInfo.s_memberByValue.TryGetValue(value, out result); + return EnumInfo.s_memberByValue!.TryGetValue(value, out result); } @@ -345,7 +345,7 @@ public static bool IsDefined(T value) } else { - return EnumInfo.s_memberByValue.ContainsKey(value); + return EnumInfo.s_memberByValue!.ContainsKey(value); } @@ -399,7 +399,7 @@ public static bool TryGetMember(T value, [NotNullWhen(true)] out Member? resu } else { - return EnumInfo.s_memberByValue.TryGetValue(value, out result); + return EnumInfo.s_memberByValue!.TryGetValue(value, out result); } @@ -435,7 +435,7 @@ public static bool IsDefined(T value) } else { - return EnumInfo.s_memberByValue.ContainsKey(value); + return EnumInfo.s_memberByValue!.ContainsKey(value); } @@ -489,7 +489,7 @@ public static bool TryGetMember(T value, [NotNullWhen(true)] out Member? resu } else { - return EnumInfo.s_memberByValue.TryGetValue(value, out result); + return EnumInfo.s_memberByValue!.TryGetValue(value, out result); } @@ -525,7 +525,7 @@ public static bool IsDefined(T value) } else { - return EnumInfo.s_memberByValue.ContainsKey(value); + return EnumInfo.s_memberByValue!.ContainsKey(value); } @@ -579,7 +579,7 @@ public static bool TryGetMember(T value, [NotNullWhen(true)] out Member? resu } else { - return EnumInfo.s_memberByValue.TryGetValue(value, out result); + return EnumInfo.s_memberByValue!.TryGetValue(value, out result); } @@ -615,7 +615,7 @@ public static bool IsDefined(T value) } else { - return EnumInfo.s_memberByValue.ContainsKey(value); + return EnumInfo.s_memberByValue!.ContainsKey(value); } @@ -669,7 +669,7 @@ public static bool TryGetMember(T value, [NotNullWhen(true)] out Member? resu } else { - return EnumInfo.s_memberByValue.TryGetValue(value, out result); + return EnumInfo.s_memberByValue!.TryGetValue(value, out result); } @@ -705,7 +705,7 @@ public static bool IsDefined(T value) } else { - return EnumInfo.s_memberByValue.ContainsKey(value); + return EnumInfo.s_memberByValue!.ContainsKey(value); } @@ -759,7 +759,7 @@ public static bool TryGetMember(T value, [NotNullWhen(true)] out Member? resu } else { - return EnumInfo.s_memberByValue.TryGetValue(value, out result); + return EnumInfo.s_memberByValue!.TryGetValue(value, out result); } @@ -795,7 +795,7 @@ public static bool IsDefined(T value) } else { - return EnumInfo.s_memberByValue.ContainsKey(value); + return EnumInfo.s_memberByValue!.ContainsKey(value); } @@ -849,7 +849,7 @@ public static bool TryGetMember(T value, [NotNullWhen(true)] out Member? resu } else { - return EnumInfo.s_memberByValue.TryGetValue(value, out result); + return EnumInfo.s_memberByValue!.TryGetValue(value, out result); }