2
0
mirror of https://github.com/esiur/esiur-dotnet.git synced 2026-06-13 14:38:43 +00:00
This commit is contained in:
2026-05-24 18:27:22 +03:00
parent a6e8bed31d
commit eb323e8bf8
69 changed files with 6532 additions and 3371 deletions
+164 -49
View File
@@ -100,12 +100,7 @@ public static class Codec
static AsyncParser[] TypedAsyncParsers = new AsyncParser[]
{
DataDeserializer.RecordParserAsync,
DataDeserializer.TypedListParserAsync,
DataDeserializer.TypedMapParserAsync,
DataDeserializer.TupleParserAsync,
DataDeserializer.EnumParserAsync,
DataDeserializer.ConstantParserAsync,
DataDeserializer.TypedParserAsync,
};
static AsyncParser[] ExtendedAsyncParsers = new AsyncParser[]
@@ -170,12 +165,13 @@ public static class Codec
static SyncParser[] TypedParsers = new SyncParser[]
{
DataDeserializer.RecordParser,
DataDeserializer.TypedListParser,
DataDeserializer.TypedMapParser,
DataDeserializer.TupleParser,
DataDeserializer.EnumParser,
DataDeserializer.ConstantParser,
DataDeserializer.TypedParser,
//DataDeserializer.RecordParser,
//DataDeserializer.TypedListParser,
//DataDeserializer.TypedMapParser,
//DataDeserializer.TupleParser,
//DataDeserializer.EnumParser,
//DataDeserializer.ConstantParser,
};
static SyncParser[] ExtendedParsers = new SyncParser[]
@@ -192,81 +188,197 @@ public static class Codec
/// <param name="connection">EpConnection is required in case a structure in the array holds items at the other end.</param>
/// <param name="dataType">DataType, in case the data is not prepended with DataType</param>
/// <returns>Value</returns>
public static (uint, object) ParseAsync(byte[] data, uint offset, EpConnection connection, uint[] requestSequence)
public static AsyncReply<ParseResult<object>> ParseAsync(byte[] data, uint offset, EpConnection connection, uint[] requestSequence)
{
var rt = new AsyncReply<ParseResult<object>>();
var tdu = ParsedTdu.Parse(data, offset, (uint)data.Length);
ParsedTdu.ParseAsync(data, offset, (uint)data.Length, connection).Then(tdu =>
{
if (tdu.Class == TduClass.Invalid)
throw new NullReferenceException("DataType can't be parsed.");
if (tdu.Class == TduClass.Invalid)
throw new NullReferenceException("DataType can't be parsed.");
object result;
if (tdu.Class == TduClass.Fixed)
{
return ((uint)tdu.TotalLength, FixedAsyncParsers[tdu.Exponent][tdu.Index](tdu, connection, requestSequence));
}
else if (tdu.Class == TduClass.Dynamic)
{
return ((uint)tdu.TotalLength, DynamicAsyncParsers[tdu.Index](tdu, connection, requestSequence));
}
else if (tdu.Class == TduClass.Typed)
{
return ((uint)tdu.TotalLength, TypedAsyncParsers[tdu.Index](tdu, connection, requestSequence));
}
else // if (tt.Class == TDUClass.Extension)
{
return ((uint)tdu.TotalLength, ExtendedAsyncParsers[tdu.Index](tdu, connection, requestSequence));
if (tdu.Class == TduClass.Fixed)
{
result = FixedAsyncParsers[tdu.Exponent][tdu.Index](tdu, connection, requestSequence);
}
else if (tdu.Class == TduClass.Dynamic)
{
result = DynamicAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
else if (tdu.Class == TduClass.Typed)
{
result = TypedAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
else // if (tt.Class == TDUClass.Extension)
{
result = ExtendedAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
if (result is AsyncReply asyncReply)
{
asyncReply.Then(value =>
{
rt.Trigger(new ParseResult<object>(value, (uint)tdu.TotalLength));
});
}
else
{
rt.Trigger(new ParseResult<object>(result, (uint)tdu.TotalLength));
}
});
return rt;
}
}
public static (uint, object) ParseAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence)
public static object ParseAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence)
{
if (tdu.Class == TduClass.Invalid)
throw new NullReferenceException("DataType can't be parsed.");
if (tdu.Class == TduClass.Fixed)
{
return ((uint)tdu.TotalLength, FixedAsyncParsers[tdu.Exponent][tdu.Index](tdu, connection, requestSequence));
return FixedAsyncParsers[tdu.Exponent][tdu.Index](tdu, connection, requestSequence);
}
else if (tdu.Class == TduClass.Dynamic)
{
return ((uint)tdu.TotalLength, DynamicAsyncParsers[tdu.Index](tdu, connection, requestSequence));
return DynamicAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
else if (tdu.Class == TduClass.Typed)
{
return ((uint)tdu.TotalLength, TypedAsyncParsers[tdu.Index](tdu, connection, requestSequence));
return TypedAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
else // if (tt.Class == TDUClass.Extension)
{
return ((uint)tdu.TotalLength, ExtendedAsyncParsers[tdu.Index](tdu, connection, requestSequence));
return ExtendedAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
}
public static (uint, object) ParseSync(ParsedTdu tdu, Warehouse warehouse)
public static object Parse(PlainTdu plainTdu, EpConnection connection, uint[] requestSequence)
{
if (tdu.Class == TduClass.Fixed)
var parsedTdu = ParsedTdu.Parse(plainTdu.Data, plainTdu.TduOffset, (uint)plainTdu.Ends, connection);
if (parsedTdu is ParsedTdu tdu)
{
return ((uint)tdu.TotalLength, FixedParsers[tdu.Exponent][tdu.Index](tdu, warehouse));
if (tdu.Class == TduClass.Invalid)
throw new NullReferenceException("TDU can't be parsed.");
if (tdu.Class == TduClass.Fixed)
{
return FixedAsyncParsers[tdu.Exponent][tdu.Index](tdu, connection, requestSequence);
}
else if (tdu.Class == TduClass.Dynamic)
{
return DynamicAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
else if (tdu.Class == TduClass.Typed)
{
return TypedAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
else // if (tt.Class == TDUClass.Extension)
{
return ExtendedAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
}
else if (parsedTdu is AsyncReply<ParsedTdu> asyncReply)
{
var rt = new AsyncReply();
asyncReply.Then(tdu =>
{
if (tdu.Class == TduClass.Invalid)
throw new NullReferenceException("TDU can't be parsed.");
object result;
if (tdu.Class == TduClass.Fixed)
{
result = FixedAsyncParsers[tdu.Exponent][tdu.Index](tdu, connection, requestSequence);
}
else if (tdu.Class == TduClass.Dynamic)
{
result = DynamicAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
else if (tdu.Class == TduClass.Typed)
{
result = TypedAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
else // if (tt.Class == TDUClass.Extension)
{
result = ExtendedAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
if (result is AsyncReply resultReply)
{
resultReply.Then(rt.Trigger)
.Error(rt.TriggerError);
}
else
{
rt.Trigger(result);
}
});
return rt;
}
throw new NotImplementedException();
}
public static object ParseSync(PlainTdu plainTdu, Warehouse warehouse)
{
var tdu = ParsedTdu.ParseSync(plainTdu.Data, plainTdu.TduOffset, plainTdu.Ends, warehouse);
if (tdu.Class == TduClass.Invalid)
{
throw new Exception("Invalid TDU.");
}
else if (tdu.Class == TduClass.Fixed)
{
return FixedParsers[tdu.Exponent][tdu.Index](tdu, warehouse);
}
else if (tdu.Class == TduClass.Dynamic)
{
return ((uint)tdu.TotalLength, DynamicParsers[tdu.Index](tdu, warehouse));
return DynamicParsers[tdu.Index](tdu, warehouse);
}
else if (tdu.Class == TduClass.Typed)
{
return ((uint)tdu.TotalLength, TypedParsers[tdu.Index](tdu, warehouse));
return TypedParsers[tdu.Index](tdu, warehouse);
}
else // Extension
{
return ((uint)tdu.TotalLength, ExtendedParsers[tdu.Index](tdu, warehouse));
return ExtendedParsers[tdu.Index](tdu, warehouse);
}
}
public static object ParseSync(ParsedTdu tdu, Warehouse warehouse)
{
if (tdu.Class == TduClass.Fixed)
{
return FixedParsers[tdu.Exponent][tdu.Index](tdu, warehouse);
}
else if (tdu.Class == TduClass.Dynamic)
{
return DynamicParsers[tdu.Index](tdu, warehouse);
}
else if (tdu.Class == TduClass.Typed)
{
return TypedParsers[tdu.Index](tdu, warehouse);
}
else // Extension
{
return ExtendedParsers[tdu.Index](tdu, warehouse);
}
}
public static (uint, object) ParseSync(byte[] data, uint offset, Warehouse warehouse)
{
var tdu = ParsedTdu.Parse(data, offset, (uint)data.Length);
var tdu = ParsedTdu.ParseSync(data, offset, (uint)data.Length, warehouse);
if (tdu.Class == TduClass.Invalid)
throw new NullReferenceException("DataType can't be parsed.");
@@ -301,7 +413,7 @@ public static class Codec
throw new NullReferenceException("Resource is null.");
if (resource is EpResource)
if (((EpResource)(resource)).DistributedResourceConnection == connection)
if (((EpResource)(resource)).ResourceConnection == connection)
return true;
return false;
@@ -385,7 +497,7 @@ public static class Codec
ComposeInternal(object valueOrSource, Warehouse warehouse, EpConnection connection)
{
if (valueOrSource == null)
return new Tdu(TduIdentifier.Null, null, 0);
return new Tdu(TduIdentifier.Null, null, 0, null, null);
var type = valueOrSource.GetType();
@@ -412,7 +524,7 @@ public static class Codec
valueOrSource = ((IUserType)valueOrSource).Get();
if (valueOrSource == null)
return new Tdu(TduIdentifier.Null, null, 0);
return new Tdu(TduIdentifier.Null, null, 0, null, null);
type = valueOrSource.GetType();
@@ -488,7 +600,7 @@ public static class Codec
}
return new Tdu(TduIdentifier.Null, null, 0);
return new Tdu(TduIdentifier.Null, null, 0, null, null);
}
@@ -541,6 +653,9 @@ public static class Codec
{
while (type != null)
{
if (type == typeof(object))
return false;
if (type == iface)
return true;
File diff suppressed because it is too large Load Diff
+165 -190
View File
@@ -23,7 +23,7 @@ public static class DataSerializer
if (v >= sbyte.MinValue && v <= sbyte.MaxValue)
{
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1);
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1, null, null);
}
else if (v >= short.MinValue && v <= short.MaxValue)
{
@@ -32,7 +32,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((short*)ptr) = (short)v;
return new Tdu(TduIdentifier.Int16, rt, 2);
return new Tdu(TduIdentifier.Int16, rt, 2, null, null);
}
else
{
@@ -40,7 +40,7 @@ public static class DataSerializer
var rt = new byte[4];
fixed (byte* ptr = rt)
*((int*)ptr) = v;
return new Tdu(TduIdentifier.Int32, rt, 4);
return new Tdu(TduIdentifier.Int32, rt, 4, null, null);
}
}
@@ -51,7 +51,7 @@ public static class DataSerializer
if (v <= byte.MaxValue)
{
// Fits in 1 byte
return new Tdu(TduIdentifier.UInt8, new byte[] { (byte)v }, 1);
return new Tdu(TduIdentifier.UInt8, new byte[] { (byte)v }, 1, null, null);
}
else if (v <= ushort.MaxValue)
{
@@ -60,7 +60,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((ushort*)ptr) = (ushort)v;
return new Tdu(TduIdentifier.UInt16, rt, 2);
return new Tdu(TduIdentifier.UInt16, rt, 2, null, null);
}
else
{
@@ -69,7 +69,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((uint*)ptr) = v;
return new Tdu(TduIdentifier.UInt32, rt, 4);
return new Tdu(TduIdentifier.UInt32, rt, 4, null, null);
}
}
@@ -80,7 +80,7 @@ public static class DataSerializer
if (v >= sbyte.MinValue && v <= sbyte.MaxValue)
{
// Fits in 1 byte
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1);
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1, null, null);
}
else
{
@@ -89,7 +89,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((short*)ptr) = v;
return new Tdu(TduIdentifier.Int16, rt, 2);
return new Tdu(TduIdentifier.Int16, rt, 2, null, null);
}
}
@@ -100,7 +100,7 @@ public static class DataSerializer
if (v <= byte.MaxValue)
{
// Fits in 1 byte
return new Tdu(TduIdentifier.UInt8, new byte[] { (byte)v }, 1);
return new Tdu(TduIdentifier.UInt8, new byte[] { (byte)v }, 1, null, null);
}
else
{
@@ -109,7 +109,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((ushort*)ptr) = v;
return new Tdu(TduIdentifier.UInt16, rt, 2);
return new Tdu(TduIdentifier.UInt16, rt, 2, null, null);
}
}
@@ -121,7 +121,7 @@ public static class DataSerializer
// Special IEEE-754 values
if (float.IsNaN(v) || float.IsInfinity(v))
{
return new Tdu(TduIdentifier.Infinity, new byte[0], 0);
return new Tdu(TduIdentifier.Infinity, new byte[0], 0, null, null);
}
// If v is an exact integer, prefer smallest signed width up to Int32
@@ -130,7 +130,7 @@ public static class DataSerializer
// Note: casts are safe because we check bounds first.
if (v >= sbyte.MinValue && v <= sbyte.MaxValue)
{
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1);
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1, null, null);
}
if (v >= short.MinValue && v <= short.MaxValue)
@@ -138,7 +138,7 @@ public static class DataSerializer
var rt = new byte[2];
fixed (byte* ptr = rt)
*((short*)ptr) = (short)v;
return new Tdu(TduIdentifier.Int16, rt, 2);
return new Tdu(TduIdentifier.Int16, rt, 2, null, null);
}
}
@@ -147,7 +147,7 @@ public static class DataSerializer
var rt = new byte[4];
fixed (byte* ptr = rt)
*((float*)ptr) = v;
return new Tdu(TduIdentifier.Float32, rt, 4);
return new Tdu(TduIdentifier.Float32, rt, 4, null, null);
}
}
@@ -159,14 +159,14 @@ public static class DataSerializer
// Special IEEE-754 values
if (double.IsNaN(v) || double.IsInfinity(v))
{
return new Tdu(TduIdentifier.Infinity, new byte[0], 0);
return new Tdu(TduIdentifier.Infinity, new byte[0], 0, null, null);
}
// If v is an exact integer, choose the smallest signed width
if (v == Math.Truncate(v))
{
if (v >= sbyte.MinValue && v <= sbyte.MaxValue)
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1);
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1, null, null);
if (v >= short.MinValue && v <= short.MaxValue)
{
@@ -175,7 +175,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((short*)ptr) = (short)v;
return new Tdu(TduIdentifier.Int16, rt, 2);
return new Tdu(TduIdentifier.Int16, rt, 2, null, null);
}
if (v >= int.MinValue && v <= int.MaxValue)
@@ -185,7 +185,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((int*)ptr) = (int)v;
return new Tdu(TduIdentifier.Int32, rt, 4);
return new Tdu(TduIdentifier.Int32, rt, 4, null, null);
}
// If it's integral but outside Int64 range, fall through to Float64.
@@ -200,7 +200,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((float*)ptr) = f;
return new Tdu(TduIdentifier.Float32, rt, 4);
return new Tdu(TduIdentifier.Float32, rt, 4, null, null);
}
// Default: Float64
@@ -208,7 +208,7 @@ public static class DataSerializer
var rt = new byte[8];
fixed (byte* ptr = rt)
*((double*)ptr) = v;
return new Tdu(TduIdentifier.Float64, rt, 8);
return new Tdu(TduIdentifier.Float64, rt, 8, null, null);
}
}
public static unsafe Tdu Int64Composer(object value, Warehouse warehouse, EpConnection connection)
@@ -218,7 +218,7 @@ public static class DataSerializer
if (v >= sbyte.MinValue && v <= sbyte.MaxValue)
{
// Fits in 1 byte
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1);
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1, null, null);
}
else if (v >= short.MinValue && v <= short.MaxValue)
{
@@ -227,7 +227,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((short*)ptr) = (short)v;
return new Tdu(TduIdentifier.Int16, rt, 2);
return new Tdu(TduIdentifier.Int16, rt, 2, null, null);
}
else if (v >= int.MinValue && v <= int.MaxValue)
{
@@ -236,7 +236,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((int*)ptr) = (int)v;
return new Tdu(TduIdentifier.Int32, rt, 4);
return new Tdu(TduIdentifier.Int32, rt, 4, null, null);
}
else
{
@@ -245,7 +245,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((long*)ptr) = v;
return new Tdu(TduIdentifier.Int64, rt, 8);
return new Tdu(TduIdentifier.Int64, rt, 8, null, null);
}
}
@@ -256,7 +256,7 @@ public static class DataSerializer
if (v <= byte.MaxValue)
{
// Fits in 1 byte
return new Tdu(TduIdentifier.UInt8, new byte[] { (byte)v }, 1);
return new Tdu(TduIdentifier.UInt8, new byte[] { (byte)v }, 1, null, null);
}
else if (v <= ushort.MaxValue)
{
@@ -265,7 +265,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((ushort*)ptr) = (ushort)v;
return new Tdu(TduIdentifier.UInt16, rt, 2);
return new Tdu(TduIdentifier.UInt16, rt, 2, null, null);
}
else if (v <= uint.MaxValue)
{
@@ -274,7 +274,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((uint*)ptr) = (uint)v;
return new Tdu(TduIdentifier.UInt32, rt, 4);
return new Tdu(TduIdentifier.UInt32, rt, 4, null, null);
}
else
{
@@ -283,7 +283,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((ulong*)ptr) = v;
return new Tdu(TduIdentifier.UInt64, rt, 8);
return new Tdu(TduIdentifier.UInt64, rt, 8, null, null);
}
}
@@ -295,7 +295,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((long*)ptr) = v;
return new Tdu(TduIdentifier.DateTime, rt, 8);
return new Tdu(TduIdentifier.DateTime, rt, 8, null, null);
}
//public static unsafe TDU Decimal128Composer(object value, Warehouse warehouse, EpConnection connection)
@@ -320,27 +320,27 @@ public static class DataSerializer
if (scale == 0)
{
if (v >= sbyte.MinValue && v <= sbyte.MaxValue)
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1);
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1, null, null);
if (v >= short.MinValue && v <= short.MaxValue)
{
var b = new byte[2];
BinaryPrimitives.WriteInt16LittleEndian(b, (short)v);
return new Tdu(TduIdentifier.Int16, b, 2);
return new Tdu(TduIdentifier.Int16, b, 2, null, null);
}
if (v >= int.MinValue && v <= int.MaxValue)
{
var b = new byte[4];
BinaryPrimitives.WriteInt32LittleEndian(b, (int)v);
return new Tdu(TduIdentifier.Int32, b, 4);
return new Tdu(TduIdentifier.Int32, b, 4, null, null);
}
if (v >= long.MinValue && v <= long.MaxValue)
{
var b = new byte[8];
BinaryPrimitives.WriteInt64LittleEndian(b, (long)v);
return new Tdu(TduIdentifier.Int64, b, 8);
return new Tdu(TduIdentifier.Int64, b, 8, null, null);
}
// else fall through (needs 96+ bits)
}
@@ -355,7 +355,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((float*)ptr) = f;
return new Tdu(TduIdentifier.Float32, rt, 4);
return new Tdu(TduIdentifier.Float32, rt, 4, null, null);
}
// Try exact Float64 (8 bytes)
@@ -367,7 +367,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((double*)ptr) = d;
return new Tdu(TduIdentifier.Float64, rt, 8);
return new Tdu(TduIdentifier.Float64, rt, 8, null, null);
}
{
@@ -377,7 +377,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((decimal*)ptr) = v;
return new Tdu(TduIdentifier.Decimal128, rt, 16);
return new Tdu(TduIdentifier.Decimal128, rt, 16, null, null);
}
}
@@ -385,58 +385,55 @@ public static class DataSerializer
{
var b = Encoding.UTF8.GetBytes((string)value);
return new Tdu(TduIdentifier.String, b, (uint)b.Length);
return new Tdu(TduIdentifier.String, b, (uint)b.Length, null, null);
}
public static Tdu ResourceLinkComposer(object value, Warehouse warehouse, EpConnection connection)
{
var b = Encoding.UTF8.GetBytes((ResourceLink)value);
return new Tdu(TduIdentifier.ResourceLink, b, (uint)b.Length);
return new Tdu(TduIdentifier.ResourceLink, b, (uint)b.Length, null, null);
}
public static Tdu EnumComposer(object value, Warehouse warehouse, EpConnection connection)
{
if (value == null)
return new Tdu(TduIdentifier.Null, null, 0);
return new Tdu(TduIdentifier.Null, null, 0, null, null);
//var warehouse = connection?.Instance?.Warehouse ?? connection?.Server?.Instance?.Warehouse;
//if (warehouse == null)
// throw new Exception("Warehouse not set.");
var valueType = value.GetType();
var typeDef = warehouse.GetTypeDefByType(value.GetType());
var typeDef = warehouse.GetLocalTypeDefByType(valueType);
var intVal = Convert.ChangeType(value, (value as Enum).GetTypeCode());
var ct = typeDef.Constants.FirstOrDefault(x => x.Value.Equals(intVal));
if (ct == null)
return new Tdu(TduIdentifier.Null, null, 0);
return new Tdu(TduIdentifier.Null, null, 0, null, null);
//return Codec.ComposeInternal(intVal, warehouse, connection);
return new Tdu(TduIdentifier.TypedEnum,
new byte[] { ct.Index }, 1, typeDef.Id.Data);
return new Tdu(TduIdentifier.Typed, new byte[] { ct.Index }, 1,
Tru.FromType(valueType, warehouse), connection);
}
public static Tdu UInt8Composer(object value, Warehouse warehouse, EpConnection connection)
{
return new Tdu(TduIdentifier.UInt8,
new byte[] { (byte)value }, 1);
new byte[] { (byte)value }, 1, null, null);
}
public static Tdu Int8Composer(object value, Warehouse warehouse, EpConnection connection)
{
return new Tdu(TduIdentifier.Int8,
new byte[] { (byte)(sbyte)value }, 1);
new byte[] { (byte)(sbyte)value }, 1, null, null);
}
public static Tdu Char8Composer(object value, Warehouse warehouse, EpConnection connection)
{
return new Tdu(TduIdentifier.Int8,
new byte[] { (byte)(char)value }, 1);
new byte[] { (byte)(char)value }, 1, null, null);
}
public static unsafe Tdu Char16Composer(object value, Warehouse warehouse, EpConnection connection)
@@ -446,7 +443,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((char*)ptr) = v;
return new Tdu(TduIdentifier.Char16, rt, 2);
return new Tdu(TduIdentifier.Char16, rt, 2, null, null);
}
@@ -454,30 +451,30 @@ public static class DataSerializer
{
if ((bool)value)
{
return new Tdu(TduIdentifier.True, null, 0);
return new Tdu(TduIdentifier.True, null, 0, null, null);
}
else
{
return new Tdu(TduIdentifier.False, null, 0);
return new Tdu(TduIdentifier.False, null, 0, null, null);
}
}
public static Tdu NotModifiedComposer(object value, Warehouse warehouse, EpConnection connection)
{
return new Tdu(TduIdentifier.NotModified, null, 0);
return new Tdu(TduIdentifier.NotModified, null, 0, null, null);
}
public static Tdu RawDataComposerFromArray(object value, Warehouse warehouse, EpConnection connection)
{
var b = (byte[])value;
return new Tdu(TduIdentifier.RawData, b, (uint)b.Length);
return new Tdu(TduIdentifier.RawData, b, (uint)b.Length, null, null);
}
public static Tdu RawDataComposerFromList(dynamic value, Warehouse warehouse, EpConnection connection)
{
var b = value as List<byte>;
return new Tdu(TduIdentifier.RawData, b.ToArray(), (uint)b.Count);
return new Tdu(TduIdentifier.RawData, b.ToArray(), (uint)b.Count, null, null);
}
//public static (TDUIdentifier, byte[]) ListComposerFromArray(dynamic value, EpConnection connection)
@@ -497,10 +494,10 @@ public static class DataSerializer
var composed = DynamicArrayComposer((IEnumerable)value, warehouse, connection);
if (composed == null)
return new Tdu(TduIdentifier.Null, new byte[0], 0);
return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null);
else
{
return new Tdu(TduIdentifier.List, composed, (uint)composed.Length);
return new Tdu(TduIdentifier.List, composed, (uint)composed.Length, null, null);
}
//if (value == null)
@@ -541,7 +538,6 @@ public static class DataSerializer
if (value == null)
return null;
if (tru.Identifier == TruIdentifier.Int32)
{
composed = GroupInt32Codec.Encode((IList<int>)value);
@@ -566,52 +562,49 @@ public static class DataSerializer
{
composed = GroupUInt16Codec.Encode((IList<ushort>)value);
}
else if (tru.Identifier == TruIdentifier.Enum)
{
//else if (tru.Identifier == TruIdentifier.Enum)
//{
// var rt = new List<byte>();
// var typeDef = warehouse.GetTypeDefByType(tru.GetRuntimeType(warehouse));
var rt = new List<byte>();
var typeDef = warehouse.GetTypeDefByType(tru.GetRuntimeType(warehouse));
// foreach (var v in value)
// {
// var intVal = Convert.ChangeType(v, (v as Enum).GetTypeCode());
// var ct = typeDef.Constants.FirstOrDefault(x => x.Value.Equals(intVal));
// if (ct == null)
// throw new Exception("Unknown Enum.");
// rt.Add(ct.Index);
// }
foreach (var v in value)
{
var intVal = Convert.ChangeType(v, (v as Enum).GetTypeCode());
var ct = typeDef.Constants.FirstOrDefault(x => x.Value.Equals(intVal));
if (ct == null)
throw new Exception("Unknown Enum.");
rt.Add(ct.Index);
}
composed = rt.ToArray();
}
// composed = rt.ToArray();
//}
else
{
var rt = new List<byte>();
Tdu? previous = null;
var isTyped = tru.IsTyped();
//var isTyped = tru.TypeDefId != null;// tru.IsTyped();
foreach (var i in value)
{
var tdu = Codec.ComposeInternal(i, warehouse, connection);
var currentTru = Tru.FromType(i?.GetType());
var currentTru = Tru.FromType(i?.GetType(), warehouse);
if (isTyped && tru.Match(currentTru))
if (tdu.Class == TduClass.Typed && tru.Match(currentTru))
{
var d = tdu.Composed.Clip(tdu.ContentOffset,
(uint)tdu.Composed.Length - tdu.ContentOffset);
var ntd = new Tdu(TduIdentifier.TypeOfTarget, d, (ulong)d.Length);
var ntd = new Tdu(TduIdentifier.TypeOfTarget, d, (ulong)d.Length, null, null);
rt.AddRange(ntd.Composed);
}
else
if (previous != null && tdu.MatchType(previous.Value))
else if (previous != null && tdu.MatchType(previous.Value))
{
var d = tdu.Composed.Clip(tdu.ContentOffset,
(uint)tdu.Composed.Length - tdu.ContentOffset);
var ntd = new Tdu(TduIdentifier.TypeContinuation, d, (ulong)d.Length);
var ntd = new Tdu(TduIdentifier.TypeContinuation, d, (ulong)d.Length, null, null);
rt.AddRange(ntd.Composed);
}
else
@@ -632,33 +625,24 @@ public static class DataSerializer
public static Tdu TypedListComposer(IEnumerable value, Type type, Warehouse warehouse, EpConnection connection)
{
var tru = Tru.FromType(type);
var elementTru = Tru.FromType(type, warehouse);
byte[] composed = TypedArrayComposer(value, tru, warehouse, connection);
byte[] composed = TypedArrayComposer(value, elementTru, warehouse, connection);
if (composed == null)
return new Tdu(TduIdentifier.Null, new byte[0], 0);
return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null);
var metadata = tru.Compose();
var metadata = new TruComposite(TruIdentifier.TypedList, false,
new Tru[] { elementTru }, value.GetType());
return new Tdu(TduIdentifier.TypedList, composed, (uint)composed.Length, metadata);
return new Tdu(TduIdentifier.Typed, composed, (uint)composed.Length, metadata, connection);
}
//public static byte[] PropertyValueComposer(PropertyValue propertyValue, EpConnection connection)//, bool includeAge = true)
//{
// var rt = new BinaryList();
// return
// .AddUInt64(propertyValue.Age)
// .AddDateTime(propertyValue.Date)
// .AddUInt8Array(Codec.Compose(propertyValue.Value, connection))
// .ToArray();
//}
public static Tdu PropertyValueArrayComposer(object value, Warehouse warehouse, EpConnection connection)
{
if (value == null)
return new Tdu(TduIdentifier.Null, new byte[0], 0);
return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null);
var rt = new List<byte>();
var ar = value as PropertyValue[];
@@ -671,16 +655,16 @@ public static class DataSerializer
}
return new Tdu(TduIdentifier.RawData, rt.ToArray(),
(uint)rt.Count);
(uint)rt.Count, null, null);
}
public static Tdu TypedMapComposer(object value, Type keyType, Type valueType, Warehouse warehouse, EpConnection connection)
{
if (value == null)
return new Tdu(TduIdentifier.Null, new byte[0], 0);
return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null);
var kt = Tru.FromType(keyType);
var vt = Tru.FromType(valueType);
var kt = Tru.FromType(keyType, warehouse);
var vt = Tru.FromType(valueType, warehouse);
//var rt = new List<byte>();
@@ -692,34 +676,33 @@ public static class DataSerializer
var compsedKeys = TypedArrayComposer(keys, kt, warehouse, connection);
var compsedValues = TypedArrayComposer(values, vt, warehouse, connection);
var ktb = kt.Compose();
var vtb = vt.Compose();
//var ktb = kt.Compose();
//var vtb = vt.Compose();
var metadata = DC.Combine(ktb, 0, (uint)ktb.Length, vtb, 0, (uint)vtb.Length);
//var metadata = DC.Combine(ktb, 0, (uint)ktb.Length, vtb, 0, (uint)vtb.Length);
//foreach (var el in map.Serialize())
// rt.AddRange(Codec.Compose(el, warehouse, connection));
var keysTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedKeys, (uint)compsedKeys.Length).Composed;
var valuesTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedValues, (uint)compsedValues.Length).Composed;
var keysTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedKeys, (uint)compsedKeys.Length, null, null).Composed;
var valuesTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedValues, (uint)compsedValues.Length, null, null).Composed;
var all = DC.Combine(keysTdu, 0, (uint)keysTdu.Length, valuesTdu, 0, (uint)valuesTdu.Length);
return new Tdu(TduIdentifier.TypedMap, all, (uint)all.Length, metadata);
return new Tdu(TduIdentifier.Typed, all, (uint)all.Length,
new TruComposite(TruIdentifier.TypedMap, false, new Tru[] { kt, vt }, value.GetType()), connection);
//return new Tdu(TduIdentifier.TypedMap, all, (uint)all.Length, metadata);
//return new TDU(TDUIdentifier.TypedMap, rt.ToArray(), (uint)rt.Count,
// );
}
public static Tdu TypedDictionaryComposer(object value, Type keyType, Type valueType, Warehouse warehouse, EpConnection connection)
{
if (value == null)
return new Tdu(TduIdentifier.Null, new byte[0], 0);
return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null);
var kt = Tru.FromType(keyType);
var vt = Tru.FromType(valueType);
var kt = Tru.FromType(keyType, warehouse);
var vt = Tru.FromType(valueType, warehouse);
//var rt = new List<byte>();
@@ -731,48 +714,18 @@ public static class DataSerializer
var compsedKeys = TypedArrayComposer(keys, kt, warehouse, connection);
var compsedValues = TypedArrayComposer(values, vt, warehouse, connection);
var ktb = kt.Compose();
var vtb = vt.Compose();
var metadata = DC.Combine(ktb, 0, (uint)ktb.Length, vtb, 0, (uint)vtb.Length);
//foreach (var el in map.Serialize())
// rt.AddRange(Codec.Compose(el, warehouse, connection));
var keysTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedKeys, (uint)compsedKeys.Length).Composed;
var valuesTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedValues, (uint)compsedValues.Length).Composed;
var keysTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedKeys, (uint)compsedKeys.Length, null, null).Composed;
var valuesTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedValues, (uint)compsedValues.Length, null, null).Composed;
var all = DC.Combine(keysTdu, 0, (uint)keysTdu.Length, valuesTdu, 0, (uint)valuesTdu.Length);
return new Tdu(TduIdentifier.TypedMap, all, (uint)all.Length, metadata);
return new Tdu(TduIdentifier.Typed, all, (uint)all.Length,
new TruComposite(TruIdentifier.TypedMap, false, new Tru[] { kt, vt },
value.GetType()),
connection);
//if (value == null)
// return new TDU(TDUIdentifier.Null, null, 0);
//var kt = TRU.FromType(keyType).Compose();
//var vt = TRU.FromType(valueType).Compose();
//var rt = new List<byte>();
////rt.AddRange(kt);
////rt.AddRange(vt);
//var dic = (IDictionary)value;
//var ar = new List<object>();
//foreach (var k in dic.Keys)
//{
// ar.Add(k);
// ar.Add(dic[k]);
//}
//foreach (var el in ar)
// rt.AddRange(Codec.Compose(el, warehouse, connection));
//return new TDU(TDUIdentifier.TypedMap, rt.ToArray(), (uint)rt.Count,
// DC.Combine(kt, 0, (uint)kt.Length, vt, 0, (uint)vt.Length));
}
public static byte[] DynamicArrayComposer(IEnumerable value, Warehouse warehouse, EpConnection connection)
@@ -792,7 +745,7 @@ public static class DataSerializer
var d = tdu.Composed.Clip(tdu.ContentOffset,
(uint)tdu.Composed.Length - tdu.ContentOffset);
var ntd = new Tdu(TduIdentifier.TypeContinuation, d, (ulong)d.Length);
var ntd = new Tdu(TduIdentifier.TypeContinuation, d, (ulong)d.Length, null, null);
rt.AddRange(ntd.Composed);
}
else
@@ -809,23 +762,23 @@ public static class DataSerializer
public static Tdu ResourceListComposer(object value, Warehouse warehouse, EpConnection connection)
{
if (value == null)
return new Tdu(TduIdentifier.Null, new byte[0], 0);
return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null);
var composed = DynamicArrayComposer((IEnumerable)value, warehouse, connection);
return new Tdu(TduIdentifier.ResourceList, composed,
(uint)composed.Length);
(uint)composed.Length, null, null);
}
public static Tdu RecordListComposer(object value, Warehouse warehouse, EpConnection connection)
{
if (value == null)
return new Tdu(TduIdentifier.Null, new byte[0], 0);
return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null);
var composed = DynamicArrayComposer((IEnumerable)value, warehouse, connection);
return new Tdu(TduIdentifier.RecordList,
composed, (uint)composed.Length);
composed, (uint)composed.Length, null, null);
}
@@ -835,54 +788,54 @@ public static class DataSerializer
if (resource.Instance == null || resource.Instance.IsDestroyed)
{
return new Tdu(TduIdentifier.Null, null, 0);
return new Tdu(TduIdentifier.Null, null, 0, null, null);
}
if (Codec.IsLocalResource(resource, connection))
{
var rid = (resource as EpResource).DistributedResourceInstanceId;
var rid = (resource as EpResource).ResourceInstanceId;
if (rid <= 0xFF)
return new Tdu(TduIdentifier.LocalResource8, new byte[] { (byte)rid }, 1);
return new Tdu(TduIdentifier.LocalResource8, new byte[] { (byte)rid }, 1, null, null);
else if (rid <= 0xFFFF)
{
var rt = new byte[2];
fixed (byte* ptr = rt)
*((ushort*)ptr) = (ushort)rid;
return new Tdu(TduIdentifier.LocalResource16, rt, 2);
return new Tdu(TduIdentifier.LocalResource16, rt, 2, null, null);
}
else
{
var rt = new byte[4];
fixed (byte* ptr = rt)
*((uint*)ptr) = rid;
return new Tdu(TduIdentifier.LocalResource32, rt, 4);
return new Tdu(TduIdentifier.LocalResource32, rt, 4, null, null);
}
}
else
{
connection.cache.Add(value as IResource, DateTime.UtcNow);
connection._cache.Add(value as IResource, DateTime.UtcNow);
var rid = resource.Instance.Id;
if (rid <= 0xFF)
return new Tdu(TduIdentifier.RemoteResource8, new byte[] { (byte)rid }, 1);
return new Tdu(TduIdentifier.RemoteResource8, new byte[] { (byte)rid }, 1, null, null);
else if (rid <= 0xFFFF)
{
var rt = new byte[2];
fixed (byte* ptr = rt)
*((ushort*)ptr) = (ushort)rid;
return new Tdu(TduIdentifier.RemoteResource16, rt, 2);
return new Tdu(TduIdentifier.RemoteResource16, rt, 2, null, null);
}
else
{
var rt = new byte[4];
fixed (byte* ptr = rt)
*((uint*)ptr) = rid;
return new Tdu(TduIdentifier.RemoteResource32, rt, 4);
return new Tdu(TduIdentifier.RemoteResource32, rt, 4, null, null);
}
}
}
@@ -890,7 +843,7 @@ public static class DataSerializer
public static unsafe Tdu MapComposer(object value, Warehouse warehouse, EpConnection connection)
{
if (value == null)
return new Tdu(TduIdentifier.Null, new byte[0], 1);
return new Tdu(TduIdentifier.Null, new byte[0], 1, null, null);
var rt = new List<byte>();
var map = (IMap)value;
@@ -898,12 +851,12 @@ public static class DataSerializer
foreach (var el in map.Serialize())
rt.AddRange(Codec.Compose(el, warehouse, connection));
return new Tdu(TduIdentifier.Map, rt.ToArray(), (uint)rt.Count);
return new Tdu(TduIdentifier.Map, rt.ToArray(), (uint)rt.Count, null, null);
}
public static unsafe Tdu UUIDComposer(object value, Warehouse warehouse, EpConnection connection)
{
return new Tdu(TduIdentifier.UUID, ((Uuid)value).Data, 16);
return new Tdu(TduIdentifier.UUID, ((Uuid)value).Data, 16, null, null);
}
@@ -912,34 +865,47 @@ public static class DataSerializer
var rt = new List<byte>();
var record = (IRecord)value;
var typeDef = warehouse.GetTypeDefByType(record.GetType());
var recordTru = Tru.FromType(value.GetType(), warehouse) ;
TypeDef typeDef = null;
if (value is Record typedRecord)
{
typeDef = typedRecord.TypeDef;
}
else if (recordTru is TruTypeDef recordTypeDefTru)
{
// @TODO need to enhance performance, maybe cache this in the connection or something
typeDef = recordTypeDefTru.TypeDef;// recordTru.type;// .GetTypeDef(warehouse, connection.RemoteDomain);
}
else
{
throw new Exception("Unsupported.");
}
foreach (var pt in typeDef.Properties)
{
var propValue = pt.PropertyInfo.GetValue(record, null);
//if (propValue == null)
// return TDU(TDUIdentifier.Null, null, 0);
var tru = Tru.FromType(propValue?.GetType());
var tru = Tru.FromType(propValue?.GetType(), warehouse);
var tdu = Codec.ComposeInternal(propValue, warehouse, connection);
if (pt.ValueType.IsTyped() &&
if (tdu.Class == TduClass.Typed && // pt.ValueType.IsTyped() &&
pt.ValueType.Match(tru))
{
// strip metadata
var len = (uint)tdu.Composed.Length - tdu.ContentOffset;
tdu = new Tdu(TduIdentifier.TypeOfTarget,
tdu.Composed.Clip(tdu.ContentOffset, len), len);
tdu.Composed.Clip(tdu.ContentOffset, len), len, null, null);
}
rt.AddRange(tdu.Composed);
}
return new Tdu(TduIdentifier.Record, rt.ToArray(),
(uint)rt.Count,
typeDef.Id.Data);
// @TODO: serialize metadata type Id to byte, ushort or uint depending on size.
return new Tdu(TduIdentifier.Typed, rt.ToArray(), (uint)rt.Count, recordTru, connection);
}
public static byte[] HistoryComposer(KeyList<PropertyDef, PropertyValue[]> history, Warehouse warehouse,
@@ -961,18 +927,13 @@ public static class DataSerializer
public static Tdu TupleComposer(object value, Warehouse warehouse, EpConnection connection)
{
if (value == null)
return new Tdu(TduIdentifier.Null, new byte[0], 0);
return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null);
var fields = value.GetType().GetFields();
var list = fields.Select(x => x.GetValue(value)).ToArray();
var trus = fields.Select(x => Tru.FromType(x.FieldType)).ToArray();
var trus = fields.Select(x => Tru.FromType(x.FieldType, warehouse)).ToArray();
var metadata = new List<byte>();
foreach (var t in trus)
metadata.AddRange(t.Compose());
var rt = new List<byte>();
for (var i = 0; i < fields.Length; i++)
@@ -982,22 +943,36 @@ public static class DataSerializer
var tdu = Codec.ComposeInternal(tupleValue, warehouse, connection);
var valueTru = Tru.FromType(tupleValue?.GetType());
var valueTru = Tru.FromType(tupleValue?.GetType(), warehouse);
if (targetTru.IsTyped() &&
if (tdu.Class == TduClass.Typed && // targetTru.IsTyped() &&
targetTru.Match(valueTru))
{
// strip metadata
var len = (uint)tdu.Composed.Length - tdu.ContentOffset;
tdu = new Tdu(TduIdentifier.TypeOfTarget,
tdu.Composed.Clip(tdu.ContentOffset, len), len);
tdu.Composed.Clip(tdu.ContentOffset, len), len, null, null);
}
rt.AddRange(tdu.Composed);
}
return new Tdu(TduIdentifier.TypedTuple, rt.ToArray(),
(uint)rt.Count, metadata.ToArray());
//return new Tdu(TduIdentifier.TypedTuple, rt.ToArray(),
// (uint)rt.Count, metadata.ToArray());
var truIdentifier = trus.Length switch
{
2 => TruIdentifier.Tuple2,
3 => TruIdentifier.Tuple3,
4 => TruIdentifier.Tuple4,
5 => TruIdentifier.Tuple5,
6 => TruIdentifier.Tuple6,
7 => TruIdentifier.Tuple7,
_ => throw new NotSupportedException("Tuples with more than 7 or less than 2 elements are not supported.")
};
return new Tdu(TduIdentifier.Typed, rt.ToArray(),
(uint)rt.Count, new TruComposite(truIdentifier, false, trus, value.GetType()), connection);
}
}
+12
View File
@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
{
public interface IParseResult<out T>
{
public T Value { get; }
public uint Size { get; }
}
}
+12
View File
@@ -0,0 +1,12 @@
using Esiur.Data.Types;
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
{
public interface IRemoteRecord: IRecord
{
public RemoteTypeDef TypeDef { get; }
}
}
+1 -5
View File
@@ -96,11 +96,7 @@ public class Map<KT, VT> : Dictionary<KT, VT>, IMap // IEnumerable<KeyValuePair<
public override string ToString()
{
var rt = "";
foreach (var kv in this)
rt += kv.Key + ": " + kv.Value.ToString() + " \r\n";
return rt.TrimEnd('\r', '\n');
return "{" + string.Join(", ", this.Select(x => $"{x.Key}: {x.Value}")) + "}";
}
//public Map(Map<KT,VT> source)
+18
View File
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
{
public readonly struct ParseResult<T>: IParseResult<T>
{
public T Value { get; }
public uint Size { get; }
public ParseResult(T value, uint size)
{
Value = value;
Size = size;
}
}
}
+346 -16
View File
@@ -1,4 +1,7 @@
using System;
using Esiur.Core;
using Esiur.Protocol;
using Esiur.Resource;
using System;
using System.Collections.Generic;
using System.Text;
@@ -9,16 +12,22 @@ namespace Esiur.Data
public TduIdentifier Identifier;
public int Index;
public TduClass Class;
public uint Offset;
public ulong ContentLength;
public uint PayloadOffset;
public ulong PayloadLength;
public byte[] Data;
public byte Exponent;
public ulong TotalLength;
public byte[] Metadata;
public Tru Metadata;
public uint Ends;
public static ParsedTdu Parse(byte[] data, uint offset, uint ends)
public static AsyncReply<ParsedTdu> ParseAsync(byte[] data, EpConnection connection)
{
return ParseAsync(data, (uint)0, (uint)data.Length, connection);
}
public static async AsyncReply<ParsedTdu> ParseAsync(byte[] data, uint offset, uint ends, EpConnection connection)
{
// @TODO: add protection against memory allocation attacks by checking the length of the data before parsing it.
var h = data[offset++];
@@ -33,11 +42,11 @@ namespace Esiur.Data
{
Identifier = (TduIdentifier)h,
Data = data,
Offset = offset,
PayloadOffset = offset,
Class = cls,
Exponent = (byte)exp,
Index = (byte)h & 0x7,
ContentLength = 0,
PayloadLength = 0,
TotalLength = 1,
Ends = ends
};
@@ -57,9 +66,9 @@ namespace Esiur.Data
{
Identifier = (TduIdentifier)h,
Data = data,
Offset = offset,
PayloadOffset = offset,
Class = cls,
ContentLength = cl,
PayloadLength = cl,
TotalLength = 1 + cl,
Exponent = (byte)exp,
Index = (byte)h & 0x7,
@@ -89,20 +98,22 @@ namespace Esiur.Data
Class = TduClass.Invalid,
};
var metaData = DC.Clip(data, offset + 1, data[offset]);
offset += data[offset] + (uint)1;
//var metaData = DC.Clip(data, offset + 1, data[offset]);
//offset += data[offset] + (uint)1;
var metaDataTru = await Tru.ParseAsync(data, offset, connection, null);
offset += metaDataTru.Size;
return new ParsedTdu()
{
Identifier = (TduIdentifier)(h & 0xC7),
Data = data,
Offset = offset,
PayloadOffset = offset,
Class = cls,
ContentLength = cl - 1 - (uint)metaData.Length,
PayloadLength = cl - metaDataTru.Size,
TotalLength = 1 + cl + cll,
Index = (byte)h & 0x7,
Metadata = metaData,
Metadata = metaDataTru.Value,
Ends = ends
};
}
@@ -135,9 +146,9 @@ namespace Esiur.Data
{
Identifier = (TduIdentifier)(h & 0xC7),
Data = data,
Offset = offset,
PayloadOffset = offset,
Class = cls,
ContentLength = cl,
PayloadLength = cl,
TotalLength = 1 + cl + cll,
Index = (byte)h & 0x7,
Ends = ends
@@ -145,5 +156,324 @@ namespace Esiur.Data
}
}
public static object Parse(byte[] data, uint offset, uint ends, EpConnection connection)
{
// @TODO: add protection against memory allocation attacks by checking the length of the data before parsing it.
var h = data[offset++];
var cls = (TduClass)(h >> 6);
if (cls == TduClass.Fixed)
{
var exp = (h & 0x38) >> 3;
if (exp == 0)
return new ParsedTdu()
{
Identifier = (TduIdentifier)h,
Data = data,
PayloadOffset = offset,
Class = cls,
Exponent = (byte)exp,
Index = (byte)h & 0x7,
PayloadLength = 0,
TotalLength = 1,
Ends = ends
};
ulong cl = (ulong)(1 << (exp - 1));
if (ends - offset < cl)
return new ParsedTdu()
{
Class = TduClass.Invalid,
TotalLength = (cl - (ends - offset))
};
//offset += (uint)cl;
return new ParsedTdu()
{
Identifier = (TduIdentifier)h,
Data = data,
PayloadOffset = offset,
Class = cls,
PayloadLength = cl,
TotalLength = 1 + cl,
Exponent = (byte)exp,
Index = (byte)h & 0x7,
Ends = ends
};
}
else if (cls == TduClass.Typed)
{
ulong cll = (ulong)(h >> 3) & 0x7;
if (ends - offset < cll)
return new ParsedTdu()
{
Class = TduClass.Invalid,
TotalLength = (cll - (ends - offset))
};
ulong cl = 0;
for (uint i = 0; i < cll; i++)
cl = cl << 8 | data[offset++];
if (ends - offset < cl)
return new ParsedTdu()
{
TotalLength = (cl - (ends - offset)),
Class = TduClass.Invalid,
};
//var metaData = DC.Clip(data, offset + 1, data[offset]);
//offset += data[offset] + (uint)1;
var rt = new AsyncReply<ParsedTdu>();
Tru.ParseAsync(data, offset, connection, null).Then(metaDataTru =>
{
offset += metaDataTru.Size;
rt.Trigger(new ParsedTdu()
{
Identifier = (TduIdentifier)(h & 0xC7),
Data = data,
PayloadOffset = offset,
Class = cls,
PayloadLength = cl - metaDataTru.Size,
TotalLength = 1 + cl + cll,
Index = (byte)h & 0x7,
Metadata = metaDataTru.Value,
Ends = ends
});
});
return rt;
}
else
{
ulong cll = (ulong)(h >> 3) & 0x7;
if (ends - offset < cll)
return new ParsedTdu()
{
Class = TduClass.Invalid,
TotalLength = (cll - (ends - offset))
};
ulong cl = 0;
for (uint i = 0; i < cll; i++)
cl = cl << 8 | data[offset++];
if (ends - offset < cl)
return new ParsedTdu()
{
Class = TduClass.Invalid,
TotalLength = (cl - (ends - offset))
};
return
new ParsedTdu()
{
Identifier = (TduIdentifier)(h & 0xC7),
Data = data,
PayloadOffset = offset,
Class = cls,
PayloadLength = cl,
TotalLength = 1 + cl + cll,
Index = (byte)h & 0x7,
Ends = ends
};
}
}
public static byte[] ClipTduData(byte[] data, uint offset, uint ends)
{
var oOffset = (int)offset;
var h = data[offset++];
var cls = (TduClass)(h >> 6);
if (cls == TduClass.Fixed)
{
var exp = (h & 0x38) >> 3;
if (exp == 0)
{
return new byte[] { h };
}
ulong cl = (ulong)(1 << (exp - 1));
if (ends - offset < cl)
{
return null; // failded
}
var rt = new byte[1 + cl];
Buffer.BlockCopy(data, oOffset, rt, 0, (int)cl);
return rt;
}
else
{
ulong cll = (ulong)(h >> 3) & 0x7;
if (ends - offset < cll)
return null;
ulong cl = 0;
for (uint i = 0; i < cll; i++)
cl = cl << 8 | data[offset++];
if (ends - offset < cl)
return null;
var rt = new byte[1 + cll + cl];
Buffer.BlockCopy(data, oOffset, rt, 0, rt.Length);
return rt;
}
}
public static ParsedTdu ParseSync(byte[] data, uint offset, uint ends, Warehouse warehouse)
{
// @TODO: add protection against memory allocation attacks by checking the length of the data before parsing it.
var h = data[offset++];
var cls = (TduClass)(h >> 6);
if (cls == TduClass.Fixed)
{
var exp = (h & 0x38) >> 3;
if (exp == 0)
return new ParsedTdu()
{
Identifier = (TduIdentifier)h,
Data = data,
PayloadOffset = offset,
Class = cls,
Exponent = (byte)exp,
Index = (byte)h & 0x7,
PayloadLength = 0,
TotalLength = 1,
Ends = ends
};
ulong cl = (ulong)(1 << (exp - 1));
if (ends - offset < cl)
return new ParsedTdu()
{
Class = TduClass.Invalid,
TotalLength = (cl - (ends - offset))
};
//offset += (uint)cl;
return new ParsedTdu()
{
Identifier = (TduIdentifier)h,
Data = data,
PayloadOffset = offset,
Class = cls,
PayloadLength = cl,
TotalLength = 1 + cl,
Exponent = (byte)exp,
Index = (byte)h & 0x7,
Ends = ends
};
}
else if (cls == TduClass.Typed)
{
ulong cll = (ulong)(h >> 3) & 0x7;
if (ends - offset < cll)
return new ParsedTdu()
{
Class = TduClass.Invalid,
TotalLength = (cll - (ends - offset))
};
ulong cl = 0;
for (uint i = 0; i < cll; i++)
cl = cl << 8 | data[offset++];
if (ends - offset < cl)
return new ParsedTdu()
{
TotalLength = (cl - (ends - offset)),
Class = TduClass.Invalid,
};
//var metaData = DC.Clip(data, offset + 1, data[offset]);
//offset += data[offset] + (uint)1;
var metaDataTru = Tru.Parse(data, offset, warehouse);
offset += metaDataTru.Size;
return new ParsedTdu()
{
Identifier = (TduIdentifier)(h & 0xC7),
Data = data,
PayloadOffset = offset,
Class = cls,
PayloadLength = cl - metaDataTru.Size,
TotalLength = 1 + cl + cll,
Index = (byte)h & 0x7,
Metadata = metaDataTru.Value,
Ends = ends
};
}
else
{
ulong cll = (ulong)(h >> 3) & 0x7;
if (ends - offset < cll)
return new ParsedTdu()
{
Class = TduClass.Invalid,
TotalLength = (cll - (ends - offset))
};
ulong cl = 0;
for (uint i = 0; i < cll; i++)
cl = cl << 8 | data[offset++];
if (ends - offset < cl)
return new ParsedTdu()
{
Class = TduClass.Invalid,
TotalLength = (cl - (ends - offset))
};
return
new ParsedTdu()
{
Identifier = (TduIdentifier)(h & 0xC7),
Data = data,
PayloadOffset = offset,
Class = cls,
PayloadLength = cl,
TotalLength = 1 + cl + cll,
Index = (byte)h & 0x7,
Ends = ends
};
}
}
}
}
+155
View File
@@ -0,0 +1,155 @@
using Esiur.Core;
using Esiur.Protocol;
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
{
public struct PlainTdu
{
public TduIdentifier Identifier;
public int Index;
public TduClass Class;
public uint TduOffset;
public uint PayloadOffset;
public ulong PayloadLength;
public byte[] Data;
public byte Exponent;
public ulong TotalLength;
public uint Ends;
public static PlainTdu Parse(byte[] data, uint offset, uint ends)
{
var oOffset = offset;
// @TODO: add protection against memory allocation attacks by checking the length of the data before parsing it.
var h = data[offset++];
var cls = (TduClass)(h >> 6);
if (cls == TduClass.Fixed)
{
var exp = (h & 0x38) >> 3;
if (exp == 0)
return new PlainTdu()
{
Identifier = (TduIdentifier)h,
Data = data,
TduOffset = oOffset,
PayloadOffset = offset,
Class = cls,
Exponent = (byte)exp,
Index = (byte)h & 0x7,
PayloadLength = 0,
TotalLength = 1,
Ends = ends
};
ulong cl = (ulong)(1 << (exp - 1));
if (ends - offset < cl)
return new PlainTdu()
{
Class = TduClass.Invalid,
TotalLength = (cl - (ends - offset))
};
//offset += (uint)cl;
return new PlainTdu()
{
Identifier = (TduIdentifier)h,
Data = data,
TduOffset= oOffset,
PayloadOffset = offset,
Class = cls,
PayloadLength = cl,
TotalLength = 1 + cl,
Exponent = (byte)exp,
Index = (byte)h & 0x7,
Ends = ends
};
}
else if (cls == TduClass.Typed)
{
ulong cll = (ulong)(h >> 3) & 0x7;
if (ends - offset < cll)
return new PlainTdu()
{
Class = TduClass.Invalid,
TotalLength = (cll - (ends - offset))
};
ulong cl = 0;
for (uint i = 0; i < cll; i++)
cl = cl << 8 | data[offset++];
if (ends - offset < cl)
return new PlainTdu()
{
TotalLength = (cl - (ends - offset)),
Class = TduClass.Invalid,
};
return new PlainTdu()
{
Identifier = (TduIdentifier)(h & 0xC7),
Data = data,
TduOffset = oOffset,
PayloadOffset = offset,
Class = cls,
PayloadLength = cl,
TotalLength = 1 + cl + cll,
Index = (byte)h & 0x7,
Ends = ends
};
}
else
{
ulong cll = (ulong)(h >> 3) & 0x7;
if (ends - offset < cll)
return new PlainTdu()
{
Class = TduClass.Invalid,
TotalLength = (cll - (ends - offset))
};
ulong cl = 0;
for (uint i = 0; i < cll; i++)
cl = cl << 8 | data[offset++];
if (ends - offset < cl)
return new PlainTdu()
{
Class = TduClass.Invalid,
TotalLength = (cl - (ends - offset))
};
return
new PlainTdu()
{
Identifier = (TduIdentifier)(h & 0xC7),
Data = data,
TduOffset = oOffset,
PayloadOffset = offset,
Class = cls,
PayloadLength = cl,
TotalLength = 1 + cl + cll,
Index = (byte)h & 0x7,
Ends = ends
};
}
}
}
}
+13 -1
View File
@@ -1,9 +1,21 @@
using System;
using Esiur.Data.Types;
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data;
public class Record : KeyList<string, object>, IRecord
{
public TypeDef TypeDef { get; private set; }
public Record(TypeDef typeDef)
{
TypeDef = typeDef;
}
public override string ToString()
{
return $"Record<{TypeDef.Name}> {{{string.Join(", ", this.Select(x=>x.Key + ": " + x.Value))}}}";
}
}
+31 -49
View File
@@ -1,4 +1,5 @@
using System;
using Esiur.Protocol;
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Linq;
@@ -18,7 +19,7 @@ public struct Tdu
public byte[] Composed;
//public uint Offset;
public byte[] Metadata;
public Tru Metadata;
public uint ContentOffset;
@@ -64,7 +65,7 @@ public struct Tdu
}
public Tdu(TduIdentifier identifier,
byte[] data, ulong length, byte[] metadata = null)
byte[] data, ulong length, Tru metadata, EpConnection connection)
{
Identifier = identifier;
//Index = (byte)identifier & 0x7;
@@ -174,34 +175,20 @@ public struct Tdu
if (metadata == null)
throw new Exception("Metadata must be provided for types.");
if (metadata.Length > 0xFF)
throw new Exception("Metadata can't exceed 255 bytes in length.");
var metaLen = (byte)metadata.Length;
var len = 1 + (ulong)metaLen + length;
var metadataData = metadata.Compose(connection);
if (length == 0 && (metadata == null || metadata.Length == 0))
{
Composed = new byte[1] { (byte)Identifier };
throw new Exception("Need check");
}
else if (metadata.Length > 0xFF)
{
throw new Exception("Metadata can't exceed 255 bytes in length.");
}
else if (length <= 0xFF)
var len = (ulong)metadataData.Length + length;
if (len <= 0xFF)
{
Composed = new byte[2 + len];
Composed[0] = (byte)((byte)Identifier | 0x8);
Composed[1] = (byte)len;
Composed[2] = metaLen;
ContentOffset = metaLen + (uint)3;
ContentOffset = (uint)metadataData.Length + (uint)2;
Buffer.BlockCopy(metadata, 0, Composed, 3, metaLen);
Buffer.BlockCopy(data, 0, Composed, 3 + metaLen, (int)length);
Buffer.BlockCopy(metadataData, 0, Composed, 2, metadataData.Length);
Buffer.BlockCopy(data, 0, Composed, 2 + metadataData.Length, (int)length);
}
else if (len <= 0xFF_FF)
{
@@ -209,11 +196,10 @@ public struct Tdu
Composed[0] = (byte)((byte)identifier | 0x10);
Composed[1] = (byte)((len >> 8) & 0xFF);
Composed[2] = (byte)(len & 0xFF);
Composed[3] = metaLen;
ContentOffset = metaLen + (uint)4;
ContentOffset = (uint)metadataData.Length + (uint)3;
Buffer.BlockCopy(metadata, 0, Composed, 4, metaLen);
Buffer.BlockCopy(data, 0, Composed, 4 + metaLen, (int)length);
Buffer.BlockCopy(metadataData, 0, Composed, 3, metadataData.Length);
Buffer.BlockCopy(data, 0, Composed, 3 + metadataData.Length, (int)length);
}
else if (len <= 0xFF_FF_FF)
{
@@ -222,11 +208,10 @@ public struct Tdu
Composed[1] = (byte)((len >> 16) & 0xFF);
Composed[2] = (byte)((len >> 8) & 0xFF);
Composed[3] = (byte)(len & 0xFF);
Composed[4] = metaLen;
ContentOffset = metaLen + (uint)5;
ContentOffset = (uint)metadataData.Length + (uint)4;
Buffer.BlockCopy(metadata, 0, Composed, 5, metaLen);
Buffer.BlockCopy(data, 0, Composed, 5 + metaLen, (int)length);
Buffer.BlockCopy(metadataData, 0, Composed, 4, metadataData.Length);
Buffer.BlockCopy(data, 0, Composed, 4 + metadataData.Length, (int)length);
}
else if (len <= 0xFF_FF_FF_FF)
@@ -237,11 +222,10 @@ public struct Tdu
Composed[2] = (byte)((len >> 16) & 0xFF);
Composed[3] = (byte)((len >> 8) & 0xFF);
Composed[4] = (byte)(len & 0xFF);
Composed[5] = metaLen;
ContentOffset = metaLen + (uint)6;
ContentOffset = (uint)metadataData.Length + (uint)5;
Buffer.BlockCopy(metadata, 0, Composed, 6, metaLen);
Buffer.BlockCopy(data, 0, Composed, 6 + metaLen, (int)length);
Buffer.BlockCopy(metadataData, 0, Composed, 5, metadataData.Length);
Buffer.BlockCopy(data, 0, Composed, 5 + metadataData.Length, (int)length);
}
else if (len <= 0xFF_FF_FF_FF_FF)
{
@@ -252,11 +236,10 @@ public struct Tdu
Composed[3] = (byte)((len >> 16) & 0xFF);
Composed[4] = (byte)((len >> 8) & 0xFF);
Composed[5] = (byte)(len & 0xFF);
Composed[6] = metaLen;
ContentOffset = metaLen + (uint)7;
ContentOffset = (uint)metadataData.Length + (uint)6;
Buffer.BlockCopy(metadata, 0, Composed, 7, metaLen);
Buffer.BlockCopy(data, 0, Composed, 7 + metaLen, (int)length);
Buffer.BlockCopy(metadataData, 0, Composed, 6, metadataData.Length);
Buffer.BlockCopy(data, 0, Composed, 6 + metadataData.Length, (int)length);
}
else if (len <= 0xFF_FF_FF_FF_FF_FF)
{
@@ -268,11 +251,10 @@ public struct Tdu
Composed[4] = (byte)((len >> 16) & 0xFF);
Composed[5] = (byte)((len >> 8) & 0xFF);
Composed[6] = (byte)(len & 0xFF);
Composed[7] = metaLen;
ContentOffset = metaLen + (uint)8;
ContentOffset = (uint)metadataData.Length + (uint)7;
Buffer.BlockCopy(metadata, 0, Composed, 8, metaLen);
Buffer.BlockCopy(data, 0, Composed, 8 + metaLen, (int)length);
Buffer.BlockCopy(metadataData, 0, Composed, 7, metadataData.Length);
Buffer.BlockCopy(data, 0, Composed, 7 + metadataData.Length, (int)length);
}
else //if (len <= 0xFF_FF_FF_FF_FF_FF_FF)
{
@@ -285,11 +267,11 @@ public struct Tdu
Composed[5] = (byte)((len >> 16) & 0xFF);
Composed[6] = (byte)((len >> 8) & 0xFF);
Composed[7] = (byte)(len & 0xFF);
Composed[8] = metaLen;
ContentOffset = metaLen + (uint)9;
Buffer.BlockCopy(metadata, 0, Composed, 9, metaLen);
Buffer.BlockCopy(data, 0, Composed, 9 + metaLen, (int)length);
ContentOffset = (uint)metadataData.Length + (uint)8;
Buffer.BlockCopy(metadataData, 0, Composed, 8, metadataData.Length);
Buffer.BlockCopy(data, 0, Composed, 8 + metadataData.Length, (int)length);
}
}
@@ -305,7 +287,7 @@ public struct Tdu
if (Class != TduClass.Typed || with.Class != TduClass.Typed)
return false;
if (!Metadata.SequenceEqual(with.Metadata))
if (!Metadata.Match(with.Metadata))
return false;
return true;
+8 -6
View File
@@ -50,12 +50,14 @@ namespace Esiur.Data
Map = 0x46,
MapList = 0x47,
Record = 0x80,
TypedList = 0x81,
TypedMap = 0x82,
TypedTuple = 0x83,
TypedEnum = 0x84,
TypedConstant = 0x85,
Typed = 0x80,
//Record = 0x80,
//TypedList = 0x81,
//TypedMap = 0x82,
//TypedTuple = 0x83,
//TypedEnum = 0x84,
//TypedConstant = 0x85,
TypeContinuation = 0xC0,
TypeOfTarget = 0xC1,
File diff suppressed because it is too large Load Diff
+125
View File
@@ -0,0 +1,125 @@
using Esiur.Data.Types;
using Esiur.Protocol;
using Esiur.Resource;
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
{
internal class TruComposite : Tru
{
public Tru[] SubTypes;
Type _runtimeType;
public override Type RuntimeType { get; protected set; }
public TruComposite(TruIdentifier identifier, bool nullable, Tru[] subTypes, Type type)
{
Identifier = identifier;
Nullable = nullable;
SubTypes = subTypes;
RuntimeType = type;
//_runtimeType = typeof(Tuple).MakeGenericType(subTypes.Select(x => x.RuntimeType).ToArray());
}
public override void SetNull(List<byte> flags)
{
if (RefTypes.Contains(Identifier))
{
Nullable = (flags.FirstOrDefault() == 2);
if (flags.Count > 0)
flags.RemoveAt(0);
}
foreach (var st in SubTypes)
st.SetNull(flags);
}
public override void SetNull(byte flag)
{
if (RefTypes.Contains(Identifier))
{
Nullable = (flag == 2);
}
foreach (var st in SubTypes)
st.SetNull(flag);
}
public override void SetNotNull(List<byte> flags)
{
if (RefTypes.Contains(Identifier))
{
Nullable = (flags.FirstOrDefault() != 1);
if (flags.Count > 0)
flags.RemoveAt(0);
}
foreach (var st in SubTypes)
st.SetNotNull(flags);
}
public override void SetNotNull(byte flag)
{
if (RefTypes.Contains(Identifier))
{
Nullable = (flag != 1);
}
if (SubTypes != null)
foreach (var st in SubTypes)
st.SetNotNull(flag);
}
public override bool Match(Tru other)
{
if (other is TruComposite otherComposite)
{
if (other.Identifier != Identifier)
return false;
if (otherComposite.SubTypes.Length != (SubTypes?.Length ?? -1))
return false;
for (var i = 0; i < SubTypes?.Length; i++)
if (!SubTypes[i].Match(otherComposite.SubTypes[i]))
return false;
return true;
}
return false;
}
public override string ToString()
{
return Identifier.ToString() + "<" + String.Join(",", SubTypes.Select(x => x.ToString())) + ">" + (Nullable ? "?" : "");
}
public override byte[] Compose(EpConnection connection)
{
var rt = new BinaryList();
if (Nullable)
rt.AddUInt8((byte)(0x80 | (byte)Identifier));
else
rt.AddUInt8((byte)Identifier);
for (var i = 0; i < SubTypes.Length; i++)
rt.AddUInt8Array(SubTypes[i].Compose(connection));
return rt.ToArray();
}
public override Tru ToNullable()
{
throw new NotImplementedException();
}
}
}
+13 -3
View File
@@ -30,10 +30,20 @@ namespace Esiur.Data
Record,
List,
Map,
Enum = 0x44,
TypedResource = 0x45, // Followed by UUID
TypedRecord = 0x46, // Followed by UUID
//Enum = 0x44,
//TypedResource = 0x45, // Followed by UUID
//TypedRecord = 0x46, // Followed by UUID
LocalType8 = 0x40,
RemoteType8 = 0x41,
LocalType16 = 0x42,
RemoteType16 = 0x43,
LocalType32 = 0x44,
RemoteType32 = 0x45,
LocalType64 = 0x46,
RemoteType64 = 0x47,
TypedList = 0x48, // Followed by element type
Tuple2 = 0x50, // Followed by element type
TypedMap = 0x51, // Followed by key type and value type
Tuple3 = 0x58,
+95
View File
@@ -0,0 +1,95 @@
using Esiur.Data.Types;
using Esiur.Protocol;
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
{
public class TruPrimitive:Tru
{
public override Type RuntimeType { get; protected set; }
public override string ToString()
{
return Identifier.ToString() + (Nullable ? "?" : "");
}
public TruPrimitive(TruIdentifier identifier, bool nullable, Type type)
{
Identifier = identifier;
Nullable = nullable;
RuntimeType = type;
}
public override void SetNull(List<byte> flags)
{
if (RefTypes.Contains(Identifier))
{
Nullable = (flags.FirstOrDefault() == 2);
if (flags.Count > 0)
flags.RemoveAt(0);
}
}
public override void SetNull(byte flag)
{
if (RefTypes.Contains(Identifier))
{
Nullable = (flag == 2);
}
}
public override void SetNotNull(List<byte> flags)
{
if (RefTypes.Contains(Identifier))
{
Nullable = (flags.FirstOrDefault() != 1);
if (flags.Count > 0)
flags.RemoveAt(0);
}
}
public override void SetNotNull(byte flag)
{
if (RefTypes.Contains(Identifier))
{
Nullable = (flag != 1);
}
}
public override bool Match(Tru other)
{
if (other is TruPrimitive otherComposite)
{
if (other.Identifier != Identifier)
return false;
return true;
}
return false;
}
public override byte[] Compose(EpConnection connection)
{
var rt = new BinaryList();
if (Nullable)
rt.AddUInt8((byte)(0x80 | (byte)Identifier));
else
rt.AddUInt8((byte)Identifier);
return rt.ToArray();
}
public override Tru ToNullable()
{
throw new NotImplementedException();
}
}
}
+133
View File
@@ -0,0 +1,133 @@
using Esiur.Data.Types;
using Esiur.Protocol;
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
{
public class TruTypeDef : Tru
{
public TypeDef? TypeDef;
public override Type RuntimeType { get; protected set; }
public override void SetNull(List<byte> flags)
{
if (RefTypes.Contains(Identifier))
{
Nullable = (flags.FirstOrDefault() == 2);
if (flags.Count > 0)
flags.RemoveAt(0);
}
}
public TruTypeDef(bool nullable, TypeDef typeDef)
{
Nullable = nullable;
TypeDef = typeDef;
if (typeDef is LocalTypeDef localTypeDef)
RuntimeType = localTypeDef.DefinedType;
else if (typeDef is RemoteTypeDef remoteTypeDef)
RuntimeType = remoteTypeDef.ProxyType;
}
public override void SetNull(byte flag)
{
Nullable = (flag == 2);
}
public override void SetNotNull(List<byte> flags)
{
Nullable = (flags.FirstOrDefault() != 1);
if (flags.Count > 0)
flags.RemoveAt(0);
}
public override void SetNotNull(byte flag)
{
Nullable = (flag != 1);
}
public override bool Match(Tru other)
{
if (other is TruTypeDef otherComposite)
{
if (otherComposite.TypeDef != TypeDef)
return false;
return true;
}
return false;
}
public override string ToString()
{
return Identifier.ToString() + (Nullable ? "?" : "");
}
public override byte[] Compose(EpConnection connection)
{
var rt = new BinaryList();
if (TypeDef is RemoteTypeDef remoteTypeDef)
{
if (connection.RemoteDomain == remoteTypeDef.Domain)
{
// this is local in respect to the connection, send the remote typdef id.
if (Nullable)
rt.AddUInt8(0x80 | (byte)TruIdentifier.LocalType8);
else
rt.AddUInt8((byte)TruIdentifier.LocalType8);
rt.AddUInt8((byte)remoteTypeDef.Id);
}
else
{
// this is remote in respect to the connection and the local typedef id is used.
if (Nullable)
rt.AddUInt8(0x80 | (byte)TruIdentifier.RemoteType8);
else
rt.AddUInt8((byte)TruIdentifier.RemoteType8);
rt.AddUInt8((byte)remoteTypeDef.LocalTypeDefId);
}
}
else if (TypeDef is LocalTypeDef localTypeDef)
{
if (connection == null)
{
// if there is no connection, we assume it's local.
if (Nullable)
rt.AddUInt8(0x80 | (byte)TruIdentifier.LocalType8);
else
rt.AddUInt8((byte)TruIdentifier.LocalType8);
rt.AddUInt8((byte)localTypeDef.Id);
}
else
{
// this is remote, unless the connection is to self @TODO: solve for this state.
if (Nullable)
rt.AddUInt8(0x80 | (byte)TruIdentifier.RemoteType8);
else
rt.AddUInt8((byte)TruIdentifier.RemoteType8);
rt.AddUInt8((byte)localTypeDef.Id);
}
}
else
throw new NotImplementedException();
return rt.ToArray();
}
public override Tru ToNullable()
{
throw new NotImplementedException();
}
}
}
+51
View File
@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
{
public struct TypeDefId
{
public ulong Value;
public bool Remote;
public TypeDefId(ulong value, bool remote)
{
Value = value;
Remote = remote;
}
public unsafe override int GetHashCode()
{
// Fallback implementation when System.HashCode is not available.
unchecked
{
int hash = ((int)Value) ^ ((int)(Value >> 32));
hash = (hash * 397) ^ (Remote ? 1 : 0);
return hash;
}
}
public override string ToString()=> $"{(Remote ? "Remote" : "Local")}TypeDef{Value}";
public override bool Equals(object obj)
{
if (obj is TypeDefId b)
return Value == b.Value && Remote == b.Remote;
return false;
}
public static bool operator == (TypeDefId a, TypeDefId b)
{
return a.Equals(b);
}
public static bool operator !=(TypeDefId a, TypeDefId b)
{
return !(a == b);
}
}
}
+14 -11
View File
@@ -1,8 +1,11 @@
using Esiur.Data;
using Esiur.Core;
using Esiur.Data;
using Esiur.Protocol;
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Reflection;
using System.Text;
namespace Esiur.Data.Types;
@@ -20,7 +23,7 @@ public class ArgumentDef
public Map<string, string> Annotations { get; set; }
public static (uint, ArgumentDef) Parse(byte[] data, uint offset, int index)
public static async AsyncReply<ParseResult<ArgumentDef>> ParseAsync(byte[] data, uint offset, int index, EpConnection connection, ulong[] requestSequence)
{
var optional = (data[offset] & 0x1) == 0x1;
var hasAnnotations = (data[offset++] & 0x2) == 0x2;
@@ -28,9 +31,9 @@ public class ArgumentDef
var cs = (uint)data[offset++];
var name = data.GetString(offset, cs);
offset += cs;
var (size, type) = Tru.Parse(data, offset);
var type = await Tru.ParseAsync(data, offset, connection, requestSequence);
offset += size;
offset += type.Size;
Map<string, string> annotations = null;
@@ -46,14 +49,14 @@ public class ArgumentDef
cs += l;
}
return (cs + 2 + size, new ArgumentDef()
return new ParseResult<ArgumentDef>(new ArgumentDef()
{
Name = name,
Index = index,
Type = type,
Type = type.Value,
Optional = optional,
Annotations = annotations
});
}, cs + 2 + type.Size);
}
public ArgumentDef()
@@ -70,7 +73,7 @@ public class ArgumentDef
return $"{Name}: {Type} ";
}
public byte[] Compose()
public byte[] Compose(EpConnection connection)
{
var name = DC.ToBytes(Name);
@@ -80,7 +83,7 @@ public class ArgumentDef
.AddUInt8(Optional ? (byte)1 : (byte)0)
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(Type.Compose())
.AddUInt8Array(Type.Compose(connection))
.ToArray();
}
else
@@ -91,7 +94,7 @@ public class ArgumentDef
.AddUInt8((byte)(0x2 | (Optional ? 1 : 0)))
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(Type.Compose())
.AddUInt8Array(Type.Compose(connection))
.AddUInt8Array(exp)
.ToArray();
}
+19 -12
View File
@@ -3,7 +3,9 @@ using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using Esiur.Core;
using Esiur.Data;
using Esiur.Protocol;
using Esiur.Resource;
namespace Esiur.Data.Types;
@@ -18,7 +20,7 @@ public class ConstantDef : MemberDef
public FieldInfo FieldInfo { get; set; }
public static (uint, ConstantDef) Parse(byte[] data, uint offset, byte index, bool inherited)
public static async AsyncReply< ParseResult<ConstantDef>> ParseAsync(byte[] data, uint offset, byte index, bool inherited, EpConnection connection, ulong[] requestSequence)
{
var oOffset = offset;
@@ -27,11 +29,16 @@ public class ConstantDef : MemberDef
var name = data.GetString(offset + 1, data[offset]);
offset += (uint)data[offset] + 1;
var (dts, valueType) = Tru.Parse(data, offset);
//Console.WriteLine("Parsing constantDef " + name);
offset += dts;
//var (dts, valueType)
var valueType = await Tru.ParseAsync(data, offset, connection, requestSequence);
(dts, var value) = Codec.ParseSync(data, offset, Warehouse.Default);
//Console.WriteLine("Parsing constantDef 2 " + name);
offset += valueType.Size;
(var dts, var value) = Codec.ParseSync(data, offset, Warehouse.Default);
offset += dts;
@@ -48,19 +55,19 @@ public class ConstantDef : MemberDef
offset += len;
}
return (offset - oOffset, new ConstantDef()
return new ParseResult<ConstantDef>( new ConstantDef()
{
Index = index,
Name = name,
Inherited = inherited,
ValueType = valueType,
ValueType = valueType.Value,
Value = value,
Annotations = annotations
});
}, offset - oOffset);
}
public byte[] Compose()
public byte[] Compose(EpConnection connection)
{
var name = DC.ToBytes(Name);
@@ -75,7 +82,7 @@ public class ConstantDef : MemberDef
.AddUInt8(hdr)
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(ValueType.Compose())
.AddUInt8Array(ValueType.Compose(connection))
.AddUInt8Array(Codec.Compose(Value, null, null))
.AddInt32(exp.Length)
.AddUInt8Array(exp)
@@ -89,18 +96,18 @@ public class ConstantDef : MemberDef
.AddUInt8(hdr)
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(ValueType.Compose())
.AddUInt8Array(ValueType.Compose(connection))
.AddUInt8Array(Codec.Compose(Value, null, null))
.ToArray();
}
}
public static ConstantDef MakeConstantDef(Type type, FieldInfo ci, byte index = 0, string customName = null, TypeDef typeDef = null)
public static ConstantDef MakeConstantDef(Warehouse warehouse, Type type, FieldInfo ci, byte index = 0, string customName = null, TypeDef typeDef = null)
{
var annotationAttrs = ci.GetCustomAttributes<AnnotationAttribute>(true);
var valueType = Tru.FromType(ci.FieldType);
var valueType = Tru.FromType(ci.FieldType, warehouse);
if (valueType == null)
throw new Exception($"Unsupported type `{ci.FieldType}` in constant `{type.Name}.{ci.Name}`");
+13 -11
View File
@@ -1,5 +1,6 @@
using Esiur.Core;
using Esiur.Data;
using Esiur.Protocol;
using Esiur.Resource;
using System;
using System.Collections.Generic;
@@ -32,7 +33,7 @@ public class EventDef : MemberDef
public Tru ArgumentType { get; set; }
public static (uint, EventDef) Parse(byte[] data, uint offset, byte index, bool inherited)
public static async AsyncReply<ParseResult<EventDef>> ParseAsync(byte[] data, uint offset, byte index, bool inherited, EpConnection connection, ulong[] requestSequence)
{
var oOffset = offset;
@@ -42,9 +43,9 @@ public class EventDef : MemberDef
var name = data.GetString(offset + 1, data[offset]);
offset += (uint)data[offset] + 1;
var (dts, argType) = Tru.Parse(data, offset);
var argType = await Tru.ParseAsync(data, offset, connection, requestSequence);
offset += dts;
offset += argType.Size;
// Annotation ?
Map<string, string> annotations = null;
@@ -59,18 +60,18 @@ public class EventDef : MemberDef
offset += len;
}
return (offset - oOffset, new EventDef()
return new ParseResult<EventDef>(new EventDef()
{
Index = index,
Name = name,
Inherited = inherited,
ArgumentType = argType,
ArgumentType = argType.Value,
Subscribable = subscribable,
Annotations = annotations
});
}, offset - oOffset);
}
public byte[] Compose()
public byte[] Compose(EpConnection connection)
{
var name = Name.ToBytes();
@@ -87,7 +88,7 @@ public class EventDef : MemberDef
.AddUInt8(hdr)
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(ArgumentType.Compose())
.AddUInt8Array(ArgumentType.Compose(connection))
.AddInt32(exp.Length)
.AddUInt8Array(exp)
.ToArray();
@@ -99,12 +100,12 @@ public class EventDef : MemberDef
.AddUInt8(hdr)
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(ArgumentType.Compose())
.AddUInt8Array(ArgumentType.Compose(connection))
.ToArray();
}
public static EventDef MakeEventDef(Type type, EventInfo ei, byte index, string name, TypeDef schema)
public static EventDef MakeEventDef(Warehouse warehouse, Type type, EventInfo ei, byte index, string name, TypeDef schema)
{
if (!ei.EventHandlerType.IsGenericType)
@@ -116,7 +117,8 @@ public class EventDef : MemberDef
var argType = ei.EventHandlerType.GenericTypeArguments[0];
var evtType = Tru.FromType(argType);
// @TODO: need to check if the type is remote
var evtType = Tru.FromType(argType, warehouse);
if (evtType == null)
throw new Exception($"Unsupported type `{argType}` in event `{type.Name}.{ei.Name}`");
+22 -20
View File
@@ -40,7 +40,7 @@ public class FunctionDef : MemberDef
}
public static (uint, FunctionDef) Parse(byte[] data, uint offset, byte index, bool inherited)
public static async AsyncReply<ParseResult<FunctionDef>> ParseAsync(byte[] data, uint offset, byte index, bool inherited, EpConnection connection, ulong[] requestSequence)
{
var oOffset = offset;
@@ -51,19 +51,21 @@ public class FunctionDef : MemberDef
var name = data.GetString(offset + 1, data[offset]);
offset += (uint)data[offset] + 1;
// return type
var (rts, returnType) = Tru.Parse(data, offset);
offset += rts;
//Console.WriteLine("Parsing functionDef " + name);
// return type
var returnType = await Tru.ParseAsync(data, offset, connection, requestSequence);
offset += returnType.Size;
// arguments count
var argsCount = data[offset++];
List<ArgumentDef> arguments = new();
for (var a = 0; a < argsCount; a++)
{
var (cs, argType) = ArgumentDef.Parse(data, offset, a);
arguments.Add(argType);
offset += cs;
var argType = await ArgumentDef.ParseAsync(data, offset, a, connection, requestSequence);
arguments.Add(argType.Value);
offset += argType.Size;
}
Map<string, string> annotations = null;
@@ -79,7 +81,7 @@ public class FunctionDef : MemberDef
offset += len;
}
return (offset - oOffset, new FunctionDef()
return new ParseResult<FunctionDef>( new FunctionDef()
{
Index = index,
Name = name,
@@ -87,11 +89,11 @@ public class FunctionDef : MemberDef
IsStatic = isStatic,
Inherited = inherited,
Annotations = annotations,
ReturnType = returnType,
});
ReturnType = returnType.Value,
}, offset - oOffset);
}
public byte[] Compose()
public byte[] Compose(EpConnection connection)
{
var name = DC.ToBytes(Name);
@@ -99,11 +101,11 @@ public class FunctionDef : MemberDef
var bl = new BinaryList()
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(ReturnType.Compose())
.AddUInt8Array(ReturnType.Compose(connection))
.AddUInt8((byte)Arguments.Length);
for (var i = 0; i < Arguments.Length; i++)
bl.AddUInt8Array(Arguments[i].Compose());
bl.AddUInt8Array(Arguments[i].Compose(connection));
if (Annotations != null)
@@ -119,7 +121,7 @@ public class FunctionDef : MemberDef
}
public static FunctionDef MakeFunctionDef(Type type, MethodInfo mi, byte index, string name, TypeDef schema)
public static FunctionDef MakeFunctionDef(Warehouse warehouse, Type type, MethodInfo mi, byte index, string name, TypeDef schema)
{
var genericRtType = mi.ReturnType.IsGenericType ? mi.ReturnType.GetGenericTypeDefinition() : null;
@@ -128,23 +130,23 @@ public class FunctionDef : MemberDef
if (genericRtType == typeof(AsyncReply<>))
{
rtType = Tru.FromType(mi.ReturnType.GetGenericArguments()[0]);
rtType = Tru.FromType(mi.ReturnType.GetGenericArguments()[0], warehouse);
}
else if (genericRtType == typeof(Task<>))
{
rtType = Tru.FromType(mi.ReturnType.GetGenericArguments()[0]);
rtType = Tru.FromType(mi.ReturnType.GetGenericArguments()[0], warehouse);
}
else if (genericRtType == typeof(IEnumerable<>) || genericRtType == typeof(IAsyncEnumerable<>))
{
// get export
rtType = Tru.FromType(mi.ReturnType.GetGenericArguments()[0]);
rtType = Tru.FromType(mi.ReturnType.GetGenericArguments()[0], warehouse);
}
else
{
if (mi.ReturnType == typeof(Task))
rtType = Tru.FromType(null);
rtType = Tru.FromType(null, warehouse);
else
rtType = Tru.FromType(mi.ReturnType);
rtType = Tru.FromType(mi.ReturnType, warehouse);
}
if (rtType == null)
@@ -214,7 +216,7 @@ public class FunctionDef : MemberDef
var arguments = args.Select(x =>
{
var argType = Tru.FromType(x.ParameterType);
var argType = Tru.FromType(x.ParameterType, warehouse);
if (argType == null)
throw new Exception($"Unsupported type `{x.ParameterType}` in method `{type.Name}.{mi.Name}` parameter `{x.Name}`");
+579
View File
@@ -0,0 +1,579 @@
using Esiur.Core;
using Esiur.Data;
using Esiur.Misc;
using Esiur.Protocol;
using Esiur.Proxy;
using Esiur.Resource;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
namespace Esiur.Data.Types;
public class LocalTypeDef:TypeDef
{
Type _definedType { get; set; }
Type _parentDefinedType { get; set; }
public Type DefinedType => _definedType;
public Type ParentDefinedType => _parentDefinedType;
public TypeDef ParentTypeDef { get; private set; }
public static Uuid GetTypeUUID(Type type)
{
var attr = type.GetCustomAttribute<TypeIdAttribute>();
if (attr != null)
return attr.Id;
var tn = Encoding.UTF8.GetBytes(GetTypeName(type));
var hash = SHA256.Create().ComputeHash(tn).Clip(0, 16);
hash[6] = (byte)((hash[6] & 0xF) | 0x80);
hash[8] = (byte)((hash[8] & 0xF) | 0x80);
var rt = new Uuid(hash);
return rt;
}
static Type[] GetDistributedTypes(Type type)
{
if (type.IsArray)
return GetDistributedTypes(type.GetElementType());
else if (type.IsEnum)
return new Type[] { type };
else if (Codec.ImplementsInterface(type, typeof(IRecord))
|| Codec.ImplementsInterface(type, typeof(IResource)))
{
return new Type[] { type };
}
else if (type.IsGenericType)
{
var genericType = type.GetGenericTypeDefinition();
var genericTypeArgs = type.GetGenericArguments();
if (genericType == typeof(List<>)
|| genericType == typeof(PropertyContext<>)
|| genericType == typeof(AsyncReply<>)
|| genericType == typeof(ResourceLink<>))
{
return GetDistributedTypes(genericTypeArgs[0]);
}
else if (genericType == typeof(Tuple<>)
|| genericType == typeof(Map<,>))
{
var rt = new List<Type>();
for (var i = 0; i < genericTypeArgs.Length; i++)
{
var depTypes = GetDistributedTypes(genericTypeArgs[i]);
foreach (var depType in depTypes)
if (!rt.Contains(depType))
rt.Add(depType);
}
return rt.ToArray();
}
}
return new Type[0];
}
public static TypeDef[] GetDependencies(LocalTypeDef typeDef, Warehouse warehouse)
{
var list = new List<TypeDef>();
// Add self
list.Add(typeDef);
Action<LocalTypeDef> getDependenciesFunc = null;
getDependenciesFunc = (LocalTypeDef td) =>
{
if (td.DefinedType == null)
return;
// Add parents
var parentType = td.ParentDefinedType;
// Get parents
while (parentType != null)
{
var parentTypeDef = warehouse.GetLocalTypeDefByType(parentType);
if (parentTypeDef != null)
{
if (!list.Contains(parentTypeDef))
{
list.Add(parentTypeDef);
if (parentTypeDef is LocalTypeDef pltd)
{
parentType = pltd.DefinedType;
}
}
}
}
// functions
foreach (var f in td._functions)
{
var functionReturnTypes = GetDistributedTypes(f.MethodInfo.ReturnType);
foreach (var functionReturnType in functionReturnTypes)
{
var functionReturnTypeDef = warehouse.GetLocalTypeDefByType(functionReturnType);
if (functionReturnTypeDef != null)
{
if (!list.Contains(functionReturnTypeDef))
{
list.Add(functionReturnTypeDef);
if (functionReturnTypeDef is LocalTypeDef frtd)
getDependenciesFunc(frtd);
}
}
}
var args = f.MethodInfo.GetParameters();
for (var i = 0; i < args.Length - 1; i++)
{
var fpTypes = GetDistributedTypes(args[i].ParameterType);
foreach (var fpType in fpTypes)
{
var fpt = warehouse.GetLocalTypeDefByType(fpType);
if (fpt != null)
{
if (!list.Contains(fpt))
{
list.Add(fpt);
if (fpt is LocalTypeDef ltd)
getDependenciesFunc(ltd);
}
}
}
}
// skip EpConnection argument
if (args.Length > 0)
{
var last = args.Last();
if (last.ParameterType != typeof(EpConnection))
{
var fpTypes = GetDistributedTypes(last.ParameterType);
foreach (var fpType in fpTypes)
{
var fpt = warehouse.GetLocalTypeDefByType(fpType);
if (fpt != null)
{
if (!list.Contains(fpt))
{
list.Add(fpt);
if (fpt is LocalTypeDef ltd)
getDependenciesFunc(ltd);
}
}
}
}
}
}
// properties
foreach (var p in td._properties)
{
var propertyTypes = GetDistributedTypes(p.PropertyInfo.PropertyType);
foreach (var propertyType in propertyTypes)
{
var propertyTypeDef = warehouse.GetLocalTypeDefByType(propertyType);
if (propertyTypeDef != null)
{
if (!list.Contains(propertyTypeDef))
{
list.Add(propertyTypeDef);
if (propertyTypeDef is LocalTypeDef ltd)
getDependenciesFunc(ltd);
}
}
}
}
// events
foreach (var e in td._events)
{
var eventTypes = GetDistributedTypes(e.EventInfo.EventHandlerType.GenericTypeArguments[0]);
foreach (var eventType in eventTypes)
{
var eventTypeDef = warehouse.GetLocalTypeDefByType(eventType);
if (eventTypeDef != null)
{
if (!list.Contains(eventTypeDef))
{
list.Add(eventTypeDef);
if (eventTypeDef is LocalTypeDef ltd)
getDependenciesFunc(ltd);
}
}
}
}
};
getDependenciesFunc(typeDef);
return list.Distinct().ToArray();
}
public static string GetTypeName(Type type, char separator = '.')
{
if (type.IsGenericType)
{
var index = type.Name.IndexOf("`");
var name = $"{type.Namespace}{separator}{((index > -1) ? type.Name.Substring(0, index) : type.Name)}Of";
foreach (var t in type.GenericTypeArguments)
name += GetTypeName(t, '_');
return name;
}
else
return $"{type.Namespace?.Replace('.', separator) ?? "Global"}{separator}{type.Name}";
}
public LocalTypeDef(Type type, Warehouse warehouse)
{
if (Codec.ImplementsInterface(type, typeof(IResource)))
_typeDefKind = TypeDefKind.Resource;
else if (Codec.ImplementsInterface(type, typeof(IRecord)))
_typeDefKind = TypeDefKind.Record;
else if (type.IsEnum)
_typeDefKind = TypeDefKind.Enum;
else
throw new Exception("Type must implement IResource, IRecord or inherit from DistributedResource.");
//IsWrapper = Codec.InheritsClass(type, typeof(EpResource));
type = ResourceProxy.GetBaseType(type);
_definedType = type;
_typeName = GetTypeName(type);
warehouse.TryRegisterLocalTypeDef(this);
var hierarchy = GetHierarchy(type);
if (hierarchy.ContainsKey(MemberTypes.Field))
{
foreach (var cd in hierarchy[MemberTypes.Field])
{
_constants.Add(ConstantDef.MakeConstantDef
(warehouse, type, (FieldInfo)cd.GetMemberInfo(), cd.Index, cd.Name, this));
}
}
if (hierarchy.ContainsKey(MemberTypes.Property))
{
foreach (var pd in hierarchy[MemberTypes.Property])
{
_properties.Add(PropertyDef.MakePropertyDef
( warehouse, type, (PropertyInfo)pd.GetMemberInfo(), pd.Name, pd.Index, pd.PropertyPermission, this));
}
}
if (_typeDefKind == TypeDefKind.Resource)
{
if (hierarchy.ContainsKey(MemberTypes.Method))
{
foreach (var fd in hierarchy[MemberTypes.Method])
{
_functions.Add(FunctionDef.MakeFunctionDef
(warehouse, type, (MethodInfo)fd.GetMemberInfo(), fd.Index, fd.Name, this));
}
}
if (hierarchy.ContainsKey(MemberTypes.Event))
{
foreach (var ed in hierarchy[MemberTypes.Event])
{
_events.Add(EventDef.MakeEventDef
( warehouse, type, (EventInfo)ed.GetMemberInfo(), ed.Index, ed.Name, this));
}
}
}
// add attributes
var attrs = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.GetCustomAttribute<AttributeAttribute>() != null);
foreach (var attr in attrs)
{
var attrAttr = attr.GetCustomAttribute<AttributeAttribute>();
_attributes.Add(AttributeDef
.MakeAttributeDef(type, attr, 0, attrAttr?.Name ?? attr.Name, this));
}
// find the first parent type that implements IResource
var hasParent = HasParent(type);
var classAnnotations = type.GetCustomAttributes<AnnotationAttribute>(false);
var hasClassAnnotation = (classAnnotations != null) && (classAnnotations.Count() > 0);
if (hasParent)
{
// find the first parent type that implements IResource
_parentDefinedType = ResourceProxy.GetBaseType(type.BaseType);
var parentTypeDef = warehouse.GetLocalTypeDefByType(_parentDefinedType);
if (parentTypeDef == null)
throw new Exception("Can't find parent TypeDef.");
ParentTypeDef = parentTypeDef;
}
if (hasClassAnnotation)
{
Annotations = new Map<string, string>();
foreach (var ann in classAnnotations)
Annotations.Add(ann.Key, ann.Value);
}
}
public override byte[] Compose(EpConnection connection)
{
// bake it binarily
var b = new BinaryList();
// find the first parent type that implements IResource
var hasParent = ParentTypeDef != null;
var hasClassAnnotation = Annotations != null && Annotations.Count() > 0;
var typeNameBytes = DC.ToBytes(_typeName);
b.AddUInt8((byte)((hasParent ? 0x80 : 0) | (hasClassAnnotation ? 0x40 : 0x0) | (byte)_typeDefKind))
.AddUInt64(_typeId)
.AddUInt8((byte)typeNameBytes.Length)
.AddUInt8Array(typeNameBytes);
if (hasParent)
{
b.AddUInt64(ParentTypeDef.Id);
}
if (hasClassAnnotation)
{
//foreach (var ann in Annotations)
// Annotations.Add(ann.Key, ann.Value);
var classAnnotationBytes = Codec.Compose(Annotations, null, null);
b.AddUInt8Array(classAnnotationBytes);
}
b.AddInt32(_version)
.AddUInt16((ushort)(_functions.Count + _properties.Count + _events.Count + _constants.Count));
foreach (var ft in _functions)
b.AddUInt8Array(ft.Compose(connection ));
foreach (var pt in _properties)
b.AddUInt8Array(pt.Compose(connection));
foreach (var et in _events)
b.AddUInt8Array(et.Compose(connection));
foreach (var ct in _constants)
b.AddUInt8Array(ct.Compose(connection));
return b.ToArray();
}
public static bool HasParent(Type type)
{
var parent = type.BaseType;
while (parent != null)
{
if (parent == typeof(Esiur.Resource.Resource)
|| parent == typeof(Record)
|| parent == typeof(EntryPoint))
return false;
if (parent.GetInterfaces().Contains(typeof(IResource))
|| parent.GetInterfaces().Contains(typeof(IRecord)))
return true;
parent = parent.BaseType;
}
return false;
}
public static Dictionary<MemberTypes, List<MemberData>> GetHierarchy(Type type)
{
var members = new List<MemberData>();
var order = 0;
while (type != null)
{
var classIsPublic = type.IsEnum || (type.GetCustomAttribute<ExportAttribute>() != null);
if (classIsPublic)
{
// get public instance members only.
var mis = type.GetMembers(BindingFlags.Public | BindingFlags.Instance
| BindingFlags.DeclaredOnly | BindingFlags.Static)
.Where(x => x.MemberType == MemberTypes.Property || x.MemberType == MemberTypes.Field
|| x.MemberType == MemberTypes.Event || x.MemberType == MemberTypes.Method)
.Where(x => !(x is FieldInfo c && !c.IsStatic))
.Where(x => x.GetCustomAttribute<IgnoreAttribute>() == null)
.Where(x => x.Name != "Instance")
.Where(x => x.Name != "Trigger")
.Where(x => !(x is MethodInfo m && m.IsSpecialName))
.Where(x => !(x is EventInfo e &&
!(e.EventHandlerType.IsGenericType &&
(e.EventHandlerType.GetGenericTypeDefinition() == typeof(ResourceEventHandler<>)
|| e.EventHandlerType.GetGenericTypeDefinition() == typeof(CustomResourceEventHandler<>))
)
))
.Select(x => new MemberData(
info: x,
order: order
))
.OrderBy(x => x.Name);
members.AddRange(mis.ToArray());
}
else
{
// allow private and public members that are marked with [Export] attribute.
var mis = type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
| BindingFlags.DeclaredOnly | BindingFlags.Static)
.Where(x => x.MemberType == MemberTypes.Property || x.MemberType == MemberTypes.Field
|| x.MemberType == MemberTypes.Event || x.MemberType == MemberTypes.Method)
.Where(x => !(x is FieldInfo c && !c.IsStatic))
.Where(x => x.GetCustomAttribute<ExportAttribute>() != null)
.Where(x => !(x is MethodInfo m && m.IsSpecialName))
.Select(x => new MemberData(
info: x,
order: order
))
.OrderBy(x => x.Name);
members.AddRange(mis.ToArray());
}
type = type.BaseType;
if (type == null
|| type == typeof(Esiur.Resource.Resource)
|| type == typeof(Record)
|| type == typeof(EntryPoint))
break;
if (type.GetInterfaces().Contains(typeof(IResource))
|| type.GetInterfaces().Contains(typeof(IRecord)))
{
order++;
continue;
}
break;
}
// round 2: check for duplicates
for (var i = 0; i < members.Count; i++)
{
var mi = members[i];
for (var j = i + 1; j < members.Count; j++)
{
var pi = members[j];
if (pi.Info.MemberType != mi.Info.MemberType)
continue;
//if (ci.Info.Name == mi.Info.Name && ci.Order == mi.Order)
// throw new Exception($"Method overload is not supported. Method '{ci.Info.Name}'.");
if (pi.Name == mi.Name)
{
if (pi.Order == mi.Order)
throw new Exception($"Duplicate definitions for members public name '{mi.Info.DeclaringType.Name}:{mi.Info.Name}' and '{pi.Info.DeclaringType.Name}:{pi.Info.Name}'.");
else
{
// @TODO: check for return type and parameters they must match
if (pi.Info.Name != mi.Info.Name)
throw new Exception($"Duplicate definitions for members public name '{mi.Info.DeclaringType.Name}:{mi.Info.Name}' and '{pi.Info.DeclaringType.Name}:{pi.Info.Name}'.");
}
mi.Parent = pi;
pi.Child = mi;
}
}
}
// assign indexes
var groups = members.Where(x => x.Parent == null)
.OrderBy(x => x.Name).OrderByDescending(x => x.Order)
.GroupBy(x => x.Info.MemberType);
foreach (var group in groups)
{
byte index = 0;
foreach (var mi in group)
{
//if (mi.Parent == null)
mi.Index = index++;
}
}
var rt = groups.ToDictionary(g => g.Key, g => g.ToList());
return rt;
}
//public Map<byte, object> CastProperties(Map<string, object> properties)
//{
// var rt = new Map<byte, object>();
// foreach (var kv in properties)
// {
// var pt = GetPropertyDefByName(kv.Key);
// if (pt == null) continue;
// rt.Add(pt.Index, kv.Value);
// }
// return rt;
//}
}
+17 -13
View File
@@ -1,4 +1,5 @@
using Esiur.Data;
using Esiur.Core;
using Esiur.Data;
using Esiur.Protocol;
using Esiur.Resource;
using System;
@@ -80,7 +81,7 @@ public class PropertyDef : MemberDef
return $"{Name}: {ValueType}";
}
public static (uint, PropertyDef) Parse(byte[] data, uint offset, byte index, bool inherited)
public static async AsyncReply<ParseResult<PropertyDef>> ParseAsync(byte[] data, uint offset, byte index, bool inherited, EpConnection connection, ulong[] requestSequence)
{
var oOffset = offset;
@@ -91,11 +92,13 @@ public class PropertyDef : MemberDef
var permission = (PropertyPermission)((data[offset++] >> 1) & 0x3);
var name = data.GetString(offset + 1, data[offset]);
//Console.WriteLine("Parsing propdef " + name);
offset += (uint)data[offset] + 1;
var (dts, valueType) = Tru.Parse(data, offset);
var valueType = await Tru.ParseAsync(data, offset, connection, requestSequence);
offset += dts;
offset += valueType.Size;
Map<string, string> annotations = null;
@@ -110,20 +113,20 @@ public class PropertyDef : MemberDef
offset += len;
}
return (offset - oOffset, new PropertyDef()
return new ParseResult<PropertyDef>(new PropertyDef()
{
Index = index,
Name = name,
Inherited = inherited,
Permission = permission,
HasHistory = hasHistory,
ValueType = valueType,
ValueType = valueType.Value,
Annotations = annotations
});
}, offset - oOffset);
}
public byte[] Compose()
public byte[] Compose(EpConnection connection)
{
var name = DC.ToBytes(Name);
@@ -178,7 +181,7 @@ public class PropertyDef : MemberDef
.AddUInt8((byte)(0x28 | pv))
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(ValueType.Compose())
.AddUInt8Array(ValueType.Compose(connection))
.AddUInt8Array(rexp)
.ToArray();
}
@@ -188,20 +191,21 @@ public class PropertyDef : MemberDef
.AddUInt8((byte)(0x20 | pv))
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(ValueType.Compose())
.AddUInt8Array(ValueType.Compose(connection))
.ToArray();
}
}
public static PropertyDef MakePropertyDef(Type type, PropertyInfo pi, string name, byte index, PropertyPermission permission, TypeDef schema)
public static PropertyDef MakePropertyDef(Warehouse warehouse, Type type, PropertyInfo pi, string name, byte index, PropertyPermission permission, TypeDef schema)
{
var genericPropType = pi.PropertyType.IsGenericType ? pi.PropertyType.GetGenericTypeDefinition() : null;
// @TODO: need to check if the type is remote
var propType = genericPropType == typeof(PropertyContext<>) ?
Tru.FromType(pi.PropertyType.GetGenericArguments()[0]) :
Tru.FromType(pi.PropertyType);
Tru.FromType(pi.PropertyType.GetGenericArguments()[0], warehouse) :
Tru.FromType(pi.PropertyType, warehouse);
if (propType == null)
throw new Exception($"Unsupported type `{pi.PropertyType}` in property `{type.Name}.{pi.Name}`");
+130
View File
@@ -0,0 +1,130 @@
using Esiur.Core;
using Esiur.Data;
using Esiur.Misc;
using Esiur.Protocol;
using Esiur.Proxy;
using Esiur.Resource;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
namespace Esiur.Data.Types;
public class RemoteTypeDef:TypeDef
{
Type _proxyType { get; set; }
public Type ProxyType => _proxyType;
string _domain;
public string Domain => _domain;
public uint _localTypeDefId;
public uint LocalTypeDefId
{
get => _localTypeDefId;
internal set => _localTypeDefId = value;
}
public static AsyncReply<RemoteTypeDef> Parse(RemoteTypeDef od, string domain, byte[] data, EpConnection connection, ulong[] requestSequence)
{
return Parse(od, domain, data, 0, (uint)data.Length, connection, requestSequence);
}
public static async AsyncReply<RemoteTypeDef> Parse(RemoteTypeDef od, string domain, byte[] data, uint offset, uint contentLength, EpConnection connection, ulong[] requestSequence)
{
uint ends = offset + contentLength;
uint oOffset = offset;
// start parsing...
od._domain = domain;
od._content = data.Clip(offset, contentLength);
var hasParent = (data[offset] & 0x80) > 0;
var hasClassAnnotation = (data[offset] & 0x40) > 0;
od._typeDefKind = (TypeDefKind)(data[offset++] & 0xF);
od._typeId = data.GetUInt64(offset, Endian.Little);
offset += 8;
od._typeName = data.GetString(offset + 1, data[offset]);
offset += (uint)data[offset] + 1;
if (hasParent)
{
od._parentTypeId = data.GetUInt64(offset, Endian.Little);
offset += 8;
}
if (hasClassAnnotation)
{
var (len, anns) = Codec.ParseSync(data, offset, null);
if (anns is Map<string, string> annotations)
od.Annotations = annotations;
offset += len;
}
od._version = data.GetInt32(offset, Endian.Little);
offset += 4;
ushort methodsCount = data.GetUInt16(offset, Endian.Little);
offset += 2;
byte functionIndex = 0;
byte propertyIndex = 0;
byte eventIndex = 0;
byte constantIndex = 0;
for (int i = 0; i < methodsCount; i++)
{
var inherited = (data[offset] & 0x80) > 0;
var type = (data[offset] >> 5) & 0x3;
if (type == 0) // function
{
var ft = await FunctionDef.ParseAsync(data, offset, functionIndex++, inherited, connection, requestSequence);
offset += ft.Size;
od._functions.Add(ft.Value);
}
else if (type == 1) // property
{
var pt = await PropertyDef.ParseAsync(data, offset, propertyIndex++, inherited, connection, requestSequence);
offset += pt.Size;
od._properties.Add(pt.Value);
}
else if (type == 2) // Event
{
var et = await EventDef.ParseAsync(data, offset, eventIndex++, inherited, connection, requestSequence);
offset += et.Size;
od._events.Add(et.Value);
}
// constant
else if (type == 3)
{
var ct = await ConstantDef.ParseAsync(data, offset, constantIndex++, inherited, connection, requestSequence);
offset += ct.Size;
od._constants.Add(ct.Value);
}
}
return od;
}
}
+50 -636
View File
@@ -18,46 +18,51 @@ namespace Esiur.Data.Types;
public class TypeDef
{
protected Uuid typeId;
protected Uuid? parentId;
protected ulong _typeId;
protected ulong? _parentTypeId;
public Map<string, string> Annotations { get; set; }
string typeName;
List<FunctionDef> functions = new List<FunctionDef>();
List<EventDef> events = new List<EventDef>();
List<PropertyDef> properties = new List<PropertyDef>();
List<AttributeDef> attributes = new List<AttributeDef>();
List<ConstantDef> constants = new();
int version;
TypeDefKind typeDefKind;
protected string _typeName;
protected List<FunctionDef> _functions = new List<FunctionDef>();
protected List<EventDef> _events = new List<EventDef>();
protected List<PropertyDef> _properties = new List<PropertyDef>();
protected List<AttributeDef> _attributes = new List<AttributeDef>();
protected List<ConstantDef> _constants = new();
protected int _version;
protected TypeDefKind _typeDefKind;
//Type _definedType { get; set; }
//Type _proxyType { get; set; }
//Type _parentDefinedType { get; set; }
//bool _isLocal;
//public Type DefinedType => _definedType;
//public Type ParentDefinedType => _parentDefinedType;
//public Type ProxyType => _proxyType;
public override string ToString()
{
return typeName;
return _typeName;
}
protected byte[] _content;
protected byte[] content;
public ulong? ParentTypeId => _parentTypeId;
public Uuid? ParentId => parentId;
//public byte[] Content => _content;
public byte[] Content
{
get { return content; }
}
public TypeDefKind Kind => typeDefKind;
public TypeDefKind Kind => _typeDefKind;
public Type DefinedType { get; set; }
public Type ParentDefinedType { get; set; }
public EventDef GetEventDefByName(string eventName)
{
foreach (var i in events)
foreach (var i in _events)
if (i.Name == eventName)
return i;
return null;
@@ -65,7 +70,7 @@ public class TypeDef
public EventDef GetEventDefByIndex(byte index)
{
foreach (var i in events)
foreach (var i in _events)
if (i.Index == index)
return i;
return null;
@@ -73,14 +78,14 @@ public class TypeDef
public FunctionDef GetFunctionDefByName(string functionName)
{
foreach (var i in functions)
foreach (var i in _functions)
if (i.Name == functionName)
return i;
return null;
}
public FunctionDef GetFunctionDefByIndex(byte index)
{
foreach (var i in functions)
foreach (var i in _functions)
if (i.Index == index)
return i;
return null;
@@ -88,7 +93,7 @@ public class TypeDef
public PropertyDef GetPropertyDefByIndex(byte index)
{
foreach (var i in properties)
foreach (var i in _properties)
if (i.Index == index)
return i;
return null;
@@ -96,7 +101,7 @@ public class TypeDef
public PropertyDef GetPropertyDefByName(string propertyName)
{
foreach (var i in properties)
foreach (var i in _properties)
if (i.Name == propertyName)
return i;
return null;
@@ -104,645 +109,47 @@ public class TypeDef
public AttributeDef GetAttributeDef(string attributeName)
{
foreach (var i in attributes)
foreach (var i in _attributes)
if (i.Name == attributeName)
return i;
return null;
}
public Uuid Id
public ulong Id
{
get { return typeId; }
get { return _typeId; }
internal set { _typeId = value; }
}
public string Name
{
get { return typeName; }
get { return _typeName; }
}
public FunctionDef[] Functions
{
get { return functions.ToArray(); }
get { return _functions.ToArray(); }
}
public EventDef[] Events
{
get { return events.ToArray(); }
get { return _events.ToArray(); }
}
public PropertyDef[] Properties
{
get { return properties.ToArray(); }
get { return _properties.ToArray(); }
}
public ConstantDef[] Constants => constants.ToArray();
public ConstantDef[] Constants => _constants.ToArray();
public TypeDef()
{
}
public static Uuid GetTypeUUID(Type type)
{
var attr = type.GetCustomAttribute<TypeIdAttribute>();
if (attr != null)
return attr.Id;
var tn = Encoding.UTF8.GetBytes(GetTypeName(type));
var hash = SHA256.Create().ComputeHash(tn).Clip(0, 16);
hash[6] = (byte)((hash[6] & 0xF) | 0x80);
hash[8] = (byte)((hash[8] & 0xF) | 0x80);
var rt = new Uuid(hash);
return rt;
}
static Type[] GetDistributedTypes(Type type)
{
if (type.IsArray)
return GetDistributedTypes(type.GetElementType());
else if (type.IsEnum)
return new Type[] { type };
else if (Codec.ImplementsInterface(type, typeof(IRecord))
|| Codec.ImplementsInterface(type, typeof(IResource)))
{
return new Type[] { type };
}
else if (type.IsGenericType)
{
var genericType = type.GetGenericTypeDefinition();
var genericTypeArgs = type.GetGenericArguments();
if (genericType == typeof(List<>)
|| genericType == typeof(PropertyContext<>)
|| genericType == typeof(AsyncReply<>)
|| genericType == typeof(ResourceLink<>))
{
return GetDistributedTypes(genericTypeArgs[0]);
}
else if (genericType == typeof(Tuple<>)
|| genericType == typeof(Map<,>))
{
var rt = new List<Type>();
for (var i = 0; i < genericTypeArgs.Length; i++)
{
var depTypes = GetDistributedTypes(genericTypeArgs[i]);
foreach (var depType in depTypes)
if (!rt.Contains(depType))
rt.Add(depType);
}
return rt.ToArray();
}
}
return new Type[0];
}
public static TypeDef[] GetDependencies(TypeDef schema, Warehouse warehouse)
{
var list = new List<TypeDef>();
// Add self
list.Add(schema);
Action<TypeDef, List<TypeDef>> getDependenciesFunc = null;
getDependenciesFunc = (TypeDef sch, List<TypeDef> bag) =>
{
if (schema.DefinedType == null)
return;
// Add parents
var parentType = sch.ParentDefinedType;
// Get parents
while (parentType != null)
{
var parentTypeDef = warehouse.GetTypeDefByType(parentType);
if (parentTypeDef != null)
{
list.Add(parentTypeDef);
parentType = parentTypeDef.ParentDefinedType;
}
}
// functions
foreach (var f in sch.functions)
{
var functionReturnTypes = GetDistributedTypes(f.MethodInfo.ReturnType);
foreach (var functionReturnType in functionReturnTypes)
{
var functionReturnTypeDef = warehouse.GetTypeDefByType(functionReturnType);
if (functionReturnTypeDef != null)
{
if (!bag.Contains(functionReturnTypeDef))
{
list.Add(functionReturnTypeDef);
getDependenciesFunc(functionReturnTypeDef, bag);
}
}
}
var args = f.MethodInfo.GetParameters();
for (var i = 0; i < args.Length - 1; i++)
{
var fpTypes = GetDistributedTypes(args[i].ParameterType);
foreach (var fpType in fpTypes)
{
var fpt = warehouse.GetTypeDefByType(fpType);
if (fpt != null)
{
if (!bag.Contains(fpt))
{
bag.Add(fpt);
getDependenciesFunc(fpt, bag);
}
}
}
}
// skip EpConnection argument
if (args.Length > 0)
{
var last = args.Last();
if (last.ParameterType != typeof(EpConnection))
{
var fpTypes = GetDistributedTypes(last.ParameterType);
foreach (var fpType in fpTypes)
{
var fpt = warehouse.GetTypeDefByType(fpType);
if (fpt != null)
{
if (!bag.Contains(fpt))
{
bag.Add(fpt);
getDependenciesFunc(fpt, bag);
}
}
}
}
}
}
// properties
foreach (var p in sch.properties)
{
var propertyTypes = GetDistributedTypes(p.PropertyInfo.PropertyType);
foreach (var propertyType in propertyTypes)
{
var propertyTypeDef = warehouse.GetTypeDefByType(propertyType);
if (propertyTypeDef != null)
{
if (!bag.Contains(propertyTypeDef))
{
bag.Add(propertyTypeDef);
getDependenciesFunc(propertyTypeDef, bag);
}
}
}
}
// events
foreach (var e in sch.events)
{
var eventTypes = GetDistributedTypes(e.EventInfo.EventHandlerType.GenericTypeArguments[0]);
foreach (var eventType in eventTypes)
{
var eventTypeDef = warehouse.GetTypeDefByType(eventType);
if (eventTypeDef != null)
{
if (!bag.Contains(eventTypeDef))
{
bag.Add(eventTypeDef);
getDependenciesFunc(eventTypeDef, bag);
}
}
}
}
};
getDependenciesFunc(schema, list);
return list.Distinct().ToArray();
}
public static string GetTypeName(Type type, char separator = '.')
{
if (type.IsGenericType)
{
var index = type.Name.IndexOf("`");
var name = $"{type.Namespace}{separator}{((index > -1) ? type.Name.Substring(0, index) : type.Name)}Of";
foreach (var t in type.GenericTypeArguments)
name += GetTypeName(t, '_');
return name;
}
else
return $"{type.Namespace?.Replace('.', separator) ?? "Global"}{separator}{type.Name}";
}
public bool IsWrapper { get; private set; }
public TypeDef(Type type, Warehouse warehouse = null)
{
if (Codec.ImplementsInterface(type, typeof(IResource)))
typeDefKind = TypeDefKind.Resource;
else if (Codec.ImplementsInterface(type, typeof(IRecord)))
typeDefKind = TypeDefKind.Record;
else if (type.IsEnum)
typeDefKind = TypeDefKind.Enum;
else
throw new Exception("Type must implement IResource, IRecord or inherit from DistributedResource.");
IsWrapper = Codec.InheritsClass(type, typeof(EpResource));
type = ResourceProxy.GetBaseType(type);
DefinedType = type;
typeName = GetTypeName(type);
// set guid
typeId = GetTypeUUID(type);
if (warehouse != null)
warehouse.RegisterTypeDef(this);
var hierarchy = GetHierarchy(type);
if (hierarchy.ContainsKey(MemberTypes.Field))
{
foreach (var cd in hierarchy[MemberTypes.Field])
{
constants.Add(ConstantDef.MakeConstantDef
(type, (FieldInfo)cd.GetMemberInfo(), cd.Index, cd.Name, this));
}
}
if (hierarchy.ContainsKey(MemberTypes.Property))
{
foreach (var pd in hierarchy[MemberTypes.Property])
{
properties.Add(PropertyDef.MakePropertyDef
(type, (PropertyInfo)pd.GetMemberInfo(), pd.Name, pd.Index, pd.PropertyPermission, this));
}
}
if (typeDefKind == TypeDefKind.Resource)
{
if (hierarchy.ContainsKey(MemberTypes.Method))
{
foreach (var fd in hierarchy[MemberTypes.Method])
{
functions.Add(FunctionDef.MakeFunctionDef
(type, (MethodInfo)fd.GetMemberInfo(), fd.Index, fd.Name, this));
}
}
if (hierarchy.ContainsKey(MemberTypes.Event))
{
foreach (var ed in hierarchy[MemberTypes.Event])
{
events.Add(EventDef.MakeEventDef
(type, (EventInfo)ed.GetMemberInfo(), ed.Index, ed.Name, this));
}
}
}
// add attributes
var attrs = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.GetCustomAttribute<AttributeAttribute>() != null);
foreach (var attr in attrs)
{
var attrAttr = attr.GetCustomAttribute<AttributeAttribute>();
attributes.Add(AttributeDef
.MakeAttributeDef(type, attr, 0, attrAttr?.Name ?? attr.Name, this));
}
// bake it binarily
var b = new BinaryList();
// find the first parent type that implements IResource
var hasParent = HasParent(type);
var classAnnotations = type.GetCustomAttributes<AnnotationAttribute>(false);
var hasClassAnnotation = (classAnnotations != null) && (classAnnotations.Count() > 0);
var typeNameBytes = DC.ToBytes(typeName);
b.AddUInt8((byte)((hasParent ? 0x80 : 0) | (hasClassAnnotation ? 0x40 : 0x0) | (byte)typeDefKind))
.AddUUID(typeId)
.AddUInt8((byte)typeNameBytes.Length)
.AddUInt8Array(typeNameBytes);
if (hasParent)
{
// find the first parent type that implements IResource
ParentDefinedType = ResourceProxy.GetBaseType(type.BaseType);
var parentId = GetTypeUUID(ParentDefinedType);
b.AddUUID(parentId);
}
if (hasClassAnnotation)
{
Annotations = new Map<string, string>();
foreach (var ann in classAnnotations)
Annotations.Add(ann.Key, ann.Value);
var classAnnotationBytes = Codec.Compose (Annotations, null, null);
b.AddUInt8Array(classAnnotationBytes);
}
b.AddInt32(version)
.AddUInt16((ushort)(functions.Count + properties.Count + events.Count + constants.Count));
foreach (var ft in functions)
b.AddUInt8Array(ft.Compose());
foreach (var pt in properties)
b.AddUInt8Array(pt.Compose());
foreach (var et in events)
b.AddUInt8Array(et.Compose());
foreach (var ct in constants)
b.AddUInt8Array(ct.Compose());
content = b.ToArray();
}
public static bool HasParent(Type type)
{
var parent = type.BaseType;
while (parent != null)
{
if (parent == typeof(Esiur.Resource.Resource)
|| parent == typeof(Record)
|| parent == typeof(EntryPoint))
return false;
if (parent.GetInterfaces().Contains(typeof(IResource))
|| parent.GetInterfaces().Contains(typeof(IRecord)))
return true;
parent = parent.BaseType;
}
return false;
}
public static Dictionary<MemberTypes, List<MemberData>> GetHierarchy(Type type)
{
var members = new List<MemberData>();
var order = 0;
while (type != null)
{
var classIsPublic = type.IsEnum || (type.GetCustomAttribute<ExportAttribute>() != null);
if (classIsPublic)
{
// get public instance members only.
var mis = type.GetMembers(BindingFlags.Public | BindingFlags.Instance
| BindingFlags.DeclaredOnly | BindingFlags.Static)
.Where(x => x.MemberType == MemberTypes.Property || x.MemberType == MemberTypes.Field
|| x.MemberType == MemberTypes.Event || x.MemberType == MemberTypes.Method)
.Where(x => !(x is FieldInfo c && !c.IsStatic))
.Where(x => x.GetCustomAttribute<IgnoreAttribute>() == null)
.Where(x => x.Name != "Instance")
.Where(x => x.Name != "Trigger")
.Where(x => !(x is MethodInfo m && m.IsSpecialName))
.Where(x => !(x is EventInfo e &&
!(e.EventHandlerType.IsGenericType &&
(e.EventHandlerType.GetGenericTypeDefinition() == typeof(ResourceEventHandler<>)
|| e.EventHandlerType.GetGenericTypeDefinition() == typeof(CustomResourceEventHandler<>))
)
))
.Select(x => new MemberData(
info: x,
order: order
))
.OrderBy(x => x.Name);
members.AddRange(mis.ToArray());
}
else
{
// allow private and public members that are marked with [Export] attribute.
var mis = type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
| BindingFlags.DeclaredOnly | BindingFlags.Static)
.Where(x => x.MemberType == MemberTypes.Property || x.MemberType == MemberTypes.Field
|| x.MemberType == MemberTypes.Event || x.MemberType == MemberTypes.Method)
.Where(x => !(x is FieldInfo c && !c.IsStatic))
.Where(x => x.GetCustomAttribute<ExportAttribute>() != null)
.Where(x => !(x is MethodInfo m && m.IsSpecialName))
.Select(x => new MemberData(
info: x,
order: order
))
.OrderBy(x => x.Name);
members.AddRange(mis.ToArray());
}
type = type.BaseType;
if (type == null
|| type == typeof(Esiur.Resource.Resource)
|| type == typeof(Record)
|| type == typeof(EntryPoint))
break;
if (type.GetInterfaces().Contains(typeof(IResource))
|| type.GetInterfaces().Contains(typeof(IRecord)))
{
order++;
continue;
}
break;
}
// round 2: check for duplicates
for (var i = 0; i < members.Count; i++)
{
var mi = members[i];
for (var j = i + 1; j < members.Count; j++)
{
var pi = members[j];
if (pi.Info.MemberType != mi.Info.MemberType)
continue;
//if (ci.Info.Name == mi.Info.Name && ci.Order == mi.Order)
// throw new Exception($"Method overload is not supported. Method '{ci.Info.Name}'.");
if (pi.Name == mi.Name)
{
if (pi.Order == mi.Order)
throw new Exception($"Duplicate definitions for members public name '{mi.Info.DeclaringType.Name}:{mi.Info.Name}' and '{pi.Info.DeclaringType.Name}:{pi.Info.Name}'.");
else
{
// @TODO: check for return type and parameters they must match
if (pi.Info.Name != mi.Info.Name)
throw new Exception($"Duplicate definitions for members public name '{mi.Info.DeclaringType.Name}:{mi.Info.Name}' and '{pi.Info.DeclaringType.Name}:{pi.Info.Name}'.");
}
mi.Parent = pi;
pi.Child = mi;
}
}
}
// assign indexes
var groups = members.Where(x => x.Parent == null)
.OrderBy(x => x.Name).OrderByDescending(x => x.Order)
.GroupBy(x => x.Info.MemberType);
foreach (var group in groups)
{
byte index = 0;
foreach (var mi in group)
{
//if (mi.Parent == null)
mi.Index = index++;
}
}
var rt = groups.ToDictionary(g => g.Key, g => g.ToList());
return rt;
}
public static TypeDef Parse(byte[] data)
{
return Parse(data, 0, (uint)data.Length);
}
public static TypeDef Parse(byte[] data, uint offset, uint contentLength)
{
uint ends = offset + contentLength;
uint oOffset = offset;
// start parsing...
var od = new TypeDef();
od.content = data.Clip(offset, contentLength);
var hasParent = (data[offset] & 0x80) > 0;
var hasClassAnnotation = (data[offset] & 0x40) > 0;
od.typeDefKind = (TypeDefKind)(data[offset++] & 0xF);
od.typeId = data.GetUUID(offset);
offset += 16;
od.typeName = data.GetString(offset + 1, data[offset]);
offset += (uint)data[offset] + 1;
if (hasParent)
{
od.parentId = data.GetUUID(offset);
offset += 16;
}
if (hasClassAnnotation)
{
var (len, anns) = Codec.ParseSync(data, offset, null);
if (anns is Map<string, string> annotations)
od.Annotations = annotations;
offset += len;
}
od.version = data.GetInt32(offset, Endian.Little);
offset += 4;
ushort methodsCount = data.GetUInt16(offset, Endian.Little);
offset += 2;
byte functionIndex = 0;
byte propertyIndex = 0;
byte eventIndex = 0;
byte constantIndex = 0;
for (int i = 0; i < methodsCount; i++)
{
var inherited = (data[offset] & 0x80) > 0;
var type = (data[offset] >> 5) & 0x3;
if (type == 0) // function
{
var (len, ft) = FunctionDef.Parse(data, offset, functionIndex++, inherited);
offset += len;
od.functions.Add(ft);
}
else if (type == 1) // property
{
var (len, pt) = PropertyDef.Parse(data, offset, propertyIndex++, inherited);
offset += len;
od.properties.Add(pt);
}
else if (type == 2) // Event
{
var (len, et) = EventDef.Parse(data, offset, eventIndex++, inherited);
offset += len;
od.events.Add(et);
}
// constant
else if (type == 3)
{
var (len, ct) = ConstantDef.Parse(data, offset, constantIndex++, inherited);
offset += len;
od.constants.Add(ct);
}
}
return od;
}
public Map<byte, object> CastProperties(Map<string, object> properties)
{
var rt = new Map<byte, object>();
@@ -755,5 +162,12 @@ public class TypeDef
return rt;
}
public virtual byte[] Compose(EpConnection connection)
{
return null;
}
}