1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-16 15:24:10 +02:00
Beef/BeefLibs/corlib/src/HashCode.bf

161 lines
3.4 KiB
Beef
Raw Normal View History

2023-11-18 07:40:54 -05:00
using System.Reflection;
namespace System;
static class HashCode
{
class HashHelper<T>
{
[OnCompile(.TypeInit), Comptime]
public static void Generate()
{
var t = typeof(T);
String code = scope .();
code.AppendF($"public static int Get(T value)\n");
code.Append("{\n");
if (t.IsTypedPrimitive == true)
{
2023-11-18 07:50:51 -05:00
code.AppendF($"\treturn SelfOuter.Get(({t.UnderlyingType})value);");
2023-11-18 07:40:54 -05:00
}
else if (t.IsEnum)
{
code.Append("\tint hash = 0;\n");
if ((t.IsEnum) && (t.UnderlyingType == null))
{
code.AppendF($"\tswitch (value)\n");
code.Append("\t{\n");
int enumCaseCount = 0;
for (var field in t.GetFields())
{
if (!field.IsEnumCase)
continue;
code.AppendF($"\tcase .{field.Name}");
int tupleMemberCount = 0;
var fieldType = field.FieldType;
if (fieldType.IsTuple)
{
code.Append("(");
for (var tupleField in fieldType.GetFields())
{
if (tupleMemberCount > 0)
code.Append(", ");
code.AppendF($"var val{tupleMemberCount}");
tupleMemberCount++;
}
code.Append(")");
}
code.Append(":\n");
if (enumCaseCount > 0)
code.AppendF($"\t\thash = {enumCaseCount};\n");
2023-11-18 07:40:54 -05:00
for (int tupleMemberIdx < tupleMemberCount)
{
if ((enumCaseCount == 0) && (tupleMemberIdx == 0))
code.AppendF($"\t\thash = SelfOuter.Get(val{tupleMemberIdx});\n");
else
code.AppendF($"\t\thash = Mix(hash, val{tupleMemberIdx});\n");
}
2023-11-18 07:40:54 -05:00
enumCaseCount++;
}
code.Append("\t}\n");
}
code.Append("\treturn hash;\n");
}
else if (t.IsUnion)
{
code.AppendF($"\treturn SelfOuter.Get(&value, {t.Size});\n");
}
else
{
if (!t.IsValueType)
{
code.AppendF("\tif (value == null)\n");
code.AppendF("\t\treturn 0;\n");
}
code.Append("\tint hash = 0;\n");
if (var sizedArray = t as SizedArrayType)
{
code.AppendF($"\tfor (int i < {sizedArray.ElementCount})\n");
code.AppendF($"\t\thash = Mix(hash, value[i]);\n");
}
else
{
int fieldCount = 0;
for (var field in t.GetFields())
{
if (field.IsStatic)
continue;
if (fieldCount == 0)
2023-11-18 07:53:23 -05:00
code.AppendF($"\thash = SelfOuter.Get(value.");
2023-11-18 07:40:54 -05:00
else
2023-11-18 07:53:23 -05:00
code.AppendF($"\thash = Mix(hash, value.");
if (!field.IsPublic)
code.Append("[Friend]");
code.AppendF($"{field.Name});\n");
2023-11-18 07:40:54 -05:00
++fieldCount;
}
}
code.Append("\treturn hash;\n");
}
code.Append("}");
Compiler.EmitTypeBody(typeof(Self), code);
}
}
public static int Get<T>(T value)
{
return HashHelper<T>.Get(value);
}
public static int Get<T>(T value) where T : IHashable
{
if (value == null)
return 0;
return value.GetHashCode();
}
public static int Get(void* ptr, int size)
{
int bytesLeft = size;
int hash = 0;
uint8* curPtr = (.)ptr;
let intSize = sizeof(int);
while (bytesLeft >= intSize)
{
hash = (hash ^ *((int*)curPtr)) &+ (hash &* 16777619);
bytesLeft -= intSize;
curPtr += intSize;
}
while (bytesLeft >= 1)
{
hash = ((hash ^ (int)*curPtr) << 5) &- hash;
bytesLeft--;
curPtr++;
}
return hash;
}
public static int Mix(int hash, int hash2)
{
return ((hash ^ hash2) << 5) &- hash;
}
public static int Mix<T>(int hash, T value)
{
return ((hash ^ Get(value)) << 5) &- hash;
}
}