TypeDef Id

This commit is contained in:
2026-07-25 17:07:06 +03:00
parent 3fc5991a84
commit 53f9819d40
3 changed files with 110 additions and 32 deletions
+43 -32
View File
@@ -89,48 +89,20 @@ namespace Esiur.Data
if (TypeDef is RemoteTypeDef remoteTypeDef) if (TypeDef is RemoteTypeDef remoteTypeDef)
{ {
if (connection.RemoteDomain == remoteTypeDef.Domain) if (connection.RemoteDomain == remoteTypeDef.Domain)
{
// this is local in respect to the connection, send the remote typdef id. // this is local in respect to the connection, send the remote typdef id.
if (Nullable) WriteTypeReference(rt, isLocal: true, Nullable, remoteTypeDef.Id);
rt.AddUInt8(0x80 | (byte)TruIdentifier.LocalType8);
else
rt.AddUInt8((byte)TruIdentifier.LocalType8);
rt.AddUInt8((byte)remoteTypeDef.Id);
}
else else
{
// this is remote in respect to the connection and the local typedef id is used. // this is remote in respect to the connection and the local typedef id is used.
if (Nullable) WriteTypeReference(rt, isLocal: false, Nullable, remoteTypeDef.LocalTypeDefId);
rt.AddUInt8(0x80 | (byte)TruIdentifier.RemoteType8);
else
rt.AddUInt8((byte)TruIdentifier.RemoteType8);
rt.AddUInt8((byte)remoteTypeDef.LocalTypeDefId);
}
} }
else if (TypeDef is LocalTypeDef localTypeDef) else if (TypeDef is LocalTypeDef localTypeDef)
{ {
if (connection == null) if (connection == null)
{
// if there is no connection, we assume it's local. // if there is no connection, we assume it's local.
if (Nullable) WriteTypeReference(rt, isLocal: true, Nullable, localTypeDef.Id);
rt.AddUInt8(0x80 | (byte)TruIdentifier.LocalType8);
else
rt.AddUInt8((byte)TruIdentifier.LocalType8);
rt.AddUInt8((byte)localTypeDef.Id);
}
else else
{
// this is remote, unless the connection is to self @TODO: solve for this state. // this is remote, unless the connection is to self @TODO: solve for this state.
if (Nullable) WriteTypeReference(rt, isLocal: false, Nullable, localTypeDef.Id);
rt.AddUInt8(0x80 | (byte)TruIdentifier.RemoteType8);
else
rt.AddUInt8((byte)TruIdentifier.RemoteType8);
rt.AddUInt8((byte)localTypeDef.Id);
}
} }
else else
throw new NotImplementedException(); throw new NotImplementedException();
@@ -138,6 +110,45 @@ namespace Esiur.Data
return rt.ToArray(); return rt.ToArray();
} }
// Picks the narrowest Local/RemoteType{8,16,32,64} identifier that fits `id`,
// matching the widths Tru.Parse already knows how to read.
// Internal (rather than private) so it can be unit-tested directly without
// needing a real LocalTypeDef/Warehouse registration for every width tier.
internal static void WriteTypeReference(BinaryList rt, bool isLocal, bool nullable, ulong id)
{
TruIdentifier identifier;
if (id <= byte.MaxValue)
identifier = isLocal ? TruIdentifier.LocalType8 : TruIdentifier.RemoteType8;
else if (id <= ushort.MaxValue)
identifier = isLocal ? TruIdentifier.LocalType16 : TruIdentifier.RemoteType16;
else if (id <= uint.MaxValue)
identifier = isLocal ? TruIdentifier.LocalType32 : TruIdentifier.RemoteType32;
else
identifier = isLocal ? TruIdentifier.LocalType64 : TruIdentifier.RemoteType64;
rt.AddUInt8((byte)((nullable ? 0x80 : 0) | (byte)identifier));
switch (identifier)
{
case TruIdentifier.LocalType8:
case TruIdentifier.RemoteType8:
rt.AddUInt8((byte)id);
break;
case TruIdentifier.LocalType16:
case TruIdentifier.RemoteType16:
rt.AddUInt16((ushort)id);
break;
case TruIdentifier.LocalType32:
case TruIdentifier.RemoteType32:
rt.AddUInt32((uint)id);
break;
default:
rt.AddUInt64(id);
break;
}
}
public override Tru ToNullable() public override Tru ToNullable()
{ {
throw new NotImplementedException(); throw new NotImplementedException();
@@ -64,6 +64,7 @@ public class TypeDefInfo : IndexedStructure
Version = definition.Version, Version = definition.Version,
Id = definition.Id, Id = definition.Id,
Name = definition.Name, Name = definition.Name,
Namespace = (definition as LocalTypeDef)?.DefinedType?.Namespace,
Kind = definition.Kind, Kind = definition.Kind,
Parent = definition.ParentTypeId, Parent = definition.ParentTypeId,
Usage = definition.Usage, Usage = definition.Usage,
@@ -127,4 +127,70 @@ public class TypeDefInfoSerializationTests
Assert.Equal(TruIdentifier.String, Assert.Single(parsed.Events!).ArgumentType.Identifier); Assert.Equal(TruIdentifier.String, Assert.Single(parsed.Events!).ArgumentType.Identifier);
Assert.Equal(100, Convert.ToInt32(Assert.Single(parsed.Constants!).Value)); Assert.Equal(100, Convert.ToInt32(Assert.Single(parsed.Constants!).Value));
} }
[Theory]
[InlineData(0ul, TruIdentifier.LocalType8, 1)]
[InlineData(255ul, TruIdentifier.LocalType8, 1)]
[InlineData(256ul, TruIdentifier.LocalType16, 2)]
[InlineData(65535ul, TruIdentifier.LocalType16, 2)]
[InlineData(65536ul, TruIdentifier.LocalType32, 4)]
[InlineData(4294967295ul, TruIdentifier.LocalType32, 4)]
[InlineData(4294967296ul, TruIdentifier.LocalType64, 8)]
public void WriteTypeReference_SelectsNarrowestSufficientWidth(ulong id, TruIdentifier expectedIdentifier, int expectedIdBytes)
{
var rt = new BinaryList();
TruTypeDef.WriteTypeReference(rt, isLocal: true, nullable: false, id);
var bytes = rt.ToArray();
Assert.Equal(1 + expectedIdBytes, bytes.Length);
Assert.Equal((byte)expectedIdentifier, bytes[0]);
ulong roundTripped = expectedIdBytes switch
{
1 => bytes[1],
2 => bytes.GetUInt16(1, Endian.Little),
4 => bytes.GetUInt32(1, Endian.Little),
_ => bytes.GetUInt64(1, Endian.Little),
};
Assert.Equal(id, roundTripped);
}
[Fact]
public void WriteTypeReference_NullableAndRemote_SetHeaderBitsCorrectly()
{
var rt = new BinaryList();
TruTypeDef.WriteTypeReference(rt, isLocal: false, nullable: true, 42ul);
var bytes = rt.ToArray();
Assert.Equal((byte)(0x80 | (byte)TruIdentifier.RemoteType8), bytes[0]);
Assert.Equal(42, bytes[1]);
}
[Fact]
public void TypeDefInfo_FromTypeDef_PopulatesNamespaceFromDefinedType()
{
var typeDef = new LocalTypeDef(typeof(TruTypeDefTestRecord), Warehouse.Default);
var info = TypeDefInfo.FromTypeDef(typeDef);
Assert.Equal(typeof(TruTypeDefTestRecord).Namespace, info.Namespace);
Assert.False(string.IsNullOrEmpty(info.Namespace));
}
[Fact]
public void TruTypeDef_Compose_LocalTypeDefWithoutConnection_RoundTrips()
{
var typeDef = new LocalTypeDef(typeof(TruTypeDefTestRecord), Warehouse.Default);
var tru = new TruTypeDef(false, typeDef);
var bytes = tru.Compose(null);
var result = Tru.Parse(bytes, 0, Warehouse.Default);
Assert.Equal((uint)bytes.Length, result.Size);
var parsed = Assert.IsType<TruTypeDef>(result.Value);
Assert.Equal(typeDef.Id, parsed.TypeDef!.Id);
}
}
public sealed class TruTypeDefTestRecord : IRecord
{
} }