From f282c9c0d6d8c9c50788a805ab01fb7ad291aab3 Mon Sep 17 00:00:00 2001 From: Phill Date: Fri, 21 Mar 2025 11:32:49 -0400 Subject: [PATCH 01/12] refactor to be able to store interface types #10 --- core/Functions/Register.cs | 17 +- core/TypeBuffer.cs | 56 +++++ core/TypeLayout.cs | 265 +++++++--------------- core/TypeRegistry.cs | 78 +++---- core/Variable.cs | 135 +++++++++++ core/VariableBuffer.cs | 59 +++++ generator/Generators/TypeBankGenerator.cs | 19 +- tests/BankTests.cs | 6 +- tests/LayoutTests.cs | 20 +- 9 files changed, 407 insertions(+), 248 deletions(-) create mode 100644 core/TypeBuffer.cs create mode 100644 core/Variable.cs create mode 100644 core/VariableBuffer.cs diff --git a/core/Functions/Register.cs b/core/Functions/Register.cs index a011288..2fa28d1 100644 --- a/core/Functions/Register.cs +++ b/core/Functions/Register.cs @@ -15,11 +15,22 @@ internal Register(Action action) } /// - /// Registers a type with the given . + /// Registers a type with the given and . /// - public unsafe readonly void Invoke(ReadOnlySpan variables) where T : unmanaged + public unsafe readonly void Invoke(ReadOnlySpan variables, ReadOnlySpan interfaces) where T : unmanaged { - TypeLayout type = new(TypeLayout.GetFullName(), (ushort)sizeof(T), variables); + TypeLayout type = new(TypeLayout.GetFullName(), (ushort)sizeof(T), variables, interfaces); + Input input = new(type, RuntimeTypeTable.GetHandle()); + action(input); + TypeInstanceCreator.Initialize(type); + } + + /// + /// Registers a type with the given and . + /// + public unsafe readonly void Invoke(VariableBuffer variables, byte variableCount, TypeBuffer interfaces, byte interfaceCount) where T : unmanaged + { + TypeLayout type = new(TypeLayout.GetFullName(), (ushort)sizeof(T), variables, variableCount, interfaces, interfaceCount); Input input = new(type, RuntimeTypeTable.GetHandle()); action(input); TypeInstanceCreator.Initialize(type); diff --git a/core/TypeBuffer.cs b/core/TypeBuffer.cs new file mode 100644 index 0000000..d3cd1c3 --- /dev/null +++ b/core/TypeBuffer.cs @@ -0,0 +1,56 @@ +using System; +using System.Diagnostics; + +namespace Types +{ + /// + /// Buffer for storing values. + /// + public unsafe struct TypeBuffer + { + /// + /// Maximum amount of values that can be stored. + /// + public const int Capacity = 32; + + private fixed long hashes[Capacity]; + + /// + /// Indexer for accessing a value at the given . + /// + public TypeLayout this[int index] + { + readonly get + { + long hash = hashes[index]; + return TypeRegistry.Get(hash); + } + set + { + hashes[index] = value.Hash; + } + } + + /// + /// Creates a new buffer containing the given . + /// + public TypeBuffer(ReadOnlySpan types) + { + ThrowIfCantFit(types.Length); + + for (int i = 0; i < types.Length; i++) + { + hashes[i] = types[i].Hash; + } + } + + [Conditional("DEBUG")] + private static void ThrowIfCantFit(int length) + { + if (length > Capacity) + { + throw new ArgumentOutOfRangeException(nameof(length), $"Cannot fit {length} types into a buffer of capacity {Capacity}"); + } + } + } +} \ No newline at end of file diff --git a/core/TypeLayout.cs b/core/TypeLayout.cs index 2b9249e..c9203dc 100644 --- a/core/TypeLayout.cs +++ b/core/TypeLayout.cs @@ -10,41 +10,55 @@ namespace Types [SkipLocalsInit] public readonly struct TypeLayout : IEquatable { - /// - /// Maximum amount of variables per type. - /// - public const byte Capacity = 32; - /// /// Size of the type in bytes. /// public readonly ushort size; - + /// /// Amount of s the type has. /// public readonly byte variableCount; - + + /// + /// Amount of interfaces the type implements. + /// + public readonly byte interfaceCount; + private readonly long hash; - private readonly VariablesCollection variables; + private readonly VariableBuffer variables; + private readonly TypeBuffer interfaces; /// /// Hash value unique to this type. /// public readonly long Hash => hash; - [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)] - private readonly Variable[] Variables + /// + /// All variables declared in the type. + /// + public unsafe readonly ReadOnlySpan Variables { get { - Variable[] variables = new Variable[variableCount]; - for (int i = 0; i < variableCount; i++) + fixed (void* pointer = &variables) { - variables[i] = this.variables[i]; + return new ReadOnlySpan(pointer, variableCount); } + } + } - return variables; + /// + /// All interfaces implemented by this type. + /// + public unsafe readonly ReadOnlySpan Interfaces + { + get + { + fixed (void* pointer = &interfaces) + { + return new ReadOnlySpan(pointer, interfaceCount); + } } } @@ -90,16 +104,6 @@ public readonly ReadOnlySpan Name } } - /// - /// Indexer for variables. - /// - public readonly Variable this[int index] => variables[index]; - - /// - /// Indexer for variables. - /// - public readonly Variable this[uint index] => variables[(int)index]; - #if NET /// /// Default constructor not supported. @@ -136,36 +140,39 @@ public TypeLayout(string fullName, ushort size) /// /// Creates a new type layout. /// - public TypeLayout(ReadOnlySpan fullName, ushort size, ReadOnlySpan variables) + public TypeLayout(ReadOnlySpan fullName, ushort size, ReadOnlySpan variables, ReadOnlySpan interfaces) { - ThrowIfGreaterThanCapacity(variables.Length); - this.size = size; variableCount = (byte)variables.Length; - this.variables = new(); - for (int i = 0; i < variableCount; i++) - { - this.variables[i] = variables[i]; - } - + this.variables = new(variables); + interfaceCount = (byte)interfaces.Length; + this.interfaces = new(interfaces); hash = TypeNames.Set(fullName); } /// /// Creates a new type layout. /// - public TypeLayout(string fullName, ushort size, ReadOnlySpan variables) + public TypeLayout(ReadOnlySpan fullName, ushort size, VariableBuffer variables, byte variableCount, TypeBuffer interfaces, byte interfaceCount) { - ThrowIfGreaterThanCapacity(variables.Length); + this.size = size; + this.variableCount = variableCount; + this.variables = variables; + this.interfaceCount = interfaceCount; + this.interfaces = interfaces; + hash = TypeNames.Set(fullName); + } + /// + /// Creates a new type layout. + /// + public TypeLayout(string fullName, ushort size, ReadOnlySpan variables, ReadOnlySpan interfaces) + { this.size = size; variableCount = (byte)variables.Length; - this.variables = new(); - for (int i = 0; i < variableCount; i++) - { - this.variables[i] = variables[i]; - } - + this.variables = new(variables); + interfaceCount = (byte)interfaces.Length; + this.interfaces = new(interfaces); hash = TypeNames.Set(fullName); } @@ -185,6 +192,26 @@ public readonly int ToString(Span destination) return fullName.Length; } + /// + /// Retrieves the field at the given . + /// + public readonly Variable GetVariable(int index) + { + ThrowIfVariableIndexOutOfRange(index); + + return variables[index]; + } + + /// + /// Retrieves the implemented interface at the given . + /// + public readonly TypeLayout GetInterface(int index) + { + ThrowIfInterfaceIndexOutOfRange(index); + + return interfaces[index]; + } + /// /// Checks if this type metadata represents type . /// @@ -438,11 +465,20 @@ private readonly void ThrowIfVariableIsMissing(ReadOnlySpan name) } [Conditional("DEBUG")] - private static void ThrowIfGreaterThanCapacity(int length) + private readonly void ThrowIfVariableIndexOutOfRange(int index) { - if (length > Capacity) + if (index < 0 || index >= variableCount) { - throw new InvalidOperationException($"TypeLayout has reached its capacity of {Capacity} variables"); + throw new IndexOutOfRangeException($"Variable index {index} is out of range for type {FullName.ToString()}"); + } + } + + [Conditional("DEBUG")] + private readonly void ThrowIfInterfaceIndexOutOfRange(int index) + { + if (index < 0 || index >= interfaceCount) + { + throw new IndexOutOfRangeException($"Interface index {index} is out of range for type {FullName.ToString()}"); } } @@ -478,146 +514,5 @@ public readonly override int GetHashCode() { return !(left == right); } - - /// - /// Describes a variable part of a . - /// - public readonly struct Variable : IEquatable - { - internal readonly long nameHash; - internal readonly long typeHash; - - /// - /// Name of the variable. - /// - public readonly ReadOnlySpan Name => TypeNames.Get(nameHash); - - /// - /// Type layout of the variable. - /// - public readonly TypeLayout Type => TypeRegistry.Get(typeHash); - - /// - /// Size of the variable in bytes. - /// - public readonly ushort Size => Type.size; - - /// - /// Creates a new variable with the given and . - /// - public Variable(string name, string fullTypeName) - { - this.nameHash = TypeNames.Set(name); - typeHash = fullTypeName.GetLongHashCode(); - } - - internal Variable(long typeHash, long nameHash) - { - this.typeHash = typeHash; - this.nameHash = nameHash; - } - - /// - /// Creates a new variable with the given and . - /// - public Variable(ReadOnlySpan name, ReadOnlySpan fullTypeName) - { - this.nameHash = TypeNames.Set(name); - typeHash = fullTypeName.GetLongHashCode(); - } - - /// - /// Creates a new variable with the given and . - /// - public Variable(string name, int typeHash) - { - this.nameHash = TypeNames.Set(name); - this.typeHash = typeHash; - } - - /// - public readonly override string ToString() - { - Span buffer = stackalloc char[256]; - int length = ToString(buffer); - return buffer.Slice(0, length).ToString(); - } - - /// - /// Builds a string representation of this variable and writes it to . - /// - /// Amount of characters written. - public readonly int ToString(Span buffer) - { - TypeLayout typeLayout = Type; - typeLayout.Name.CopyTo(buffer); - int length = typeLayout.Name.Length; - buffer[length++] = '='; - Name.CopyTo(buffer.Slice(length)); - length += Name.Length; - return length; - } - - /// - public readonly override bool Equals(object? obj) - { - return obj is Variable variable && Equals(variable); - } - - /// - public readonly bool Equals(Variable other) - { - return nameHash == other.nameHash && typeHash == other.typeHash; - } - - /// - public readonly override int GetHashCode() - { - unchecked - { - int hashCode = 17; - ReadOnlySpan name = Name; - for (int i = 0; i < name.Length; i++) - { - hashCode = hashCode * 31 + name[i]; - } - - hashCode = hashCode * 31 + (int)typeHash; - return hashCode; - } - } - - /// - public static bool operator ==(Variable left, Variable right) - { - return left.Equals(right); - } - - /// - public static bool operator !=(Variable left, Variable right) - { - return !(left == right); - } - } - - internal unsafe struct VariablesCollection - { - private fixed long variables[TypeLayout.Capacity * 2]; - - public Variable this[int index] - { - readonly get - { - long typeHash = variables[index * 2 + 0]; - long nameHash = variables[index * 2 + 1]; - return new(typeHash, nameHash); - } - set - { - variables[index * 2 + 0] = value.typeHash; - variables[index * 2 + 1] = value.nameHash; - } - } - } } } \ No newline at end of file diff --git a/core/TypeRegistry.cs b/core/TypeRegistry.cs index 539c67a..0adce56 100644 --- a/core/TypeRegistry.cs +++ b/core/TypeRegistry.cs @@ -43,45 +43,45 @@ static unsafe TypeRegistry() Register(new(TypeLayout.GetFullName(), (ushort)sizeof(Half)), RuntimeTypeTable.GetHandle()); #endif - Span buffer = stackalloc TypeLayout.Variable[16]; - - buffer[0] = new("x", TypeLayout.GetFullName()); - buffer[1] = new("y", TypeLayout.GetFullName()); - buffer[2] = new("z", TypeLayout.GetFullName()); - buffer[3] = new("w", TypeLayout.GetFullName()); - Register(new(TypeLayout.GetFullName(), (ushort)sizeof(Vector2), buffer.Slice(0, 2)), RuntimeTypeTable.GetHandle()); - Register(new(TypeLayout.GetFullName(), (ushort)sizeof(Vector3), buffer.Slice(0, 3)), RuntimeTypeTable.GetHandle()); - Register(new(TypeLayout.GetFullName(), (ushort)sizeof(Vector4), buffer.Slice(0, 4)), RuntimeTypeTable.GetHandle()); - Register(new(TypeLayout.GetFullName(), (ushort)sizeof(Quaternion), buffer.Slice(0, 4)), RuntimeTypeTable.GetHandle()); - - buffer[0] = new("M11", TypeLayout.GetFullName()); - buffer[1] = new("M12", TypeLayout.GetFullName()); - buffer[2] = new("M21", TypeLayout.GetFullName()); - buffer[3] = new("M22", TypeLayout.GetFullName()); - buffer[4] = new("M31", TypeLayout.GetFullName()); - buffer[5] = new("M32", TypeLayout.GetFullName()); - Register(new(TypeLayout.GetFullName(), (ushort)sizeof(Matrix3x2), buffer.Slice(0, 6)), RuntimeTypeTable.GetHandle()); - - buffer[0] = new("M11", TypeLayout.GetFullName()); - buffer[1] = new("M12", TypeLayout.GetFullName()); - buffer[2] = new("M13", TypeLayout.GetFullName()); - buffer[3] = new("M14", TypeLayout.GetFullName()); - buffer[4] = new("M21", TypeLayout.GetFullName()); - buffer[5] = new("M22", TypeLayout.GetFullName()); - buffer[6] = new("M23", TypeLayout.GetFullName()); - buffer[7] = new("M24", TypeLayout.GetFullName()); - buffer[8] = new("M31", TypeLayout.GetFullName()); - buffer[9] = new("M32", TypeLayout.GetFullName()); - buffer[10] = new("M33", TypeLayout.GetFullName()); - buffer[11] = new("M34", TypeLayout.GetFullName()); - buffer[12] = new("M41", TypeLayout.GetFullName()); - buffer[13] = new("M42", TypeLayout.GetFullName()); - buffer[14] = new("M43", TypeLayout.GetFullName()); - buffer[15] = new("M44", TypeLayout.GetFullName()); - Register(new(TypeLayout.GetFullName(), (ushort)sizeof(Matrix4x4), buffer.Slice(0, 16)), RuntimeTypeTable.GetHandle()); - - buffer[0] = new("_dateData", TypeLayout.GetFullName()); - Register(new(TypeLayout.GetFullName(), (ushort)sizeof(DateTime), buffer.Slice(0, 1)), RuntimeTypeTable.GetHandle()); + VariableBuffer variableBuffer = new(); + TypeBuffer interfaceBuffer = new(); + variableBuffer[0] = new("x", TypeLayout.GetFullName()); + variableBuffer[1] = new("y", TypeLayout.GetFullName()); + variableBuffer[2] = new("z", TypeLayout.GetFullName()); + variableBuffer[3] = new("w", TypeLayout.GetFullName()); + Register(new(TypeLayout.GetFullName(), (ushort)sizeof(Vector2), variableBuffer, 2, interfaceBuffer, 0), RuntimeTypeTable.GetHandle()); + Register(new(TypeLayout.GetFullName(), (ushort)sizeof(Vector3), variableBuffer, 3, interfaceBuffer, 0), RuntimeTypeTable.GetHandle()); + Register(new(TypeLayout.GetFullName(), (ushort)sizeof(Vector4), variableBuffer, 4, interfaceBuffer, 0), RuntimeTypeTable.GetHandle()); + Register(new(TypeLayout.GetFullName(), (ushort)sizeof(Quaternion), variableBuffer, 4, interfaceBuffer, 0), RuntimeTypeTable.GetHandle()); + + variableBuffer[0] = new("M11", TypeLayout.GetFullName()); + variableBuffer[1] = new("M12", TypeLayout.GetFullName()); + variableBuffer[2] = new("M21", TypeLayout.GetFullName()); + variableBuffer[3] = new("M22", TypeLayout.GetFullName()); + variableBuffer[4] = new("M31", TypeLayout.GetFullName()); + variableBuffer[5] = new("M32", TypeLayout.GetFullName()); + Register(new(TypeLayout.GetFullName(), (ushort)sizeof(Matrix3x2), variableBuffer, 6, interfaceBuffer, 0), RuntimeTypeTable.GetHandle()); + + variableBuffer[0] = new("M11", TypeLayout.GetFullName()); + variableBuffer[1] = new("M12", TypeLayout.GetFullName()); + variableBuffer[2] = new("M13", TypeLayout.GetFullName()); + variableBuffer[3] = new("M14", TypeLayout.GetFullName()); + variableBuffer[4] = new("M21", TypeLayout.GetFullName()); + variableBuffer[5] = new("M22", TypeLayout.GetFullName()); + variableBuffer[6] = new("M23", TypeLayout.GetFullName()); + variableBuffer[7] = new("M24", TypeLayout.GetFullName()); + variableBuffer[8] = new("M31", TypeLayout.GetFullName()); + variableBuffer[9] = new("M32", TypeLayout.GetFullName()); + variableBuffer[10] = new("M33", TypeLayout.GetFullName()); + variableBuffer[11] = new("M34", TypeLayout.GetFullName()); + variableBuffer[12] = new("M41", TypeLayout.GetFullName()); + variableBuffer[13] = new("M42", TypeLayout.GetFullName()); + variableBuffer[14] = new("M43", TypeLayout.GetFullName()); + variableBuffer[15] = new("M44", TypeLayout.GetFullName()); + Register(new(TypeLayout.GetFullName(), (ushort)sizeof(Matrix4x4), variableBuffer, 16, interfaceBuffer, 0), RuntimeTypeTable.GetHandle()); + + variableBuffer[0] = new("_dateData", TypeLayout.GetFullName()); + Register(new(TypeLayout.GetFullName(), (ushort)sizeof(DateTime), variableBuffer, 1, interfaceBuffer, 0), RuntimeTypeTable.GetHandle()); } /// diff --git a/core/Variable.cs b/core/Variable.cs new file mode 100644 index 0000000..f847b3b --- /dev/null +++ b/core/Variable.cs @@ -0,0 +1,135 @@ +using System; + +namespace Types +{ + /// + /// Describes a variable part of a . + /// + public readonly struct Variable : IEquatable + { + internal readonly long typeHash; + internal readonly long nameHash; + + /// + /// Type layout of the variable. + /// + public readonly TypeLayout Type => TypeRegistry.Get(typeHash); + + /// + /// Name of the variable. + /// + public readonly ReadOnlySpan Name => TypeNames.Get(nameHash); + + /// + /// Size of the variable in bytes. + /// + public readonly ushort Size => Type.size; + +#if NET + /// + /// Not supported. + /// + [Obsolete("Default constructor not supported", true)] + public Variable() + { + } +#endif + + /// + /// Creates a new variable with the given and . + /// + public Variable(string name, string fullTypeName) + { + nameHash = TypeNames.Set(name); + typeHash = fullTypeName.GetLongHashCode(); + } + + internal Variable(long typeHash, long nameHash) + { + this.typeHash = typeHash; + this.nameHash = nameHash; + } + + /// + /// Creates a new variable with the given and . + /// + public Variable(ReadOnlySpan name, ReadOnlySpan fullTypeName) + { + nameHash = TypeNames.Set(name); + typeHash = fullTypeName.GetLongHashCode(); + } + + /// + /// Creates a new variable with the given and . + /// + public Variable(string name, int typeHash) + { + nameHash = TypeNames.Set(name); + this.typeHash = typeHash; + } + + /// + public readonly override string ToString() + { + Span buffer = stackalloc char[256]; + int length = ToString(buffer); + return buffer.Slice(0, length).ToString(); + } + + /// + /// Builds a string representation of this variable and writes it to . + /// + /// Amount of characters written. + public readonly int ToString(Span buffer) + { + TypeLayout typeLayout = Type; + typeLayout.Name.CopyTo(buffer); + int length = typeLayout.Name.Length; + buffer[length++] = '='; + Name.CopyTo(buffer.Slice(length)); + length += Name.Length; + return length; + } + + /// + public readonly override bool Equals(object? obj) + { + return obj is Variable variable && Equals(variable); + } + + /// + public readonly bool Equals(Variable other) + { + return nameHash == other.nameHash && typeHash == other.typeHash; + } + + /// + public readonly override int GetHashCode() + { + unchecked + { + int hashCode = 17; + ReadOnlySpan name = Name; + for (int i = 0; i < name.Length; i++) + { + hashCode = hashCode * 31 + name[i]; + } + + hashCode = hashCode * 31 + (int)typeHash; + return hashCode; + } + } + + /// + public static bool operator ==(Variable left, Variable right) + { + return left.Equals(right); + } + + /// + public static bool operator !=(Variable left, Variable right) + { + return !(left == right); + } + } +} \ No newline at end of file diff --git a/core/VariableBuffer.cs b/core/VariableBuffer.cs new file mode 100644 index 0000000..1f82912 --- /dev/null +++ b/core/VariableBuffer.cs @@ -0,0 +1,59 @@ +using System; +using System.Diagnostics; + +namespace Types +{ + /// + /// Buffer for storing values. + /// + public unsafe struct VariableBuffer + { + /// + /// Maximum amount that can be stored. + /// + public const int Capacity = 32; + + private fixed long variables[Capacity]; + + /// + /// Indexer for accessing the value at the given . + /// + public Variable this[int index] + { + readonly get + { + long typeHash = variables[index * 2 + 0]; + long nameHash = variables[index * 2 + 1]; + return new(typeHash, nameHash); + } + set + { + variables[index * 2 + 0] = value.typeHash; + variables[index * 2 + 1] = value.nameHash; + } + } + + /// + /// Creates a new buffer containing the given . + /// + public VariableBuffer(ReadOnlySpan variables) + { + ThrowIfCantFit(variables.Length); + + for (int i = 0; i < variables.Length; i++) + { + this.variables[i * 2 + 0] = variables[i].typeHash; + this.variables[i * 2 + 1] = variables[i].nameHash; + } + } + + [Conditional("DEBUG")] + private static void ThrowIfCantFit(int length) + { + if (length > Capacity) + { + throw new ArgumentOutOfRangeException(nameof(length), $"Cannot fit {length} types into a buffer of capacity {Capacity}"); + } + } + } +} \ No newline at end of file diff --git a/generator/Generators/TypeBankGenerator.cs b/generator/Generators/TypeBankGenerator.cs index 8416eeb..e8bb599 100644 --- a/generator/Generators/TypeBankGenerator.cs +++ b/generator/Generators/TypeBankGenerator.cs @@ -113,7 +113,8 @@ public static string Generate(IReadOnlyList types, out string typeN source.AppendLine("readonly void ITypeBank.Load(Register register)"); source.BeginGroup(); { - source.AppendLine("Span buffer = stackalloc TypeLayout.Variable[(int)TypeLayout.Capacity];"); + source.AppendLine("VariableBuffer variableBuffer = new();"); + source.AppendLine("TypeBuffer interfaceBuffer = new();"); foreach (ITypeSymbol type in types) { AppendRegister(source, type); @@ -139,24 +140,26 @@ private static void AppendRegister(SourceBuilder source, ITypeSymbol type) return; } - byte count = 0; + byte variableCount = 0; + byte interfaceCount = 0; HashSet fieldNames = new(); foreach (IFieldSymbol field in type.GetFields()) { if (fieldNames.Add(field.Name)) { - AppendVariable(source, field, ref count); + AppendVariable(source, field, ref variableCount); } } source.Append("register.Invoke<"); source.Append(fullName); source.Append(">("); - if (count > 0) + if (variableCount > 0) { - source.Append("buffer.Slice(0, "); - source.Append(count); - source.Append(')'); + source.Append("variableBuffer, "); + source.Append(variableCount); + source.Append(", interfaceBuffer, "); + source.Append(interfaceCount); } source.Append(");"); @@ -164,7 +167,7 @@ private static void AppendRegister(SourceBuilder source, ITypeSymbol type) static void AppendVariable(SourceBuilder source, IFieldSymbol field, ref byte count) { - source.Append("buffer["); + source.Append("variableBuffer["); source.Append(count); source.Append("] = new(\""); source.Append(field.Name); diff --git a/tests/BankTests.cs b/tests/BankTests.cs index 98e805a..4c72d98 100644 --- a/tests/BankTests.cs +++ b/tests/BankTests.cs @@ -7,16 +7,16 @@ public unsafe class BankTests : TypeTests [Test] public void LoadCustomBank() { - Assert.That(TypeRegistry.IsRegistered(), Is.False); + Assert.That(TypeRegistry.IsRegistered(), Is.False); TypeRegistry.Load(); - Assert.That(TypeRegistry.IsRegistered(), Is.True); + Assert.That(TypeRegistry.IsRegistered(), Is.True); } public readonly struct CustomTypeBank : ITypeBank { void ITypeBank.Load(Register register) { - register.Invoke(); + register.Invoke(); } } } diff --git a/tests/LayoutTests.cs b/tests/LayoutTests.cs index 0dc8eea..edc0fba 100644 --- a/tests/LayoutTests.cs +++ b/tests/LayoutTests.cs @@ -13,16 +13,16 @@ public void VerifyLayoutOfRegisteredTypes() Assert.That(type.Name.ToString(), Is.EqualTo("Stress")); Assert.That(type.size, Is.EqualTo((uint)sizeof(Stress))); Assert.That(type.variableCount, Is.EqualTo(5)); - Assert.That(type[0].Size, Is.EqualTo(1)); - Assert.That(type[0].Name.ToString(), Is.EqualTo("first")); - Assert.That(type[1].Size, Is.EqualTo(2)); - Assert.That(type[1].Name.ToString(), Is.EqualTo("second")); - Assert.That(type[2].Size, Is.EqualTo(4)); - Assert.That(type[2].Name.ToString(), Is.EqualTo("third")); - Assert.That(type[3].Size, Is.EqualTo(4)); - Assert.That(type[3].Name.ToString(), Is.EqualTo("fourth")); - Assert.That(type[4].Size, Is.EqualTo((uint)sizeof(Cherry))); - Assert.That(type[4].Name.ToString(), Is.EqualTo("cherry")); + Assert.That(type.Variables[0].Size, Is.EqualTo(1)); + Assert.That(type.Variables[0].Name.ToString(), Is.EqualTo("first")); + Assert.That(type.Variables[1].Size, Is.EqualTo(2)); + Assert.That(type.Variables[1].Name.ToString(), Is.EqualTo("second")); + Assert.That(type.Variables[2].Size, Is.EqualTo(4)); + Assert.That(type.Variables[2].Name.ToString(), Is.EqualTo("third")); + Assert.That(type.Variables[3].Size, Is.EqualTo(4)); + Assert.That(type.Variables[3].Name.ToString(), Is.EqualTo("fourth")); + Assert.That(type.Variables[4].Size, Is.EqualTo((uint)sizeof(Cherry))); + Assert.That(type.Variables[4].Name.ToString(), Is.EqualTo("cherry")); } [Test] From a96d5d831437b26d9c822d77c79ae70b73b1b4dd Mon Sep 17 00:00:00 2001 From: Phill Date: Fri, 21 Mar 2025 11:55:33 -0400 Subject: [PATCH 02/12] rename type layout to just type, and variable to field --- core/{Variable.cs => Field.cs} | 60 +++--- core/{VariableBuffer.cs => FieldBuffer.cs} | 28 +-- core/Functions/Register.cs | 14 +- core/{TypeLayout.cs => Type.cs} | 224 ++++++++------------- core/TypeBuffer.cs | 14 +- core/TypeInstanceCreator.cs | 6 +- core/TypeRegistry.cs | 158 +++++++-------- generator/Generators/TypeBankGenerator.cs | 31 ++- tests/BankTests.cs | 6 +- tests/LayoutTests.cs | 45 +++-- tests/RuntimeTypeHandleTests.cs | 2 +- 11 files changed, 283 insertions(+), 305 deletions(-) rename core/{Variable.cs => Field.cs} (61%) rename core/{VariableBuffer.cs => FieldBuffer.cs} (57%) rename core/{TypeLayout.cs => Type.cs} (58%) diff --git a/core/Variable.cs b/core/Field.cs similarity index 61% rename from core/Variable.cs rename to core/Field.cs index f847b3b..2aa2360 100644 --- a/core/Variable.cs +++ b/core/Field.cs @@ -3,25 +3,25 @@ namespace Types { /// - /// Describes a variable part of a . + /// Describes a field declared in a . /// - public readonly struct Variable : IEquatable + public readonly struct Field : IEquatable { internal readonly long typeHash; internal readonly long nameHash; /// - /// Type layout of the variable. + /// The type of the field. /// - public readonly TypeLayout Type => TypeRegistry.Get(typeHash); + public readonly Type Type => TypeRegistry.Get(typeHash); /// - /// Name of the variable. + /// Name of the field. /// public readonly ReadOnlySpan Name => TypeNames.Get(nameHash); /// - /// Size of the variable in bytes. + /// Size of the field in bytes. /// public readonly ushort Size => Type.size; @@ -30,41 +30,41 @@ namespace Types /// Not supported. /// [Obsolete("Default constructor not supported", true)] - public Variable() + public Field() { } #endif - /// - /// Creates a new variable with the given and . - /// - public Variable(string name, string fullTypeName) + internal Field(long typeHash, long nameHash) { - nameHash = TypeNames.Set(name); - typeHash = fullTypeName.GetLongHashCode(); + this.typeHash = typeHash; + this.nameHash = nameHash; } - internal Variable(long typeHash, long nameHash) + /// + /// Creates a new field with the given and . + /// + public Field(string fieldName, string fullTypeName) { - this.typeHash = typeHash; - this.nameHash = nameHash; + nameHash = TypeNames.Set(fieldName); + typeHash = fullTypeName.GetLongHashCode(); } /// - /// Creates a new variable with the given and . + /// Creates a new field with the given and . /// - public Variable(ReadOnlySpan name, ReadOnlySpan fullTypeName) + public Field(ReadOnlySpan fieldName, ReadOnlySpan fullTypeName) { - nameHash = TypeNames.Set(name); + nameHash = TypeNames.Set(fieldName); typeHash = fullTypeName.GetLongHashCode(); } /// - /// Creates a new variable with the given and . + /// Creates a new field with the given and . /// - public Variable(string name, int typeHash) + public Field(string fieldName, int typeHash) { - nameHash = TypeNames.Set(name); + nameHash = TypeNames.Set(fieldName); this.typeHash = typeHash; } @@ -77,14 +77,14 @@ public readonly override string ToString() } /// - /// Builds a string representation of this variable and writes it to . + /// Builds a string representation of this field and writes it to . /// /// Amount of characters written. public readonly int ToString(Span buffer) { - TypeLayout typeLayout = Type; - typeLayout.Name.CopyTo(buffer); - int length = typeLayout.Name.Length; + Type type = Type; + type.Name.CopyTo(buffer); + int length = type.Name.Length; buffer[length++] = '='; Name.CopyTo(buffer.Slice(length)); length += Name.Length; @@ -94,11 +94,11 @@ public readonly int ToString(Span buffer) /// public readonly override bool Equals(object? obj) { - return obj is Variable variable && Equals(variable); + return obj is Field field && Equals(field); } /// - public readonly bool Equals(Variable other) + public readonly bool Equals(Field other) { return nameHash == other.nameHash && typeHash == other.typeHash; } @@ -121,13 +121,13 @@ public readonly override int GetHashCode() } /// - public static bool operator ==(Variable left, Variable right) + public static bool operator ==(Field left, Field right) { return left.Equals(right); } /// - public static bool operator !=(Variable left, Variable right) + public static bool operator !=(Field left, Field right) { return !(left == right); } diff --git a/core/VariableBuffer.cs b/core/FieldBuffer.cs similarity index 57% rename from core/VariableBuffer.cs rename to core/FieldBuffer.cs index 1f82912..832ea8b 100644 --- a/core/VariableBuffer.cs +++ b/core/FieldBuffer.cs @@ -4,46 +4,46 @@ namespace Types { /// - /// Buffer for storing values. + /// Buffer for storing values. /// - public unsafe struct VariableBuffer + public unsafe struct FieldBuffer { /// /// Maximum amount that can be stored. /// public const int Capacity = 32; - private fixed long variables[Capacity]; + private fixed long buffer[Capacity]; /// /// Indexer for accessing the value at the given . /// - public Variable this[int index] + public Field this[int index] { readonly get { - long typeHash = variables[index * 2 + 0]; - long nameHash = variables[index * 2 + 1]; + long typeHash = buffer[index * 2 + 0]; + long nameHash = buffer[index * 2 + 1]; return new(typeHash, nameHash); } set { - variables[index * 2 + 0] = value.typeHash; - variables[index * 2 + 1] = value.nameHash; + buffer[index * 2 + 0] = value.typeHash; + buffer[index * 2 + 1] = value.nameHash; } } /// - /// Creates a new buffer containing the given . + /// Creates a new buffer containing the given . /// - public VariableBuffer(ReadOnlySpan variables) + public FieldBuffer(ReadOnlySpan fields) { - ThrowIfCantFit(variables.Length); + ThrowIfCantFit(fields.Length); - for (int i = 0; i < variables.Length; i++) + for (int i = 0; i < fields.Length; i++) { - this.variables[i * 2 + 0] = variables[i].typeHash; - this.variables[i * 2 + 1] = variables[i].nameHash; + buffer[i * 2 + 0] = fields[i].typeHash; + buffer[i * 2 + 1] = fields[i].nameHash; } } diff --git a/core/Functions/Register.cs b/core/Functions/Register.cs index 2fa28d1..8b893bc 100644 --- a/core/Functions/Register.cs +++ b/core/Functions/Register.cs @@ -17,9 +17,9 @@ internal Register(Action action) /// /// Registers a type with the given and . /// - public unsafe readonly void Invoke(ReadOnlySpan variables, ReadOnlySpan interfaces) where T : unmanaged + public unsafe readonly void Invoke(ReadOnlySpan variables, ReadOnlySpan interfaces) where T : unmanaged { - TypeLayout type = new(TypeLayout.GetFullName(), (ushort)sizeof(T), variables, interfaces); + Type type = new(Type.GetFullName(), (ushort)sizeof(T), variables, interfaces); Input input = new(type, RuntimeTypeTable.GetHandle()); action(input); TypeInstanceCreator.Initialize(type); @@ -28,9 +28,9 @@ public unsafe readonly void Invoke(ReadOnlySpan variables, ReadOnly /// /// Registers a type with the given and . /// - public unsafe readonly void Invoke(VariableBuffer variables, byte variableCount, TypeBuffer interfaces, byte interfaceCount) where T : unmanaged + public unsafe readonly void Invoke(FieldBuffer variables, byte variableCount, TypeBuffer interfaces, byte interfaceCount) where T : unmanaged { - TypeLayout type = new(TypeLayout.GetFullName(), (ushort)sizeof(T), variables, variableCount, interfaces, interfaceCount); + Type type = new(Type.GetFullName(), (ushort)sizeof(T), variables, variableCount, interfaces, interfaceCount); Input input = new(type, RuntimeTypeTable.GetHandle()); action(input); TypeInstanceCreator.Initialize(type); @@ -41,7 +41,7 @@ public unsafe readonly void Invoke(VariableBuffer variables, byte variableCou /// public unsafe readonly void Invoke() where T : unmanaged { - TypeLayout type = new(TypeLayout.GetFullName(), (ushort)sizeof(T)); + Type type = new(Type.GetFullName(), (ushort)sizeof(T)); Input input = new(type, RuntimeTypeTable.GetHandle()); action(input); TypeInstanceCreator.Initialize(type); @@ -55,7 +55,7 @@ public readonly struct Input /// /// Metadata of the type. /// - public readonly TypeLayout type; + public readonly Type type; private readonly nint handle; @@ -67,7 +67,7 @@ public readonly struct Input /// /// Creates the input argument. /// - public Input(TypeLayout type, RuntimeTypeHandle handle) + public Input(Type type, RuntimeTypeHandle handle) { this.type = type; this.handle = RuntimeTypeTable.GetAddress(handle); diff --git a/core/TypeLayout.cs b/core/Type.cs similarity index 58% rename from core/TypeLayout.cs rename to core/Type.cs index c9203dc..71d9018 100644 --- a/core/TypeLayout.cs +++ b/core/Type.cs @@ -8,25 +8,17 @@ namespace Types /// Describes metadata for a type. /// [SkipLocalsInit] - public readonly struct TypeLayout : IEquatable + public readonly struct Type : IEquatable { /// /// Size of the type in bytes. /// public readonly ushort size; - /// - /// Amount of s the type has. - /// - public readonly byte variableCount; - - /// - /// Amount of interfaces the type implements. - /// - public readonly byte interfaceCount; - + private readonly byte fieldCount; + private readonly byte interfaceCount; private readonly long hash; - private readonly VariableBuffer variables; + private readonly FieldBuffer fields; private readonly TypeBuffer interfaces; /// @@ -35,15 +27,15 @@ namespace Types public readonly long Hash => hash; /// - /// All variables declared in the type. + /// All fields declared in the type. /// - public unsafe readonly ReadOnlySpan Variables + public unsafe readonly ReadOnlySpan Fields { get { - fixed (void* pointer = &variables) + fixed (void* pointer = &fields) { - return new ReadOnlySpan(pointer, variableCount); + return new ReadOnlySpan(pointer, fieldCount); } } } @@ -51,26 +43,26 @@ public unsafe readonly ReadOnlySpan Variables /// /// All interfaces implemented by this type. /// - public unsafe readonly ReadOnlySpan Interfaces + public unsafe readonly ReadOnlySpan Interfaces { get { fixed (void* pointer = &interfaces) { - return new ReadOnlySpan(pointer, interfaceCount); + return new ReadOnlySpan(pointer, interfaceCount); } } } /// - /// The underlying system type that this layout represents. + /// The underlying system type that this represents. /// - public readonly Type SystemType + public readonly System.Type SystemType { get { RuntimeTypeHandle handle = TypeHandle; - return Type.GetTypeFromHandle(handle) ?? throw new InvalidOperationException($"System type not found for handle {handle}"); + return System.Type.GetTypeFromHandle(handle) ?? throw new InvalidOperationException($"System type not found for handle {handle}"); } } @@ -109,68 +101,68 @@ public readonly ReadOnlySpan Name /// Default constructor not supported. /// [Obsolete("Default constructor not supported", true)] - public TypeLayout() + public Type() { throw new NotSupportedException(); } #endif /// - /// Creates a new type layout without any variables set. + /// Creates a new type without any fields or interfaces set. /// - public TypeLayout(ReadOnlySpan fullName, ushort size) + public Type(ReadOnlySpan fullName, ushort size) { this.size = size; - variableCount = 0; - variables = default; + fieldCount = 0; + fields = default; hash = TypeNames.Set(fullName); } /// - /// Creates a new type layout without any variables set. + /// Creates a new type without any fields or interfaces set. /// - public TypeLayout(string fullName, ushort size) + public Type(string fullName, ushort size) { this.size = size; - variableCount = 0; - variables = default; + fieldCount = 0; + fields = default; hash = TypeNames.Set(fullName); } /// - /// Creates a new type layout. + /// Creates a new type. /// - public TypeLayout(ReadOnlySpan fullName, ushort size, ReadOnlySpan variables, ReadOnlySpan interfaces) + public Type(ReadOnlySpan fullName, ushort size, ReadOnlySpan fields, ReadOnlySpan interfaces) { this.size = size; - variableCount = (byte)variables.Length; - this.variables = new(variables); + fieldCount = (byte)fields.Length; + this.fields = new(fields); interfaceCount = (byte)interfaces.Length; this.interfaces = new(interfaces); hash = TypeNames.Set(fullName); } /// - /// Creates a new type layout. + /// Creates a new type. /// - public TypeLayout(ReadOnlySpan fullName, ushort size, VariableBuffer variables, byte variableCount, TypeBuffer interfaces, byte interfaceCount) + public Type(ReadOnlySpan fullName, ushort size, FieldBuffer fields, byte fieldCount, TypeBuffer interfaces, byte interfaceCount) { this.size = size; - this.variableCount = variableCount; - this.variables = variables; + this.fieldCount = fieldCount; + this.fields = fields; this.interfaceCount = interfaceCount; this.interfaces = interfaces; hash = TypeNames.Set(fullName); } /// - /// Creates a new type layout. + /// Creates a new type. /// - public TypeLayout(string fullName, ushort size, ReadOnlySpan variables, ReadOnlySpan interfaces) + public Type(string fullName, ushort size, ReadOnlySpan fields, ReadOnlySpan interfaces) { this.size = size; - variableCount = (byte)variables.Length; - this.variables = new(variables); + fieldCount = (byte)fields.Length; + this.fields = new(fields); interfaceCount = (byte)interfaces.Length; this.interfaces = new(interfaces); hash = TypeNames.Set(fullName); @@ -183,7 +175,7 @@ public readonly override string ToString() } /// - /// Writes a string representation of this type layout to . + /// Writes a string representation of this type to . /// public readonly int ToString(Span destination) { @@ -192,32 +184,12 @@ public readonly int ToString(Span destination) return fullName.Length; } - /// - /// Retrieves the field at the given . - /// - public readonly Variable GetVariable(int index) - { - ThrowIfVariableIndexOutOfRange(index); - - return variables[index]; - } - - /// - /// Retrieves the implemented interface at the given . - /// - public readonly TypeLayout GetInterface(int index) - { - ThrowIfInterfaceIndexOutOfRange(index); - - return interfaces[index]; - } - /// /// Checks if this type metadata represents type . /// public readonly bool Is() where T : unmanaged { - TypeRegistry.handleToType.TryGetValue(RuntimeTypeTable.GetHandle(), out TypeLayout otherType); + TypeRegistry.handleToType.TryGetValue(RuntimeTypeTable.GetHandle(), out Type otherType); return hash == otherType.hash; } @@ -241,27 +213,27 @@ public readonly object CreateInstance() } /// - /// Copies all variables in this type to the . + /// Copies all fields in this type to the . /// - public readonly byte CopyVariablesTo(Span destination) + public readonly byte CopyFieldsTo(Span destination) { - for (int i = 0; i < variableCount; i++) + for (int i = 0; i < fieldCount; i++) { - destination[i] = variables[i]; + destination[i] = fields[i]; } - return variableCount; + return fieldCount; } /// - /// Checks if this type contains a variable with the given . + /// Checks if this type contains a fields with the given . /// - public readonly bool ContainsVariable(string name) + public readonly bool ContainsField(string fieldName) { - ReadOnlySpan nameSpan = name.AsSpan(); - for (int i = 0; i < variableCount; i++) + ReadOnlySpan nameSpan = fieldName.AsSpan(); + for (int i = 0; i < fieldCount; i++) { - if (variables[i].Name.SequenceEqual(nameSpan)) + if (fields[i].Name.SequenceEqual(nameSpan)) { return true; } @@ -271,14 +243,14 @@ public readonly bool ContainsVariable(string name) } /// - /// Checks if this type contains a variable with the given . + /// Checks if this type contains a fields with the given . /// - public readonly bool ContainsVariable(ReadOnlySpan name) + public readonly bool ContainsField(ReadOnlySpan fieldName) { - ReadOnlySpan nameSpan = name; - for (int i = 0; i < variableCount; i++) + ReadOnlySpan nameSpan = fieldName; + for (int i = 0; i < fieldCount; i++) { - if (variables[i].Name.SequenceEqual(nameSpan)) + if (fields[i].Name.SequenceEqual(nameSpan)) { return true; } @@ -288,18 +260,18 @@ public readonly bool ContainsVariable(ReadOnlySpan name) } /// - /// Retrieves the variable in this type with the given . + /// Retrieves the field in this type with the given . /// - public readonly Variable GetVariable(string name) + public readonly Field GetField(string fieldName) { - ThrowIfVariableIsMissing(name); + ThrowIfFieldIsMissing(fieldName); - for (int i = 0; i < variableCount; i++) + for (int i = 0; i < fieldCount; i++) { - Variable variable = variables[i]; - if (variable.Name.SequenceEqual(name)) + Field field = fields[i]; + if (field.Name.SequenceEqual(fieldName)) { - return variable; + return field; } } @@ -307,18 +279,18 @@ public readonly Variable GetVariable(string name) } /// - /// Retrieves the variable in this type with the given . + /// Retrieves the field in this type with the given . /// - public readonly Variable GetVariable(ReadOnlySpan name) + public readonly Field GetField(ReadOnlySpan fieldName) { - ThrowIfVariableIsMissing(name); + ThrowIfFieldIsMissing(fieldName); - for (int i = 0; i < variableCount; i++) + for (int i = 0; i < fieldCount; i++) { - Variable variable = variables[i]; - if (variable.Name.SequenceEqual(name)) + Field field = fields[i]; + if (field.Name.SequenceEqual(fieldName)) { - return variable; + return field; } } @@ -326,16 +298,16 @@ public readonly Variable GetVariable(ReadOnlySpan name) } /// - /// Retrieves the index of the variable with the given . + /// Retrieves the index of the field with the given . /// - public readonly int IndexOf(string name) + public readonly int IndexOf(string fieldName) { - ThrowIfVariableIsMissing(name); + ThrowIfFieldIsMissing(fieldName); - for (int i = 0; i < variableCount; i++) + for (int i = 0; i < fieldCount; i++) { - Variable variable = variables[i]; - if (variable.Name.SequenceEqual(name)) + Field field = fields[i]; + if (field.Name.SequenceEqual(fieldName)) { return i; } @@ -345,16 +317,16 @@ public readonly int IndexOf(string name) } /// - /// Retrieves the index of the variable with the given . + /// Retrieves the index of the field with the given . /// - public readonly int IndexOf(ReadOnlySpan name) + public readonly int IndexOf(ReadOnlySpan fieldName) { - ThrowIfVariableIsMissing(name); + ThrowIfFieldIsMissing(fieldName); - for (int i = 0; i < variableCount; i++) + for (int i = 0; i < fieldCount; i++) { - Variable variable = variables[i]; - if (variable.Name.SequenceEqual(name)) + Field field = fields[i]; + if (field.Name.SequenceEqual(fieldName)) { return i; } @@ -366,7 +338,7 @@ public readonly int IndexOf(ReadOnlySpan name) /// /// Retrieves the full type name for the given . /// - public static int GetFullName(Type type, Span buffer) + public static int GetFullName(System.Type type, Span buffer) { int length = 0; AppendType(buffer, ref length, type); @@ -386,14 +358,14 @@ static void InsertSpan(Span buffer, ReadOnlySpan text, ref int lengt length += text.Length; } - static void AppendType(Span fullName, ref int length, Type type) + static void AppendType(Span fullName, ref int length, System.Type type) { //todo: handle case where the type name is System.Collections.Generic.List`1+Enumerator[etc, etc] - Type? current = type; + System.Type? current = type; string? currentNameSpace = current.Namespace; while (current is not null) { - Type[] genericTypes = current.GenericTypeArguments; + System.Type[] genericTypes = current.GenericTypeArguments; string name = current.Name; if (genericTypes.Length > 0) { @@ -438,7 +410,7 @@ static void AppendType(Span fullName, ref int length, Type type) /// /// Retrieves the full type name for the given . /// - public static string GetFullName(Type type) + public static string GetFullName(System.Type type) { Span buffer = stackalloc char[512]; int length = GetFullName(type, buffer); @@ -456,40 +428,22 @@ public static string GetFullName() } [Conditional("DEBUG")] - private readonly void ThrowIfVariableIsMissing(ReadOnlySpan name) - { - if (!ContainsVariable(name)) - { - throw new InvalidOperationException($"Variable `{name.ToString()}` not found in type {FullName.ToString()}"); - } - } - - [Conditional("DEBUG")] - private readonly void ThrowIfVariableIndexOutOfRange(int index) - { - if (index < 0 || index >= variableCount) - { - throw new IndexOutOfRangeException($"Variable index {index} is out of range for type {FullName.ToString()}"); - } - } - - [Conditional("DEBUG")] - private readonly void ThrowIfInterfaceIndexOutOfRange(int index) + private readonly void ThrowIfFieldIsMissing(ReadOnlySpan fieldName) { - if (index < 0 || index >= interfaceCount) + if (!ContainsField(fieldName)) { - throw new IndexOutOfRangeException($"Interface index {index} is out of range for type {FullName.ToString()}"); + throw new InvalidOperationException($"Field with name `{fieldName.ToString()}` not found in type {FullName.ToString()}"); } } /// public readonly override bool Equals(object? obj) { - return obj is TypeLayout layout && Equals(layout); + return obj is Type type && Equals(type); } /// - public readonly bool Equals(TypeLayout other) + public readonly bool Equals(Type other) { return hash == other.hash; } @@ -504,13 +458,13 @@ public readonly override int GetHashCode() } /// - public static bool operator ==(TypeLayout left, TypeLayout right) + public static bool operator ==(Type left, Type right) { return left.Equals(right); } /// - public static bool operator !=(TypeLayout left, TypeLayout right) + public static bool operator !=(Type left, Type right) { return !(left == right); } diff --git a/core/TypeBuffer.cs b/core/TypeBuffer.cs index d3cd1c3..65005a7 100644 --- a/core/TypeBuffer.cs +++ b/core/TypeBuffer.cs @@ -4,7 +4,7 @@ namespace Types { /// - /// Buffer for storing values. + /// Buffer for storing values. /// public unsafe struct TypeBuffer { @@ -13,34 +13,34 @@ public unsafe struct TypeBuffer /// public const int Capacity = 32; - private fixed long hashes[Capacity]; + private fixed long buffer[Capacity]; /// /// Indexer for accessing a value at the given . /// - public TypeLayout this[int index] + public Type this[int index] { readonly get { - long hash = hashes[index]; + long hash = buffer[index]; return TypeRegistry.Get(hash); } set { - hashes[index] = value.Hash; + buffer[index] = value.Hash; } } /// /// Creates a new buffer containing the given . /// - public TypeBuffer(ReadOnlySpan types) + public TypeBuffer(ReadOnlySpan types) { ThrowIfCantFit(types.Length); for (int i = 0; i < types.Length; i++) { - hashes[i] = types[i].Hash; + buffer[i] = types[i].Hash; } } diff --git a/core/TypeInstanceCreator.cs b/core/TypeInstanceCreator.cs index b2e60bd..76b4d52 100644 --- a/core/TypeInstanceCreator.cs +++ b/core/TypeInstanceCreator.cs @@ -5,9 +5,9 @@ namespace Types { internal static class TypeInstanceCreator { - private static readonly Dictionary functions = new(); + private static readonly Dictionary functions = new(); - public unsafe static void Initialize(TypeLayout type) where T : unmanaged + public unsafe static void Initialize(Type type) where T : unmanaged { functions[type] = static (bytes) => { @@ -19,7 +19,7 @@ public unsafe static void Initialize(TypeLayout type) where T : unmanaged }; } - public static object Do(TypeLayout type, ReadOnlySpan bytes) + public static object Do(Type type, ReadOnlySpan bytes) { Create action = functions[type]; return action(bytes); diff --git a/core/TypeRegistry.cs b/core/TypeRegistry.cs index 0adce56..f036b05 100644 --- a/core/TypeRegistry.cs +++ b/core/TypeRegistry.cs @@ -12,80 +12,80 @@ namespace Types /// public static class TypeRegistry { - private static readonly List types = new(); - internal static readonly Dictionary handleToType = new(); + private static readonly List types = new(); + internal static readonly Dictionary handleToType = new(); private static readonly Dictionary typeToHandle = new(); - private static readonly Dictionary hashToType = new(); + private static readonly Dictionary hashToType = new(); /// - /// All registered type layouts. + /// All registered types. /// - public static IReadOnlyCollection All => types; + public static IReadOnlyCollection All => types; [SkipLocalsInit] static unsafe TypeRegistry() { - Register(new(TypeLayout.GetFullName(), sizeof(byte)), RuntimeTypeTable.GetHandle()); - Register(new(TypeLayout.GetFullName(), sizeof(sbyte)), RuntimeTypeTable.GetHandle()); - Register(new(TypeLayout.GetFullName(), sizeof(short)), RuntimeTypeTable.GetHandle()); - Register(new(TypeLayout.GetFullName(), sizeof(ushort)), RuntimeTypeTable.GetHandle()); - Register(new(TypeLayout.GetFullName(), sizeof(int)), RuntimeTypeTable.GetHandle()); - Register(new(TypeLayout.GetFullName(), sizeof(uint)), RuntimeTypeTable.GetHandle()); - Register(new(TypeLayout.GetFullName(), sizeof(long)), RuntimeTypeTable.GetHandle()); - Register(new(TypeLayout.GetFullName(), sizeof(ulong)), RuntimeTypeTable.GetHandle()); - Register(new(TypeLayout.GetFullName(), sizeof(float)), RuntimeTypeTable.GetHandle()); - Register(new(TypeLayout.GetFullName(), sizeof(double)), RuntimeTypeTable.GetHandle()); - Register(new(TypeLayout.GetFullName(), sizeof(char)), RuntimeTypeTable.GetHandle()); - Register(new(TypeLayout.GetFullName(), sizeof(bool)), RuntimeTypeTable.GetHandle()); - Register(new(TypeLayout.GetFullName(), (ushort)sizeof(nint)), RuntimeTypeTable.GetHandle()); - Register(new(TypeLayout.GetFullName(), (ushort)sizeof(nuint)), RuntimeTypeTable.GetHandle()); + Register(new(Type.GetFullName(), sizeof(byte)), RuntimeTypeTable.GetHandle()); + Register(new(Type.GetFullName(), sizeof(sbyte)), RuntimeTypeTable.GetHandle()); + Register(new(Type.GetFullName(), sizeof(short)), RuntimeTypeTable.GetHandle()); + Register(new(Type.GetFullName(), sizeof(ushort)), RuntimeTypeTable.GetHandle()); + Register(new(Type.GetFullName(), sizeof(int)), RuntimeTypeTable.GetHandle()); + Register(new(Type.GetFullName(), sizeof(uint)), RuntimeTypeTable.GetHandle()); + Register(new(Type.GetFullName(), sizeof(long)), RuntimeTypeTable.GetHandle()); + Register(new(Type.GetFullName(), sizeof(ulong)), RuntimeTypeTable.GetHandle()); + Register(new(Type.GetFullName(), sizeof(float)), RuntimeTypeTable.GetHandle()); + Register(new(Type.GetFullName(), sizeof(double)), RuntimeTypeTable.GetHandle()); + Register(new(Type.GetFullName(), sizeof(char)), RuntimeTypeTable.GetHandle()); + Register(new(Type.GetFullName(), sizeof(bool)), RuntimeTypeTable.GetHandle()); + Register(new(Type.GetFullName(), (ushort)sizeof(nint)), RuntimeTypeTable.GetHandle()); + Register(new(Type.GetFullName(), (ushort)sizeof(nuint)), RuntimeTypeTable.GetHandle()); #if NET - Register(new(TypeLayout.GetFullName(), (ushort)sizeof(Half)), RuntimeTypeTable.GetHandle()); + Register(new(Type.GetFullName(), (ushort)sizeof(Half)), RuntimeTypeTable.GetHandle()); #endif - VariableBuffer variableBuffer = new(); - TypeBuffer interfaceBuffer = new(); - variableBuffer[0] = new("x", TypeLayout.GetFullName()); - variableBuffer[1] = new("y", TypeLayout.GetFullName()); - variableBuffer[2] = new("z", TypeLayout.GetFullName()); - variableBuffer[3] = new("w", TypeLayout.GetFullName()); - Register(new(TypeLayout.GetFullName(), (ushort)sizeof(Vector2), variableBuffer, 2, interfaceBuffer, 0), RuntimeTypeTable.GetHandle()); - Register(new(TypeLayout.GetFullName(), (ushort)sizeof(Vector3), variableBuffer, 3, interfaceBuffer, 0), RuntimeTypeTable.GetHandle()); - Register(new(TypeLayout.GetFullName(), (ushort)sizeof(Vector4), variableBuffer, 4, interfaceBuffer, 0), RuntimeTypeTable.GetHandle()); - Register(new(TypeLayout.GetFullName(), (ushort)sizeof(Quaternion), variableBuffer, 4, interfaceBuffer, 0), RuntimeTypeTable.GetHandle()); - - variableBuffer[0] = new("M11", TypeLayout.GetFullName()); - variableBuffer[1] = new("M12", TypeLayout.GetFullName()); - variableBuffer[2] = new("M21", TypeLayout.GetFullName()); - variableBuffer[3] = new("M22", TypeLayout.GetFullName()); - variableBuffer[4] = new("M31", TypeLayout.GetFullName()); - variableBuffer[5] = new("M32", TypeLayout.GetFullName()); - Register(new(TypeLayout.GetFullName(), (ushort)sizeof(Matrix3x2), variableBuffer, 6, interfaceBuffer, 0), RuntimeTypeTable.GetHandle()); - - variableBuffer[0] = new("M11", TypeLayout.GetFullName()); - variableBuffer[1] = new("M12", TypeLayout.GetFullName()); - variableBuffer[2] = new("M13", TypeLayout.GetFullName()); - variableBuffer[3] = new("M14", TypeLayout.GetFullName()); - variableBuffer[4] = new("M21", TypeLayout.GetFullName()); - variableBuffer[5] = new("M22", TypeLayout.GetFullName()); - variableBuffer[6] = new("M23", TypeLayout.GetFullName()); - variableBuffer[7] = new("M24", TypeLayout.GetFullName()); - variableBuffer[8] = new("M31", TypeLayout.GetFullName()); - variableBuffer[9] = new("M32", TypeLayout.GetFullName()); - variableBuffer[10] = new("M33", TypeLayout.GetFullName()); - variableBuffer[11] = new("M34", TypeLayout.GetFullName()); - variableBuffer[12] = new("M41", TypeLayout.GetFullName()); - variableBuffer[13] = new("M42", TypeLayout.GetFullName()); - variableBuffer[14] = new("M43", TypeLayout.GetFullName()); - variableBuffer[15] = new("M44", TypeLayout.GetFullName()); - Register(new(TypeLayout.GetFullName(), (ushort)sizeof(Matrix4x4), variableBuffer, 16, interfaceBuffer, 0), RuntimeTypeTable.GetHandle()); - - variableBuffer[0] = new("_dateData", TypeLayout.GetFullName()); - Register(new(TypeLayout.GetFullName(), (ushort)sizeof(DateTime), variableBuffer, 1, interfaceBuffer, 0), RuntimeTypeTable.GetHandle()); + FieldBuffer fields = new(); + TypeBuffer interfaces = new(); + fields[0] = new("x", Type.GetFullName()); + fields[1] = new("y", Type.GetFullName()); + fields[2] = new("z", Type.GetFullName()); + fields[3] = new("w", Type.GetFullName()); + Register(new(Type.GetFullName(), (ushort)sizeof(Vector2), fields, 2, interfaces, 0), RuntimeTypeTable.GetHandle()); + Register(new(Type.GetFullName(), (ushort)sizeof(Vector3), fields, 3, interfaces, 0), RuntimeTypeTable.GetHandle()); + Register(new(Type.GetFullName(), (ushort)sizeof(Vector4), fields, 4, interfaces, 0), RuntimeTypeTable.GetHandle()); + Register(new(Type.GetFullName(), (ushort)sizeof(Quaternion), fields, 4, interfaces, 0), RuntimeTypeTable.GetHandle()); + + fields[0] = new("M11", Type.GetFullName()); + fields[1] = new("M12", Type.GetFullName()); + fields[2] = new("M21", Type.GetFullName()); + fields[3] = new("M22", Type.GetFullName()); + fields[4] = new("M31", Type.GetFullName()); + fields[5] = new("M32", Type.GetFullName()); + Register(new(Type.GetFullName(), (ushort)sizeof(Matrix3x2), fields, 6, interfaces, 0), RuntimeTypeTable.GetHandle()); + + fields[0] = new("M11", Type.GetFullName()); + fields[1] = new("M12", Type.GetFullName()); + fields[2] = new("M13", Type.GetFullName()); + fields[3] = new("M14", Type.GetFullName()); + fields[4] = new("M21", Type.GetFullName()); + fields[5] = new("M22", Type.GetFullName()); + fields[6] = new("M23", Type.GetFullName()); + fields[7] = new("M24", Type.GetFullName()); + fields[8] = new("M31", Type.GetFullName()); + fields[9] = new("M32", Type.GetFullName()); + fields[10] = new("M33", Type.GetFullName()); + fields[11] = new("M34", Type.GetFullName()); + fields[12] = new("M41", Type.GetFullName()); + fields[13] = new("M42", Type.GetFullName()); + fields[14] = new("M43", Type.GetFullName()); + fields[15] = new("M44", Type.GetFullName()); + Register(new(Type.GetFullName(), (ushort)sizeof(Matrix4x4), fields, 16, interfaces, 0), RuntimeTypeTable.GetHandle()); + + fields[0] = new("_dateData", Type.GetFullName()); + Register(new(Type.GetFullName(), (ushort)sizeof(DateTime), fields, 1, interfaces, 0), RuntimeTypeTable.GetHandle()); } /// - /// Loads all s from the bank of type . + /// Loads all s from the bank of type . /// public unsafe static void Load() where T : unmanaged, ITypeBank { @@ -104,7 +104,7 @@ public static void Register(Register.Input input) /// /// Manually registers the given . /// - public static void Register(TypeLayout type, RuntimeTypeHandle handle) + public static void Register(Type type, RuntimeTypeHandle handle) { ThrowIfAlreadyRegistered(type); @@ -117,7 +117,7 @@ public static void Register(TypeLayout type, RuntimeTypeHandle handle) /// /// Tries to manually register the given . /// - public static bool TryRegister(TypeLayout type, RuntimeTypeHandle handle) + public static bool TryRegister(Type type, RuntimeTypeHandle handle) { if (types.Contains(type)) { @@ -132,30 +132,30 @@ public static bool TryRegister(TypeLayout type, RuntimeTypeHandle handle) } /// - /// Manually registers type without any variables. + /// Manually registers type without any fields or interfaces. /// public unsafe static void Register() where T : unmanaged { //todo: need to add a warning here when trying to register a type bank itself ushort size = (ushort)sizeof(T); - TypeLayout type = new(TypeLayout.GetFullName(), size); + Type type = new(Type.GetFullName(), size); Register(type, RuntimeTypeTable.GetHandle()); } /// - /// Tries to manually register type without any variables. + /// Tries to manually register type without any fields or interfaces. /// public unsafe static bool TryRegister() where T : unmanaged { ushort size = (ushort)sizeof(T); - TypeLayout type = new(TypeLayout.GetFullName(), size); + Type type = new(Type.GetFullName(), size); return TryRegister(type, RuntimeTypeTable.GetHandle()); } /// /// Retrieves the metadata for . /// - public static TypeLayout Get() where T : unmanaged + public static Type Get() where T : unmanaged { ThrowIfNotRegistered(); @@ -166,7 +166,7 @@ public static TypeLayout Get() where T : unmanaged /// Retrieves the metadata for , or registers /// it if it's not already registered without any variables. /// - public static TypeLayout GetOrRegister() where T : unmanaged + public static Type GetOrRegister() where T : unmanaged { return LazyCache.value; } @@ -174,7 +174,7 @@ public static TypeLayout GetOrRegister() where T : unmanaged /// /// Retrieves the metadata for the type with the given . /// - public static TypeLayout Get(long typeHash) + public static Type Get(long typeHash) { ThrowIfNotRegistered(typeHash); @@ -184,7 +184,7 @@ public static TypeLayout Get(long typeHash) /// /// Tries to get the metadata for the type with the given . /// - public static bool TryGet(long typeHash, out TypeLayout type) + public static bool TryGet(long typeHash, out Type type) { return hashToType.TryGetValue(typeHash, out type); } @@ -192,7 +192,7 @@ public static bool TryGet(long typeHash, out TypeLayout type) /// /// Retrieves the metadata for the of the wanted type. /// - public static TypeLayout Get(RuntimeTypeHandle handle) + public static Type Get(RuntimeTypeHandle handle) { ThrowIfNotRegistered(handle); @@ -220,7 +220,7 @@ public static bool IsRegistered() where T : unmanaged /// /// Checks if the given is registered. /// - public static bool IsRegistered(Type type) + public static bool IsRegistered(System.Type type) { return handleToType.ContainsKey(RuntimeTypeTable.GetHandle(type)); } @@ -231,7 +231,7 @@ public static bool IsRegistered(Type type) public static bool IsRegistered(ReadOnlySpan fullTypeName) { long hash = fullTypeName.GetLongHashCode(); - foreach (TypeLayout type in types) + foreach (Type type in types) { if (type.Hash == hash) { @@ -248,7 +248,7 @@ public static bool IsRegistered(ReadOnlySpan fullTypeName) public static bool IsRegistered(string fullTypeName) { long hash = fullTypeName.GetLongHashCode(); - foreach (TypeLayout type in types) + foreach (Type type in types) { if (type.Hash == hash) { @@ -282,7 +282,7 @@ private static void ThrowIfNotRegistered(RuntimeTypeHandle handle) { if (!handleToType.ContainsKey(handle)) { - Type? type = Type.GetTypeFromHandle(handle); + System.Type? type = System.Type.GetTypeFromHandle(handle); if (type is not null) { throw new InvalidOperationException($"Type `{type}` is not registered"); @@ -295,7 +295,7 @@ private static void ThrowIfNotRegistered(RuntimeTypeHandle handle) } [Conditional("DEBUG")] - private static void ThrowIfAlreadyRegistered(TypeLayout type) + private static void ThrowIfAlreadyRegistered(Type type) { if (types.Contains(type)) { @@ -305,7 +305,7 @@ private static void ThrowIfAlreadyRegistered(TypeLayout type) private static class Cache where T : unmanaged { - public static readonly TypeLayout value; + public static readonly Type value; static Cache() { @@ -318,14 +318,14 @@ static Cache() private unsafe static class LazyCache where T : unmanaged { - public static readonly TypeLayout value; + public static readonly Type value; static LazyCache() { RuntimeTypeHandle key = RuntimeTypeTable.GetHandle(); if (!handleToType.TryGetValue(key, out value)) { - value = new(TypeLayout.GetFullName(), (ushort)sizeof(T)); + value = new(Type.GetFullName(), (ushort)sizeof(T)); Register(value, key); } } diff --git a/generator/Generators/TypeBankGenerator.cs b/generator/Generators/TypeBankGenerator.cs index e8bb599..99199f8 100644 --- a/generator/Generators/TypeBankGenerator.cs +++ b/generator/Generators/TypeBankGenerator.cs @@ -11,6 +11,10 @@ namespace Types.Generator public class TypeBankGenerator : IIncrementalGenerator { public const string TypeNameFormat = "{0}TypeBank"; + public const string FieldBufferTypeName = "FieldBuffer"; + public const string InterfaceBufferTypeName = "TypeBuffer"; + public const string FieldBufferVariableName = "fields"; + public const string InterfaceBufferVariableName = "types"; void IIncrementalGenerator.Initialize(IncrementalGeneratorInitializationContext context) { @@ -113,8 +117,18 @@ public static string Generate(IReadOnlyList types, out string typeN source.AppendLine("readonly void ITypeBank.Load(Register register)"); source.BeginGroup(); { - source.AppendLine("VariableBuffer variableBuffer = new();"); - source.AppendLine("TypeBuffer interfaceBuffer = new();"); + source.Append(FieldBufferTypeName); + source.Append(' '); + source.Append(FieldBufferVariableName); + source.Append(" = new();"); + source.AppendLine(); + + source.Append(InterfaceBufferTypeName); + source.Append(' '); + source.Append(InterfaceBufferVariableName); + source.Append(" = new();"); + source.AppendLine(); + foreach (ITypeSymbol type in types) { AppendRegister(source, type); @@ -156,9 +170,15 @@ private static void AppendRegister(SourceBuilder source, ITypeSymbol type) source.Append(">("); if (variableCount > 0) { - source.Append("variableBuffer, "); + source.Append(FieldBufferVariableName); + source.Append(','); + source.Append(' '); source.Append(variableCount); - source.Append(", interfaceBuffer, "); + source.Append(','); + source.Append(' '); + source.Append(InterfaceBufferVariableName); + source.Append(','); + source.Append(' '); source.Append(interfaceCount); } @@ -167,7 +187,8 @@ private static void AppendRegister(SourceBuilder source, ITypeSymbol type) static void AppendVariable(SourceBuilder source, IFieldSymbol field, ref byte count) { - source.Append("variableBuffer["); + source.Append(FieldBufferVariableName); + source.Append('['); source.Append(count); source.Append("] = new(\""); source.Append(field.Name); diff --git a/tests/BankTests.cs b/tests/BankTests.cs index 4c72d98..41a2bc6 100644 --- a/tests/BankTests.cs +++ b/tests/BankTests.cs @@ -7,16 +7,16 @@ public unsafe class BankTests : TypeTests [Test] public void LoadCustomBank() { - Assert.That(TypeRegistry.IsRegistered(), Is.False); + Assert.That(TypeRegistry.IsRegistered(), Is.False); TypeRegistry.Load(); - Assert.That(TypeRegistry.IsRegistered(), Is.True); + Assert.That(TypeRegistry.IsRegistered(), Is.True); } public readonly struct CustomTypeBank : ITypeBank { void ITypeBank.Load(Register register) { - register.Invoke(); + register.Invoke(); } } } diff --git a/tests/LayoutTests.cs b/tests/LayoutTests.cs index edc0fba..dd51869 100644 --- a/tests/LayoutTests.cs +++ b/tests/LayoutTests.cs @@ -1,4 +1,5 @@ using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions; +using System; using System.Numerics; namespace Types.Tests @@ -8,21 +9,23 @@ public unsafe class LayoutTests : TypeTests [Test] public void VerifyLayoutOfRegisteredTypes() { - TypeLayout type = TypeRegistry.Get(); + Type type = TypeRegistry.Get(); Assert.That(type.SystemType, Is.EqualTo(typeof(Stress))); Assert.That(type.Name.ToString(), Is.EqualTo("Stress")); Assert.That(type.size, Is.EqualTo((uint)sizeof(Stress))); - Assert.That(type.variableCount, Is.EqualTo(5)); - Assert.That(type.Variables[0].Size, Is.EqualTo(1)); - Assert.That(type.Variables[0].Name.ToString(), Is.EqualTo("first")); - Assert.That(type.Variables[1].Size, Is.EqualTo(2)); - Assert.That(type.Variables[1].Name.ToString(), Is.EqualTo("second")); - Assert.That(type.Variables[2].Size, Is.EqualTo(4)); - Assert.That(type.Variables[2].Name.ToString(), Is.EqualTo("third")); - Assert.That(type.Variables[3].Size, Is.EqualTo(4)); - Assert.That(type.Variables[3].Name.ToString(), Is.EqualTo("fourth")); - Assert.That(type.Variables[4].Size, Is.EqualTo((uint)sizeof(Cherry))); - Assert.That(type.Variables[4].Name.ToString(), Is.EqualTo("cherry")); + + ReadOnlySpan fields = type.Fields; + Assert.That(fields.Length, Is.EqualTo(5)); + Assert.That(fields[0].Size, Is.EqualTo(1)); + Assert.That(fields[0].Name.ToString(), Is.EqualTo("first")); + Assert.That(fields[1].Size, Is.EqualTo(2)); + Assert.That(fields[1].Name.ToString(), Is.EqualTo("second")); + Assert.That(fields[2].Size, Is.EqualTo(4)); + Assert.That(fields[2].Name.ToString(), Is.EqualTo("third")); + Assert.That(fields[3].Size, Is.EqualTo(4)); + Assert.That(fields[3].Name.ToString(), Is.EqualTo("fourth")); + Assert.That(fields[4].Size, Is.EqualTo((uint)sizeof(Cherry))); + Assert.That(fields[4].Name.ToString(), Is.EqualTo("cherry")); } [Test] @@ -47,7 +50,7 @@ public void PrimitiveTypesAreAvailable() Assert.That(TypeRegistry.IsRegistered(typeof(short).FullName ?? typeof(short).Name), Is.True); Assert.That(TypeRegistry.Get().size, Is.EqualTo((uint)sizeof(Vector3))); - Assert.That(TypeRegistry.Get().variableCount, Is.EqualTo(3)); + Assert.That(TypeRegistry.Get().Fields.Length, Is.EqualTo(3)); } [Test] @@ -55,8 +58,8 @@ public void CheckLayouts() { Assert.That(TypeRegistry.IsRegistered(), Is.True); Assert.That(TypeRegistry.IsRegistered(), Is.True); - TypeLayout boolean = TypeRegistry.Get(); - TypeLayout byteType = TypeRegistry.Get(); + Type boolean = TypeRegistry.Get(); + Type byteType = TypeRegistry.Get(); Assert.That(boolean.size, Is.EqualTo(1)); Assert.That(byteType.size, Is.EqualTo(1)); Assert.That(boolean.GetHashCode(), Is.EqualTo(TypeRegistry.Get().GetHashCode())); @@ -66,7 +69,7 @@ public void CheckLayouts() [Test] public void CheckIfLayoutIs() { - TypeLayout layout = TypeRegistry.Get(); + Type layout = TypeRegistry.Get(); Assert.That(layout.Is(), Is.True); Assert.That(layout.Is(), Is.False); @@ -84,20 +87,20 @@ public void CheckNamesOfTypes() [Test] public void GetFullNameOfType() { - string c = TypeLayout.GetFullName(); + string c = Type.GetFullName(); Assert.That(c.ToString(), Is.EqualTo("System.Boolean")); - string a = TypeLayout.GetFullName>(); + string a = Type.GetFullName>(); Assert.That(a.ToString(), Is.EqualTo("Types.Tests.Dictionary")); - string b = TypeLayout.GetFullName>>(); + string b = Type.GetFullName>>(); Assert.That(b.ToString(), Is.EqualTo("Types.Tests.Dictionary>")); } [Test] public void CreateObjectFromTypeLayout() { - TypeLayout layout = TypeRegistry.Get(); + Type layout = TypeRegistry.Get(); object instance = layout.CreateInstance(); Assert.That(instance, Is.InstanceOf()); Assert.That((Stress)instance, Is.EqualTo(default(Stress))); @@ -107,7 +110,7 @@ public void CreateObjectFromTypeLayout() public void GetOrRegister() { Assert.That(TypeRegistry.IsRegistered(), Is.False); - TypeLayout type = TypeRegistry.GetOrRegister(); + Type type = TypeRegistry.GetOrRegister(); Assert.That(TypeRegistry.IsRegistered(), Is.True); Assert.That(type.Is(), Is.True); } diff --git a/tests/RuntimeTypeHandleTests.cs b/tests/RuntimeTypeHandleTests.cs index b480341..a337dfe 100644 --- a/tests/RuntimeTypeHandleTests.cs +++ b/tests/RuntimeTypeHandleTests.cs @@ -8,7 +8,7 @@ public class RuntimeTypeHandleTests public void CastTypeAddressBackToHandle() { nint address = RuntimeTypeTable.GetAddress(); - Type? type = Type.GetTypeFromHandle(RuntimeTypeTable.GetHandle(address)); + System.Type? type = System.Type.GetTypeFromHandle(RuntimeTypeTable.GetHandle(address)); Assert.That(type, Is.EqualTo(typeof(string))); } } From 597522dca149189208593c28ebbc82b896d7a6c4 Mon Sep 17 00:00:00 2001 From: Phill Date: Fri, 21 Mar 2025 11:57:10 -0400 Subject: [PATCH 03/12] update dummy type in use for get or register test --- tests/LayoutTests.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/LayoutTests.cs b/tests/LayoutTests.cs index dd51869..e8a91ba 100644 --- a/tests/LayoutTests.cs +++ b/tests/LayoutTests.cs @@ -1,5 +1,4 @@ -using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions; -using System; +using System; using System.Numerics; namespace Types.Tests @@ -109,10 +108,10 @@ public void CreateObjectFromTypeLayout() [Test] public void GetOrRegister() { - Assert.That(TypeRegistry.IsRegistered(), Is.False); - Type type = TypeRegistry.GetOrRegister(); - Assert.That(TypeRegistry.IsRegistered(), Is.True); - Assert.That(type.Is(), Is.True); + Assert.That(TypeRegistry.IsRegistered(), Is.False); + Type type = TypeRegistry.GetOrRegister(); + Assert.That(TypeRegistry.IsRegistered(), Is.True); + Assert.That(type.Is(), Is.True); } } } \ No newline at end of file From 5e2c6345746428d70d41a34ff8485fd869f9fae3 Mon Sep 17 00:00:00 2001 From: Phill Date: Fri, 21 Mar 2025 12:50:42 -0400 Subject: [PATCH 04/12] generate registration of interfaces --- core/Field.cs | 2 +- core/Functions/Register.cs | 21 +- core/Interface.cs | 91 +++++ .../{TypeBuffer.cs => InterfaceTypeBuffer.cs} | 10 +- core/Type.cs | 110 +----- core/TypeRegistry.cs | 374 +++++++++++++----- generator/Generators/TypeBankGenerator.cs | 63 ++- tests/BankTests.cs | 6 +- tests/LayoutTests.cs | 75 ++-- tests/Types/Stress.cs | 10 +- 10 files changed, 508 insertions(+), 254 deletions(-) create mode 100644 core/Interface.cs rename core/{TypeBuffer.cs => InterfaceTypeBuffer.cs} (82%) diff --git a/core/Field.cs b/core/Field.cs index 2aa2360..dc3e612 100644 --- a/core/Field.cs +++ b/core/Field.cs @@ -13,7 +13,7 @@ namespace Types /// /// The type of the field. /// - public readonly Type Type => TypeRegistry.Get(typeHash); + public readonly Type Type => TypeRegistry.GetType(typeHash); /// /// Name of the field. diff --git a/core/Functions/Register.cs b/core/Functions/Register.cs index 8b893bc..e82c1ff 100644 --- a/core/Functions/Register.cs +++ b/core/Functions/Register.cs @@ -17,9 +17,9 @@ internal Register(Action action) /// /// Registers a type with the given and . /// - public unsafe readonly void Invoke(ReadOnlySpan variables, ReadOnlySpan interfaces) where T : unmanaged + public unsafe readonly void RegisterType(ReadOnlySpan variables, ReadOnlySpan interfaces) where T : unmanaged { - Type type = new(Type.GetFullName(), (ushort)sizeof(T), variables, interfaces); + Type type = new(TypeRegistry.GetFullName(), (ushort)sizeof(T), variables, interfaces); Input input = new(type, RuntimeTypeTable.GetHandle()); action(input); TypeInstanceCreator.Initialize(type); @@ -28,9 +28,9 @@ public unsafe readonly void Invoke(ReadOnlySpan variables, ReadOnlySpa /// /// Registers a type with the given and . /// - public unsafe readonly void Invoke(FieldBuffer variables, byte variableCount, TypeBuffer interfaces, byte interfaceCount) where T : unmanaged + public unsafe readonly void RegisterType(FieldBuffer variables, byte variableCount, InterfaceTypeBuffer interfaces, byte interfaceCount) where T : unmanaged { - Type type = new(Type.GetFullName(), (ushort)sizeof(T), variables, variableCount, interfaces, interfaceCount); + Type type = new(TypeRegistry.GetFullName(), (ushort)sizeof(T), variables, variableCount, interfaces, interfaceCount); Input input = new(type, RuntimeTypeTable.GetHandle()); action(input); TypeInstanceCreator.Initialize(type); @@ -39,14 +39,23 @@ public unsafe readonly void Invoke(FieldBuffer variables, byte variableCount, /// /// Registers a type without variables specified. /// - public unsafe readonly void Invoke() where T : unmanaged + public unsafe readonly void RegisterType() where T : unmanaged { - Type type = new(Type.GetFullName(), (ushort)sizeof(T)); + Type type = new(TypeRegistry.GetFullName(), (ushort)sizeof(T)); Input input = new(type, RuntimeTypeTable.GetHandle()); action(input); TypeInstanceCreator.Initialize(type); } + /// + /// Registers a type without variables specified. + /// + public readonly void RegisterInterface() + { + Interface interfaceValue = new(TypeRegistry.GetFullName()); + TypeRegistry.RegisterInterface(interfaceValue, RuntimeTypeTable.GetHandle()); + } + /// /// Input parameter. /// diff --git a/core/Interface.cs b/core/Interface.cs new file mode 100644 index 0000000..423dbd4 --- /dev/null +++ b/core/Interface.cs @@ -0,0 +1,91 @@ +using System; + +namespace Types +{ + /// + /// Describes an type that a implements. + /// + public readonly struct Interface : IEquatable + { + private readonly long hash; + + /// + /// The unique hash for this interface. + /// + public readonly long Hash => hash; + + /// + /// The full name of the interface. + /// + public readonly ReadOnlySpan FullName => TypeNames.Get(hash); + + /// + /// Name of the interface. + /// + public readonly ReadOnlySpan Name + { + get + { + ReadOnlySpan fullName = TypeNames.Get(hash); + int index = fullName.LastIndexOf('.'); + if (index != -1) + { + return fullName.Slice(index + 1); + } + else + { + return fullName; + } + } + } + + /// + /// Initializes an existing interface. + /// + public Interface(string fullTypeName) + { + hash = TypeNames.Set(fullTypeName); + } + + /// + /// Initializes an existing interface. + /// + public Interface(ReadOnlySpan fullTypeName) + { + hash = TypeNames.Set(fullTypeName); + } + + /// + public readonly override bool Equals(object? obj) + { + return obj is Interface type && Equals(type); + } + + /// + public readonly bool Equals(Interface other) + { + return hash == other.hash; + } + + /// + public readonly override int GetHashCode() + { + unchecked + { + return (int)hash; + } + } + + /// + public static bool operator ==(Interface left, Interface right) + { + return left.Equals(right); + } + + /// + public static bool operator !=(Interface left, Interface right) + { + return !(left == right); + } + } +} \ No newline at end of file diff --git a/core/TypeBuffer.cs b/core/InterfaceTypeBuffer.cs similarity index 82% rename from core/TypeBuffer.cs rename to core/InterfaceTypeBuffer.cs index 65005a7..0512862 100644 --- a/core/TypeBuffer.cs +++ b/core/InterfaceTypeBuffer.cs @@ -4,9 +4,9 @@ namespace Types { /// - /// Buffer for storing values. + /// Buffer for storing values. /// - public unsafe struct TypeBuffer + public unsafe struct InterfaceTypeBuffer { /// /// Maximum amount of values that can be stored. @@ -18,12 +18,12 @@ public unsafe struct TypeBuffer /// /// Indexer for accessing a value at the given . /// - public Type this[int index] + public Interface this[int index] { readonly get { long hash = buffer[index]; - return TypeRegistry.Get(hash); + return TypeRegistry.GetInterface(hash); } set { @@ -34,7 +34,7 @@ readonly get /// /// Creates a new buffer containing the given . /// - public TypeBuffer(ReadOnlySpan types) + public InterfaceTypeBuffer(ReadOnlySpan types) { ThrowIfCantFit(types.Length); diff --git a/core/Type.cs b/core/Type.cs index 71d9018..a17b035 100644 --- a/core/Type.cs +++ b/core/Type.cs @@ -5,7 +5,7 @@ namespace Types { /// - /// Describes metadata for a type. + /// Describes metadata for a type. /// [SkipLocalsInit] public readonly struct Type : IEquatable @@ -19,7 +19,7 @@ namespace Types private readonly byte interfaceCount; private readonly long hash; private readonly FieldBuffer fields; - private readonly TypeBuffer interfaces; + private readonly InterfaceTypeBuffer interfaces; /// /// Hash value unique to this type. @@ -43,13 +43,13 @@ public unsafe readonly ReadOnlySpan Fields /// /// All interfaces implemented by this type. /// - public unsafe readonly ReadOnlySpan Interfaces + public unsafe readonly ReadOnlySpan Interfaces { get { fixed (void* pointer = &interfaces) { - return new ReadOnlySpan(pointer, interfaceCount); + return new ReadOnlySpan(pointer, interfaceCount); } } } @@ -108,7 +108,7 @@ public Type() #endif /// - /// Creates a new type without any fields or interfaces set. + /// Initializes an existing value type. /// public Type(ReadOnlySpan fullName, ushort size) { @@ -119,7 +119,7 @@ public Type(ReadOnlySpan fullName, ushort size) } /// - /// Creates a new type without any fields or interfaces set. + /// Initializes an existing value type. /// public Type(string fullName, ushort size) { @@ -132,7 +132,7 @@ public Type(string fullName, ushort size) /// /// Creates a new type. /// - public Type(ReadOnlySpan fullName, ushort size, ReadOnlySpan fields, ReadOnlySpan interfaces) + public Type(ReadOnlySpan fullName, ushort size, ReadOnlySpan fields, ReadOnlySpan interfaces) { this.size = size; fieldCount = (byte)fields.Length; @@ -145,7 +145,7 @@ public Type(ReadOnlySpan fullName, ushort size, ReadOnlySpan fields /// /// Creates a new type. /// - public Type(ReadOnlySpan fullName, ushort size, FieldBuffer fields, byte fieldCount, TypeBuffer interfaces, byte interfaceCount) + public Type(ReadOnlySpan fullName, ushort size, FieldBuffer fields, byte fieldCount, InterfaceTypeBuffer interfaces, byte interfaceCount) { this.size = size; this.fieldCount = fieldCount; @@ -158,7 +158,7 @@ public Type(ReadOnlySpan fullName, ushort size, FieldBuffer fields, byte f /// /// Creates a new type. /// - public Type(string fullName, ushort size, ReadOnlySpan fields, ReadOnlySpan interfaces) + public Type(string fullName, ushort size, ReadOnlySpan fields, ReadOnlySpan interfaces) { this.size = size; fieldCount = (byte)fields.Length; @@ -335,98 +335,6 @@ public readonly int IndexOf(ReadOnlySpan fieldName) return -1; } - /// - /// Retrieves the full type name for the given . - /// - public static int GetFullName(System.Type type, Span buffer) - { - int length = 0; - AppendType(buffer, ref length, type); - return length; - - static void Insert(Span buffer, char character, ref int length) - { - buffer.Slice(0, length).CopyTo(buffer.Slice(1)); - buffer[0] = character; - length++; - } - - static void InsertSpan(Span buffer, ReadOnlySpan text, ref int length) - { - buffer.Slice(0, length).CopyTo(buffer.Slice(text.Length)); - text.CopyTo(buffer); - length += text.Length; - } - - static void AppendType(Span fullName, ref int length, System.Type type) - { - //todo: handle case where the type name is System.Collections.Generic.List`1+Enumerator[etc, etc] - System.Type? current = type; - string? currentNameSpace = current.Namespace; - while (current is not null) - { - System.Type[] genericTypes = current.GenericTypeArguments; - string name = current.Name; - if (genericTypes.Length > 0) - { - Insert(fullName, '>', ref length); - for (int i = genericTypes.Length - 1; i >= 0; i--) - { - AppendType(fullName, ref length, genericTypes[i]); - if (i > 0) - { - InsertSpan(fullName, ", ", ref length); - } - } - - Insert(fullName, '<', ref length); - int index = name.IndexOf('`'); - if (index != -1) - { - string trimmedName = name[..index]; - InsertSpan(fullName, trimmedName, ref length); - } - } - else - { - InsertSpan(fullName, name, ref length); - } - - current = current.DeclaringType; - if (current is not null) - { - Insert(fullName, '.', ref length); - } - } - - if (currentNameSpace is not null) - { - Insert(fullName, '.', ref length); - InsertSpan(fullName, currentNameSpace, ref length); - } - } - } - - /// - /// Retrieves the full type name for the given . - /// - public static string GetFullName(System.Type type) - { - Span buffer = stackalloc char[512]; - int length = GetFullName(type, buffer); - return buffer.Slice(0, length).ToString(); - } - - /// - /// Retrieves the full type name for the type . - /// - public static string GetFullName() - { - Span buffer = stackalloc char[512]; - int length = GetFullName(typeof(T), buffer); - return buffer.Slice(0, length).ToString(); - } - [Conditional("DEBUG")] private readonly void ThrowIfFieldIsMissing(ReadOnlySpan fieldName) { diff --git a/core/TypeRegistry.cs b/core/TypeRegistry.cs index f036b05..512ab39 100644 --- a/core/TypeRegistry.cs +++ b/core/TypeRegistry.cs @@ -8,80 +8,84 @@ namespace Types { /// - /// Stores metadata about types. + /// Stores metadata about types and interfaces. /// public static class TypeRegistry { private static readonly List types = new(); + private static readonly List interfaces = new(); internal static readonly Dictionary handleToType = new(); + internal static readonly Dictionary handleToInterface = new(); private static readonly Dictionary typeToHandle = new(); - private static readonly Dictionary hashToType = new(); + private static readonly Dictionary interfaceToHandle = new(); + private static readonly Dictionary hashToValueType = new(); + private static readonly Dictionary hashToInterfaceType = new(); /// /// All registered types. /// - public static IReadOnlyCollection All => types; + public static IReadOnlyCollection Types => types; [SkipLocalsInit] static unsafe TypeRegistry() { - Register(new(Type.GetFullName(), sizeof(byte)), RuntimeTypeTable.GetHandle()); - Register(new(Type.GetFullName(), sizeof(sbyte)), RuntimeTypeTable.GetHandle()); - Register(new(Type.GetFullName(), sizeof(short)), RuntimeTypeTable.GetHandle()); - Register(new(Type.GetFullName(), sizeof(ushort)), RuntimeTypeTable.GetHandle()); - Register(new(Type.GetFullName(), sizeof(int)), RuntimeTypeTable.GetHandle()); - Register(new(Type.GetFullName(), sizeof(uint)), RuntimeTypeTable.GetHandle()); - Register(new(Type.GetFullName(), sizeof(long)), RuntimeTypeTable.GetHandle()); - Register(new(Type.GetFullName(), sizeof(ulong)), RuntimeTypeTable.GetHandle()); - Register(new(Type.GetFullName(), sizeof(float)), RuntimeTypeTable.GetHandle()); - Register(new(Type.GetFullName(), sizeof(double)), RuntimeTypeTable.GetHandle()); - Register(new(Type.GetFullName(), sizeof(char)), RuntimeTypeTable.GetHandle()); - Register(new(Type.GetFullName(), sizeof(bool)), RuntimeTypeTable.GetHandle()); - Register(new(Type.GetFullName(), (ushort)sizeof(nint)), RuntimeTypeTable.GetHandle()); - Register(new(Type.GetFullName(), (ushort)sizeof(nuint)), RuntimeTypeTable.GetHandle()); + RegisterType(new(GetFullName(), sizeof(byte)), RuntimeTypeTable.GetHandle()); + RegisterType(new(GetFullName(), sizeof(sbyte)), RuntimeTypeTable.GetHandle()); + RegisterType(new(GetFullName(), sizeof(short)), RuntimeTypeTable.GetHandle()); + RegisterType(new(GetFullName(), sizeof(ushort)), RuntimeTypeTable.GetHandle()); + RegisterType(new(GetFullName(), sizeof(int)), RuntimeTypeTable.GetHandle()); + RegisterType(new(GetFullName(), sizeof(uint)), RuntimeTypeTable.GetHandle()); + RegisterType(new(GetFullName(), sizeof(long)), RuntimeTypeTable.GetHandle()); + RegisterType(new(GetFullName(), sizeof(ulong)), RuntimeTypeTable.GetHandle()); + RegisterType(new(GetFullName(), sizeof(float)), RuntimeTypeTable.GetHandle()); + RegisterType(new(GetFullName(), sizeof(double)), RuntimeTypeTable.GetHandle()); + RegisterType(new(GetFullName(), sizeof(char)), RuntimeTypeTable.GetHandle()); + RegisterType(new(GetFullName(), sizeof(bool)), RuntimeTypeTable.GetHandle()); + RegisterType(new(GetFullName(), (ushort)sizeof(nint)), RuntimeTypeTable.GetHandle()); + RegisterType(new(GetFullName(), (ushort)sizeof(nuint)), RuntimeTypeTable.GetHandle()); #if NET - Register(new(Type.GetFullName(), (ushort)sizeof(Half)), RuntimeTypeTable.GetHandle()); + RegisterType(new(GetFullName(), (ushort)sizeof(Half)), RuntimeTypeTable.GetHandle()); #endif FieldBuffer fields = new(); - TypeBuffer interfaces = new(); - fields[0] = new("x", Type.GetFullName()); - fields[1] = new("y", Type.GetFullName()); - fields[2] = new("z", Type.GetFullName()); - fields[3] = new("w", Type.GetFullName()); - Register(new(Type.GetFullName(), (ushort)sizeof(Vector2), fields, 2, interfaces, 0), RuntimeTypeTable.GetHandle()); - Register(new(Type.GetFullName(), (ushort)sizeof(Vector3), fields, 3, interfaces, 0), RuntimeTypeTable.GetHandle()); - Register(new(Type.GetFullName(), (ushort)sizeof(Vector4), fields, 4, interfaces, 0), RuntimeTypeTable.GetHandle()); - Register(new(Type.GetFullName(), (ushort)sizeof(Quaternion), fields, 4, interfaces, 0), RuntimeTypeTable.GetHandle()); - - fields[0] = new("M11", Type.GetFullName()); - fields[1] = new("M12", Type.GetFullName()); - fields[2] = new("M21", Type.GetFullName()); - fields[3] = new("M22", Type.GetFullName()); - fields[4] = new("M31", Type.GetFullName()); - fields[5] = new("M32", Type.GetFullName()); - Register(new(Type.GetFullName(), (ushort)sizeof(Matrix3x2), fields, 6, interfaces, 0), RuntimeTypeTable.GetHandle()); - - fields[0] = new("M11", Type.GetFullName()); - fields[1] = new("M12", Type.GetFullName()); - fields[2] = new("M13", Type.GetFullName()); - fields[3] = new("M14", Type.GetFullName()); - fields[4] = new("M21", Type.GetFullName()); - fields[5] = new("M22", Type.GetFullName()); - fields[6] = new("M23", Type.GetFullName()); - fields[7] = new("M24", Type.GetFullName()); - fields[8] = new("M31", Type.GetFullName()); - fields[9] = new("M32", Type.GetFullName()); - fields[10] = new("M33", Type.GetFullName()); - fields[11] = new("M34", Type.GetFullName()); - fields[12] = new("M41", Type.GetFullName()); - fields[13] = new("M42", Type.GetFullName()); - fields[14] = new("M43", Type.GetFullName()); - fields[15] = new("M44", Type.GetFullName()); - Register(new(Type.GetFullName(), (ushort)sizeof(Matrix4x4), fields, 16, interfaces, 0), RuntimeTypeTable.GetHandle()); - - fields[0] = new("_dateData", Type.GetFullName()); - Register(new(Type.GetFullName(), (ushort)sizeof(DateTime), fields, 1, interfaces, 0), RuntimeTypeTable.GetHandle()); + InterfaceTypeBuffer interfaces = new(); + fields[0] = new("x", GetFullName()); + fields[1] = new("y", GetFullName()); + fields[2] = new("z", GetFullName()); + fields[3] = new("w", GetFullName()); + RegisterType(new(GetFullName(), (ushort)sizeof(Vector2), fields, 2, interfaces, 0), RuntimeTypeTable.GetHandle()); + RegisterType(new(GetFullName(), (ushort)sizeof(Vector3), fields, 3, interfaces, 0), RuntimeTypeTable.GetHandle()); + RegisterType(new(GetFullName(), (ushort)sizeof(Vector4), fields, 4, interfaces, 0), RuntimeTypeTable.GetHandle()); + RegisterType(new(GetFullName(), (ushort)sizeof(Quaternion), fields, 4, interfaces, 0), RuntimeTypeTable.GetHandle()); + + fields[0] = new("M11", GetFullName()); + fields[1] = new("M12", GetFullName()); + fields[2] = new("M21", GetFullName()); + fields[3] = new("M22", GetFullName()); + fields[4] = new("M31", GetFullName()); + fields[5] = new("M32", GetFullName()); + RegisterType(new(GetFullName(), (ushort)sizeof(Matrix3x2), fields, 6, interfaces, 0), RuntimeTypeTable.GetHandle()); + + fields[0] = new("M11", GetFullName()); + fields[1] = new("M12", GetFullName()); + fields[2] = new("M13", GetFullName()); + fields[3] = new("M14", GetFullName()); + fields[4] = new("M21", GetFullName()); + fields[5] = new("M22", GetFullName()); + fields[6] = new("M23", GetFullName()); + fields[7] = new("M24", GetFullName()); + fields[8] = new("M31", GetFullName()); + fields[9] = new("M32", GetFullName()); + fields[10] = new("M33", GetFullName()); + fields[11] = new("M34", GetFullName()); + fields[12] = new("M41", GetFullName()); + fields[13] = new("M42", GetFullName()); + fields[14] = new("M43", GetFullName()); + fields[15] = new("M44", GetFullName()); + RegisterType(new(GetFullName(), (ushort)sizeof(Matrix4x4), fields, 16, interfaces, 0), RuntimeTypeTable.GetHandle()); + + fields[0] = new("_dateData", GetFullName()); + RegisterType(new(GetFullName(), (ushort)sizeof(DateTime), fields, 1, interfaces, 0), RuntimeTypeTable.GetHandle()); } /// @@ -91,33 +95,43 @@ public unsafe static void Load() where T : unmanaged, ITypeBank { T bank = default; bank.Load(new(Register)); - } - /// - /// Registers a type using the information in the given . - /// - public static void Register(Register.Input input) - { - Register(input.type, input.Handle); + static void Register(Register.Input input) + { + RegisterType(input.type, input.Handle); + } } /// /// Manually registers the given . /// - public static void Register(Type type, RuntimeTypeHandle handle) + public static void RegisterType(Type type, RuntimeTypeHandle handle) { ThrowIfAlreadyRegistered(type); types.Add(type); handleToType.Add(handle, type); typeToHandle.Add(type.Hash, handle); - hashToType.Add(type.Hash, type); + hashToValueType.Add(type.Hash, type); + } + + /// + /// Manually registers the given . + /// + public static void RegisterInterface(Interface interfaceValue, RuntimeTypeHandle handle) + { + ThrowIfAlreadyRegistered(interfaceValue); + + interfaces.Add(interfaceValue); + handleToInterface.Add(handle, interfaceValue); + interfaceToHandle.Add(interfaceValue.Hash, handle); + hashToInterfaceType.Add(interfaceValue.Hash, interfaceValue); } /// /// Tries to manually register the given . /// - public static bool TryRegister(Type type, RuntimeTypeHandle handle) + public static bool TryRegisterType(Type type, RuntimeTypeHandle handle) { if (types.Contains(type)) { @@ -127,72 +141,101 @@ public static bool TryRegister(Type type, RuntimeTypeHandle handle) types.Add(type); handleToType.Add(handle, type); typeToHandle.Add(type.Hash, handle); - hashToType.Add(type.Hash, type); + hashToValueType.Add(type.Hash, type); return true; } /// /// Manually registers type without any fields or interfaces. /// - public unsafe static void Register() where T : unmanaged + public unsafe static void RegisterType() where T : unmanaged { //todo: need to add a warning here when trying to register a type bank itself ushort size = (ushort)sizeof(T); - Type type = new(Type.GetFullName(), size); - Register(type, RuntimeTypeTable.GetHandle()); + Type type = new(GetFullName(), size); + RegisterType(type, RuntimeTypeTable.GetHandle()); + } + + /// + /// Manually registers the interface of type . + /// + public static void RegisterInterface() where T : unmanaged + { + Interface interfaceValue = new(GetFullName()); + RegisterInterface(interfaceValue, RuntimeTypeTable.GetHandle()); } /// /// Tries to manually register type without any fields or interfaces. /// - public unsafe static bool TryRegister() where T : unmanaged + public unsafe static bool TryRegisterType() where T : unmanaged { ushort size = (ushort)sizeof(T); - Type type = new(Type.GetFullName(), size); - return TryRegister(type, RuntimeTypeTable.GetHandle()); + Type type = new(GetFullName(), size); + return TryRegisterType(type, RuntimeTypeTable.GetHandle()); } /// /// Retrieves the metadata for . /// - public static Type Get() where T : unmanaged + public static Type GetType() where T : unmanaged { - ThrowIfNotRegistered(); + ThrowIfTypeNotRegistered(); - return Cache.value; + return TypeCache.value; + } + + /// + /// Retrieves the metadata for . + /// + public static Interface GetInterface() where T : unmanaged + { + ThrowIfTypeNotRegistered(); + + return InterfaceCache.value; } /// /// Retrieves the metadata for , or registers /// it if it's not already registered without any variables. /// - public static Type GetOrRegister() where T : unmanaged + public static Type GetOrRegisterType() where T : unmanaged { - return LazyCache.value; + return LazyTypeCache.value; } /// /// Retrieves the metadata for the type with the given . /// - public static Type Get(long typeHash) + public static Type GetType(long typeHash) { - ThrowIfNotRegistered(typeHash); + ThrowIfTypeNotRegistered(typeHash); - return hashToType[typeHash]; + return hashToValueType[typeHash]; + } + + /// + /// Retrieves the metadata for the type with the given . + /// + public static Interface GetInterface(long typeHash) + { + ThrowIfTypeNotRegistered(typeHash); + + return hashToInterfaceType[typeHash]; } /// /// Tries to get the metadata for the type with the given . /// - public static bool TryGet(long typeHash, out Type type) + public static bool TryGetType(long typeHash, out Type type) { - return hashToType.TryGetValue(typeHash, out type); + return hashToValueType.TryGetValue(typeHash, out type); } /// /// Retrieves the metadata for the of the wanted type. /// - public static Type Get(RuntimeTypeHandle handle) + public static Type GetType(RuntimeTypeHandle handle) { ThrowIfNotRegistered(handle); @@ -204,7 +247,7 @@ public static Type Get(RuntimeTypeHandle handle) /// public static RuntimeTypeHandle GetRuntimeTypeHandle(long typeHash) { - ThrowIfNotRegistered(typeHash); + ThrowIfTypeNotRegistered(typeHash); return typeToHandle[typeHash]; } @@ -212,15 +255,23 @@ public static RuntimeTypeHandle GetRuntimeTypeHandle(long typeHash) /// /// Checks if type is registered. /// - public static bool IsRegistered() where T : unmanaged + public static bool IsTypeRegistered() where T : unmanaged { return handleToType.ContainsKey(RuntimeTypeTable.GetHandle()); } + /// + /// Checks if type is registered. + /// + public static bool IsInterfaceRegistered() where T : unmanaged + { + return handleToInterface.ContainsKey(RuntimeTypeTable.GetHandle()); + } + /// /// Checks if the given is registered. /// - public static bool IsRegistered(System.Type type) + public static bool IsTypeRegistered(System.Type type) { return handleToType.ContainsKey(RuntimeTypeTable.GetHandle(type)); } @@ -259,19 +310,111 @@ public static bool IsRegistered(string fullTypeName) return false; } + /// + /// Retrieves the full type name for the given . + /// + public static int GetFullName(System.Type type, Span buffer) + { + int length = 0; + AppendType(buffer, ref length, type); + return length; + + static void Insert(Span buffer, char character, ref int length) + { + buffer.Slice(0, length).CopyTo(buffer.Slice(1)); + buffer[0] = character; + length++; + } + + static void InsertSpan(Span buffer, ReadOnlySpan text, ref int length) + { + buffer.Slice(0, length).CopyTo(buffer.Slice(text.Length)); + text.CopyTo(buffer); + length += text.Length; + } + + static void AppendType(Span fullName, ref int length, System.Type type) + { + //todo: handle case where the type name is System.Collections.Generic.List`1+Enumerator[etc, etc] + System.Type? current = type; + string? currentNameSpace = current.Namespace; + while (current is not null) + { + System.Type[] genericTypes = current.GenericTypeArguments; + string name = current.Name; + if (genericTypes.Length > 0) + { + Insert(fullName, '>', ref length); + for (int i = genericTypes.Length - 1; i >= 0; i--) + { + AppendType(fullName, ref length, genericTypes[i]); + if (i > 0) + { + InsertSpan(fullName, ", ", ref length); + } + } + + Insert(fullName, '<', ref length); + int index = name.IndexOf('`'); + if (index != -1) + { + string trimmedName = name[..index]; + InsertSpan(fullName, trimmedName, ref length); + } + } + else + { + InsertSpan(fullName, name, ref length); + } + + current = current.DeclaringType; + if (current is not null) + { + Insert(fullName, '.', ref length); + } + } + + if (currentNameSpace is not null) + { + Insert(fullName, '.', ref length); + InsertSpan(fullName, currentNameSpace, ref length); + } + } + } + + /// + /// Retrieves the full type name for the given . + /// + public static string GetFullName(System.Type type) + { + Span buffer = stackalloc char[512]; + int length = GetFullName(type, buffer); + return buffer.Slice(0, length).ToString(); + } + + /// + /// Retrieves the full type name for the type . + /// + public static string GetFullName() + { + Span buffer = stackalloc char[512]; + int length = GetFullName(typeof(T), buffer); + return buffer.Slice(0, length).ToString(); + } + [Conditional("DEBUG")] - private static void ThrowIfNotRegistered() where T : unmanaged + private static void ThrowIfTypeNotRegistered() where T : unmanaged { - if (!IsRegistered()) + if (!IsTypeRegistered()) { throw new InvalidOperationException($"Type `{typeof(T)}` is not registered"); } } [Conditional("DEBUG")] - private static void ThrowIfNotRegistered(long hash) + private static void ThrowIfTypeNotRegistered(long hash) { - if (!hashToType.ContainsKey(hash)) + if (!hashToValueType.ContainsKey(hash)) { throw new InvalidOperationException($"Type with hash `{hash}` is not registered"); } @@ -303,11 +446,20 @@ private static void ThrowIfAlreadyRegistered(Type type) } } - private static class Cache where T : unmanaged + [Conditional("DEBUG")] + private static void ThrowIfAlreadyRegistered(Interface interfaceValue) + { + if (interfaces.Contains(interfaceValue)) + { + throw new InvalidOperationException($"Interface `{interfaceValue}` is already registered"); + } + } + + private static class TypeCache where T : unmanaged { public static readonly Type value; - static Cache() + static TypeCache() { if (!handleToType.TryGetValue(RuntimeTypeTable.GetHandle(), out value)) { @@ -316,17 +468,45 @@ static Cache() } } - private unsafe static class LazyCache where T : unmanaged + private static class InterfaceCache + { + public static readonly Interface value; + + static InterfaceCache() + { + if (!handleToInterface.TryGetValue(RuntimeTypeTable.GetHandle(), out value)) + { + throw new InvalidOperationException($"Interface `{typeof(T)}` is not registered"); + } + } + } + + private unsafe static class LazyTypeCache where T : unmanaged { public static readonly Type value; - static LazyCache() + static LazyTypeCache() { RuntimeTypeHandle key = RuntimeTypeTable.GetHandle(); if (!handleToType.TryGetValue(key, out value)) { - value = new(Type.GetFullName(), (ushort)sizeof(T)); - Register(value, key); + value = new(GetFullName(), (ushort)sizeof(T)); + RegisterType(value, key); + } + } + } + + private unsafe static class LazyInterfaceCache where T : unmanaged + { + public static readonly Interface value; + + static LazyInterfaceCache() + { + RuntimeTypeHandle key = RuntimeTypeTable.GetHandle(); + if (!handleToInterface.TryGetValue(key, out value)) + { + value = new(GetFullName()); + RegisterInterface(value, key); } } } diff --git a/generator/Generators/TypeBankGenerator.cs b/generator/Generators/TypeBankGenerator.cs index 99199f8..89582d6 100644 --- a/generator/Generators/TypeBankGenerator.cs +++ b/generator/Generators/TypeBankGenerator.cs @@ -12,9 +12,9 @@ public class TypeBankGenerator : IIncrementalGenerator { public const string TypeNameFormat = "{0}TypeBank"; public const string FieldBufferTypeName = "FieldBuffer"; - public const string InterfaceBufferTypeName = "TypeBuffer"; + public const string InterfaceBufferTypeName = "InterfaceTypeBuffer"; public const string FieldBufferVariableName = "fields"; - public const string InterfaceBufferVariableName = "types"; + public const string InterfaceBufferVariableName = "interfaces"; void IIncrementalGenerator.Initialize(IncrementalGeneratorInitializationContext context) { @@ -129,9 +129,29 @@ public static string Generate(IReadOnlyList types, out string typeN source.Append(" = new();"); source.AppendLine(); + //register all interfaces first + HashSet interfaceTypes = []; foreach (ITypeSymbol type in types) { - AppendRegister(source, type); + foreach (INamedTypeSymbol interfaceType in type.AllInterfaces) + { + if (interfaceType.IsGenericType) + { + continue; + } + + interfaceTypes.Add(interfaceType); + } + } + + foreach (ITypeSymbol interfaceType in interfaceTypes) + { + AppendRegisterInterface(source, interfaceType); + } + + foreach (ITypeSymbol type in types) + { + AppendRegisterType(source, type); } } source.EndGroup(); @@ -146,7 +166,16 @@ public static string Generate(IReadOnlyList types, out string typeN return source.ToString(); } - private static void AppendRegister(SourceBuilder source, ITypeSymbol type) + private static void AppendRegisterInterface(SourceBuilder source, ITypeSymbol interfaceType) + { + string fullName = interfaceType.GetFullTypeName(); + source.Append("register.RegisterInterface<"); + source.Append(fullName); + source.Append(">();"); + source.AppendLine(); + } + + private static void AppendRegisterType(SourceBuilder source, ITypeSymbol type) { string fullName = type.GetFullTypeName(); if (fullName.EndsWith("e__FixedBuffer")) @@ -165,10 +194,20 @@ private static void AppendRegister(SourceBuilder source, ITypeSymbol type) } } - source.Append("register.Invoke<"); + foreach (INamedTypeSymbol interfaceType in type.AllInterfaces) + { + if (interfaceType.IsGenericType) + { + continue; + } + + AppendInterface(source, interfaceType, ref interfaceCount); + } + + source.Append("register.RegisterType<"); source.Append(fullName); source.Append(">("); - if (variableCount > 0) + if (variableCount > 0 || interfaceCount > 0) { source.Append(FieldBufferVariableName); source.Append(','); @@ -210,6 +249,18 @@ static void AppendVariable(SourceBuilder source, IFieldSymbol field, ref byte co source.AppendLine(); count++; } + + static void AppendInterface(SourceBuilder source, INamedTypeSymbol interfaceType, ref byte count) + { + source.Append(InterfaceBufferVariableName); + source.Append('['); + source.Append(count); + source.Append("] = new(\""); + source.Append(interfaceType.GetFullTypeName()); + source.Append("\");"); + source.AppendLine(); + count++; + } } } } \ No newline at end of file diff --git a/tests/BankTests.cs b/tests/BankTests.cs index 41a2bc6..ec451ec 100644 --- a/tests/BankTests.cs +++ b/tests/BankTests.cs @@ -7,16 +7,16 @@ public unsafe class BankTests : TypeTests [Test] public void LoadCustomBank() { - Assert.That(TypeRegistry.IsRegistered(), Is.False); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.False); TypeRegistry.Load(); - Assert.That(TypeRegistry.IsRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); } public readonly struct CustomTypeBank : ITypeBank { void ITypeBank.Load(Register register) { - register.Invoke(); + register.RegisterType(); } } } diff --git a/tests/LayoutTests.cs b/tests/LayoutTests.cs index e8a91ba..d49c978 100644 --- a/tests/LayoutTests.cs +++ b/tests/LayoutTests.cs @@ -8,7 +8,7 @@ public unsafe class LayoutTests : TypeTests [Test] public void VerifyLayoutOfRegisteredTypes() { - Type type = TypeRegistry.Get(); + Type type = TypeRegistry.GetType(); Assert.That(type.SystemType, Is.EqualTo(typeof(Stress))); Assert.That(type.Name.ToString(), Is.EqualTo("Stress")); Assert.That(type.size, Is.EqualTo((uint)sizeof(Stress))); @@ -30,45 +30,45 @@ public void VerifyLayoutOfRegisteredTypes() [Test] public void PrimitiveTypesAreAvailable() { - Assert.That(TypeRegistry.IsRegistered(), Is.True); - Assert.That(TypeRegistry.IsRegistered(), Is.True); - Assert.That(TypeRegistry.IsRegistered(), Is.True); - Assert.That(TypeRegistry.IsRegistered(), Is.True); - Assert.That(TypeRegistry.IsRegistered(), Is.True); - Assert.That(TypeRegistry.IsRegistered(), Is.True); - Assert.That(TypeRegistry.IsRegistered(), Is.True); - Assert.That(TypeRegistry.IsRegistered(), Is.True); - Assert.That(TypeRegistry.IsRegistered(), Is.True); - Assert.That(TypeRegistry.IsRegistered(), Is.True); - Assert.That(TypeRegistry.IsRegistered(), Is.True); - Assert.That(TypeRegistry.IsRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); Assert.That(TypeRegistry.IsRegistered(typeof(bool).FullName ?? typeof(bool).Name), Is.True); Assert.That(TypeRegistry.IsRegistered(typeof(byte).FullName ?? typeof(byte).Name), Is.True); Assert.That(TypeRegistry.IsRegistered(typeof(sbyte).FullName ?? typeof(sbyte).Name), Is.True); Assert.That(TypeRegistry.IsRegistered(typeof(short).FullName ?? typeof(short).Name), Is.True); - Assert.That(TypeRegistry.Get().size, Is.EqualTo((uint)sizeof(Vector3))); - Assert.That(TypeRegistry.Get().Fields.Length, Is.EqualTo(3)); + Assert.That(TypeRegistry.GetType().size, Is.EqualTo((uint)sizeof(Vector3))); + Assert.That(TypeRegistry.GetType().Fields.Length, Is.EqualTo(3)); } [Test] public void CheckLayouts() { - Assert.That(TypeRegistry.IsRegistered(), Is.True); - Assert.That(TypeRegistry.IsRegistered(), Is.True); - Type boolean = TypeRegistry.Get(); - Type byteType = TypeRegistry.Get(); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Type boolean = TypeRegistry.GetType(); + Type byteType = TypeRegistry.GetType(); Assert.That(boolean.size, Is.EqualTo(1)); Assert.That(byteType.size, Is.EqualTo(1)); - Assert.That(boolean.GetHashCode(), Is.EqualTo(TypeRegistry.Get().GetHashCode())); - Assert.That(byteType.GetHashCode(), Is.EqualTo(TypeRegistry.Get().GetHashCode())); + Assert.That(boolean.GetHashCode(), Is.EqualTo(TypeRegistry.GetType().GetHashCode())); + Assert.That(byteType.GetHashCode(), Is.EqualTo(TypeRegistry.GetType().GetHashCode())); } [Test] public void CheckIfLayoutIs() { - Type layout = TypeRegistry.Get(); + Type layout = TypeRegistry.GetType(); Assert.That(layout.Is(), Is.True); Assert.That(layout.Is(), Is.False); @@ -77,29 +77,29 @@ public void CheckIfLayoutIs() [Test] public void CheckNamesOfTypes() { - Assert.That(TypeRegistry.Get().Name.ToString(), Is.EqualTo("Boolean")); - Assert.That(TypeRegistry.Get().FullName.ToString(), Is.EqualTo("System.Boolean")); - Assert.That(TypeRegistry.Get().Name.ToString(), Is.EqualTo("Cherry")); - Assert.That(TypeRegistry.Get().FullName.ToString(), Is.EqualTo("Types.Tests.Cherry")); + Assert.That(TypeRegistry.GetType().Name.ToString(), Is.EqualTo("Boolean")); + Assert.That(TypeRegistry.GetType().FullName.ToString(), Is.EqualTo("System.Boolean")); + Assert.That(TypeRegistry.GetType().Name.ToString(), Is.EqualTo("Cherry")); + Assert.That(TypeRegistry.GetType().FullName.ToString(), Is.EqualTo("Types.Tests.Cherry")); } [Test] public void GetFullNameOfType() { - string c = Type.GetFullName(); + string c = TypeRegistry.GetFullName(); Assert.That(c.ToString(), Is.EqualTo("System.Boolean")); - string a = Type.GetFullName>(); + string a = TypeRegistry.GetFullName>(); Assert.That(a.ToString(), Is.EqualTo("Types.Tests.Dictionary")); - string b = Type.GetFullName>>(); + string b = TypeRegistry.GetFullName>>(); Assert.That(b.ToString(), Is.EqualTo("Types.Tests.Dictionary>")); } [Test] public void CreateObjectFromTypeLayout() { - Type layout = TypeRegistry.Get(); + Type layout = TypeRegistry.GetType(); object instance = layout.CreateInstance(); Assert.That(instance, Is.InstanceOf()); Assert.That((Stress)instance, Is.EqualTo(default(Stress))); @@ -108,10 +108,19 @@ public void CreateObjectFromTypeLayout() [Test] public void GetOrRegister() { - Assert.That(TypeRegistry.IsRegistered(), Is.False); - Type type = TypeRegistry.GetOrRegister(); - Assert.That(TypeRegistry.IsRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.False); + Type type = TypeRegistry.GetOrRegisterType(); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); Assert.That(type.Is(), Is.True); } + + [Test] + public void GetImplementedInterfaces() + { + Type type = TypeRegistry.GetType(); + ReadOnlySpan interfaces = type.Interfaces; + Assert.That(interfaces.Length, Is.EqualTo(1)); + Assert.That(interfaces[0].Name.ToString(), Is.EqualTo("IDisposable")); + } } } \ No newline at end of file diff --git a/tests/Types/Stress.cs b/tests/Types/Stress.cs index 6a3f84d..cca4dd2 100644 --- a/tests/Types/Stress.cs +++ b/tests/Types/Stress.cs @@ -1,6 +1,8 @@ -namespace Types.Tests +using System; + +namespace Types.Tests { - public readonly struct Stress + public readonly struct Stress : IDisposable { public readonly byte first; public readonly ushort second; @@ -12,5 +14,9 @@ public readonly override string ToString() { return $"Stress: {first}, {second}, {third}, {fourth}, {cherry}"; } + + readonly void IDisposable.Dispose() + { + } } } \ No newline at end of file From 81fa7674ee0cde0ea51b65b72f62733aa4acd06a Mon Sep 17 00:00:00 2001 From: Phill Date: Fri, 21 Mar 2025 12:56:32 -0400 Subject: [PATCH 05/12] rename register function to be more detailed --- .../{Register.cs => RegisterFunction.cs} | 18 ++++-------------- core/ITypeBank.cs | 2 +- core/TypeRegistry.cs | 8 +------- generator/Generators/TypeBankGenerator.cs | 7 ++++++- tests/BankTests.cs | 2 +- 5 files changed, 13 insertions(+), 24 deletions(-) rename core/Functions/{Register.cs => RegisterFunction.cs} (84%) diff --git a/core/Functions/Register.cs b/core/Functions/RegisterFunction.cs similarity index 84% rename from core/Functions/Register.cs rename to core/Functions/RegisterFunction.cs index e82c1ff..d0ba6a8 100644 --- a/core/Functions/Register.cs +++ b/core/Functions/RegisterFunction.cs @@ -5,23 +5,15 @@ namespace Types.Functions /// /// Function to register a type. /// - public readonly struct Register + public readonly struct RegisterFunction { - private readonly Action action; - - internal Register(Action action) - { - this.action = action; - } - /// /// Registers a type with the given and . /// public unsafe readonly void RegisterType(ReadOnlySpan variables, ReadOnlySpan interfaces) where T : unmanaged { Type type = new(TypeRegistry.GetFullName(), (ushort)sizeof(T), variables, interfaces); - Input input = new(type, RuntimeTypeTable.GetHandle()); - action(input); + TypeRegistry.RegisterType(type, RuntimeTypeTable.GetHandle()); TypeInstanceCreator.Initialize(type); } @@ -31,8 +23,7 @@ public unsafe readonly void RegisterType(ReadOnlySpan variables, ReadO public unsafe readonly void RegisterType(FieldBuffer variables, byte variableCount, InterfaceTypeBuffer interfaces, byte interfaceCount) where T : unmanaged { Type type = new(TypeRegistry.GetFullName(), (ushort)sizeof(T), variables, variableCount, interfaces, interfaceCount); - Input input = new(type, RuntimeTypeTable.GetHandle()); - action(input); + TypeRegistry.RegisterType(type, RuntimeTypeTable.GetHandle()); TypeInstanceCreator.Initialize(type); } @@ -42,8 +33,7 @@ public unsafe readonly void RegisterType(FieldBuffer variables, byte variable public unsafe readonly void RegisterType() where T : unmanaged { Type type = new(TypeRegistry.GetFullName(), (ushort)sizeof(T)); - Input input = new(type, RuntimeTypeTable.GetHandle()); - action(input); + TypeRegistry.RegisterType(type, RuntimeTypeTable.GetHandle()); TypeInstanceCreator.Initialize(type); } diff --git a/core/ITypeBank.cs b/core/ITypeBank.cs index 6d19e9a..8814b7e 100644 --- a/core/ITypeBank.cs +++ b/core/ITypeBank.cs @@ -10,6 +10,6 @@ public interface ITypeBank /// /// Loads type metadata into . /// - void Load(Register register); + void Load(RegisterFunction register); } } \ No newline at end of file diff --git a/core/TypeRegistry.cs b/core/TypeRegistry.cs index 512ab39..fe4abd9 100644 --- a/core/TypeRegistry.cs +++ b/core/TypeRegistry.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using System.Numerics; using System.Runtime.CompilerServices; -using Types.Functions; namespace Types { @@ -94,12 +93,7 @@ static unsafe TypeRegistry() public unsafe static void Load() where T : unmanaged, ITypeBank { T bank = default; - bank.Load(new(Register)); - - static void Register(Register.Input input) - { - RegisterType(input.type, input.Handle); - } + bank.Load(new()); } /// diff --git a/generator/Generators/TypeBankGenerator.cs b/generator/Generators/TypeBankGenerator.cs index 89582d6..b88a29a 100644 --- a/generator/Generators/TypeBankGenerator.cs +++ b/generator/Generators/TypeBankGenerator.cs @@ -15,6 +15,7 @@ public class TypeBankGenerator : IIncrementalGenerator public const string InterfaceBufferTypeName = "InterfaceTypeBuffer"; public const string FieldBufferVariableName = "fields"; public const string InterfaceBufferVariableName = "interfaces"; + public const string RegisterFunctionTypeName = "RegisterFunction"; void IIncrementalGenerator.Initialize(IncrementalGeneratorInitializationContext context) { @@ -114,7 +115,11 @@ public static string Generate(IReadOnlyList types, out string typeN source.BeginGroup(); { - source.AppendLine("readonly void ITypeBank.Load(Register register)"); + source.Append("readonly void ITypeBank.Load("); + source.Append(RegisterFunctionTypeName); + source.Append(" register)"); + source.AppendLine(); + source.BeginGroup(); { source.Append(FieldBufferTypeName); diff --git a/tests/BankTests.cs b/tests/BankTests.cs index ec451ec..f168121 100644 --- a/tests/BankTests.cs +++ b/tests/BankTests.cs @@ -14,7 +14,7 @@ public void LoadCustomBank() public readonly struct CustomTypeBank : ITypeBank { - void ITypeBank.Load(Register register) + void ITypeBank.Load(RegisterFunction register) { register.RegisterType(); } From 3f7b981b7bf0b70a212e85e8a8d09b21089102e6 Mon Sep 17 00:00:00 2001 From: Phill Date: Fri, 21 Mar 2025 13:09:21 -0400 Subject: [PATCH 06/12] rename test type --- tests/BankTests.cs | 2 +- tests/LayoutTests.cs | 126 ------------------------------------------- tests/Tests.cs | 32 +++++++++++ tests/TypeTests.cs | 122 ++++++++++++++++++++++++++++++++++++----- 4 files changed, 142 insertions(+), 140 deletions(-) delete mode 100644 tests/LayoutTests.cs create mode 100644 tests/Tests.cs diff --git a/tests/BankTests.cs b/tests/BankTests.cs index f168121..10fcee3 100644 --- a/tests/BankTests.cs +++ b/tests/BankTests.cs @@ -2,7 +2,7 @@ namespace Types.Tests { - public unsafe class BankTests : TypeTests + public unsafe class BankTests : Tests { [Test] public void LoadCustomBank() diff --git a/tests/LayoutTests.cs b/tests/LayoutTests.cs deleted file mode 100644 index d49c978..0000000 --- a/tests/LayoutTests.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Numerics; - -namespace Types.Tests -{ - public unsafe class LayoutTests : TypeTests - { - [Test] - public void VerifyLayoutOfRegisteredTypes() - { - Type type = TypeRegistry.GetType(); - Assert.That(type.SystemType, Is.EqualTo(typeof(Stress))); - Assert.That(type.Name.ToString(), Is.EqualTo("Stress")); - Assert.That(type.size, Is.EqualTo((uint)sizeof(Stress))); - - ReadOnlySpan fields = type.Fields; - Assert.That(fields.Length, Is.EqualTo(5)); - Assert.That(fields[0].Size, Is.EqualTo(1)); - Assert.That(fields[0].Name.ToString(), Is.EqualTo("first")); - Assert.That(fields[1].Size, Is.EqualTo(2)); - Assert.That(fields[1].Name.ToString(), Is.EqualTo("second")); - Assert.That(fields[2].Size, Is.EqualTo(4)); - Assert.That(fields[2].Name.ToString(), Is.EqualTo("third")); - Assert.That(fields[3].Size, Is.EqualTo(4)); - Assert.That(fields[3].Name.ToString(), Is.EqualTo("fourth")); - Assert.That(fields[4].Size, Is.EqualTo((uint)sizeof(Cherry))); - Assert.That(fields[4].Name.ToString(), Is.EqualTo("cherry")); - } - - [Test] - public void PrimitiveTypesAreAvailable() - { - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - - Assert.That(TypeRegistry.IsRegistered(typeof(bool).FullName ?? typeof(bool).Name), Is.True); - Assert.That(TypeRegistry.IsRegistered(typeof(byte).FullName ?? typeof(byte).Name), Is.True); - Assert.That(TypeRegistry.IsRegistered(typeof(sbyte).FullName ?? typeof(sbyte).Name), Is.True); - Assert.That(TypeRegistry.IsRegistered(typeof(short).FullName ?? typeof(short).Name), Is.True); - - Assert.That(TypeRegistry.GetType().size, Is.EqualTo((uint)sizeof(Vector3))); - Assert.That(TypeRegistry.GetType().Fields.Length, Is.EqualTo(3)); - } - - [Test] - public void CheckLayouts() - { - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Type boolean = TypeRegistry.GetType(); - Type byteType = TypeRegistry.GetType(); - Assert.That(boolean.size, Is.EqualTo(1)); - Assert.That(byteType.size, Is.EqualTo(1)); - Assert.That(boolean.GetHashCode(), Is.EqualTo(TypeRegistry.GetType().GetHashCode())); - Assert.That(byteType.GetHashCode(), Is.EqualTo(TypeRegistry.GetType().GetHashCode())); - } - - [Test] - public void CheckIfLayoutIs() - { - Type layout = TypeRegistry.GetType(); - - Assert.That(layout.Is(), Is.True); - Assert.That(layout.Is(), Is.False); - } - - [Test] - public void CheckNamesOfTypes() - { - Assert.That(TypeRegistry.GetType().Name.ToString(), Is.EqualTo("Boolean")); - Assert.That(TypeRegistry.GetType().FullName.ToString(), Is.EqualTo("System.Boolean")); - Assert.That(TypeRegistry.GetType().Name.ToString(), Is.EqualTo("Cherry")); - Assert.That(TypeRegistry.GetType().FullName.ToString(), Is.EqualTo("Types.Tests.Cherry")); - } - - [Test] - public void GetFullNameOfType() - { - string c = TypeRegistry.GetFullName(); - Assert.That(c.ToString(), Is.EqualTo("System.Boolean")); - - string a = TypeRegistry.GetFullName>(); - Assert.That(a.ToString(), Is.EqualTo("Types.Tests.Dictionary")); - - string b = TypeRegistry.GetFullName>>(); - Assert.That(b.ToString(), Is.EqualTo("Types.Tests.Dictionary>")); - } - - [Test] - public void CreateObjectFromTypeLayout() - { - Type layout = TypeRegistry.GetType(); - object instance = layout.CreateInstance(); - Assert.That(instance, Is.InstanceOf()); - Assert.That((Stress)instance, Is.EqualTo(default(Stress))); - } - - [Test] - public void GetOrRegister() - { - Assert.That(TypeRegistry.IsTypeRegistered(), Is.False); - Type type = TypeRegistry.GetOrRegisterType(); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(type.Is(), Is.True); - } - - [Test] - public void GetImplementedInterfaces() - { - Type type = TypeRegistry.GetType(); - ReadOnlySpan interfaces = type.Interfaces; - Assert.That(interfaces.Length, Is.EqualTo(1)); - Assert.That(interfaces[0].Name.ToString(), Is.EqualTo("IDisposable")); - } - } -} \ No newline at end of file diff --git a/tests/Tests.cs b/tests/Tests.cs new file mode 100644 index 0000000..b21ef53 --- /dev/null +++ b/tests/Tests.cs @@ -0,0 +1,32 @@ +using System; + +namespace Types.Tests +{ + public abstract class Tests + { + static Tests() + { + TypeRegistry.Load(); + } + + [SetUp] + protected virtual void SetUp() + { + } + + [TearDown] + protected virtual void TearDown() + { + } + + protected static bool IsRunningRemotely() + { + if (Environment.GetEnvironmentVariable("GITHUB_ACTIONS") is not null) + { + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/tests/TypeTests.cs b/tests/TypeTests.cs index cb0e462..21e9d3a 100644 --- a/tests/TypeTests.cs +++ b/tests/TypeTests.cs @@ -1,32 +1,128 @@ using System; +using System.Numerics; namespace Types.Tests { - public abstract class TypeTests + public unsafe class TypeTests : Tests { - static TypeTests() + [Test] + public void VerifyLayoutOfRegisteredTypes() { - TypeRegistry.Load(); + Type type = TypeRegistry.GetType(); + Assert.That(type.SystemType, Is.EqualTo(typeof(Stress))); + Assert.That(type.Name.ToString(), Is.EqualTo("Stress")); + Assert.That(type.size, Is.EqualTo((uint)sizeof(Stress))); + + ReadOnlySpan fields = type.Fields; + Assert.That(fields.Length, Is.EqualTo(5)); + Assert.That(fields[0].Size, Is.EqualTo(1)); + Assert.That(fields[0].Name.ToString(), Is.EqualTo("first")); + Assert.That(fields[1].Size, Is.EqualTo(2)); + Assert.That(fields[1].Name.ToString(), Is.EqualTo("second")); + Assert.That(fields[2].Size, Is.EqualTo(4)); + Assert.That(fields[2].Name.ToString(), Is.EqualTo("third")); + Assert.That(fields[3].Size, Is.EqualTo(4)); + Assert.That(fields[3].Name.ToString(), Is.EqualTo("fourth")); + Assert.That(fields[4].Size, Is.EqualTo((uint)sizeof(Cherry))); + Assert.That(fields[4].Name.ToString(), Is.EqualTo("cherry")); + } + + [Test] + public void PrimitiveTypesAreAvailable() + { + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + + Assert.That(TypeRegistry.IsRegistered(typeof(bool).FullName ?? typeof(bool).Name), Is.True); + Assert.That(TypeRegistry.IsRegistered(typeof(byte).FullName ?? typeof(byte).Name), Is.True); + Assert.That(TypeRegistry.IsRegistered(typeof(sbyte).FullName ?? typeof(sbyte).Name), Is.True); + Assert.That(TypeRegistry.IsRegistered(typeof(short).FullName ?? typeof(short).Name), Is.True); + + Assert.That(TypeRegistry.GetType().size, Is.EqualTo((uint)sizeof(Vector3))); + Assert.That(TypeRegistry.GetType().Fields.Length, Is.EqualTo(3)); + } + + [Test] + public void CheckLayouts() + { + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Type boolean = TypeRegistry.GetType(); + Type byteType = TypeRegistry.GetType(); + Assert.That(boolean.size, Is.EqualTo(1)); + Assert.That(byteType.size, Is.EqualTo(1)); + Assert.That(boolean.GetHashCode(), Is.EqualTo(TypeRegistry.GetType().GetHashCode())); + Assert.That(byteType.GetHashCode(), Is.EqualTo(TypeRegistry.GetType().GetHashCode())); + } + + [Test] + public void CheckIfLayoutIs() + { + Type layout = TypeRegistry.GetType(); + + Assert.That(layout.Is(), Is.True); + Assert.That(layout.Is(), Is.False); + } + + [Test] + public void CheckNamesOfTypes() + { + Assert.That(TypeRegistry.GetType().Name.ToString(), Is.EqualTo("Boolean")); + Assert.That(TypeRegistry.GetType().FullName.ToString(), Is.EqualTo("System.Boolean")); + Assert.That(TypeRegistry.GetType().Name.ToString(), Is.EqualTo("Cherry")); + Assert.That(TypeRegistry.GetType().FullName.ToString(), Is.EqualTo("Types.Tests.Cherry")); } - [SetUp] - protected virtual void SetUp() + [Test] + public void GetFullNameOfType() { + string c = TypeRegistry.GetFullName(); + Assert.That(c.ToString(), Is.EqualTo("System.Boolean")); + + string a = TypeRegistry.GetFullName>(); + Assert.That(a.ToString(), Is.EqualTo("Types.Tests.Dictionary")); + + string b = TypeRegistry.GetFullName>>(); + Assert.That(b.ToString(), Is.EqualTo("Types.Tests.Dictionary>")); } - [TearDown] - protected virtual void TearDown() + [Test] + public void CreateObjectFromTypeLayout() { + Type layout = TypeRegistry.GetType(); + object instance = layout.CreateInstance(); + Assert.That(instance, Is.InstanceOf()); + Assert.That((Stress)instance, Is.EqualTo(default(Stress))); } - protected static bool IsRunningRemotely() + [Test] + public void GetOrRegister() { - if (Environment.GetEnvironmentVariable("GITHUB_ACTIONS") is not null) - { - return true; - } + Assert.That(TypeRegistry.IsTypeRegistered(), Is.False); + Type type = TypeRegistry.GetOrRegisterType(); + Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(type.Is(), Is.True); + } - return false; + [Test] + public void GetImplementedInterfaces() + { + Type type = TypeRegistry.GetType(); + ReadOnlySpan interfaces = type.Interfaces; + Assert.That(interfaces.Length, Is.EqualTo(1)); + Assert.That(interfaces[0].Name.ToString(), Is.EqualTo("IDisposable")); + Assert.That(type.Implements(), Is.True); + Assert.That(type.Implements(), Is.False); } } } \ No newline at end of file From fd1939825b2c0aa1821fd91d083058f65d846263 Mon Sep 17 00:00:00 2001 From: Phill Date: Fri, 21 Mar 2025 13:09:45 -0400 Subject: [PATCH 07/12] method to check if a type implements an interface --- core/Interface.cs | 5 +++++ core/Type.cs | 35 +++++++++++++++++++++++++++++++++++ core/TypeRegistry.cs | 38 +++++++++++++++++++++++++++++--------- 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/core/Interface.cs b/core/Interface.cs index 423dbd4..c8b4235 100644 --- a/core/Interface.cs +++ b/core/Interface.cs @@ -39,6 +39,11 @@ public readonly ReadOnlySpan Name } } + /// + /// Retrieves the raw handle for this interface. + /// + public readonly RuntimeTypeHandle TypeHandle => TypeRegistry.GetRuntimeInterfaceHandle(hash); + /// /// Initializes an existing interface. /// diff --git a/core/Type.cs b/core/Type.cs index a17b035..d3f5409 100644 --- a/core/Type.cs +++ b/core/Type.cs @@ -193,6 +193,41 @@ public readonly bool Is() where T : unmanaged return hash == otherType.hash; } + /// + /// Checks if the type implements the given + /// . + /// + public readonly bool Implements() + { + RuntimeTypeHandle interfaceHandle = RuntimeTypeTable.GetHandle(); + for (int i = 0; i < interfaceCount; i++) + { + if (interfaces[i].TypeHandle.Equals(interfaceHandle)) + { + return true; + } + } + + return false; + } + + /// + /// Checks if the type implements the given . + /// + public readonly bool Implements(Interface interfaceValue) + { + long hash = interfaceValue.Hash; + for (int i = 0; i < interfaceCount; i++) + { + if (interfaces[i].Hash == hash) + { + return true; + } + } + + return false; + } + /// /// Creates an instance of this type. /// diff --git a/core/TypeRegistry.cs b/core/TypeRegistry.cs index fe4abd9..7e7a2db 100644 --- a/core/TypeRegistry.cs +++ b/core/TypeRegistry.cs @@ -17,8 +17,8 @@ public static class TypeRegistry internal static readonly Dictionary handleToInterface = new(); private static readonly Dictionary typeToHandle = new(); private static readonly Dictionary interfaceToHandle = new(); - private static readonly Dictionary hashToValueType = new(); - private static readonly Dictionary hashToInterfaceType = new(); + private static readonly Dictionary hashToType = new(); + private static readonly Dictionary hashToInterface = new(); /// /// All registered types. @@ -106,7 +106,7 @@ public static void RegisterType(Type type, RuntimeTypeHandle handle) types.Add(type); handleToType.Add(handle, type); typeToHandle.Add(type.Hash, handle); - hashToValueType.Add(type.Hash, type); + hashToType.Add(type.Hash, type); } /// @@ -119,7 +119,7 @@ public static void RegisterInterface(Interface interfaceValue, RuntimeTypeHandle interfaces.Add(interfaceValue); handleToInterface.Add(handle, interfaceValue); interfaceToHandle.Add(interfaceValue.Hash, handle); - hashToInterfaceType.Add(interfaceValue.Hash, interfaceValue); + hashToInterface.Add(interfaceValue.Hash, interfaceValue); } /// @@ -135,7 +135,7 @@ public static bool TryRegisterType(Type type, RuntimeTypeHandle handle) types.Add(type); handleToType.Add(handle, type); typeToHandle.Add(type.Hash, handle); - hashToValueType.Add(type.Hash, type); + hashToType.Add(type.Hash, type); return true; } @@ -205,7 +205,7 @@ public static Type GetType(long typeHash) { ThrowIfTypeNotRegistered(typeHash); - return hashToValueType[typeHash]; + return hashToType[typeHash]; } /// @@ -215,7 +215,7 @@ public static Interface GetInterface(long typeHash) { ThrowIfTypeNotRegistered(typeHash); - return hashToInterfaceType[typeHash]; + return hashToInterface[typeHash]; } /// @@ -223,7 +223,7 @@ public static Interface GetInterface(long typeHash) /// public static bool TryGetType(long typeHash, out Type type) { - return hashToValueType.TryGetValue(typeHash, out type); + return hashToType.TryGetValue(typeHash, out type); } /// @@ -246,6 +246,16 @@ public static RuntimeTypeHandle GetRuntimeTypeHandle(long typeHash) return typeToHandle[typeHash]; } + /// + /// Retrieves the raw handle for the . + /// + public static RuntimeTypeHandle GetRuntimeInterfaceHandle(long typeHash) + { + ThrowIfInterfaceNotRegistered(typeHash); + + return interfaceToHandle[typeHash]; + } + /// /// Checks if type is registered. /// @@ -408,12 +418,22 @@ private static void ThrowIfTypeNotRegistered() where T : unmanaged [Conditional("DEBUG")] private static void ThrowIfTypeNotRegistered(long hash) { - if (!hashToValueType.ContainsKey(hash)) + if (!hashToType.ContainsKey(hash)) { throw new InvalidOperationException($"Type with hash `{hash}` is not registered"); } } + + [Conditional("DEBUG")] + private static void ThrowIfInterfaceNotRegistered(long hash) + { + if (!hashToInterface.ContainsKey(hash)) + { + throw new InvalidOperationException($"Interface with hash `{hash}` is not registered"); + } + } + [Conditional("DEBUG")] private static void ThrowIfNotRegistered(RuntimeTypeHandle handle) { From 2bc0fd425e964a4f0a0573aa9962c5d2df0638b4 Mon Sep 17 00:00:00 2001 From: Phill Date: Fri, 21 Mar 2025 13:29:53 -0400 Subject: [PATCH 08/12] add methods to check if an interface is another one --- core/Extensions/TextExtensions.cs | 15 +++++++++++++++ core/Interface.cs | 26 ++++++++++++++++++++++++++ core/InterfaceTypeBuffer.cs | 8 ++++++++ core/Type.cs | 10 ++++++---- core/TypeRegistry.cs | 4 ++-- tests/BankTests.cs | 14 +++++++++++++- tests/TypeTests.cs | 2 ++ 7 files changed, 72 insertions(+), 7 deletions(-) diff --git a/core/Extensions/TextExtensions.cs b/core/Extensions/TextExtensions.cs index c5c9a47..816da22 100644 --- a/core/Extensions/TextExtensions.cs +++ b/core/Extensions/TextExtensions.cs @@ -19,6 +19,21 @@ public static long GetLongHashCode(this string text) } } + public static long GetLongHashCode(this Span text) + { + unchecked + { + long hash = 3074457345618258791; + for (int i = 0; i < text.Length; i++) + { + hash += text[i]; + hash *= 3074457345618258799; + } + + return hash; + } + } + public static long GetLongHashCode(this ReadOnlySpan text) { unchecked diff --git a/core/Interface.cs b/core/Interface.cs index c8b4235..de2f0ec 100644 --- a/core/Interface.cs +++ b/core/Interface.cs @@ -60,6 +60,32 @@ public Interface(ReadOnlySpan fullTypeName) hash = TypeNames.Set(fullTypeName); } + /// + public readonly override string ToString() + { + return FullName.ToString(); + } + + /// + /// Writes a string representation of this interface to . + /// + public readonly int ToString(Span destination) + { + ReadOnlySpan fullName = FullName; + fullName.CopyTo(destination); + return fullName.Length; + } + + /// + /// Checks if this interface is . + /// + public readonly bool Is() + { + Span buffer = stackalloc char[512]; + int length = TypeRegistry.GetFullName(typeof(T), buffer); + return hash == buffer.Slice(0, length).GetLongHashCode(); + } + /// public readonly override bool Equals(object? obj) { diff --git a/core/InterfaceTypeBuffer.cs b/core/InterfaceTypeBuffer.cs index 0512862..d43f1e4 100644 --- a/core/InterfaceTypeBuffer.cs +++ b/core/InterfaceTypeBuffer.cs @@ -44,6 +44,14 @@ public InterfaceTypeBuffer(ReadOnlySpan types) } } + /// + /// Retrieves the raw interface hash at the given . + /// + public readonly long Get(int index) + { + return buffer[index]; + } + [Conditional("DEBUG")] private static void ThrowIfCantFit(int length) { diff --git a/core/Type.cs b/core/Type.cs index d3f5409..3f05103 100644 --- a/core/Type.cs +++ b/core/Type.cs @@ -171,7 +171,7 @@ public Type(string fullName, ushort size, ReadOnlySpan fields, ReadOnlySp /// public readonly override string ToString() { - return TypeNames.Get(hash).ToString(); + return FullName.ToString(); } /// @@ -179,7 +179,7 @@ public readonly override string ToString() /// public readonly int ToString(Span destination) { - ReadOnlySpan fullName = TypeNames.Get(hash); + ReadOnlySpan fullName = FullName; fullName.CopyTo(destination); return fullName.Length; } @@ -199,10 +199,12 @@ public readonly bool Is() where T : unmanaged /// public readonly bool Implements() { - RuntimeTypeHandle interfaceHandle = RuntimeTypeTable.GetHandle(); + Span buffer = stackalloc char[512]; + int length = TypeRegistry.GetFullName(typeof(T), buffer); + long hash = buffer.Slice(0, length).GetLongHashCode(); for (int i = 0; i < interfaceCount; i++) { - if (interfaces[i].TypeHandle.Equals(interfaceHandle)) + if (interfaces.Get(i) == hash) { return true; } diff --git a/core/TypeRegistry.cs b/core/TypeRegistry.cs index 7e7a2db..d7264fe 100644 --- a/core/TypeRegistry.cs +++ b/core/TypeRegistry.cs @@ -153,7 +153,7 @@ public unsafe static void RegisterType() where T : unmanaged /// /// Manually registers the interface of type . /// - public static void RegisterInterface() where T : unmanaged + public static void RegisterInterface() { Interface interfaceValue = new(GetFullName()); RegisterInterface(interfaceValue, RuntimeTypeTable.GetHandle()); @@ -267,7 +267,7 @@ public static bool IsTypeRegistered() where T : unmanaged /// /// Checks if type is registered. /// - public static bool IsInterfaceRegistered() where T : unmanaged + public static bool IsInterfaceRegistered() { return handleToInterface.ContainsKey(RuntimeTypeTable.GetHandle()); } diff --git a/tests/BankTests.cs b/tests/BankTests.cs index 10fcee3..19edfcc 100644 --- a/tests/BankTests.cs +++ b/tests/BankTests.cs @@ -1,4 +1,5 @@ -using Types.Functions; +using System; +using Types.Functions; namespace Types.Tests { @@ -12,6 +13,17 @@ public void LoadCustomBank() Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); } +#if DEBUG + [Test] + public void ThrowWhenRegisteringTwice() + { + Assert.That(TypeRegistry.IsInterfaceRegistered(), Is.False); + TypeRegistry.RegisterInterface(); + Assert.That(TypeRegistry.IsInterfaceRegistered(), Is.True); + Assert.Throws(() => TypeRegistry.RegisterInterface()); + } +#endif + public readonly struct CustomTypeBank : ITypeBank { void ITypeBank.Load(RegisterFunction register) diff --git a/tests/TypeTests.cs b/tests/TypeTests.cs index 21e9d3a..3193850 100644 --- a/tests/TypeTests.cs +++ b/tests/TypeTests.cs @@ -123,6 +123,8 @@ public void GetImplementedInterfaces() Assert.That(interfaces[0].Name.ToString(), Is.EqualTo("IDisposable")); Assert.That(type.Implements(), Is.True); Assert.That(type.Implements(), Is.False); + Assert.That(interfaces[0].Is(), Is.True); + Assert.That(interfaces[0].Is(), Is.False); } } } \ No newline at end of file From 34d7c3e1aadab86220650a00c74b901196c575f9 Mon Sep 17 00:00:00 2001 From: Phill Date: Fri, 21 Mar 2025 13:48:55 -0400 Subject: [PATCH 09/12] dont register types twice --- generator/Generators/TypeBankGenerator.cs | 84 ++++++++++++++--------- 1 file changed, 51 insertions(+), 33 deletions(-) diff --git a/generator/Generators/TypeBankGenerator.cs b/generator/Generators/TypeBankGenerator.cs index b88a29a..63fb8c1 100644 --- a/generator/Generators/TypeBankGenerator.cs +++ b/generator/Generators/TypeBankGenerator.cs @@ -174,10 +174,19 @@ public static string Generate(IReadOnlyList types, out string typeN private static void AppendRegisterInterface(SourceBuilder source, ITypeSymbol interfaceType) { string fullName = interfaceType.GetFullTypeName(); - source.Append("register.RegisterInterface<"); + source.Append("if (!TypeRegistry.IsInterfaceRegistered<"); source.Append(fullName); - source.Append(">();"); + source.Append(">())"); source.AppendLine(); + + source.BeginGroup(); + { + source.Append("register.RegisterInterface<"); + source.Append(fullName); + source.Append(">();"); + source.AppendLine(); + } + source.EndGroup(); } private static void AppendRegisterType(SourceBuilder source, ITypeSymbol type) @@ -188,46 +197,55 @@ private static void AppendRegisterType(SourceBuilder source, ITypeSymbol type) return; } - byte variableCount = 0; - byte interfaceCount = 0; - HashSet fieldNames = new(); - foreach (IFieldSymbol field in type.GetFields()) + source.Append("if (!TypeRegistry.IsTypeRegistered<"); + source.Append(fullName); + source.Append(">())"); + source.AppendLine(); + + source.BeginGroup(); { - if (fieldNames.Add(field.Name)) + byte variableCount = 0; + byte interfaceCount = 0; + HashSet fieldNames = new(); + foreach (IFieldSymbol field in type.GetFields()) { - AppendVariable(source, field, ref variableCount); + if (fieldNames.Add(field.Name)) + { + AppendVariable(source, field, ref variableCount); + } } - } - foreach (INamedTypeSymbol interfaceType in type.AllInterfaces) - { - if (interfaceType.IsGenericType) + foreach (INamedTypeSymbol interfaceType in type.AllInterfaces) { - continue; + if (interfaceType.IsGenericType) + { + continue; + } + + AppendInterface(source, interfaceType, ref interfaceCount); } - AppendInterface(source, interfaceType, ref interfaceCount); - } + source.Append("register.RegisterType<"); + source.Append(fullName); + source.Append(">("); + if (variableCount > 0 || interfaceCount > 0) + { + source.Append(FieldBufferVariableName); + source.Append(','); + source.Append(' '); + source.Append(variableCount); + source.Append(','); + source.Append(' '); + source.Append(InterfaceBufferVariableName); + source.Append(','); + source.Append(' '); + source.Append(interfaceCount); + } - source.Append("register.RegisterType<"); - source.Append(fullName); - source.Append(">("); - if (variableCount > 0 || interfaceCount > 0) - { - source.Append(FieldBufferVariableName); - source.Append(','); - source.Append(' '); - source.Append(variableCount); - source.Append(','); - source.Append(' '); - source.Append(InterfaceBufferVariableName); - source.Append(','); - source.Append(' '); - source.Append(interfaceCount); + source.Append(");"); + source.AppendLine(); } - - source.Append(");"); - source.AppendLine(); + source.EndGroup(); static void AppendVariable(SourceBuilder source, IFieldSymbol field, ref byte count) { From a5416ef0cf963bba7f5aeb6816574ca6be952480 Mon Sep 17 00:00:00 2001 From: Phill Date: Sat, 22 Mar 2025 15:03:38 -0400 Subject: [PATCH 10/12] rename registry type --- core/Field.cs | 2 +- core/Functions/RegisterFunction.cs | 16 ++-- core/ITypeBank.cs | 2 +- core/Interface.cs | 4 +- core/InterfaceTypeBuffer.cs | 2 +- core/{TypeRegistry.cs => MetadataRegistry.cs} | 13 ++-- core/Type.cs | 6 +- generator/Constants.cs | 10 +++ generator/Generators/TypeBankGenerator.cs | 17 +++-- .../Generators/TypeRegistryLoaderGenerator.cs | 3 +- tests/BankTests.cs | 14 ++-- tests/Tests.cs | 2 +- tests/TypeTests.cs | 76 +++++++++---------- 13 files changed, 91 insertions(+), 76 deletions(-) rename core/{TypeRegistry.cs => MetadataRegistry.cs} (98%) create mode 100644 generator/Constants.cs diff --git a/core/Field.cs b/core/Field.cs index dc3e612..6790e41 100644 --- a/core/Field.cs +++ b/core/Field.cs @@ -13,7 +13,7 @@ namespace Types /// /// The type of the field. /// - public readonly Type Type => TypeRegistry.GetType(typeHash); + public readonly Type Type => MetadataRegistry.GetType(typeHash); /// /// Name of the field. diff --git a/core/Functions/RegisterFunction.cs b/core/Functions/RegisterFunction.cs index d0ba6a8..afed186 100644 --- a/core/Functions/RegisterFunction.cs +++ b/core/Functions/RegisterFunction.cs @@ -12,8 +12,8 @@ public readonly struct RegisterFunction /// public unsafe readonly void RegisterType(ReadOnlySpan variables, ReadOnlySpan interfaces) where T : unmanaged { - Type type = new(TypeRegistry.GetFullName(), (ushort)sizeof(T), variables, interfaces); - TypeRegistry.RegisterType(type, RuntimeTypeTable.GetHandle()); + Type type = new(MetadataRegistry.GetFullName(), (ushort)sizeof(T), variables, interfaces); + MetadataRegistry.RegisterType(type, RuntimeTypeTable.GetHandle()); TypeInstanceCreator.Initialize(type); } @@ -22,8 +22,8 @@ public unsafe readonly void RegisterType(ReadOnlySpan variables, ReadO /// public unsafe readonly void RegisterType(FieldBuffer variables, byte variableCount, InterfaceTypeBuffer interfaces, byte interfaceCount) where T : unmanaged { - Type type = new(TypeRegistry.GetFullName(), (ushort)sizeof(T), variables, variableCount, interfaces, interfaceCount); - TypeRegistry.RegisterType(type, RuntimeTypeTable.GetHandle()); + Type type = new(MetadataRegistry.GetFullName(), (ushort)sizeof(T), variables, variableCount, interfaces, interfaceCount); + MetadataRegistry.RegisterType(type, RuntimeTypeTable.GetHandle()); TypeInstanceCreator.Initialize(type); } @@ -32,8 +32,8 @@ public unsafe readonly void RegisterType(FieldBuffer variables, byte variable /// public unsafe readonly void RegisterType() where T : unmanaged { - Type type = new(TypeRegistry.GetFullName(), (ushort)sizeof(T)); - TypeRegistry.RegisterType(type, RuntimeTypeTable.GetHandle()); + Type type = new(MetadataRegistry.GetFullName(), (ushort)sizeof(T)); + MetadataRegistry.RegisterType(type, RuntimeTypeTable.GetHandle()); TypeInstanceCreator.Initialize(type); } @@ -42,8 +42,8 @@ public unsafe readonly void RegisterType() where T : unmanaged /// public readonly void RegisterInterface() { - Interface interfaceValue = new(TypeRegistry.GetFullName()); - TypeRegistry.RegisterInterface(interfaceValue, RuntimeTypeTable.GetHandle()); + Interface interfaceValue = new(MetadataRegistry.GetFullName()); + MetadataRegistry.RegisterInterface(interfaceValue, RuntimeTypeTable.GetHandle()); } /// diff --git a/core/ITypeBank.cs b/core/ITypeBank.cs index 8814b7e..7b086c2 100644 --- a/core/ITypeBank.cs +++ b/core/ITypeBank.cs @@ -8,7 +8,7 @@ namespace Types public interface ITypeBank { /// - /// Loads type metadata into . + /// Loads type metadata into . /// void Load(RegisterFunction register); } diff --git a/core/Interface.cs b/core/Interface.cs index de2f0ec..467b2c2 100644 --- a/core/Interface.cs +++ b/core/Interface.cs @@ -42,7 +42,7 @@ public readonly ReadOnlySpan Name /// /// Retrieves the raw handle for this interface. /// - public readonly RuntimeTypeHandle TypeHandle => TypeRegistry.GetRuntimeInterfaceHandle(hash); + public readonly RuntimeTypeHandle TypeHandle => MetadataRegistry.GetRuntimeInterfaceHandle(hash); /// /// Initializes an existing interface. @@ -82,7 +82,7 @@ public readonly int ToString(Span destination) public readonly bool Is() { Span buffer = stackalloc char[512]; - int length = TypeRegistry.GetFullName(typeof(T), buffer); + int length = MetadataRegistry.GetFullName(typeof(T), buffer); return hash == buffer.Slice(0, length).GetLongHashCode(); } diff --git a/core/InterfaceTypeBuffer.cs b/core/InterfaceTypeBuffer.cs index d43f1e4..418d278 100644 --- a/core/InterfaceTypeBuffer.cs +++ b/core/InterfaceTypeBuffer.cs @@ -23,7 +23,7 @@ public Interface this[int index] readonly get { long hash = buffer[index]; - return TypeRegistry.GetInterface(hash); + return MetadataRegistry.GetInterface(hash); } set { diff --git a/core/TypeRegistry.cs b/core/MetadataRegistry.cs similarity index 98% rename from core/TypeRegistry.cs rename to core/MetadataRegistry.cs index d7264fe..2732ee1 100644 --- a/core/TypeRegistry.cs +++ b/core/MetadataRegistry.cs @@ -2,14 +2,13 @@ using System.Collections.Generic; using System.Diagnostics; using System.Numerics; -using System.Runtime.CompilerServices; namespace Types { /// /// Stores metadata about types and interfaces. /// - public static class TypeRegistry + public static class MetadataRegistry { private static readonly List types = new(); private static readonly List interfaces = new(); @@ -25,8 +24,12 @@ public static class TypeRegistry /// public static IReadOnlyCollection Types => types; - [SkipLocalsInit] - static unsafe TypeRegistry() + /// + /// All registered interfaces. + /// + public static IReadOnlyCollection Interfaces => interfaces; + + static unsafe MetadataRegistry() { RegisterType(new(GetFullName(), sizeof(byte)), RuntimeTypeTable.GetHandle()); RegisterType(new(GetFullName(), sizeof(sbyte)), RuntimeTypeTable.GetHandle()); @@ -90,7 +93,7 @@ static unsafe TypeRegistry() /// /// Loads all s from the bank of type . /// - public unsafe static void Load() where T : unmanaged, ITypeBank + public static void Load() where T : unmanaged, ITypeBank { T bank = default; bank.Load(new()); diff --git a/core/Type.cs b/core/Type.cs index 3f05103..c1183bf 100644 --- a/core/Type.cs +++ b/core/Type.cs @@ -69,7 +69,7 @@ public readonly System.Type SystemType /// /// Retrieves the raw handle for this type. /// - public readonly RuntimeTypeHandle TypeHandle => TypeRegistry.GetRuntimeTypeHandle(hash); + public readonly RuntimeTypeHandle TypeHandle => MetadataRegistry.GetRuntimeTypeHandle(hash); /// /// Full name of the type including the namespace. @@ -189,7 +189,7 @@ public readonly int ToString(Span destination) /// public readonly bool Is() where T : unmanaged { - TypeRegistry.handleToType.TryGetValue(RuntimeTypeTable.GetHandle(), out Type otherType); + MetadataRegistry.handleToType.TryGetValue(RuntimeTypeTable.GetHandle(), out Type otherType); return hash == otherType.hash; } @@ -200,7 +200,7 @@ public readonly bool Is() where T : unmanaged public readonly bool Implements() { Span buffer = stackalloc char[512]; - int length = TypeRegistry.GetFullName(typeof(T), buffer); + int length = MetadataRegistry.GetFullName(typeof(T), buffer); long hash = buffer.Slice(0, length).GetLongHashCode(); for (int i = 0; i < interfaceCount; i++) { diff --git a/generator/Constants.cs b/generator/Constants.cs new file mode 100644 index 0000000..f7a23a0 --- /dev/null +++ b/generator/Constants.cs @@ -0,0 +1,10 @@ +namespace Types.Generator +{ + public static class Constants + { + public const string RegistryTypeName = "MetadataRegistry"; + public const string FieldBufferTypeName = "FieldBuffer"; + public const string InterfaceBufferTypeName = "InterfaceTypeBuffer"; + public const string RegisterFunctionTypeName = "RegisterFunction"; + } +} \ No newline at end of file diff --git a/generator/Generators/TypeBankGenerator.cs b/generator/Generators/TypeBankGenerator.cs index 63fb8c1..dbe6963 100644 --- a/generator/Generators/TypeBankGenerator.cs +++ b/generator/Generators/TypeBankGenerator.cs @@ -11,11 +11,8 @@ namespace Types.Generator public class TypeBankGenerator : IIncrementalGenerator { public const string TypeNameFormat = "{0}TypeBank"; - public const string FieldBufferTypeName = "FieldBuffer"; - public const string InterfaceBufferTypeName = "InterfaceTypeBuffer"; public const string FieldBufferVariableName = "fields"; public const string InterfaceBufferVariableName = "interfaces"; - public const string RegisterFunctionTypeName = "RegisterFunction"; void IIncrementalGenerator.Initialize(IncrementalGeneratorInitializationContext context) { @@ -116,19 +113,19 @@ public static string Generate(IReadOnlyList types, out string typeN source.BeginGroup(); { source.Append("readonly void ITypeBank.Load("); - source.Append(RegisterFunctionTypeName); + source.Append(Constants.RegisterFunctionTypeName); source.Append(" register)"); source.AppendLine(); source.BeginGroup(); { - source.Append(FieldBufferTypeName); + source.Append(Constants.FieldBufferTypeName); source.Append(' '); source.Append(FieldBufferVariableName); source.Append(" = new();"); source.AppendLine(); - source.Append(InterfaceBufferTypeName); + source.Append(Constants.InterfaceBufferTypeName); source.Append(' '); source.Append(InterfaceBufferVariableName); source.Append(" = new();"); @@ -174,7 +171,9 @@ public static string Generate(IReadOnlyList types, out string typeN private static void AppendRegisterInterface(SourceBuilder source, ITypeSymbol interfaceType) { string fullName = interfaceType.GetFullTypeName(); - source.Append("if (!TypeRegistry.IsInterfaceRegistered<"); + source.Append("if (!"); + source.Append(Constants.RegistryTypeName); + source.Append(".IsInterfaceRegistered<"); source.Append(fullName); source.Append(">())"); source.AppendLine(); @@ -197,7 +196,9 @@ private static void AppendRegisterType(SourceBuilder source, ITypeSymbol type) return; } - source.Append("if (!TypeRegistry.IsTypeRegistered<"); + source.Append("if (!"); + source.Append(Constants.RegistryTypeName); + source.Append(".IsTypeRegistered<"); source.Append(fullName); source.Append(">())"); source.AppendLine(); diff --git a/generator/Generators/TypeRegistryLoaderGenerator.cs b/generator/Generators/TypeRegistryLoaderGenerator.cs index f60e55b..92720fa 100644 --- a/generator/Generators/TypeRegistryLoaderGenerator.cs +++ b/generator/Generators/TypeRegistryLoaderGenerator.cs @@ -70,7 +70,8 @@ private static string Generate(Compilation compilation) if (type.HasInterface("Types.ITypeBank")) { - builder.Append("TypeRegistry.Load<"); + builder.Append(Constants.RegistryTypeName); + builder.Append(".Load<"); builder.Append(type.GetFullTypeName()); builder.Append(">();"); builder.AppendLine(); diff --git a/tests/BankTests.cs b/tests/BankTests.cs index 19edfcc..057d051 100644 --- a/tests/BankTests.cs +++ b/tests/BankTests.cs @@ -8,19 +8,19 @@ public unsafe class BankTests : Tests [Test] public void LoadCustomBank() { - Assert.That(TypeRegistry.IsTypeRegistered(), Is.False); - TypeRegistry.Load(); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(MetadataRegistry.IsTypeRegistered(), Is.False); + MetadataRegistry.Load(); + Assert.That(MetadataRegistry.IsTypeRegistered(), Is.True); } #if DEBUG [Test] public void ThrowWhenRegisteringTwice() { - Assert.That(TypeRegistry.IsInterfaceRegistered(), Is.False); - TypeRegistry.RegisterInterface(); - Assert.That(TypeRegistry.IsInterfaceRegistered(), Is.True); - Assert.Throws(() => TypeRegistry.RegisterInterface()); + Assert.That(MetadataRegistry.IsInterfaceRegistered(), Is.False); + MetadataRegistry.RegisterInterface(); + Assert.That(MetadataRegistry.IsInterfaceRegistered(), Is.True); + Assert.Throws(() => MetadataRegistry.RegisterInterface()); } #endif diff --git a/tests/Tests.cs b/tests/Tests.cs index b21ef53..3b88988 100644 --- a/tests/Tests.cs +++ b/tests/Tests.cs @@ -6,7 +6,7 @@ public abstract class Tests { static Tests() { - TypeRegistry.Load(); + MetadataRegistry.Load(); } [SetUp] diff --git a/tests/TypeTests.cs b/tests/TypeTests.cs index 3193850..cf256de 100644 --- a/tests/TypeTests.cs +++ b/tests/TypeTests.cs @@ -8,7 +8,7 @@ public unsafe class TypeTests : Tests [Test] public void VerifyLayoutOfRegisteredTypes() { - Type type = TypeRegistry.GetType(); + Type type = MetadataRegistry.GetType(); Assert.That(type.SystemType, Is.EqualTo(typeof(Stress))); Assert.That(type.Name.ToString(), Is.EqualTo("Stress")); Assert.That(type.size, Is.EqualTo((uint)sizeof(Stress))); @@ -30,45 +30,45 @@ public void VerifyLayoutOfRegisteredTypes() [Test] public void PrimitiveTypesAreAvailable() { - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(MetadataRegistry.IsTypeRegistered(), Is.True); + Assert.That(MetadataRegistry.IsTypeRegistered(), Is.True); + Assert.That(MetadataRegistry.IsTypeRegistered(), Is.True); + Assert.That(MetadataRegistry.IsTypeRegistered(), Is.True); + Assert.That(MetadataRegistry.IsTypeRegistered(), Is.True); + Assert.That(MetadataRegistry.IsTypeRegistered(), Is.True); + Assert.That(MetadataRegistry.IsTypeRegistered(), Is.True); + Assert.That(MetadataRegistry.IsTypeRegistered(), Is.True); + Assert.That(MetadataRegistry.IsTypeRegistered(), Is.True); + Assert.That(MetadataRegistry.IsTypeRegistered(), Is.True); + Assert.That(MetadataRegistry.IsTypeRegistered(), Is.True); + Assert.That(MetadataRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsRegistered(typeof(bool).FullName ?? typeof(bool).Name), Is.True); - Assert.That(TypeRegistry.IsRegistered(typeof(byte).FullName ?? typeof(byte).Name), Is.True); - Assert.That(TypeRegistry.IsRegistered(typeof(sbyte).FullName ?? typeof(sbyte).Name), Is.True); - Assert.That(TypeRegistry.IsRegistered(typeof(short).FullName ?? typeof(short).Name), Is.True); + Assert.That(MetadataRegistry.IsRegistered(typeof(bool).FullName ?? typeof(bool).Name), Is.True); + Assert.That(MetadataRegistry.IsRegistered(typeof(byte).FullName ?? typeof(byte).Name), Is.True); + Assert.That(MetadataRegistry.IsRegistered(typeof(sbyte).FullName ?? typeof(sbyte).Name), Is.True); + Assert.That(MetadataRegistry.IsRegistered(typeof(short).FullName ?? typeof(short).Name), Is.True); - Assert.That(TypeRegistry.GetType().size, Is.EqualTo((uint)sizeof(Vector3))); - Assert.That(TypeRegistry.GetType().Fields.Length, Is.EqualTo(3)); + Assert.That(MetadataRegistry.GetType().size, Is.EqualTo((uint)sizeof(Vector3))); + Assert.That(MetadataRegistry.GetType().Fields.Length, Is.EqualTo(3)); } [Test] public void CheckLayouts() { - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); - Type boolean = TypeRegistry.GetType(); - Type byteType = TypeRegistry.GetType(); + Assert.That(MetadataRegistry.IsTypeRegistered(), Is.True); + Assert.That(MetadataRegistry.IsTypeRegistered(), Is.True); + Type boolean = MetadataRegistry.GetType(); + Type byteType = MetadataRegistry.GetType(); Assert.That(boolean.size, Is.EqualTo(1)); Assert.That(byteType.size, Is.EqualTo(1)); - Assert.That(boolean.GetHashCode(), Is.EqualTo(TypeRegistry.GetType().GetHashCode())); - Assert.That(byteType.GetHashCode(), Is.EqualTo(TypeRegistry.GetType().GetHashCode())); + Assert.That(boolean.GetHashCode(), Is.EqualTo(MetadataRegistry.GetType().GetHashCode())); + Assert.That(byteType.GetHashCode(), Is.EqualTo(MetadataRegistry.GetType().GetHashCode())); } [Test] public void CheckIfLayoutIs() { - Type layout = TypeRegistry.GetType(); + Type layout = MetadataRegistry.GetType(); Assert.That(layout.Is(), Is.True); Assert.That(layout.Is(), Is.False); @@ -77,29 +77,29 @@ public void CheckIfLayoutIs() [Test] public void CheckNamesOfTypes() { - Assert.That(TypeRegistry.GetType().Name.ToString(), Is.EqualTo("Boolean")); - Assert.That(TypeRegistry.GetType().FullName.ToString(), Is.EqualTo("System.Boolean")); - Assert.That(TypeRegistry.GetType().Name.ToString(), Is.EqualTo("Cherry")); - Assert.That(TypeRegistry.GetType().FullName.ToString(), Is.EqualTo("Types.Tests.Cherry")); + Assert.That(MetadataRegistry.GetType().Name.ToString(), Is.EqualTo("Boolean")); + Assert.That(MetadataRegistry.GetType().FullName.ToString(), Is.EqualTo("System.Boolean")); + Assert.That(MetadataRegistry.GetType().Name.ToString(), Is.EqualTo("Cherry")); + Assert.That(MetadataRegistry.GetType().FullName.ToString(), Is.EqualTo("Types.Tests.Cherry")); } [Test] public void GetFullNameOfType() { - string c = TypeRegistry.GetFullName(); + string c = MetadataRegistry.GetFullName(); Assert.That(c.ToString(), Is.EqualTo("System.Boolean")); - string a = TypeRegistry.GetFullName>(); + string a = MetadataRegistry.GetFullName>(); Assert.That(a.ToString(), Is.EqualTo("Types.Tests.Dictionary")); - string b = TypeRegistry.GetFullName>>(); + string b = MetadataRegistry.GetFullName>>(); Assert.That(b.ToString(), Is.EqualTo("Types.Tests.Dictionary>")); } [Test] public void CreateObjectFromTypeLayout() { - Type layout = TypeRegistry.GetType(); + Type layout = MetadataRegistry.GetType(); object instance = layout.CreateInstance(); Assert.That(instance, Is.InstanceOf()); Assert.That((Stress)instance, Is.EqualTo(default(Stress))); @@ -108,16 +108,16 @@ public void CreateObjectFromTypeLayout() [Test] public void GetOrRegister() { - Assert.That(TypeRegistry.IsTypeRegistered(), Is.False); - Type type = TypeRegistry.GetOrRegisterType(); - Assert.That(TypeRegistry.IsTypeRegistered(), Is.True); + Assert.That(MetadataRegistry.IsTypeRegistered(), Is.False); + Type type = MetadataRegistry.GetOrRegisterType(); + Assert.That(MetadataRegistry.IsTypeRegistered(), Is.True); Assert.That(type.Is(), Is.True); } [Test] public void GetImplementedInterfaces() { - Type type = TypeRegistry.GetType(); + Type type = MetadataRegistry.GetType(); ReadOnlySpan interfaces = type.Interfaces; Assert.That(interfaces.Length, Is.EqualTo(1)); Assert.That(interfaces[0].Name.ToString(), Is.EqualTo("IDisposable")); From 9fbf7a13cd00d9295800e5aac9fa5c98a74a626c Mon Sep 17 00:00:00 2001 From: Phill Date: Sat, 22 Mar 2025 15:04:41 -0400 Subject: [PATCH 11/12] not needed anymore --- core/MetadataRegistry.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/core/MetadataRegistry.cs b/core/MetadataRegistry.cs index 2732ee1..bef136c 100644 --- a/core/MetadataRegistry.cs +++ b/core/MetadataRegistry.cs @@ -147,7 +147,6 @@ public static bool TryRegisterType(Type type, RuntimeTypeHandle handle) /// public unsafe static void RegisterType() where T : unmanaged { - //todo: need to add a warning here when trying to register a type bank itself ushort size = (ushort)sizeof(T); Type type = new(GetFullName(), size); RegisterType(type, RuntimeTypeTable.GetHandle()); From 2905aaf1f5a127d4755c61d5d3629e6e4ca1d164 Mon Sep 17 00:00:00 2001 From: Phill Date: Sat, 22 Mar 2025 15:12:07 -0400 Subject: [PATCH 12/12] function for fetching all types that implement an interface --- core/Interface.cs | 6 +++++ core/MetadataRegistry.cs | 4 ++-- core/Type.cs | 48 +++++++++++++++++++++++++++++++++++++++- tests/TypeTests.cs | 14 ++++++++++++ 4 files changed, 69 insertions(+), 3 deletions(-) diff --git a/core/Interface.cs b/core/Interface.cs index 467b2c2..ec6acfc 100644 --- a/core/Interface.cs +++ b/core/Interface.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace Types { @@ -7,6 +8,11 @@ namespace Types /// public readonly struct Interface : IEquatable { + /// + /// All registered interfaces. + /// + public static IReadOnlyList All => MetadataRegistry.Interfaces; + private readonly long hash; /// diff --git a/core/MetadataRegistry.cs b/core/MetadataRegistry.cs index bef136c..ab45997 100644 --- a/core/MetadataRegistry.cs +++ b/core/MetadataRegistry.cs @@ -22,12 +22,12 @@ public static class MetadataRegistry /// /// All registered types. /// - public static IReadOnlyCollection Types => types; + public static IReadOnlyList Types => types; /// /// All registered interfaces. /// - public static IReadOnlyCollection Interfaces => interfaces; + public static IReadOnlyList Interfaces => interfaces; static unsafe MetadataRegistry() { diff --git a/core/Type.cs b/core/Type.cs index c1183bf..d40c9c9 100644 --- a/core/Type.cs +++ b/core/Type.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; @@ -10,6 +11,11 @@ namespace Types [SkipLocalsInit] public readonly struct Type : IEquatable { + /// + /// All registered types. + /// + public static IReadOnlyList All => MetadataRegistry.Types; + /// /// Size of the type in bytes. /// @@ -221,7 +227,7 @@ public readonly bool Implements(Interface interfaceValue) long hash = interfaceValue.Hash; for (int i = 0; i < interfaceCount; i++) { - if (interfaces[i].Hash == hash) + if (interfaces.Get(i) == hash) { return true; } @@ -402,6 +408,46 @@ public readonly override int GetHashCode() } } + /// + /// Retrieves all types that implement the given interface. + /// + public static IEnumerable GetAllThatImplement() + { + Span buffer = stackalloc char[512]; + int length = MetadataRegistry.GetFullName(typeof(T), buffer); + long hash = buffer.Slice(0, length).GetLongHashCode(); + foreach (Type type in All) + { + for (int i = 0; i < type.interfaceCount; i++) + { + if (type.interfaces.Get(i) == hash) + { + yield return type; + break; + } + } + } + } + + /// + /// Retrieves all types that implement the given . + /// + public static IEnumerable GetAllThatImplement(Interface interfaceValue) + { + long hash = interfaceValue.Hash; + foreach (Type type in All) + { + for (int i = 0; i < type.interfaceCount; i++) + { + if (type.interfaces.Get(i) == hash) + { + yield return type; + break; + } + } + } + } + /// public static bool operator ==(Type left, Type right) { diff --git a/tests/TypeTests.cs b/tests/TypeTests.cs index cf256de..b2b48a7 100644 --- a/tests/TypeTests.cs +++ b/tests/TypeTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Numerics; namespace Types.Tests @@ -126,5 +127,18 @@ public void GetImplementedInterfaces() Assert.That(interfaces[0].Is(), Is.True); Assert.That(interfaces[0].Is(), Is.False); } + + [Test] + public void IterateThroughAllDisposableTypes() + { + List types = new(); + foreach (Type type in Type.GetAllThatImplement()) + { + types.Add(type); + } + + Assert.That(types.Count, Is.EqualTo(1)); + Assert.That(types[0].Is(), Is.True); + } } } \ No newline at end of file