1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-07-03 14:56:01 +02:00
Beef/BeefLibs/corlib/src/Enum.bf

592 lines
13 KiB
Beef
Raw Normal View History

2019-08-23 11:56:54 -07:00
using System.Reflection;
2021-12-27 14:34:55 -05:00
using System.Collections;
2019-08-23 11:56:54 -07:00
namespace System
{
struct Enum
{
2022-06-24 06:45:35 -07:00
[NoShow(true)]
2022-11-22 17:17:14 -03:00
public static int GetCount(Type type)
{
int count = 0;
2022-11-22 17:17:14 -03:00
for (var field in type.GetFields())
{
if (field.IsEnumCase)
count++;
}
return count;
}
2022-06-24 06:45:35 -07:00
[NoShow(true)]
[Comptime(ConstEval=true)]
2022-11-22 17:17:14 -03:00
public static int GetCount<T>() where T : Enum
{
2022-11-22 17:17:14 -03:00
return GetCount(typeof(T));
}
2022-11-22 17:17:14 -03:00
[NoShow(true)]
public static int64 GetMinValue(Type type)
{
int64? minValue = null;
for (var data in GetValues(type))
{
2022-11-22 17:17:14 -03:00
if (minValue == null)
minValue = data;
else
minValue = Math.Min(minValue.Value, data);
}
return minValue.ValueOrDefault;
}
2022-06-24 06:45:35 -07:00
[NoShow(true)]
[Comptime(ConstEval=true)]
2022-11-22 17:17:14 -03:00
public static var GetMinValue<T>() where T : Enum
{
2022-06-24 06:45:35 -07:00
Compiler.SetReturnType(typeof(T));
2022-11-22 17:17:14 -03:00
return GetMinValue(typeof(T));
}
2022-11-22 17:17:14 -03:00
[NoShow(true)]
public static int64 GetMaxValue(Type type)
{
int64? maxValue = null;
for (var data in GetValues(type))
{
2022-11-22 17:17:14 -03:00
if (maxValue == null)
maxValue = data;
else
maxValue = Math.Max(maxValue.Value, data);
}
2022-11-22 17:17:14 -03:00
return maxValue ?? -1;
}
[NoShow(true)]
[Comptime(ConstEval=true)]
public static var GetMaxValue<T>() where T : Enum
{
Compiler.SetReturnType(typeof(T));
return GetMaxValue(typeof(T));
}
2022-06-24 06:45:35 -07:00
[NoShow(true)]
2019-08-23 11:56:54 -07:00
public static void EnumToString(Type type, String strBuffer, int64 iVal)
{
2022-11-22 17:17:14 -03:00
for (var (name, data) in GetEnumerator(type))
2019-08-23 11:56:54 -07:00
{
2022-11-22 17:17:14 -03:00
if (data == iVal)
2019-08-23 11:56:54 -07:00
{
2022-11-22 17:17:14 -03:00
strBuffer.Append(name);
2019-08-23 11:56:54 -07:00
return;
}
}
iVal.ToString(strBuffer);
2019-08-23 11:56:54 -07:00
}
2022-11-22 17:17:14 -03:00
[NoShow(true)]
public static Result<int64> Parse(Type type, StringView str, bool ignoreCase = false)
{
for (var (name, data) in GetEnumerator(type))
{
if (str.Equals(name, ignoreCase))
return .Ok(data);
if (int64.Parse(str) case .Ok(let val) && val == data)
return .Ok(data);
}
return .Err;
}
2022-06-24 06:45:35 -07:00
[NoShow(true)]
public static Result<T> Parse<T>(StringView str, bool ignoreCase = false) where T : enum
2019-08-23 11:56:54 -07:00
{
2024-03-03 06:30:18 -05:00
[IgnoreErrors(true)]
{
return EnumParser<T>.Parse(str, ignoreCase);
}
#unwarn
return .Err;
2019-08-23 11:56:54 -07:00
}
2022-11-22 17:17:14 -03:00
[NoShow(true)]
public static bool IsDefined(Type type, int64 value)
{
for (var data in GetValues(type))
{
if (data == value)
return true;
}
return false;
}
2022-06-24 06:45:35 -07:00
[NoShow(true)]
public static bool IsDefined<T>(T value) where T : Enum
where T : enum
2019-08-23 11:56:54 -07:00
{
2021-12-29 12:44:04 -03:00
for (var data in GetValues<T>())
{
2021-12-29 12:44:04 -03:00
if (data == (.)value)
return true;
}
return false;
}
2022-11-22 17:17:14 -03:00
[NoShow(true)]
public static EnumEnumerator GetEnumerator(Type type)
{
return .(type);
}
2022-06-24 06:45:35 -07:00
[NoShow(true)]
2021-12-27 14:34:55 -05:00
public static EnumEnumerator<TEnum> GetEnumerator<TEnum>()
where TEnum : enum
{
return .();
}
2022-11-22 17:17:14 -03:00
[NoShow(true)]
public static EnumValuesEnumerator GetValues(Type type)
{
return .(type);
}
2022-06-24 06:45:35 -07:00
[NoShow(true)]
2021-12-27 14:34:55 -05:00
public static EnumValuesEnumerator<TEnum> GetValues<TEnum>()
where TEnum : enum
{
return .();
}
2022-06-24 06:45:35 -07:00
2022-11-22 17:17:14 -03:00
[NoShow(true)]
public static EnumNamesEnumerator GetNames(Type type)
{
return .(type);
}
2022-06-24 06:45:35 -07:00
[NoShow(true)]
2021-12-27 14:34:55 -05:00
public static EnumNamesEnumerator<TEnum> GetNames<TEnum>()
where TEnum : enum
{
return .();
}
2022-06-24 06:45:35 -07:00
[NoShow(true)]
2022-11-22 17:17:14 -03:00
private struct EnumFieldsEnumerator
{
2021-12-27 16:06:05 -03:00
TypeInstance mTypeInstance;
int32 mIdx;
2022-11-22 17:17:14 -03:00
public this(Type type)
{
2022-11-22 17:17:14 -03:00
mTypeInstance = type as TypeInstance;
2021-12-27 16:06:05 -03:00
mIdx = -1;
}
2021-12-27 16:06:05 -03:00
public void Reset() mut
{
2021-12-27 16:06:05 -03:00
mIdx = -1;
}
2021-12-27 16:06:05 -03:00
public void Dispose()
{
}
2021-12-27 16:06:05 -03:00
public bool MoveNext() mut
{
2021-12-27 16:06:05 -03:00
if (mTypeInstance == null)
return false;
TypeInstance.FieldData* fieldData = null;
repeat
{
2021-12-27 16:06:05 -03:00
mIdx++;
if (mIdx == mTypeInstance.[Friend]mFieldDataCount)
return false;
fieldData = &mTypeInstance.[Friend]mFieldDataPtr[mIdx];
}
2021-12-27 16:06:05 -03:00
while (!fieldData.mFlags.HasFlag(.EnumCase));
return true;
}
2021-12-27 16:06:05 -03:00
public FieldInfo Current
{
2021-12-27 16:06:05 -03:00
get
{
var fieldData = &mTypeInstance.[Friend]mFieldDataPtr[mIdx];
return FieldInfo(mTypeInstance, fieldData);
}
}
2021-12-27 16:06:05 -03:00
public int32 Index
{
2021-12-27 16:06:05 -03:00
get
{
return mIdx;
}
}
public Result<FieldInfo> GetNext() mut
{
if (!MoveNext())
return .Err;
return Current;
}
}
2022-06-24 06:45:35 -07:00
[NoShow(true)]
2022-11-22 17:17:14 -03:00
public struct EnumEnumerator : EnumFieldsEnumerator, IEnumerator<(StringView name, int64 value)>
{
public this(Type type) : base(type)
{
}
public new (StringView name, int64 value) Current
{
get
{
return ((.)base.Current.[Friend]mFieldData.[Friend]mName, *(int64*)&base.Current.[Friend]mFieldData.[Friend]mData);
}
}
public new Result<(StringView name, int64 value)> GetNext() mut
{
if (!MoveNext())
return .Err;
return Current;
}
}
[NoShow(true)]
public struct EnumEnumerator<TEnum> : EnumFieldsEnumerator, IEnumerator<(StringView name, TEnum value)>
2021-12-27 14:34:55 -05:00
where TEnum : enum
{
2022-11-22 17:17:14 -03:00
public this() : base(typeof(TEnum))
{
}
2021-12-27 14:34:55 -05:00
public new (StringView name, TEnum value) Current
{
get
{
return ((.)base.Current.[Friend]mFieldData.[Friend]mName, (.)*(int64*)&base.Current.[Friend]mFieldData.[Friend]mData);
2021-12-27 14:34:55 -05:00
}
}
public new Result<(StringView name, TEnum value)> GetNext() mut
{
if (!MoveNext())
return .Err;
return Current;
}
}
2022-06-24 06:45:35 -07:00
[NoShow(true)]
2022-11-22 17:17:14 -03:00
public struct EnumValuesEnumerator : EnumFieldsEnumerator, IEnumerator<int64>
{
public this(Type type) : base(type)
{
}
public new int64 Current
{
get
{
return *(int64*)&base.Current.[Friend]mFieldData.[Friend]mData;
}
}
public new Result<int64> GetNext() mut
{
if (!MoveNext())
return .Err;
return Current;
}
}
[NoShow(true)]
public struct EnumValuesEnumerator<TEnum> : EnumFieldsEnumerator, IEnumerator<TEnum>
where TEnum : enum
{
2022-11-22 17:17:14 -03:00
public this() : base(typeof(TEnum))
{
}
public new TEnum Current
{
get
{
return (.)*(int64*)&base.Current.[Friend]mFieldData.[Friend]mData;
}
}
public new Result<TEnum> GetNext() mut
{
if (!MoveNext())
return .Err;
return Current;
}
}
2022-06-24 06:45:35 -07:00
[NoShow(true)]
2022-11-22 17:17:14 -03:00
public struct EnumNamesEnumerator : EnumFieldsEnumerator, IEnumerator<StringView>
{
public this(Type type) : base(type)
{
}
public new StringView Current
{
get
{
return (.)base.Current.[Friend]mFieldData.[Friend]mName;
}
}
public new Result<StringView> GetNext() mut
{
if (!MoveNext())
return .Err;
return Current;
}
}
[NoShow(true)]
public struct EnumNamesEnumerator<TEnum> : EnumFieldsEnumerator, IEnumerator<StringView>
where TEnum : enum
{
2022-11-22 17:17:14 -03:00
public this() : base(typeof(TEnum))
{
}
public new StringView Current
{
get
{
return (.)base.Current.[Friend]mFieldData.[Friend]mName;
}
}
public new Result<StringView> GetNext() mut
{
if (!MoveNext())
return .Err;
return Current;
}
}
2019-08-23 11:56:54 -07:00
}
class EnumParser<T>
{
[OnCompile(.TypeInit), Comptime]
public static void OnTypeInit()
{
String code = scope .();
code.Append("public static Result<T> Parse(StringView str, bool ignoreCase = false)\n");
code.Append("{\n");
2024-03-22 07:46:59 -04:00
code.Append("\tif (str.IsEmpty)\n");
code.Append("\t\treturn .Err;\n");
Type dscrType = typeof(int);
int dscrOffset = 0;
for (var fieldInfo in typeof(T).GetFields())
{
if (fieldInfo.Name == "$discriminator")
{
dscrOffset = fieldInfo.MemberOffset;
dscrType = fieldInfo.FieldType;
}
}
2024-03-22 07:46:59 -04:00
List<FieldInfo> caseList = scope .();
bool hasPayload = false;
for (var fieldInfo in typeof(T).GetFields())
{
2024-03-22 07:46:59 -04:00
if (!fieldInfo.IsEnumCase)
continue;
if (var fieldTypeInst = fieldInfo.FieldType as TypeInstance)
{
2024-03-22 07:46:59 -04:00
caseList.Add(fieldInfo);
if ((fieldTypeInst.IsTuple) && (fieldTypeInst.FieldCount > 0))
{
2024-03-22 07:46:59 -04:00
hasPayload = true;
}
}
}
2024-03-22 07:46:59 -04:00
if (hasPayload)
code.Append("\tT result = default;\n");
if ((!hasPayload) && (caseList.Count > 0))
{
2024-03-22 07:46:59 -04:00
Type underlyingType = typeof(T).UnderlyingType;
if (underlyingType.Size < 4)
underlyingType = typeof(int);
code.AppendF($"\tif (((str[0].IsDigit) || (str[0] == '-')) && ({underlyingType}.Parse(str) case let .Ok(iVal)))\n");
code.Append("\t\treturn .Ok((.)iVal);\n");
}
else if (hasPayload)
{
code.Append("""
StringView nameStr;
StringView argStr = default;
int parenPos = str.IndexOf('(');
if (parenPos != -1)
{
nameStr = str.Substring(0, parenPos);
argStr = str.Substring(parenPos + 1);
}
else
nameStr = str;
int matchIdx = -1;
2024-03-22 07:46:59 -04:00
""");
}
for (int ignoreCasePass < 2)
{
if (caseList.IsEmpty)
break;
void GetFieldName(FieldInfo fieldInfo, String outName)
{
outName.Append(fieldInfo.Name);
if (ignoreCasePass == 0)
outName.ToLower();
}
if (ignoreCasePass == 0)
code.Append("\tif (ignoreCase)\n");
else
code.Append("\telse\n");
code.Append("\t{\n");
int numBuckets = 1;
Dictionary<int, CompactList<(int hash, int caseIdx)>> buckets = scope .();
void ClearBucketList()
{
for (var list in buckets.Values)
list.Dispose();
buckets.Clear();
}
defer ClearBucketList();
for (int bucketPass < caseList.Count * 2)
{
2024-03-22 07:46:59 -04:00
ClearBucketList();
numBuckets = (bucketPass % 2 == 0) ? (caseList.Count + bucketPass / 2) : (caseList.Count - bucketPass / 2 - 1);
for (int caseIdx < caseList.Count)
{
2024-03-22 07:46:59 -04:00
var fieldInfo = caseList[caseIdx];
var fieldName = GetFieldName(fieldInfo, .. scope .(64));
int hashCode = fieldName.GetHashCode();
if (buckets.TryAdd(hashCode % numBuckets, ?, var entryList))
*entryList = default;
entryList.Add((hashCode, caseIdx));
}
2024-03-22 07:46:59 -04:00
// Try for at least a 75% occupancy to ensure we get a jump table
if (buckets.Count >= numBuckets * 0.75)
break;
}
StringView strName;
if (hasPayload)
strName = (ignoreCasePass == 0) ? "checkStr" : "nameStr";
else
strName = "str";
if (ignoreCasePass == 0)
code.AppendF($"\t\tString checkStr = scope .({(hasPayload ? "nameStr" : "str")})..ToLower();\n");
code.AppendF($"\t\tint hashCode = {strName}.GetHashCode();\n");
if (numBuckets > 1)
code.AppendF($"\t\tswitch (hashCode % {numBuckets})\n");
code.Append("\t\t{\n");
for (int bucketIdx < numBuckets)
{
if (!buckets.TryGet(bucketIdx, ?, var entryList))
continue;
if (numBuckets > 1)
code.AppendF($"\t\tcase {bucketIdx}:\n");
for (var entry in entryList)
{
var fieldInfo = caseList[entry.caseIdx];
var fieldName = GetFieldName(fieldInfo, .. scope .(64));
var fieldTypeInst = fieldInfo.FieldType as TypeInstance;
bool caseHasPayload = (fieldTypeInst.IsTuple) && (fieldTypeInst.FieldCount > 0);
if (hasPayload)
code.AppendF($"\t\t\tif ((hashCode == {fieldName.GetHashCode()}) && ({strName} == \"{fieldName}\") && ({caseHasPayload ? "!" : ""}argStr.IsEmpty))\n");
else
code.AppendF($"\t\t\tif ((hashCode == {fieldName.GetHashCode()}) && ({strName} == \"{fieldName}\"))\n");
if (caseHasPayload)
code.AppendF($"\t\t\t\tmatchIdx = {entry.caseIdx};\n");
else
code.AppendF($"\t\t\t\treturn .Ok(.{fieldInfo.Name});\n");
}
}
code.Append("\t\t}\n");
code.Append("\t}\n");
}
if (hasPayload)
{
code.Append("\tswitch (matchIdx)\n");
code.Append("\t{\n");
for (var caseIdx < caseList.Count)
{
var fieldInfo = caseList[caseIdx];
var fieldTypeInst = fieldInfo.FieldType as TypeInstance;
bool caseHasPayload = (fieldTypeInst.IsTuple) && (fieldTypeInst.FieldCount > 0);
if (caseHasPayload)
{
2024-03-22 07:46:59 -04:00
code.AppendF($"\tcase {caseIdx}:\n");
code.AppendF($"\t\t*({dscrType}*)((uint8*)&result + {dscrOffset}) = {fieldInfo.MemberOffset};\n");
code.AppendF($"\t\tvar itr = Try!(EnumFields(str.Substring({fieldInfo.Name.Length+1})));\n");
for (var tupField in fieldTypeInst.GetFields())
code.AppendF($"\t\tTry!(ParseValue(ref itr, ref *({tupField.FieldType}*)((uint8*)&result + {tupField.MemberOffset})));\n");
code.Append("\t\treturn result;\n");
}
2024-03-22 07:46:59 -04:00
}
code.Append("\t}\n");
}
2024-03-18 21:16:07 -03:00
2024-03-18 05:42:37 -04:00
code.Append("\treturn .Err;\n");
code.Append("}\n");
Compiler.EmitTypeBody(typeof(Self), code);
}
static Result<StringSplitEnumerator> EnumFields(StringView str)
{
var str;
str.Trim();
if (!str.EndsWith(')'))
return .Err;
str.RemoveFromEnd(1);
return str.Split(',');
}
static Result<void> ParseValue<TValue>(ref StringSplitEnumerator itr, ref TValue value)
{
return .Err;
}
static Result<void> ParseValue<TValue>(ref StringSplitEnumerator itr, ref TValue value) where TValue : IParseable<TValue>
{
var str = Try!(itr.GetNext());
str.Trim();
value = Try!(TValue.Parse(str));
return .Ok;
}
}
}