mirror of
https://github.com/esiur/esiur-dotnet.git
synced 2026-06-13 14:38:43 +00:00
Ver 3
This commit is contained in:
+164
-49
@@ -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
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
+738
-448
File diff suppressed because it is too large
Load Diff
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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}`");
|
||||
|
||||
@@ -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}`");
|
||||
|
||||
@@ -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}`");
|
||||
|
||||
@@ -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;
|
||||
//}
|
||||
}
|
||||
|
||||
@@ -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}`");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user