Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 12 additions & 44 deletions src/libs/FastEnum.Core/Internals/EnumInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ internal static class EnumInfo<T>
public static readonly Member<T>[] s_orderedMembers;
public static readonly CaseSensitiveStringDictionary<Member<T>> s_memberByNameCaseSensitive;
public static readonly CaseInsensitiveStringDictionary<Member<T>> s_memberByNameCaseInsensitive;
public static readonly FastReadOnlyDictionary<T, Member<T>> s_memberByValue;
public static readonly FastReadOnlyDictionary<T, Member<T>>? s_memberByValue;
public static readonly T s_minValue;
public static readonly T s_maxValue;
public static readonly bool s_isContinuous;
Expand All @@ -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<T>(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<Member<T>, string> nameSelector = x => x.Name;
Func<Member<T>, 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<Func<object,string>>(nameSelector), valueSelector);
s_orderedMembers = Unsafe.As<Member<T>[]>(provider.OrderedMembers)!;
s_memberByNameCaseSensitive = Unsafe.As<CaseSensitiveStringDictionary<Member<T>>>(provider.MemberByNameCaseSensitive)!;
s_memberByNameCaseInsensitive = Unsafe.As<CaseInsensitiveStringDictionary<Member<T>>>(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<T, sbyte>(value),
TypeCode.Byte => Unsafe.BitCast<T, byte>(value),
TypeCode.Int16 => (ulong)Unsafe.BitCast<T, short>(value),
TypeCode.UInt16 => Unsafe.BitCast<T, ushort>(value),
TypeCode.Int32 => (ulong)Unsafe.BitCast<T, int>(value),
TypeCode.UInt32 => Unsafe.BitCast<T, uint>(value),
TypeCode.Int64 => (ulong)Unsafe.BitCast<T, long>(value),
TypeCode.UInt64 => Unsafe.BitCast<T, ulong>(value),
_ => throw new InvalidOperationException(),
};
}
#endregion
(s_minValue, s_maxValue) = provider.GetMinMax<T>();
s_isContinuous = provider.IsContinuous;
s_memberByValue = Unsafe.As<FastReadOnlyDictionary<T, Member<T>>?>(provider.MemberByValue);
s_isEmpty = provider.IsEmpty;
s_isFlags = provider.IsFlags;
}
#endregion
}
128 changes: 128 additions & 0 deletions src/libs/FastEnum.Core/Internals/EnumInfoProvider.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
///
/// </summary>
internal abstract class EnumInfoProvider
{
#region Fields

public object[]? OrderedMembers;
public CaseSensitiveStringDictionary<object>? MemberByNameCaseSensitive;
public CaseInsensitiveStringDictionary<object>? MemberByNameCaseInsensitive;
public bool IsContinuous;
public bool IsEmpty;
public bool IsFlags;
public object? MemberByValue;

#endregion

public (T, T) GetMinMax<T>() where T : struct
{
return (Unsafe.As<GenericEnumInfoProvider<T>>(this).MinValue, Unsafe.As<GenericEnumInfoProvider<T>>(this).MaxValue);
}


public static EnumInfoProvider Create(Type type, TypeCode typeCode, object[] members, Func<object, string> nameSelector, object valueSelector)
{
switch (typeCode)
{
case TypeCode.SByte:
return new GenericEnumInfoProvider<sbyte>(type, members, nameSelector, Unsafe.As<Func<object, sbyte>>(valueSelector));
case TypeCode.Byte:
return new GenericEnumInfoProvider<byte>(type, members, nameSelector, Unsafe.As<Func<object, byte>>(valueSelector));
case TypeCode.Int16:
return new GenericEnumInfoProvider<short>(type, members, nameSelector, Unsafe.As<Func<object, short>>(valueSelector));
case TypeCode.UInt16:
return new GenericEnumInfoProvider<ushort>(type, members, nameSelector, Unsafe.As<Func<object, ushort>>(valueSelector));
case TypeCode.Int32:
return new GenericEnumInfoProvider<int>(type, members, nameSelector, Unsafe.As<Func<object, int>>(valueSelector));
case TypeCode.UInt32:
return new GenericEnumInfoProvider<uint>(type, members, nameSelector, Unsafe.As<Func<object, uint>>(valueSelector));
case TypeCode.Int64:
return new GenericEnumInfoProvider<long>(type, members, nameSelector, Unsafe.As<Func<object, long>>(valueSelector));
case TypeCode.UInt64:
return new GenericEnumInfoProvider<ulong>(type, members, nameSelector, Unsafe.As<Func<object, ulong>>(valueSelector));
default:
throw new InvalidOperationException();
}
}


public sealed class GenericEnumInfoProvider<T> : EnumInfoProvider where T : struct
{
public T MinValue;
public T MaxValue;

#region Constructor

public GenericEnumInfoProvider(Type type, object[] members, Func<object, string> nameSelector, Func<object, T> 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<T, sbyte>(value);
if (typeof(T) == typeof(byte))
return Unsafe.BitCast<T, byte>(value);
if (typeof(T) == typeof(short))
return (ulong)Unsafe.BitCast<T, short>(value);
if (typeof(T) == typeof(ushort))
return Unsafe.BitCast<T, ushort>(value);
if (typeof(T) == typeof(int))
return (ulong)Unsafe.BitCast<T, int>(value);
if (typeof(T) == typeof(uint))
return Unsafe.BitCast<T, uint>(value);
if (typeof(T) == typeof(long))
return (ulong)Unsafe.BitCast<T, long>(value);
if (typeof(T) == typeof(ulong))
return Unsafe.BitCast<T, ulong>(value);
throw new InvalidOperationException();
}

#endregion
}

#endregion
}
}
32 changes: 16 additions & 16 deletions src/libs/FastEnum.Core/Internals/UnderlyingOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ public static bool IsDefined(T value)
}
else
{
return EnumInfo<T>.s_memberByValue.ContainsKey(value);
return EnumInfo<T>.s_memberByValue!.ContainsKey(value);
}


Expand Down Expand Up @@ -219,7 +219,7 @@ public static bool TryGetMember(T value, [NotNullWhen(true)] out Member<T>? resu
}
else
{
return EnumInfo<T>.s_memberByValue.TryGetValue(value, out result);
return EnumInfo<T>.s_memberByValue!.TryGetValue(value, out result);
}


Expand Down Expand Up @@ -255,7 +255,7 @@ public static bool IsDefined(T value)
}
else
{
return EnumInfo<T>.s_memberByValue.ContainsKey(value);
return EnumInfo<T>.s_memberByValue!.ContainsKey(value);
}


Expand Down Expand Up @@ -309,7 +309,7 @@ public static bool TryGetMember(T value, [NotNullWhen(true)] out Member<T>? resu
}
else
{
return EnumInfo<T>.s_memberByValue.TryGetValue(value, out result);
return EnumInfo<T>.s_memberByValue!.TryGetValue(value, out result);
}


Expand Down Expand Up @@ -345,7 +345,7 @@ public static bool IsDefined(T value)
}
else
{
return EnumInfo<T>.s_memberByValue.ContainsKey(value);
return EnumInfo<T>.s_memberByValue!.ContainsKey(value);
}


Expand Down Expand Up @@ -399,7 +399,7 @@ public static bool TryGetMember(T value, [NotNullWhen(true)] out Member<T>? resu
}
else
{
return EnumInfo<T>.s_memberByValue.TryGetValue(value, out result);
return EnumInfo<T>.s_memberByValue!.TryGetValue(value, out result);
}


Expand Down Expand Up @@ -435,7 +435,7 @@ public static bool IsDefined(T value)
}
else
{
return EnumInfo<T>.s_memberByValue.ContainsKey(value);
return EnumInfo<T>.s_memberByValue!.ContainsKey(value);
}


Expand Down Expand Up @@ -489,7 +489,7 @@ public static bool TryGetMember(T value, [NotNullWhen(true)] out Member<T>? resu
}
else
{
return EnumInfo<T>.s_memberByValue.TryGetValue(value, out result);
return EnumInfo<T>.s_memberByValue!.TryGetValue(value, out result);
}


Expand Down Expand Up @@ -525,7 +525,7 @@ public static bool IsDefined(T value)
}
else
{
return EnumInfo<T>.s_memberByValue.ContainsKey(value);
return EnumInfo<T>.s_memberByValue!.ContainsKey(value);
}


Expand Down Expand Up @@ -579,7 +579,7 @@ public static bool TryGetMember(T value, [NotNullWhen(true)] out Member<T>? resu
}
else
{
return EnumInfo<T>.s_memberByValue.TryGetValue(value, out result);
return EnumInfo<T>.s_memberByValue!.TryGetValue(value, out result);
}


Expand Down Expand Up @@ -615,7 +615,7 @@ public static bool IsDefined(T value)
}
else
{
return EnumInfo<T>.s_memberByValue.ContainsKey(value);
return EnumInfo<T>.s_memberByValue!.ContainsKey(value);
}


Expand Down Expand Up @@ -669,7 +669,7 @@ public static bool TryGetMember(T value, [NotNullWhen(true)] out Member<T>? resu
}
else
{
return EnumInfo<T>.s_memberByValue.TryGetValue(value, out result);
return EnumInfo<T>.s_memberByValue!.TryGetValue(value, out result);
}


Expand Down Expand Up @@ -705,7 +705,7 @@ public static bool IsDefined(T value)
}
else
{
return EnumInfo<T>.s_memberByValue.ContainsKey(value);
return EnumInfo<T>.s_memberByValue!.ContainsKey(value);
}


Expand Down Expand Up @@ -759,7 +759,7 @@ public static bool TryGetMember(T value, [NotNullWhen(true)] out Member<T>? resu
}
else
{
return EnumInfo<T>.s_memberByValue.TryGetValue(value, out result);
return EnumInfo<T>.s_memberByValue!.TryGetValue(value, out result);
}


Expand Down Expand Up @@ -795,7 +795,7 @@ public static bool IsDefined(T value)
}
else
{
return EnumInfo<T>.s_memberByValue.ContainsKey(value);
return EnumInfo<T>.s_memberByValue!.ContainsKey(value);
}


Expand Down Expand Up @@ -849,7 +849,7 @@ public static bool TryGetMember(T value, [NotNullWhen(true)] out Member<T>? resu
}
else
{
return EnumInfo<T>.s_memberByValue.TryGetValue(value, out result);
return EnumInfo<T>.s_memberByValue!.TryGetValue(value, out result);
}


Expand Down