return underlyingType;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static EnumInfo GetEnumInfo(RuntimeType enumType, bool getNames = true)
{
- EnumInfo? entry = enumType.GenericCache as EnumInfo;
+ return enumType.GenericCache is EnumInfo info && (!getNames || info.Names is not null) ?
+ info :
+ InitializeEnumInfo(enumType, getNames);
- if (entry == null || (getNames && entry.Names == null))
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static EnumInfo InitializeEnumInfo(RuntimeType enumType, bool getNames)
{
ulong[]? values = null;
string[]? names = null;
getNames ? Interop.BOOL.TRUE : Interop.BOOL.FALSE);
bool hasFlagsAttribute = enumType.IsDefined(typeof(FlagsAttribute), inherit: false);
- entry = new EnumInfo(hasFlagsAttribute, values!, names!);
+ var entry = new EnumInfo(hasFlagsAttribute, values!, names!);
enumType.GenericCache = entry;
+ return entry;
}
-
- return entry;
}
}
}
Array.Copy(rawValues, ValuesAsUnderlyingType, numValues);
HasFlagsAttribute = isFlags;
+
+ ValuesAreSequentialFromZero = true;
+ for (int i = 0; i < values.Length; i++)
+ {
+ if (values[i] != (ulong)i)
+ {
+ ValuesAreSequentialFromZero = false;
+ break;
+ }
+ }
}
internal Type UnderlyingType { get; }
internal ulong[] Values { get; }
internal Array ValuesAsUnderlyingType { get; }
internal bool HasFlagsAttribute { get; }
+ internal bool ValuesAreSequentialFromZero { get; }
}
}
internal sealed class EnumInfo
{
public readonly bool HasFlagsAttribute;
+ public readonly bool ValuesAreSequentialFromZero;
public readonly ulong[] Values;
public readonly string[] Names;
HasFlagsAttribute = hasFlagsAttribute;
Values = values;
Names = names;
+
+ // Store whether all of the values are sequential starting from zero.
+ ValuesAreSequentialFromZero = true;
+ for (int i = 0; i < values.Length; i++)
+ {
+ if (values[i] != (ulong)i)
+ {
+ ValuesAreSequentialFromZero = false;
+ break;
+ }
+ }
}
}
}
return GetEnumName(GetEnumInfo(enumType), ulValue);
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static string? GetEnumName(EnumInfo enumInfo, ulong ulValue)
{
- int index = FindDefinedIndex(enumInfo.Values, ulValue);
- if (index >= 0)
+ if (enumInfo.ValuesAreSequentialFromZero)
+ {
+ string[] names = enumInfo.Names;
+ if (ulValue < (ulong)names.Length)
+ {
+ return names[(uint)ulValue];
+ }
+ }
+ else
{
- return enumInfo.Names[index];
+ int index = FindDefinedIndex(enumInfo.Values, ulValue);
+ if (index >= 0)
+ {
+ return enumInfo.Names[index];
+ }
}
return null; // return null so the caller knows to .ToString() the input
internal static string[] InternalGetNames(RuntimeType enumType) =>
// Get all of the names
- GetEnumInfo(enumType, true).Names;
+ GetEnumInfo(enumType).Names;
public static Type GetUnderlyingType(Type enumType)
{
internal static ulong[] InternalGetValues(RuntimeType enumType)
{
// Get all of the values
- return GetEnumInfo(enumType, false).Values;
+ return GetEnumInfo(enumType, getNames: false).Values;
}
public static bool IsDefined<TEnum>(TEnum value) where TEnum : struct, Enum
{
RuntimeType enumType = (RuntimeType)typeof(TEnum);
- ulong[] ulValues = Enum.InternalGetValues(enumType);
- ulong ulValue = Enum.ToUInt64(value);
-
- return FindDefinedIndex(ulValues, ulValue) >= 0;
+ EnumInfo info = GetEnumInfo(enumType, getNames: false);
+ ulong ulValue = ToUInt64(value);
+ ulong[] ulValues = info.Values;
+
+ // If the enum's values are all sequentially numbered starting from 0, then we can
+ // just return if the requested index is in range. Otherwise, search for the value.
+ return
+ info.ValuesAreSequentialFromZero ? ulValue < (ulong)ulValues.Length :
+ FindDefinedIndex(ulValues, ulValue) >= 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]