2
0
mirror of https://github.com/esiur/esiur-dotnet.git synced 2026-06-13 14:38:43 +00:00
This commit is contained in:
2026-05-24 18:27:22 +03:00
parent a6e8bed31d
commit eb323e8bf8
69 changed files with 6532 additions and 3371 deletions
+14 -17
View File
@@ -91,7 +91,7 @@ public class AsyncReply
}
//int timeoutMilliseconds = 0;
public AsyncReply Timeout(int milliseconds, Action callback = null)
public void Timeout(int milliseconds, Action callback = null)
{
//timeoutMilliseconds = milliseconds;
@@ -107,7 +107,6 @@ public class AsyncReply
}
});
return this;
}
public object Wait(int millisecondsTimeout)
@@ -236,7 +235,7 @@ public class AsyncReply
return this;
}
public AsyncReply Trigger(object result)
public void Trigger(object result)
{
lock (asyncLock)
{
@@ -245,13 +244,13 @@ public class AsyncReply
//timeout?.Dispose();
if (exception != null)
return this;
return;
//if (Debug)
// Console.WriteLine($"AsyncReply: {Id} Trigger");
if (resultReady)
return this;
return;
this.result = result;
@@ -269,15 +268,15 @@ public class AsyncReply
}
return this;
return;
}
public AsyncReply TriggerError(Exception exception)
public void TriggerError(Exception exception)
{
//timeout?.Dispose();
if (resultReady)
return this;
return;
if (exception is AsyncException)
this.exception = exception as AsyncException;
@@ -297,10 +296,9 @@ public class AsyncReply
mutex?.Set();
return this;
}
public AsyncReply TriggerProgress(ProgressType type, uint value, uint max)
public void TriggerProgress(ProgressType type, uint value, uint max)
{
//timeout?.Dispose();
@@ -308,10 +306,10 @@ public class AsyncReply
foreach (var cb in progressCallbacks)
cb(type, value, max);
return this;
return;
}
public AsyncReply TriggerWarning(byte level, string message)
public void TriggerWarning(byte level, string message)
{
//timeout?.Dispose();
@@ -319,11 +317,11 @@ public class AsyncReply
foreach (var cb in warningCallbacks)
cb(level, message);
return this;
return ;
}
public AsyncReply TriggerPropagation(object value)
public void TriggerPropagation(object value)
{
//timeout?.Dispose();
@@ -331,12 +329,12 @@ public class AsyncReply
foreach (var cb in propagationCallbacks)
cb(value);
return this;
return;
}
public AsyncReply TriggerChunk(object value)
public void TriggerChunk(object value)
{
//timeout?.Dispose();
@@ -347,7 +345,6 @@ public class AsyncReply
cb(value);
return this;
}
public AsyncAwaiter GetAwaiter()
+164 -49
View File
@@ -100,12 +100,7 @@ public static class Codec
static AsyncParser[] TypedAsyncParsers = new AsyncParser[]
{
DataDeserializer.RecordParserAsync,
DataDeserializer.TypedListParserAsync,
DataDeserializer.TypedMapParserAsync,
DataDeserializer.TupleParserAsync,
DataDeserializer.EnumParserAsync,
DataDeserializer.ConstantParserAsync,
DataDeserializer.TypedParserAsync,
};
static AsyncParser[] ExtendedAsyncParsers = new AsyncParser[]
@@ -170,12 +165,13 @@ public static class Codec
static SyncParser[] TypedParsers = new SyncParser[]
{
DataDeserializer.RecordParser,
DataDeserializer.TypedListParser,
DataDeserializer.TypedMapParser,
DataDeserializer.TupleParser,
DataDeserializer.EnumParser,
DataDeserializer.ConstantParser,
DataDeserializer.TypedParser,
//DataDeserializer.RecordParser,
//DataDeserializer.TypedListParser,
//DataDeserializer.TypedMapParser,
//DataDeserializer.TupleParser,
//DataDeserializer.EnumParser,
//DataDeserializer.ConstantParser,
};
static SyncParser[] ExtendedParsers = new SyncParser[]
@@ -192,81 +188,197 @@ public static class Codec
/// <param name="connection">EpConnection is required in case a structure in the array holds items at the other end.</param>
/// <param name="dataType">DataType, in case the data is not prepended with DataType</param>
/// <returns>Value</returns>
public static (uint, object) ParseAsync(byte[] data, uint offset, EpConnection connection, uint[] requestSequence)
public static AsyncReply<ParseResult<object>> ParseAsync(byte[] data, uint offset, EpConnection connection, uint[] requestSequence)
{
var rt = new AsyncReply<ParseResult<object>>();
var tdu = ParsedTdu.Parse(data, offset, (uint)data.Length);
ParsedTdu.ParseAsync(data, offset, (uint)data.Length, connection).Then(tdu =>
{
if (tdu.Class == TduClass.Invalid)
throw new NullReferenceException("DataType can't be parsed.");
if (tdu.Class == TduClass.Invalid)
throw new NullReferenceException("DataType can't be parsed.");
object result;
if (tdu.Class == TduClass.Fixed)
{
return ((uint)tdu.TotalLength, FixedAsyncParsers[tdu.Exponent][tdu.Index](tdu, connection, requestSequence));
}
else if (tdu.Class == TduClass.Dynamic)
{
return ((uint)tdu.TotalLength, DynamicAsyncParsers[tdu.Index](tdu, connection, requestSequence));
}
else if (tdu.Class == TduClass.Typed)
{
return ((uint)tdu.TotalLength, TypedAsyncParsers[tdu.Index](tdu, connection, requestSequence));
}
else // if (tt.Class == TDUClass.Extension)
{
return ((uint)tdu.TotalLength, ExtendedAsyncParsers[tdu.Index](tdu, connection, requestSequence));
if (tdu.Class == TduClass.Fixed)
{
result = FixedAsyncParsers[tdu.Exponent][tdu.Index](tdu, connection, requestSequence);
}
else if (tdu.Class == TduClass.Dynamic)
{
result = DynamicAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
else if (tdu.Class == TduClass.Typed)
{
result = TypedAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
else // if (tt.Class == TDUClass.Extension)
{
result = ExtendedAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
if (result is AsyncReply asyncReply)
{
asyncReply.Then(value =>
{
rt.Trigger(new ParseResult<object>(value, (uint)tdu.TotalLength));
});
}
else
{
rt.Trigger(new ParseResult<object>(result, (uint)tdu.TotalLength));
}
});
return rt;
}
}
public static (uint, object) ParseAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence)
public static object ParseAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence)
{
if (tdu.Class == TduClass.Invalid)
throw new NullReferenceException("DataType can't be parsed.");
if (tdu.Class == TduClass.Fixed)
{
return ((uint)tdu.TotalLength, FixedAsyncParsers[tdu.Exponent][tdu.Index](tdu, connection, requestSequence));
return FixedAsyncParsers[tdu.Exponent][tdu.Index](tdu, connection, requestSequence);
}
else if (tdu.Class == TduClass.Dynamic)
{
return ((uint)tdu.TotalLength, DynamicAsyncParsers[tdu.Index](tdu, connection, requestSequence));
return DynamicAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
else if (tdu.Class == TduClass.Typed)
{
return ((uint)tdu.TotalLength, TypedAsyncParsers[tdu.Index](tdu, connection, requestSequence));
return TypedAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
else // if (tt.Class == TDUClass.Extension)
{
return ((uint)tdu.TotalLength, ExtendedAsyncParsers[tdu.Index](tdu, connection, requestSequence));
return ExtendedAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
}
public static (uint, object) ParseSync(ParsedTdu tdu, Warehouse warehouse)
public static object Parse(PlainTdu plainTdu, EpConnection connection, uint[] requestSequence)
{
if (tdu.Class == TduClass.Fixed)
var parsedTdu = ParsedTdu.Parse(plainTdu.Data, plainTdu.TduOffset, (uint)plainTdu.Ends, connection);
if (parsedTdu is ParsedTdu tdu)
{
return ((uint)tdu.TotalLength, FixedParsers[tdu.Exponent][tdu.Index](tdu, warehouse));
if (tdu.Class == TduClass.Invalid)
throw new NullReferenceException("TDU can't be parsed.");
if (tdu.Class == TduClass.Fixed)
{
return FixedAsyncParsers[tdu.Exponent][tdu.Index](tdu, connection, requestSequence);
}
else if (tdu.Class == TduClass.Dynamic)
{
return DynamicAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
else if (tdu.Class == TduClass.Typed)
{
return TypedAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
else // if (tt.Class == TDUClass.Extension)
{
return ExtendedAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
}
else if (parsedTdu is AsyncReply<ParsedTdu> asyncReply)
{
var rt = new AsyncReply();
asyncReply.Then(tdu =>
{
if (tdu.Class == TduClass.Invalid)
throw new NullReferenceException("TDU can't be parsed.");
object result;
if (tdu.Class == TduClass.Fixed)
{
result = FixedAsyncParsers[tdu.Exponent][tdu.Index](tdu, connection, requestSequence);
}
else if (tdu.Class == TduClass.Dynamic)
{
result = DynamicAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
else if (tdu.Class == TduClass.Typed)
{
result = TypedAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
else // if (tt.Class == TDUClass.Extension)
{
result = ExtendedAsyncParsers[tdu.Index](tdu, connection, requestSequence);
}
if (result is AsyncReply resultReply)
{
resultReply.Then(rt.Trigger)
.Error(rt.TriggerError);
}
else
{
rt.Trigger(result);
}
});
return rt;
}
throw new NotImplementedException();
}
public static object ParseSync(PlainTdu plainTdu, Warehouse warehouse)
{
var tdu = ParsedTdu.ParseSync(plainTdu.Data, plainTdu.TduOffset, plainTdu.Ends, warehouse);
if (tdu.Class == TduClass.Invalid)
{
throw new Exception("Invalid TDU.");
}
else if (tdu.Class == TduClass.Fixed)
{
return FixedParsers[tdu.Exponent][tdu.Index](tdu, warehouse);
}
else if (tdu.Class == TduClass.Dynamic)
{
return ((uint)tdu.TotalLength, DynamicParsers[tdu.Index](tdu, warehouse));
return DynamicParsers[tdu.Index](tdu, warehouse);
}
else if (tdu.Class == TduClass.Typed)
{
return ((uint)tdu.TotalLength, TypedParsers[tdu.Index](tdu, warehouse));
return TypedParsers[tdu.Index](tdu, warehouse);
}
else // Extension
{
return ((uint)tdu.TotalLength, ExtendedParsers[tdu.Index](tdu, warehouse));
return ExtendedParsers[tdu.Index](tdu, warehouse);
}
}
public static object ParseSync(ParsedTdu tdu, Warehouse warehouse)
{
if (tdu.Class == TduClass.Fixed)
{
return FixedParsers[tdu.Exponent][tdu.Index](tdu, warehouse);
}
else if (tdu.Class == TduClass.Dynamic)
{
return DynamicParsers[tdu.Index](tdu, warehouse);
}
else if (tdu.Class == TduClass.Typed)
{
return TypedParsers[tdu.Index](tdu, warehouse);
}
else // Extension
{
return ExtendedParsers[tdu.Index](tdu, warehouse);
}
}
public static (uint, object) ParseSync(byte[] data, uint offset, Warehouse warehouse)
{
var tdu = ParsedTdu.Parse(data, offset, (uint)data.Length);
var tdu = ParsedTdu.ParseSync(data, offset, (uint)data.Length, warehouse);
if (tdu.Class == TduClass.Invalid)
throw new NullReferenceException("DataType can't be parsed.");
@@ -301,7 +413,7 @@ public static class Codec
throw new NullReferenceException("Resource is null.");
if (resource is EpResource)
if (((EpResource)(resource)).DistributedResourceConnection == connection)
if (((EpResource)(resource)).ResourceConnection == connection)
return true;
return false;
@@ -385,7 +497,7 @@ public static class Codec
ComposeInternal(object valueOrSource, Warehouse warehouse, EpConnection connection)
{
if (valueOrSource == null)
return new Tdu(TduIdentifier.Null, null, 0);
return new Tdu(TduIdentifier.Null, null, 0, null, null);
var type = valueOrSource.GetType();
@@ -412,7 +524,7 @@ public static class Codec
valueOrSource = ((IUserType)valueOrSource).Get();
if (valueOrSource == null)
return new Tdu(TduIdentifier.Null, null, 0);
return new Tdu(TduIdentifier.Null, null, 0, null, null);
type = valueOrSource.GetType();
@@ -488,7 +600,7 @@ public static class Codec
}
return new Tdu(TduIdentifier.Null, null, 0);
return new Tdu(TduIdentifier.Null, null, 0, null, null);
}
@@ -541,6 +653,9 @@ public static class Codec
{
while (type != null)
{
if (type == typeof(object))
return false;
if (type == iface)
return true;
File diff suppressed because it is too large Load Diff
+165 -190
View File
@@ -23,7 +23,7 @@ public static class DataSerializer
if (v >= sbyte.MinValue && v <= sbyte.MaxValue)
{
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1);
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1, null, null);
}
else if (v >= short.MinValue && v <= short.MaxValue)
{
@@ -32,7 +32,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((short*)ptr) = (short)v;
return new Tdu(TduIdentifier.Int16, rt, 2);
return new Tdu(TduIdentifier.Int16, rt, 2, null, null);
}
else
{
@@ -40,7 +40,7 @@ public static class DataSerializer
var rt = new byte[4];
fixed (byte* ptr = rt)
*((int*)ptr) = v;
return new Tdu(TduIdentifier.Int32, rt, 4);
return new Tdu(TduIdentifier.Int32, rt, 4, null, null);
}
}
@@ -51,7 +51,7 @@ public static class DataSerializer
if (v <= byte.MaxValue)
{
// Fits in 1 byte
return new Tdu(TduIdentifier.UInt8, new byte[] { (byte)v }, 1);
return new Tdu(TduIdentifier.UInt8, new byte[] { (byte)v }, 1, null, null);
}
else if (v <= ushort.MaxValue)
{
@@ -60,7 +60,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((ushort*)ptr) = (ushort)v;
return new Tdu(TduIdentifier.UInt16, rt, 2);
return new Tdu(TduIdentifier.UInt16, rt, 2, null, null);
}
else
{
@@ -69,7 +69,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((uint*)ptr) = v;
return new Tdu(TduIdentifier.UInt32, rt, 4);
return new Tdu(TduIdentifier.UInt32, rt, 4, null, null);
}
}
@@ -80,7 +80,7 @@ public static class DataSerializer
if (v >= sbyte.MinValue && v <= sbyte.MaxValue)
{
// Fits in 1 byte
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1);
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1, null, null);
}
else
{
@@ -89,7 +89,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((short*)ptr) = v;
return new Tdu(TduIdentifier.Int16, rt, 2);
return new Tdu(TduIdentifier.Int16, rt, 2, null, null);
}
}
@@ -100,7 +100,7 @@ public static class DataSerializer
if (v <= byte.MaxValue)
{
// Fits in 1 byte
return new Tdu(TduIdentifier.UInt8, new byte[] { (byte)v }, 1);
return new Tdu(TduIdentifier.UInt8, new byte[] { (byte)v }, 1, null, null);
}
else
{
@@ -109,7 +109,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((ushort*)ptr) = v;
return new Tdu(TduIdentifier.UInt16, rt, 2);
return new Tdu(TduIdentifier.UInt16, rt, 2, null, null);
}
}
@@ -121,7 +121,7 @@ public static class DataSerializer
// Special IEEE-754 values
if (float.IsNaN(v) || float.IsInfinity(v))
{
return new Tdu(TduIdentifier.Infinity, new byte[0], 0);
return new Tdu(TduIdentifier.Infinity, new byte[0], 0, null, null);
}
// If v is an exact integer, prefer smallest signed width up to Int32
@@ -130,7 +130,7 @@ public static class DataSerializer
// Note: casts are safe because we check bounds first.
if (v >= sbyte.MinValue && v <= sbyte.MaxValue)
{
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1);
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1, null, null);
}
if (v >= short.MinValue && v <= short.MaxValue)
@@ -138,7 +138,7 @@ public static class DataSerializer
var rt = new byte[2];
fixed (byte* ptr = rt)
*((short*)ptr) = (short)v;
return new Tdu(TduIdentifier.Int16, rt, 2);
return new Tdu(TduIdentifier.Int16, rt, 2, null, null);
}
}
@@ -147,7 +147,7 @@ public static class DataSerializer
var rt = new byte[4];
fixed (byte* ptr = rt)
*((float*)ptr) = v;
return new Tdu(TduIdentifier.Float32, rt, 4);
return new Tdu(TduIdentifier.Float32, rt, 4, null, null);
}
}
@@ -159,14 +159,14 @@ public static class DataSerializer
// Special IEEE-754 values
if (double.IsNaN(v) || double.IsInfinity(v))
{
return new Tdu(TduIdentifier.Infinity, new byte[0], 0);
return new Tdu(TduIdentifier.Infinity, new byte[0], 0, null, null);
}
// If v is an exact integer, choose the smallest signed width
if (v == Math.Truncate(v))
{
if (v >= sbyte.MinValue && v <= sbyte.MaxValue)
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1);
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1, null, null);
if (v >= short.MinValue && v <= short.MaxValue)
{
@@ -175,7 +175,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((short*)ptr) = (short)v;
return new Tdu(TduIdentifier.Int16, rt, 2);
return new Tdu(TduIdentifier.Int16, rt, 2, null, null);
}
if (v >= int.MinValue && v <= int.MaxValue)
@@ -185,7 +185,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((int*)ptr) = (int)v;
return new Tdu(TduIdentifier.Int32, rt, 4);
return new Tdu(TduIdentifier.Int32, rt, 4, null, null);
}
// If it's integral but outside Int64 range, fall through to Float64.
@@ -200,7 +200,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((float*)ptr) = f;
return new Tdu(TduIdentifier.Float32, rt, 4);
return new Tdu(TduIdentifier.Float32, rt, 4, null, null);
}
// Default: Float64
@@ -208,7 +208,7 @@ public static class DataSerializer
var rt = new byte[8];
fixed (byte* ptr = rt)
*((double*)ptr) = v;
return new Tdu(TduIdentifier.Float64, rt, 8);
return new Tdu(TduIdentifier.Float64, rt, 8, null, null);
}
}
public static unsafe Tdu Int64Composer(object value, Warehouse warehouse, EpConnection connection)
@@ -218,7 +218,7 @@ public static class DataSerializer
if (v >= sbyte.MinValue && v <= sbyte.MaxValue)
{
// Fits in 1 byte
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1);
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1, null, null);
}
else if (v >= short.MinValue && v <= short.MaxValue)
{
@@ -227,7 +227,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((short*)ptr) = (short)v;
return new Tdu(TduIdentifier.Int16, rt, 2);
return new Tdu(TduIdentifier.Int16, rt, 2, null, null);
}
else if (v >= int.MinValue && v <= int.MaxValue)
{
@@ -236,7 +236,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((int*)ptr) = (int)v;
return new Tdu(TduIdentifier.Int32, rt, 4);
return new Tdu(TduIdentifier.Int32, rt, 4, null, null);
}
else
{
@@ -245,7 +245,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((long*)ptr) = v;
return new Tdu(TduIdentifier.Int64, rt, 8);
return new Tdu(TduIdentifier.Int64, rt, 8, null, null);
}
}
@@ -256,7 +256,7 @@ public static class DataSerializer
if (v <= byte.MaxValue)
{
// Fits in 1 byte
return new Tdu(TduIdentifier.UInt8, new byte[] { (byte)v }, 1);
return new Tdu(TduIdentifier.UInt8, new byte[] { (byte)v }, 1, null, null);
}
else if (v <= ushort.MaxValue)
{
@@ -265,7 +265,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((ushort*)ptr) = (ushort)v;
return new Tdu(TduIdentifier.UInt16, rt, 2);
return new Tdu(TduIdentifier.UInt16, rt, 2, null, null);
}
else if (v <= uint.MaxValue)
{
@@ -274,7 +274,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((uint*)ptr) = (uint)v;
return new Tdu(TduIdentifier.UInt32, rt, 4);
return new Tdu(TduIdentifier.UInt32, rt, 4, null, null);
}
else
{
@@ -283,7 +283,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((ulong*)ptr) = v;
return new Tdu(TduIdentifier.UInt64, rt, 8);
return new Tdu(TduIdentifier.UInt64, rt, 8, null, null);
}
}
@@ -295,7 +295,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((long*)ptr) = v;
return new Tdu(TduIdentifier.DateTime, rt, 8);
return new Tdu(TduIdentifier.DateTime, rt, 8, null, null);
}
//public static unsafe TDU Decimal128Composer(object value, Warehouse warehouse, EpConnection connection)
@@ -320,27 +320,27 @@ public static class DataSerializer
if (scale == 0)
{
if (v >= sbyte.MinValue && v <= sbyte.MaxValue)
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1);
return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1, null, null);
if (v >= short.MinValue && v <= short.MaxValue)
{
var b = new byte[2];
BinaryPrimitives.WriteInt16LittleEndian(b, (short)v);
return new Tdu(TduIdentifier.Int16, b, 2);
return new Tdu(TduIdentifier.Int16, b, 2, null, null);
}
if (v >= int.MinValue && v <= int.MaxValue)
{
var b = new byte[4];
BinaryPrimitives.WriteInt32LittleEndian(b, (int)v);
return new Tdu(TduIdentifier.Int32, b, 4);
return new Tdu(TduIdentifier.Int32, b, 4, null, null);
}
if (v >= long.MinValue && v <= long.MaxValue)
{
var b = new byte[8];
BinaryPrimitives.WriteInt64LittleEndian(b, (long)v);
return new Tdu(TduIdentifier.Int64, b, 8);
return new Tdu(TduIdentifier.Int64, b, 8, null, null);
}
// else fall through (needs 96+ bits)
}
@@ -355,7 +355,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((float*)ptr) = f;
return new Tdu(TduIdentifier.Float32, rt, 4);
return new Tdu(TduIdentifier.Float32, rt, 4, null, null);
}
// Try exact Float64 (8 bytes)
@@ -367,7 +367,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((double*)ptr) = d;
return new Tdu(TduIdentifier.Float64, rt, 8);
return new Tdu(TduIdentifier.Float64, rt, 8, null, null);
}
{
@@ -377,7 +377,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((decimal*)ptr) = v;
return new Tdu(TduIdentifier.Decimal128, rt, 16);
return new Tdu(TduIdentifier.Decimal128, rt, 16, null, null);
}
}
@@ -385,58 +385,55 @@ public static class DataSerializer
{
var b = Encoding.UTF8.GetBytes((string)value);
return new Tdu(TduIdentifier.String, b, (uint)b.Length);
return new Tdu(TduIdentifier.String, b, (uint)b.Length, null, null);
}
public static Tdu ResourceLinkComposer(object value, Warehouse warehouse, EpConnection connection)
{
var b = Encoding.UTF8.GetBytes((ResourceLink)value);
return new Tdu(TduIdentifier.ResourceLink, b, (uint)b.Length);
return new Tdu(TduIdentifier.ResourceLink, b, (uint)b.Length, null, null);
}
public static Tdu EnumComposer(object value, Warehouse warehouse, EpConnection connection)
{
if (value == null)
return new Tdu(TduIdentifier.Null, null, 0);
return new Tdu(TduIdentifier.Null, null, 0, null, null);
//var warehouse = connection?.Instance?.Warehouse ?? connection?.Server?.Instance?.Warehouse;
//if (warehouse == null)
// throw new Exception("Warehouse not set.");
var valueType = value.GetType();
var typeDef = warehouse.GetTypeDefByType(value.GetType());
var typeDef = warehouse.GetLocalTypeDefByType(valueType);
var intVal = Convert.ChangeType(value, (value as Enum).GetTypeCode());
var ct = typeDef.Constants.FirstOrDefault(x => x.Value.Equals(intVal));
if (ct == null)
return new Tdu(TduIdentifier.Null, null, 0);
return new Tdu(TduIdentifier.Null, null, 0, null, null);
//return Codec.ComposeInternal(intVal, warehouse, connection);
return new Tdu(TduIdentifier.TypedEnum,
new byte[] { ct.Index }, 1, typeDef.Id.Data);
return new Tdu(TduIdentifier.Typed, new byte[] { ct.Index }, 1,
Tru.FromType(valueType, warehouse), connection);
}
public static Tdu UInt8Composer(object value, Warehouse warehouse, EpConnection connection)
{
return new Tdu(TduIdentifier.UInt8,
new byte[] { (byte)value }, 1);
new byte[] { (byte)value }, 1, null, null);
}
public static Tdu Int8Composer(object value, Warehouse warehouse, EpConnection connection)
{
return new Tdu(TduIdentifier.Int8,
new byte[] { (byte)(sbyte)value }, 1);
new byte[] { (byte)(sbyte)value }, 1, null, null);
}
public static Tdu Char8Composer(object value, Warehouse warehouse, EpConnection connection)
{
return new Tdu(TduIdentifier.Int8,
new byte[] { (byte)(char)value }, 1);
new byte[] { (byte)(char)value }, 1, null, null);
}
public static unsafe Tdu Char16Composer(object value, Warehouse warehouse, EpConnection connection)
@@ -446,7 +443,7 @@ public static class DataSerializer
fixed (byte* ptr = rt)
*((char*)ptr) = v;
return new Tdu(TduIdentifier.Char16, rt, 2);
return new Tdu(TduIdentifier.Char16, rt, 2, null, null);
}
@@ -454,30 +451,30 @@ public static class DataSerializer
{
if ((bool)value)
{
return new Tdu(TduIdentifier.True, null, 0);
return new Tdu(TduIdentifier.True, null, 0, null, null);
}
else
{
return new Tdu(TduIdentifier.False, null, 0);
return new Tdu(TduIdentifier.False, null, 0, null, null);
}
}
public static Tdu NotModifiedComposer(object value, Warehouse warehouse, EpConnection connection)
{
return new Tdu(TduIdentifier.NotModified, null, 0);
return new Tdu(TduIdentifier.NotModified, null, 0, null, null);
}
public static Tdu RawDataComposerFromArray(object value, Warehouse warehouse, EpConnection connection)
{
var b = (byte[])value;
return new Tdu(TduIdentifier.RawData, b, (uint)b.Length);
return new Tdu(TduIdentifier.RawData, b, (uint)b.Length, null, null);
}
public static Tdu RawDataComposerFromList(dynamic value, Warehouse warehouse, EpConnection connection)
{
var b = value as List<byte>;
return new Tdu(TduIdentifier.RawData, b.ToArray(), (uint)b.Count);
return new Tdu(TduIdentifier.RawData, b.ToArray(), (uint)b.Count, null, null);
}
//public static (TDUIdentifier, byte[]) ListComposerFromArray(dynamic value, EpConnection connection)
@@ -497,10 +494,10 @@ public static class DataSerializer
var composed = DynamicArrayComposer((IEnumerable)value, warehouse, connection);
if (composed == null)
return new Tdu(TduIdentifier.Null, new byte[0], 0);
return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null);
else
{
return new Tdu(TduIdentifier.List, composed, (uint)composed.Length);
return new Tdu(TduIdentifier.List, composed, (uint)composed.Length, null, null);
}
//if (value == null)
@@ -541,7 +538,6 @@ public static class DataSerializer
if (value == null)
return null;
if (tru.Identifier == TruIdentifier.Int32)
{
composed = GroupInt32Codec.Encode((IList<int>)value);
@@ -566,52 +562,49 @@ public static class DataSerializer
{
composed = GroupUInt16Codec.Encode((IList<ushort>)value);
}
else if (tru.Identifier == TruIdentifier.Enum)
{
//else if (tru.Identifier == TruIdentifier.Enum)
//{
// var rt = new List<byte>();
// var typeDef = warehouse.GetTypeDefByType(tru.GetRuntimeType(warehouse));
var rt = new List<byte>();
var typeDef = warehouse.GetTypeDefByType(tru.GetRuntimeType(warehouse));
// foreach (var v in value)
// {
// var intVal = Convert.ChangeType(v, (v as Enum).GetTypeCode());
// var ct = typeDef.Constants.FirstOrDefault(x => x.Value.Equals(intVal));
// if (ct == null)
// throw new Exception("Unknown Enum.");
// rt.Add(ct.Index);
// }
foreach (var v in value)
{
var intVal = Convert.ChangeType(v, (v as Enum).GetTypeCode());
var ct = typeDef.Constants.FirstOrDefault(x => x.Value.Equals(intVal));
if (ct == null)
throw new Exception("Unknown Enum.");
rt.Add(ct.Index);
}
composed = rt.ToArray();
}
// composed = rt.ToArray();
//}
else
{
var rt = new List<byte>();
Tdu? previous = null;
var isTyped = tru.IsTyped();
//var isTyped = tru.TypeDefId != null;// tru.IsTyped();
foreach (var i in value)
{
var tdu = Codec.ComposeInternal(i, warehouse, connection);
var currentTru = Tru.FromType(i?.GetType());
var currentTru = Tru.FromType(i?.GetType(), warehouse);
if (isTyped && tru.Match(currentTru))
if (tdu.Class == TduClass.Typed && tru.Match(currentTru))
{
var d = tdu.Composed.Clip(tdu.ContentOffset,
(uint)tdu.Composed.Length - tdu.ContentOffset);
var ntd = new Tdu(TduIdentifier.TypeOfTarget, d, (ulong)d.Length);
var ntd = new Tdu(TduIdentifier.TypeOfTarget, d, (ulong)d.Length, null, null);
rt.AddRange(ntd.Composed);
}
else
if (previous != null && tdu.MatchType(previous.Value))
else if (previous != null && tdu.MatchType(previous.Value))
{
var d = tdu.Composed.Clip(tdu.ContentOffset,
(uint)tdu.Composed.Length - tdu.ContentOffset);
var ntd = new Tdu(TduIdentifier.TypeContinuation, d, (ulong)d.Length);
var ntd = new Tdu(TduIdentifier.TypeContinuation, d, (ulong)d.Length, null, null);
rt.AddRange(ntd.Composed);
}
else
@@ -632,33 +625,24 @@ public static class DataSerializer
public static Tdu TypedListComposer(IEnumerable value, Type type, Warehouse warehouse, EpConnection connection)
{
var tru = Tru.FromType(type);
var elementTru = Tru.FromType(type, warehouse);
byte[] composed = TypedArrayComposer(value, tru, warehouse, connection);
byte[] composed = TypedArrayComposer(value, elementTru, warehouse, connection);
if (composed == null)
return new Tdu(TduIdentifier.Null, new byte[0], 0);
return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null);
var metadata = tru.Compose();
var metadata = new TruComposite(TruIdentifier.TypedList, false,
new Tru[] { elementTru }, value.GetType());
return new Tdu(TduIdentifier.TypedList, composed, (uint)composed.Length, metadata);
return new Tdu(TduIdentifier.Typed, composed, (uint)composed.Length, metadata, connection);
}
//public static byte[] PropertyValueComposer(PropertyValue propertyValue, EpConnection connection)//, bool includeAge = true)
//{
// var rt = new BinaryList();
// return
// .AddUInt64(propertyValue.Age)
// .AddDateTime(propertyValue.Date)
// .AddUInt8Array(Codec.Compose(propertyValue.Value, connection))
// .ToArray();
//}
public static Tdu PropertyValueArrayComposer(object value, Warehouse warehouse, EpConnection connection)
{
if (value == null)
return new Tdu(TduIdentifier.Null, new byte[0], 0);
return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null);
var rt = new List<byte>();
var ar = value as PropertyValue[];
@@ -671,16 +655,16 @@ public static class DataSerializer
}
return new Tdu(TduIdentifier.RawData, rt.ToArray(),
(uint)rt.Count);
(uint)rt.Count, null, null);
}
public static Tdu TypedMapComposer(object value, Type keyType, Type valueType, Warehouse warehouse, EpConnection connection)
{
if (value == null)
return new Tdu(TduIdentifier.Null, new byte[0], 0);
return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null);
var kt = Tru.FromType(keyType);
var vt = Tru.FromType(valueType);
var kt = Tru.FromType(keyType, warehouse);
var vt = Tru.FromType(valueType, warehouse);
//var rt = new List<byte>();
@@ -692,34 +676,33 @@ public static class DataSerializer
var compsedKeys = TypedArrayComposer(keys, kt, warehouse, connection);
var compsedValues = TypedArrayComposer(values, vt, warehouse, connection);
var ktb = kt.Compose();
var vtb = vt.Compose();
//var ktb = kt.Compose();
//var vtb = vt.Compose();
var metadata = DC.Combine(ktb, 0, (uint)ktb.Length, vtb, 0, (uint)vtb.Length);
//var metadata = DC.Combine(ktb, 0, (uint)ktb.Length, vtb, 0, (uint)vtb.Length);
//foreach (var el in map.Serialize())
// rt.AddRange(Codec.Compose(el, warehouse, connection));
var keysTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedKeys, (uint)compsedKeys.Length).Composed;
var valuesTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedValues, (uint)compsedValues.Length).Composed;
var keysTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedKeys, (uint)compsedKeys.Length, null, null).Composed;
var valuesTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedValues, (uint)compsedValues.Length, null, null).Composed;
var all = DC.Combine(keysTdu, 0, (uint)keysTdu.Length, valuesTdu, 0, (uint)valuesTdu.Length);
return new Tdu(TduIdentifier.TypedMap, all, (uint)all.Length, metadata);
return new Tdu(TduIdentifier.Typed, all, (uint)all.Length,
new TruComposite(TruIdentifier.TypedMap, false, new Tru[] { kt, vt }, value.GetType()), connection);
//return new Tdu(TduIdentifier.TypedMap, all, (uint)all.Length, metadata);
//return new TDU(TDUIdentifier.TypedMap, rt.ToArray(), (uint)rt.Count,
// );
}
public static Tdu TypedDictionaryComposer(object value, Type keyType, Type valueType, Warehouse warehouse, EpConnection connection)
{
if (value == null)
return new Tdu(TduIdentifier.Null, new byte[0], 0);
return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null);
var kt = Tru.FromType(keyType);
var vt = Tru.FromType(valueType);
var kt = Tru.FromType(keyType, warehouse);
var vt = Tru.FromType(valueType, warehouse);
//var rt = new List<byte>();
@@ -731,48 +714,18 @@ public static class DataSerializer
var compsedKeys = TypedArrayComposer(keys, kt, warehouse, connection);
var compsedValues = TypedArrayComposer(values, vt, warehouse, connection);
var ktb = kt.Compose();
var vtb = vt.Compose();
var metadata = DC.Combine(ktb, 0, (uint)ktb.Length, vtb, 0, (uint)vtb.Length);
//foreach (var el in map.Serialize())
// rt.AddRange(Codec.Compose(el, warehouse, connection));
var keysTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedKeys, (uint)compsedKeys.Length).Composed;
var valuesTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedValues, (uint)compsedValues.Length).Composed;
var keysTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedKeys, (uint)compsedKeys.Length, null, null).Composed;
var valuesTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedValues, (uint)compsedValues.Length, null, null).Composed;
var all = DC.Combine(keysTdu, 0, (uint)keysTdu.Length, valuesTdu, 0, (uint)valuesTdu.Length);
return new Tdu(TduIdentifier.TypedMap, all, (uint)all.Length, metadata);
return new Tdu(TduIdentifier.Typed, all, (uint)all.Length,
new TruComposite(TruIdentifier.TypedMap, false, new Tru[] { kt, vt },
value.GetType()),
connection);
//if (value == null)
// return new TDU(TDUIdentifier.Null, null, 0);
//var kt = TRU.FromType(keyType).Compose();
//var vt = TRU.FromType(valueType).Compose();
//var rt = new List<byte>();
////rt.AddRange(kt);
////rt.AddRange(vt);
//var dic = (IDictionary)value;
//var ar = new List<object>();
//foreach (var k in dic.Keys)
//{
// ar.Add(k);
// ar.Add(dic[k]);
//}
//foreach (var el in ar)
// rt.AddRange(Codec.Compose(el, warehouse, connection));
//return new TDU(TDUIdentifier.TypedMap, rt.ToArray(), (uint)rt.Count,
// DC.Combine(kt, 0, (uint)kt.Length, vt, 0, (uint)vt.Length));
}
public static byte[] DynamicArrayComposer(IEnumerable value, Warehouse warehouse, EpConnection connection)
@@ -792,7 +745,7 @@ public static class DataSerializer
var d = tdu.Composed.Clip(tdu.ContentOffset,
(uint)tdu.Composed.Length - tdu.ContentOffset);
var ntd = new Tdu(TduIdentifier.TypeContinuation, d, (ulong)d.Length);
var ntd = new Tdu(TduIdentifier.TypeContinuation, d, (ulong)d.Length, null, null);
rt.AddRange(ntd.Composed);
}
else
@@ -809,23 +762,23 @@ public static class DataSerializer
public static Tdu ResourceListComposer(object value, Warehouse warehouse, EpConnection connection)
{
if (value == null)
return new Tdu(TduIdentifier.Null, new byte[0], 0);
return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null);
var composed = DynamicArrayComposer((IEnumerable)value, warehouse, connection);
return new Tdu(TduIdentifier.ResourceList, composed,
(uint)composed.Length);
(uint)composed.Length, null, null);
}
public static Tdu RecordListComposer(object value, Warehouse warehouse, EpConnection connection)
{
if (value == null)
return new Tdu(TduIdentifier.Null, new byte[0], 0);
return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null);
var composed = DynamicArrayComposer((IEnumerable)value, warehouse, connection);
return new Tdu(TduIdentifier.RecordList,
composed, (uint)composed.Length);
composed, (uint)composed.Length, null, null);
}
@@ -835,54 +788,54 @@ public static class DataSerializer
if (resource.Instance == null || resource.Instance.IsDestroyed)
{
return new Tdu(TduIdentifier.Null, null, 0);
return new Tdu(TduIdentifier.Null, null, 0, null, null);
}
if (Codec.IsLocalResource(resource, connection))
{
var rid = (resource as EpResource).DistributedResourceInstanceId;
var rid = (resource as EpResource).ResourceInstanceId;
if (rid <= 0xFF)
return new Tdu(TduIdentifier.LocalResource8, new byte[] { (byte)rid }, 1);
return new Tdu(TduIdentifier.LocalResource8, new byte[] { (byte)rid }, 1, null, null);
else if (rid <= 0xFFFF)
{
var rt = new byte[2];
fixed (byte* ptr = rt)
*((ushort*)ptr) = (ushort)rid;
return new Tdu(TduIdentifier.LocalResource16, rt, 2);
return new Tdu(TduIdentifier.LocalResource16, rt, 2, null, null);
}
else
{
var rt = new byte[4];
fixed (byte* ptr = rt)
*((uint*)ptr) = rid;
return new Tdu(TduIdentifier.LocalResource32, rt, 4);
return new Tdu(TduIdentifier.LocalResource32, rt, 4, null, null);
}
}
else
{
connection.cache.Add(value as IResource, DateTime.UtcNow);
connection._cache.Add(value as IResource, DateTime.UtcNow);
var rid = resource.Instance.Id;
if (rid <= 0xFF)
return new Tdu(TduIdentifier.RemoteResource8, new byte[] { (byte)rid }, 1);
return new Tdu(TduIdentifier.RemoteResource8, new byte[] { (byte)rid }, 1, null, null);
else if (rid <= 0xFFFF)
{
var rt = new byte[2];
fixed (byte* ptr = rt)
*((ushort*)ptr) = (ushort)rid;
return new Tdu(TduIdentifier.RemoteResource16, rt, 2);
return new Tdu(TduIdentifier.RemoteResource16, rt, 2, null, null);
}
else
{
var rt = new byte[4];
fixed (byte* ptr = rt)
*((uint*)ptr) = rid;
return new Tdu(TduIdentifier.RemoteResource32, rt, 4);
return new Tdu(TduIdentifier.RemoteResource32, rt, 4, null, null);
}
}
}
@@ -890,7 +843,7 @@ public static class DataSerializer
public static unsafe Tdu MapComposer(object value, Warehouse warehouse, EpConnection connection)
{
if (value == null)
return new Tdu(TduIdentifier.Null, new byte[0], 1);
return new Tdu(TduIdentifier.Null, new byte[0], 1, null, null);
var rt = new List<byte>();
var map = (IMap)value;
@@ -898,12 +851,12 @@ public static class DataSerializer
foreach (var el in map.Serialize())
rt.AddRange(Codec.Compose(el, warehouse, connection));
return new Tdu(TduIdentifier.Map, rt.ToArray(), (uint)rt.Count);
return new Tdu(TduIdentifier.Map, rt.ToArray(), (uint)rt.Count, null, null);
}
public static unsafe Tdu UUIDComposer(object value, Warehouse warehouse, EpConnection connection)
{
return new Tdu(TduIdentifier.UUID, ((Uuid)value).Data, 16);
return new Tdu(TduIdentifier.UUID, ((Uuid)value).Data, 16, null, null);
}
@@ -912,34 +865,47 @@ public static class DataSerializer
var rt = new List<byte>();
var record = (IRecord)value;
var typeDef = warehouse.GetTypeDefByType(record.GetType());
var recordTru = Tru.FromType(value.GetType(), warehouse) ;
TypeDef typeDef = null;
if (value is Record typedRecord)
{
typeDef = typedRecord.TypeDef;
}
else if (recordTru is TruTypeDef recordTypeDefTru)
{
// @TODO need to enhance performance, maybe cache this in the connection or something
typeDef = recordTypeDefTru.TypeDef;// recordTru.type;// .GetTypeDef(warehouse, connection.RemoteDomain);
}
else
{
throw new Exception("Unsupported.");
}
foreach (var pt in typeDef.Properties)
{
var propValue = pt.PropertyInfo.GetValue(record, null);
//if (propValue == null)
// return TDU(TDUIdentifier.Null, null, 0);
var tru = Tru.FromType(propValue?.GetType());
var tru = Tru.FromType(propValue?.GetType(), warehouse);
var tdu = Codec.ComposeInternal(propValue, warehouse, connection);
if (pt.ValueType.IsTyped() &&
if (tdu.Class == TduClass.Typed && // pt.ValueType.IsTyped() &&
pt.ValueType.Match(tru))
{
// strip metadata
var len = (uint)tdu.Composed.Length - tdu.ContentOffset;
tdu = new Tdu(TduIdentifier.TypeOfTarget,
tdu.Composed.Clip(tdu.ContentOffset, len), len);
tdu.Composed.Clip(tdu.ContentOffset, len), len, null, null);
}
rt.AddRange(tdu.Composed);
}
return new Tdu(TduIdentifier.Record, rt.ToArray(),
(uint)rt.Count,
typeDef.Id.Data);
// @TODO: serialize metadata type Id to byte, ushort or uint depending on size.
return new Tdu(TduIdentifier.Typed, rt.ToArray(), (uint)rt.Count, recordTru, connection);
}
public static byte[] HistoryComposer(KeyList<PropertyDef, PropertyValue[]> history, Warehouse warehouse,
@@ -961,18 +927,13 @@ public static class DataSerializer
public static Tdu TupleComposer(object value, Warehouse warehouse, EpConnection connection)
{
if (value == null)
return new Tdu(TduIdentifier.Null, new byte[0], 0);
return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null);
var fields = value.GetType().GetFields();
var list = fields.Select(x => x.GetValue(value)).ToArray();
var trus = fields.Select(x => Tru.FromType(x.FieldType)).ToArray();
var trus = fields.Select(x => Tru.FromType(x.FieldType, warehouse)).ToArray();
var metadata = new List<byte>();
foreach (var t in trus)
metadata.AddRange(t.Compose());
var rt = new List<byte>();
for (var i = 0; i < fields.Length; i++)
@@ -982,22 +943,36 @@ public static class DataSerializer
var tdu = Codec.ComposeInternal(tupleValue, warehouse, connection);
var valueTru = Tru.FromType(tupleValue?.GetType());
var valueTru = Tru.FromType(tupleValue?.GetType(), warehouse);
if (targetTru.IsTyped() &&
if (tdu.Class == TduClass.Typed && // targetTru.IsTyped() &&
targetTru.Match(valueTru))
{
// strip metadata
var len = (uint)tdu.Composed.Length - tdu.ContentOffset;
tdu = new Tdu(TduIdentifier.TypeOfTarget,
tdu.Composed.Clip(tdu.ContentOffset, len), len);
tdu.Composed.Clip(tdu.ContentOffset, len), len, null, null);
}
rt.AddRange(tdu.Composed);
}
return new Tdu(TduIdentifier.TypedTuple, rt.ToArray(),
(uint)rt.Count, metadata.ToArray());
//return new Tdu(TduIdentifier.TypedTuple, rt.ToArray(),
// (uint)rt.Count, metadata.ToArray());
var truIdentifier = trus.Length switch
{
2 => TruIdentifier.Tuple2,
3 => TruIdentifier.Tuple3,
4 => TruIdentifier.Tuple4,
5 => TruIdentifier.Tuple5,
6 => TruIdentifier.Tuple6,
7 => TruIdentifier.Tuple7,
_ => throw new NotSupportedException("Tuples with more than 7 or less than 2 elements are not supported.")
};
return new Tdu(TduIdentifier.Typed, rt.ToArray(),
(uint)rt.Count, new TruComposite(truIdentifier, false, trus, value.GetType()), connection);
}
}
+12
View File
@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
{
public interface IParseResult<out T>
{
public T Value { get; }
public uint Size { get; }
}
}
+12
View File
@@ -0,0 +1,12 @@
using Esiur.Data.Types;
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
{
public interface IRemoteRecord: IRecord
{
public RemoteTypeDef TypeDef { get; }
}
}
+1 -5
View File
@@ -96,11 +96,7 @@ public class Map<KT, VT> : Dictionary<KT, VT>, IMap // IEnumerable<KeyValuePair<
public override string ToString()
{
var rt = "";
foreach (var kv in this)
rt += kv.Key + ": " + kv.Value.ToString() + " \r\n";
return rt.TrimEnd('\r', '\n');
return "{" + string.Join(", ", this.Select(x => $"{x.Key}: {x.Value}")) + "}";
}
//public Map(Map<KT,VT> source)
+18
View File
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
{
public readonly struct ParseResult<T>: IParseResult<T>
{
public T Value { get; }
public uint Size { get; }
public ParseResult(T value, uint size)
{
Value = value;
Size = size;
}
}
}
+346 -16
View File
@@ -1,4 +1,7 @@
using System;
using Esiur.Core;
using Esiur.Protocol;
using Esiur.Resource;
using System;
using System.Collections.Generic;
using System.Text;
@@ -9,16 +12,22 @@ namespace Esiur.Data
public TduIdentifier Identifier;
public int Index;
public TduClass Class;
public uint Offset;
public ulong ContentLength;
public uint PayloadOffset;
public ulong PayloadLength;
public byte[] Data;
public byte Exponent;
public ulong TotalLength;
public byte[] Metadata;
public Tru Metadata;
public uint Ends;
public static ParsedTdu Parse(byte[] data, uint offset, uint ends)
public static AsyncReply<ParsedTdu> ParseAsync(byte[] data, EpConnection connection)
{
return ParseAsync(data, (uint)0, (uint)data.Length, connection);
}
public static async AsyncReply<ParsedTdu> ParseAsync(byte[] data, uint offset, uint ends, EpConnection connection)
{
// @TODO: add protection against memory allocation attacks by checking the length of the data before parsing it.
var h = data[offset++];
@@ -33,11 +42,11 @@ namespace Esiur.Data
{
Identifier = (TduIdentifier)h,
Data = data,
Offset = offset,
PayloadOffset = offset,
Class = cls,
Exponent = (byte)exp,
Index = (byte)h & 0x7,
ContentLength = 0,
PayloadLength = 0,
TotalLength = 1,
Ends = ends
};
@@ -57,9 +66,9 @@ namespace Esiur.Data
{
Identifier = (TduIdentifier)h,
Data = data,
Offset = offset,
PayloadOffset = offset,
Class = cls,
ContentLength = cl,
PayloadLength = cl,
TotalLength = 1 + cl,
Exponent = (byte)exp,
Index = (byte)h & 0x7,
@@ -89,20 +98,22 @@ namespace Esiur.Data
Class = TduClass.Invalid,
};
var metaData = DC.Clip(data, offset + 1, data[offset]);
offset += data[offset] + (uint)1;
//var metaData = DC.Clip(data, offset + 1, data[offset]);
//offset += data[offset] + (uint)1;
var metaDataTru = await Tru.ParseAsync(data, offset, connection, null);
offset += metaDataTru.Size;
return new ParsedTdu()
{
Identifier = (TduIdentifier)(h & 0xC7),
Data = data,
Offset = offset,
PayloadOffset = offset,
Class = cls,
ContentLength = cl - 1 - (uint)metaData.Length,
PayloadLength = cl - metaDataTru.Size,
TotalLength = 1 + cl + cll,
Index = (byte)h & 0x7,
Metadata = metaData,
Metadata = metaDataTru.Value,
Ends = ends
};
}
@@ -135,9 +146,9 @@ namespace Esiur.Data
{
Identifier = (TduIdentifier)(h & 0xC7),
Data = data,
Offset = offset,
PayloadOffset = offset,
Class = cls,
ContentLength = cl,
PayloadLength = cl,
TotalLength = 1 + cl + cll,
Index = (byte)h & 0x7,
Ends = ends
@@ -145,5 +156,324 @@ namespace Esiur.Data
}
}
public static object Parse(byte[] data, uint offset, uint ends, EpConnection connection)
{
// @TODO: add protection against memory allocation attacks by checking the length of the data before parsing it.
var h = data[offset++];
var cls = (TduClass)(h >> 6);
if (cls == TduClass.Fixed)
{
var exp = (h & 0x38) >> 3;
if (exp == 0)
return new ParsedTdu()
{
Identifier = (TduIdentifier)h,
Data = data,
PayloadOffset = offset,
Class = cls,
Exponent = (byte)exp,
Index = (byte)h & 0x7,
PayloadLength = 0,
TotalLength = 1,
Ends = ends
};
ulong cl = (ulong)(1 << (exp - 1));
if (ends - offset < cl)
return new ParsedTdu()
{
Class = TduClass.Invalid,
TotalLength = (cl - (ends - offset))
};
//offset += (uint)cl;
return new ParsedTdu()
{
Identifier = (TduIdentifier)h,
Data = data,
PayloadOffset = offset,
Class = cls,
PayloadLength = cl,
TotalLength = 1 + cl,
Exponent = (byte)exp,
Index = (byte)h & 0x7,
Ends = ends
};
}
else if (cls == TduClass.Typed)
{
ulong cll = (ulong)(h >> 3) & 0x7;
if (ends - offset < cll)
return new ParsedTdu()
{
Class = TduClass.Invalid,
TotalLength = (cll - (ends - offset))
};
ulong cl = 0;
for (uint i = 0; i < cll; i++)
cl = cl << 8 | data[offset++];
if (ends - offset < cl)
return new ParsedTdu()
{
TotalLength = (cl - (ends - offset)),
Class = TduClass.Invalid,
};
//var metaData = DC.Clip(data, offset + 1, data[offset]);
//offset += data[offset] + (uint)1;
var rt = new AsyncReply<ParsedTdu>();
Tru.ParseAsync(data, offset, connection, null).Then(metaDataTru =>
{
offset += metaDataTru.Size;
rt.Trigger(new ParsedTdu()
{
Identifier = (TduIdentifier)(h & 0xC7),
Data = data,
PayloadOffset = offset,
Class = cls,
PayloadLength = cl - metaDataTru.Size,
TotalLength = 1 + cl + cll,
Index = (byte)h & 0x7,
Metadata = metaDataTru.Value,
Ends = ends
});
});
return rt;
}
else
{
ulong cll = (ulong)(h >> 3) & 0x7;
if (ends - offset < cll)
return new ParsedTdu()
{
Class = TduClass.Invalid,
TotalLength = (cll - (ends - offset))
};
ulong cl = 0;
for (uint i = 0; i < cll; i++)
cl = cl << 8 | data[offset++];
if (ends - offset < cl)
return new ParsedTdu()
{
Class = TduClass.Invalid,
TotalLength = (cl - (ends - offset))
};
return
new ParsedTdu()
{
Identifier = (TduIdentifier)(h & 0xC7),
Data = data,
PayloadOffset = offset,
Class = cls,
PayloadLength = cl,
TotalLength = 1 + cl + cll,
Index = (byte)h & 0x7,
Ends = ends
};
}
}
public static byte[] ClipTduData(byte[] data, uint offset, uint ends)
{
var oOffset = (int)offset;
var h = data[offset++];
var cls = (TduClass)(h >> 6);
if (cls == TduClass.Fixed)
{
var exp = (h & 0x38) >> 3;
if (exp == 0)
{
return new byte[] { h };
}
ulong cl = (ulong)(1 << (exp - 1));
if (ends - offset < cl)
{
return null; // failded
}
var rt = new byte[1 + cl];
Buffer.BlockCopy(data, oOffset, rt, 0, (int)cl);
return rt;
}
else
{
ulong cll = (ulong)(h >> 3) & 0x7;
if (ends - offset < cll)
return null;
ulong cl = 0;
for (uint i = 0; i < cll; i++)
cl = cl << 8 | data[offset++];
if (ends - offset < cl)
return null;
var rt = new byte[1 + cll + cl];
Buffer.BlockCopy(data, oOffset, rt, 0, rt.Length);
return rt;
}
}
public static ParsedTdu ParseSync(byte[] data, uint offset, uint ends, Warehouse warehouse)
{
// @TODO: add protection against memory allocation attacks by checking the length of the data before parsing it.
var h = data[offset++];
var cls = (TduClass)(h >> 6);
if (cls == TduClass.Fixed)
{
var exp = (h & 0x38) >> 3;
if (exp == 0)
return new ParsedTdu()
{
Identifier = (TduIdentifier)h,
Data = data,
PayloadOffset = offset,
Class = cls,
Exponent = (byte)exp,
Index = (byte)h & 0x7,
PayloadLength = 0,
TotalLength = 1,
Ends = ends
};
ulong cl = (ulong)(1 << (exp - 1));
if (ends - offset < cl)
return new ParsedTdu()
{
Class = TduClass.Invalid,
TotalLength = (cl - (ends - offset))
};
//offset += (uint)cl;
return new ParsedTdu()
{
Identifier = (TduIdentifier)h,
Data = data,
PayloadOffset = offset,
Class = cls,
PayloadLength = cl,
TotalLength = 1 + cl,
Exponent = (byte)exp,
Index = (byte)h & 0x7,
Ends = ends
};
}
else if (cls == TduClass.Typed)
{
ulong cll = (ulong)(h >> 3) & 0x7;
if (ends - offset < cll)
return new ParsedTdu()
{
Class = TduClass.Invalid,
TotalLength = (cll - (ends - offset))
};
ulong cl = 0;
for (uint i = 0; i < cll; i++)
cl = cl << 8 | data[offset++];
if (ends - offset < cl)
return new ParsedTdu()
{
TotalLength = (cl - (ends - offset)),
Class = TduClass.Invalid,
};
//var metaData = DC.Clip(data, offset + 1, data[offset]);
//offset += data[offset] + (uint)1;
var metaDataTru = Tru.Parse(data, offset, warehouse);
offset += metaDataTru.Size;
return new ParsedTdu()
{
Identifier = (TduIdentifier)(h & 0xC7),
Data = data,
PayloadOffset = offset,
Class = cls,
PayloadLength = cl - metaDataTru.Size,
TotalLength = 1 + cl + cll,
Index = (byte)h & 0x7,
Metadata = metaDataTru.Value,
Ends = ends
};
}
else
{
ulong cll = (ulong)(h >> 3) & 0x7;
if (ends - offset < cll)
return new ParsedTdu()
{
Class = TduClass.Invalid,
TotalLength = (cll - (ends - offset))
};
ulong cl = 0;
for (uint i = 0; i < cll; i++)
cl = cl << 8 | data[offset++];
if (ends - offset < cl)
return new ParsedTdu()
{
Class = TduClass.Invalid,
TotalLength = (cl - (ends - offset))
};
return
new ParsedTdu()
{
Identifier = (TduIdentifier)(h & 0xC7),
Data = data,
PayloadOffset = offset,
Class = cls,
PayloadLength = cl,
TotalLength = 1 + cl + cll,
Index = (byte)h & 0x7,
Ends = ends
};
}
}
}
}
+155
View File
@@ -0,0 +1,155 @@
using Esiur.Core;
using Esiur.Protocol;
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
{
public struct PlainTdu
{
public TduIdentifier Identifier;
public int Index;
public TduClass Class;
public uint TduOffset;
public uint PayloadOffset;
public ulong PayloadLength;
public byte[] Data;
public byte Exponent;
public ulong TotalLength;
public uint Ends;
public static PlainTdu Parse(byte[] data, uint offset, uint ends)
{
var oOffset = offset;
// @TODO: add protection against memory allocation attacks by checking the length of the data before parsing it.
var h = data[offset++];
var cls = (TduClass)(h >> 6);
if (cls == TduClass.Fixed)
{
var exp = (h & 0x38) >> 3;
if (exp == 0)
return new PlainTdu()
{
Identifier = (TduIdentifier)h,
Data = data,
TduOffset = oOffset,
PayloadOffset = offset,
Class = cls,
Exponent = (byte)exp,
Index = (byte)h & 0x7,
PayloadLength = 0,
TotalLength = 1,
Ends = ends
};
ulong cl = (ulong)(1 << (exp - 1));
if (ends - offset < cl)
return new PlainTdu()
{
Class = TduClass.Invalid,
TotalLength = (cl - (ends - offset))
};
//offset += (uint)cl;
return new PlainTdu()
{
Identifier = (TduIdentifier)h,
Data = data,
TduOffset= oOffset,
PayloadOffset = offset,
Class = cls,
PayloadLength = cl,
TotalLength = 1 + cl,
Exponent = (byte)exp,
Index = (byte)h & 0x7,
Ends = ends
};
}
else if (cls == TduClass.Typed)
{
ulong cll = (ulong)(h >> 3) & 0x7;
if (ends - offset < cll)
return new PlainTdu()
{
Class = TduClass.Invalid,
TotalLength = (cll - (ends - offset))
};
ulong cl = 0;
for (uint i = 0; i < cll; i++)
cl = cl << 8 | data[offset++];
if (ends - offset < cl)
return new PlainTdu()
{
TotalLength = (cl - (ends - offset)),
Class = TduClass.Invalid,
};
return new PlainTdu()
{
Identifier = (TduIdentifier)(h & 0xC7),
Data = data,
TduOffset = oOffset,
PayloadOffset = offset,
Class = cls,
PayloadLength = cl,
TotalLength = 1 + cl + cll,
Index = (byte)h & 0x7,
Ends = ends
};
}
else
{
ulong cll = (ulong)(h >> 3) & 0x7;
if (ends - offset < cll)
return new PlainTdu()
{
Class = TduClass.Invalid,
TotalLength = (cll - (ends - offset))
};
ulong cl = 0;
for (uint i = 0; i < cll; i++)
cl = cl << 8 | data[offset++];
if (ends - offset < cl)
return new PlainTdu()
{
Class = TduClass.Invalid,
TotalLength = (cl - (ends - offset))
};
return
new PlainTdu()
{
Identifier = (TduIdentifier)(h & 0xC7),
Data = data,
TduOffset = oOffset,
PayloadOffset = offset,
Class = cls,
PayloadLength = cl,
TotalLength = 1 + cl + cll,
Index = (byte)h & 0x7,
Ends = ends
};
}
}
}
}
+13 -1
View File
@@ -1,9 +1,21 @@
using System;
using Esiur.Data.Types;
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data;
public class Record : KeyList<string, object>, IRecord
{
public TypeDef TypeDef { get; private set; }
public Record(TypeDef typeDef)
{
TypeDef = typeDef;
}
public override string ToString()
{
return $"Record<{TypeDef.Name}> {{{string.Join(", ", this.Select(x=>x.Key + ": " + x.Value))}}}";
}
}
+31 -49
View File
@@ -1,4 +1,5 @@
using System;
using Esiur.Protocol;
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Linq;
@@ -18,7 +19,7 @@ public struct Tdu
public byte[] Composed;
//public uint Offset;
public byte[] Metadata;
public Tru Metadata;
public uint ContentOffset;
@@ -64,7 +65,7 @@ public struct Tdu
}
public Tdu(TduIdentifier identifier,
byte[] data, ulong length, byte[] metadata = null)
byte[] data, ulong length, Tru metadata, EpConnection connection)
{
Identifier = identifier;
//Index = (byte)identifier & 0x7;
@@ -174,34 +175,20 @@ public struct Tdu
if (metadata == null)
throw new Exception("Metadata must be provided for types.");
if (metadata.Length > 0xFF)
throw new Exception("Metadata can't exceed 255 bytes in length.");
var metaLen = (byte)metadata.Length;
var len = 1 + (ulong)metaLen + length;
var metadataData = metadata.Compose(connection);
if (length == 0 && (metadata == null || metadata.Length == 0))
{
Composed = new byte[1] { (byte)Identifier };
throw new Exception("Need check");
}
else if (metadata.Length > 0xFF)
{
throw new Exception("Metadata can't exceed 255 bytes in length.");
}
else if (length <= 0xFF)
var len = (ulong)metadataData.Length + length;
if (len <= 0xFF)
{
Composed = new byte[2 + len];
Composed[0] = (byte)((byte)Identifier | 0x8);
Composed[1] = (byte)len;
Composed[2] = metaLen;
ContentOffset = metaLen + (uint)3;
ContentOffset = (uint)metadataData.Length + (uint)2;
Buffer.BlockCopy(metadata, 0, Composed, 3, metaLen);
Buffer.BlockCopy(data, 0, Composed, 3 + metaLen, (int)length);
Buffer.BlockCopy(metadataData, 0, Composed, 2, metadataData.Length);
Buffer.BlockCopy(data, 0, Composed, 2 + metadataData.Length, (int)length);
}
else if (len <= 0xFF_FF)
{
@@ -209,11 +196,10 @@ public struct Tdu
Composed[0] = (byte)((byte)identifier | 0x10);
Composed[1] = (byte)((len >> 8) & 0xFF);
Composed[2] = (byte)(len & 0xFF);
Composed[3] = metaLen;
ContentOffset = metaLen + (uint)4;
ContentOffset = (uint)metadataData.Length + (uint)3;
Buffer.BlockCopy(metadata, 0, Composed, 4, metaLen);
Buffer.BlockCopy(data, 0, Composed, 4 + metaLen, (int)length);
Buffer.BlockCopy(metadataData, 0, Composed, 3, metadataData.Length);
Buffer.BlockCopy(data, 0, Composed, 3 + metadataData.Length, (int)length);
}
else if (len <= 0xFF_FF_FF)
{
@@ -222,11 +208,10 @@ public struct Tdu
Composed[1] = (byte)((len >> 16) & 0xFF);
Composed[2] = (byte)((len >> 8) & 0xFF);
Composed[3] = (byte)(len & 0xFF);
Composed[4] = metaLen;
ContentOffset = metaLen + (uint)5;
ContentOffset = (uint)metadataData.Length + (uint)4;
Buffer.BlockCopy(metadata, 0, Composed, 5, metaLen);
Buffer.BlockCopy(data, 0, Composed, 5 + metaLen, (int)length);
Buffer.BlockCopy(metadataData, 0, Composed, 4, metadataData.Length);
Buffer.BlockCopy(data, 0, Composed, 4 + metadataData.Length, (int)length);
}
else if (len <= 0xFF_FF_FF_FF)
@@ -237,11 +222,10 @@ public struct Tdu
Composed[2] = (byte)((len >> 16) & 0xFF);
Composed[3] = (byte)((len >> 8) & 0xFF);
Composed[4] = (byte)(len & 0xFF);
Composed[5] = metaLen;
ContentOffset = metaLen + (uint)6;
ContentOffset = (uint)metadataData.Length + (uint)5;
Buffer.BlockCopy(metadata, 0, Composed, 6, metaLen);
Buffer.BlockCopy(data, 0, Composed, 6 + metaLen, (int)length);
Buffer.BlockCopy(metadataData, 0, Composed, 5, metadataData.Length);
Buffer.BlockCopy(data, 0, Composed, 5 + metadataData.Length, (int)length);
}
else if (len <= 0xFF_FF_FF_FF_FF)
{
@@ -252,11 +236,10 @@ public struct Tdu
Composed[3] = (byte)((len >> 16) & 0xFF);
Composed[4] = (byte)((len >> 8) & 0xFF);
Composed[5] = (byte)(len & 0xFF);
Composed[6] = metaLen;
ContentOffset = metaLen + (uint)7;
ContentOffset = (uint)metadataData.Length + (uint)6;
Buffer.BlockCopy(metadata, 0, Composed, 7, metaLen);
Buffer.BlockCopy(data, 0, Composed, 7 + metaLen, (int)length);
Buffer.BlockCopy(metadataData, 0, Composed, 6, metadataData.Length);
Buffer.BlockCopy(data, 0, Composed, 6 + metadataData.Length, (int)length);
}
else if (len <= 0xFF_FF_FF_FF_FF_FF)
{
@@ -268,11 +251,10 @@ public struct Tdu
Composed[4] = (byte)((len >> 16) & 0xFF);
Composed[5] = (byte)((len >> 8) & 0xFF);
Composed[6] = (byte)(len & 0xFF);
Composed[7] = metaLen;
ContentOffset = metaLen + (uint)8;
ContentOffset = (uint)metadataData.Length + (uint)7;
Buffer.BlockCopy(metadata, 0, Composed, 8, metaLen);
Buffer.BlockCopy(data, 0, Composed, 8 + metaLen, (int)length);
Buffer.BlockCopy(metadataData, 0, Composed, 7, metadataData.Length);
Buffer.BlockCopy(data, 0, Composed, 7 + metadataData.Length, (int)length);
}
else //if (len <= 0xFF_FF_FF_FF_FF_FF_FF)
{
@@ -285,11 +267,11 @@ public struct Tdu
Composed[5] = (byte)((len >> 16) & 0xFF);
Composed[6] = (byte)((len >> 8) & 0xFF);
Composed[7] = (byte)(len & 0xFF);
Composed[8] = metaLen;
ContentOffset = metaLen + (uint)9;
Buffer.BlockCopy(metadata, 0, Composed, 9, metaLen);
Buffer.BlockCopy(data, 0, Composed, 9 + metaLen, (int)length);
ContentOffset = (uint)metadataData.Length + (uint)8;
Buffer.BlockCopy(metadataData, 0, Composed, 8, metadataData.Length);
Buffer.BlockCopy(data, 0, Composed, 8 + metadataData.Length, (int)length);
}
}
@@ -305,7 +287,7 @@ public struct Tdu
if (Class != TduClass.Typed || with.Class != TduClass.Typed)
return false;
if (!Metadata.SequenceEqual(with.Metadata))
if (!Metadata.Match(with.Metadata))
return false;
return true;
+8 -6
View File
@@ -50,12 +50,14 @@ namespace Esiur.Data
Map = 0x46,
MapList = 0x47,
Record = 0x80,
TypedList = 0x81,
TypedMap = 0x82,
TypedTuple = 0x83,
TypedEnum = 0x84,
TypedConstant = 0x85,
Typed = 0x80,
//Record = 0x80,
//TypedList = 0x81,
//TypedMap = 0x82,
//TypedTuple = 0x83,
//TypedEnum = 0x84,
//TypedConstant = 0x85,
TypeContinuation = 0xC0,
TypeOfTarget = 0xC1,
File diff suppressed because it is too large Load Diff
+125
View File
@@ -0,0 +1,125 @@
using Esiur.Data.Types;
using Esiur.Protocol;
using Esiur.Resource;
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
{
internal class TruComposite : Tru
{
public Tru[] SubTypes;
Type _runtimeType;
public override Type RuntimeType { get; protected set; }
public TruComposite(TruIdentifier identifier, bool nullable, Tru[] subTypes, Type type)
{
Identifier = identifier;
Nullable = nullable;
SubTypes = subTypes;
RuntimeType = type;
//_runtimeType = typeof(Tuple).MakeGenericType(subTypes.Select(x => x.RuntimeType).ToArray());
}
public override void SetNull(List<byte> flags)
{
if (RefTypes.Contains(Identifier))
{
Nullable = (flags.FirstOrDefault() == 2);
if (flags.Count > 0)
flags.RemoveAt(0);
}
foreach (var st in SubTypes)
st.SetNull(flags);
}
public override void SetNull(byte flag)
{
if (RefTypes.Contains(Identifier))
{
Nullable = (flag == 2);
}
foreach (var st in SubTypes)
st.SetNull(flag);
}
public override void SetNotNull(List<byte> flags)
{
if (RefTypes.Contains(Identifier))
{
Nullable = (flags.FirstOrDefault() != 1);
if (flags.Count > 0)
flags.RemoveAt(0);
}
foreach (var st in SubTypes)
st.SetNotNull(flags);
}
public override void SetNotNull(byte flag)
{
if (RefTypes.Contains(Identifier))
{
Nullable = (flag != 1);
}
if (SubTypes != null)
foreach (var st in SubTypes)
st.SetNotNull(flag);
}
public override bool Match(Tru other)
{
if (other is TruComposite otherComposite)
{
if (other.Identifier != Identifier)
return false;
if (otherComposite.SubTypes.Length != (SubTypes?.Length ?? -1))
return false;
for (var i = 0; i < SubTypes?.Length; i++)
if (!SubTypes[i].Match(otherComposite.SubTypes[i]))
return false;
return true;
}
return false;
}
public override string ToString()
{
return Identifier.ToString() + "<" + String.Join(",", SubTypes.Select(x => x.ToString())) + ">" + (Nullable ? "?" : "");
}
public override byte[] Compose(EpConnection connection)
{
var rt = new BinaryList();
if (Nullable)
rt.AddUInt8((byte)(0x80 | (byte)Identifier));
else
rt.AddUInt8((byte)Identifier);
for (var i = 0; i < SubTypes.Length; i++)
rt.AddUInt8Array(SubTypes[i].Compose(connection));
return rt.ToArray();
}
public override Tru ToNullable()
{
throw new NotImplementedException();
}
}
}
+13 -3
View File
@@ -30,10 +30,20 @@ namespace Esiur.Data
Record,
List,
Map,
Enum = 0x44,
TypedResource = 0x45, // Followed by UUID
TypedRecord = 0x46, // Followed by UUID
//Enum = 0x44,
//TypedResource = 0x45, // Followed by UUID
//TypedRecord = 0x46, // Followed by UUID
LocalType8 = 0x40,
RemoteType8 = 0x41,
LocalType16 = 0x42,
RemoteType16 = 0x43,
LocalType32 = 0x44,
RemoteType32 = 0x45,
LocalType64 = 0x46,
RemoteType64 = 0x47,
TypedList = 0x48, // Followed by element type
Tuple2 = 0x50, // Followed by element type
TypedMap = 0x51, // Followed by key type and value type
Tuple3 = 0x58,
+95
View File
@@ -0,0 +1,95 @@
using Esiur.Data.Types;
using Esiur.Protocol;
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
{
public class TruPrimitive:Tru
{
public override Type RuntimeType { get; protected set; }
public override string ToString()
{
return Identifier.ToString() + (Nullable ? "?" : "");
}
public TruPrimitive(TruIdentifier identifier, bool nullable, Type type)
{
Identifier = identifier;
Nullable = nullable;
RuntimeType = type;
}
public override void SetNull(List<byte> flags)
{
if (RefTypes.Contains(Identifier))
{
Nullable = (flags.FirstOrDefault() == 2);
if (flags.Count > 0)
flags.RemoveAt(0);
}
}
public override void SetNull(byte flag)
{
if (RefTypes.Contains(Identifier))
{
Nullable = (flag == 2);
}
}
public override void SetNotNull(List<byte> flags)
{
if (RefTypes.Contains(Identifier))
{
Nullable = (flags.FirstOrDefault() != 1);
if (flags.Count > 0)
flags.RemoveAt(0);
}
}
public override void SetNotNull(byte flag)
{
if (RefTypes.Contains(Identifier))
{
Nullable = (flag != 1);
}
}
public override bool Match(Tru other)
{
if (other is TruPrimitive otherComposite)
{
if (other.Identifier != Identifier)
return false;
return true;
}
return false;
}
public override byte[] Compose(EpConnection connection)
{
var rt = new BinaryList();
if (Nullable)
rt.AddUInt8((byte)(0x80 | (byte)Identifier));
else
rt.AddUInt8((byte)Identifier);
return rt.ToArray();
}
public override Tru ToNullable()
{
throw new NotImplementedException();
}
}
}
+133
View File
@@ -0,0 +1,133 @@
using Esiur.Data.Types;
using Esiur.Protocol;
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
{
public class TruTypeDef : Tru
{
public TypeDef? TypeDef;
public override Type RuntimeType { get; protected set; }
public override void SetNull(List<byte> flags)
{
if (RefTypes.Contains(Identifier))
{
Nullable = (flags.FirstOrDefault() == 2);
if (flags.Count > 0)
flags.RemoveAt(0);
}
}
public TruTypeDef(bool nullable, TypeDef typeDef)
{
Nullable = nullable;
TypeDef = typeDef;
if (typeDef is LocalTypeDef localTypeDef)
RuntimeType = localTypeDef.DefinedType;
else if (typeDef is RemoteTypeDef remoteTypeDef)
RuntimeType = remoteTypeDef.ProxyType;
}
public override void SetNull(byte flag)
{
Nullable = (flag == 2);
}
public override void SetNotNull(List<byte> flags)
{
Nullable = (flags.FirstOrDefault() != 1);
if (flags.Count > 0)
flags.RemoveAt(0);
}
public override void SetNotNull(byte flag)
{
Nullable = (flag != 1);
}
public override bool Match(Tru other)
{
if (other is TruTypeDef otherComposite)
{
if (otherComposite.TypeDef != TypeDef)
return false;
return true;
}
return false;
}
public override string ToString()
{
return Identifier.ToString() + (Nullable ? "?" : "");
}
public override byte[] Compose(EpConnection connection)
{
var rt = new BinaryList();
if (TypeDef is RemoteTypeDef remoteTypeDef)
{
if (connection.RemoteDomain == remoteTypeDef.Domain)
{
// this is local in respect to the connection, send the remote typdef id.
if (Nullable)
rt.AddUInt8(0x80 | (byte)TruIdentifier.LocalType8);
else
rt.AddUInt8((byte)TruIdentifier.LocalType8);
rt.AddUInt8((byte)remoteTypeDef.Id);
}
else
{
// this is remote in respect to the connection and the local typedef id is used.
if (Nullable)
rt.AddUInt8(0x80 | (byte)TruIdentifier.RemoteType8);
else
rt.AddUInt8((byte)TruIdentifier.RemoteType8);
rt.AddUInt8((byte)remoteTypeDef.LocalTypeDefId);
}
}
else if (TypeDef is LocalTypeDef localTypeDef)
{
if (connection == null)
{
// if there is no connection, we assume it's local.
if (Nullable)
rt.AddUInt8(0x80 | (byte)TruIdentifier.LocalType8);
else
rt.AddUInt8((byte)TruIdentifier.LocalType8);
rt.AddUInt8((byte)localTypeDef.Id);
}
else
{
// this is remote, unless the connection is to self @TODO: solve for this state.
if (Nullable)
rt.AddUInt8(0x80 | (byte)TruIdentifier.RemoteType8);
else
rt.AddUInt8((byte)TruIdentifier.RemoteType8);
rt.AddUInt8((byte)localTypeDef.Id);
}
}
else
throw new NotImplementedException();
return rt.ToArray();
}
public override Tru ToNullable()
{
throw new NotImplementedException();
}
}
}
+51
View File
@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
{
public struct TypeDefId
{
public ulong Value;
public bool Remote;
public TypeDefId(ulong value, bool remote)
{
Value = value;
Remote = remote;
}
public unsafe override int GetHashCode()
{
// Fallback implementation when System.HashCode is not available.
unchecked
{
int hash = ((int)Value) ^ ((int)(Value >> 32));
hash = (hash * 397) ^ (Remote ? 1 : 0);
return hash;
}
}
public override string ToString()=> $"{(Remote ? "Remote" : "Local")}TypeDef{Value}";
public override bool Equals(object obj)
{
if (obj is TypeDefId b)
return Value == b.Value && Remote == b.Remote;
return false;
}
public static bool operator == (TypeDefId a, TypeDefId b)
{
return a.Equals(b);
}
public static bool operator !=(TypeDefId a, TypeDefId b)
{
return !(a == b);
}
}
}
+14 -11
View File
@@ -1,8 +1,11 @@
using Esiur.Data;
using Esiur.Core;
using Esiur.Data;
using Esiur.Protocol;
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Reflection;
using System.Text;
namespace Esiur.Data.Types;
@@ -20,7 +23,7 @@ public class ArgumentDef
public Map<string, string> Annotations { get; set; }
public static (uint, ArgumentDef) Parse(byte[] data, uint offset, int index)
public static async AsyncReply<ParseResult<ArgumentDef>> ParseAsync(byte[] data, uint offset, int index, EpConnection connection, ulong[] requestSequence)
{
var optional = (data[offset] & 0x1) == 0x1;
var hasAnnotations = (data[offset++] & 0x2) == 0x2;
@@ -28,9 +31,9 @@ public class ArgumentDef
var cs = (uint)data[offset++];
var name = data.GetString(offset, cs);
offset += cs;
var (size, type) = Tru.Parse(data, offset);
var type = await Tru.ParseAsync(data, offset, connection, requestSequence);
offset += size;
offset += type.Size;
Map<string, string> annotations = null;
@@ -46,14 +49,14 @@ public class ArgumentDef
cs += l;
}
return (cs + 2 + size, new ArgumentDef()
return new ParseResult<ArgumentDef>(new ArgumentDef()
{
Name = name,
Index = index,
Type = type,
Type = type.Value,
Optional = optional,
Annotations = annotations
});
}, cs + 2 + type.Size);
}
public ArgumentDef()
@@ -70,7 +73,7 @@ public class ArgumentDef
return $"{Name}: {Type} ";
}
public byte[] Compose()
public byte[] Compose(EpConnection connection)
{
var name = DC.ToBytes(Name);
@@ -80,7 +83,7 @@ public class ArgumentDef
.AddUInt8(Optional ? (byte)1 : (byte)0)
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(Type.Compose())
.AddUInt8Array(Type.Compose(connection))
.ToArray();
}
else
@@ -91,7 +94,7 @@ public class ArgumentDef
.AddUInt8((byte)(0x2 | (Optional ? 1 : 0)))
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(Type.Compose())
.AddUInt8Array(Type.Compose(connection))
.AddUInt8Array(exp)
.ToArray();
}
+19 -12
View File
@@ -3,7 +3,9 @@ using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using Esiur.Core;
using Esiur.Data;
using Esiur.Protocol;
using Esiur.Resource;
namespace Esiur.Data.Types;
@@ -18,7 +20,7 @@ public class ConstantDef : MemberDef
public FieldInfo FieldInfo { get; set; }
public static (uint, ConstantDef) Parse(byte[] data, uint offset, byte index, bool inherited)
public static async AsyncReply< ParseResult<ConstantDef>> ParseAsync(byte[] data, uint offset, byte index, bool inherited, EpConnection connection, ulong[] requestSequence)
{
var oOffset = offset;
@@ -27,11 +29,16 @@ public class ConstantDef : MemberDef
var name = data.GetString(offset + 1, data[offset]);
offset += (uint)data[offset] + 1;
var (dts, valueType) = Tru.Parse(data, offset);
//Console.WriteLine("Parsing constantDef " + name);
offset += dts;
//var (dts, valueType)
var valueType = await Tru.ParseAsync(data, offset, connection, requestSequence);
(dts, var value) = Codec.ParseSync(data, offset, Warehouse.Default);
//Console.WriteLine("Parsing constantDef 2 " + name);
offset += valueType.Size;
(var dts, var value) = Codec.ParseSync(data, offset, Warehouse.Default);
offset += dts;
@@ -48,19 +55,19 @@ public class ConstantDef : MemberDef
offset += len;
}
return (offset - oOffset, new ConstantDef()
return new ParseResult<ConstantDef>( new ConstantDef()
{
Index = index,
Name = name,
Inherited = inherited,
ValueType = valueType,
ValueType = valueType.Value,
Value = value,
Annotations = annotations
});
}, offset - oOffset);
}
public byte[] Compose()
public byte[] Compose(EpConnection connection)
{
var name = DC.ToBytes(Name);
@@ -75,7 +82,7 @@ public class ConstantDef : MemberDef
.AddUInt8(hdr)
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(ValueType.Compose())
.AddUInt8Array(ValueType.Compose(connection))
.AddUInt8Array(Codec.Compose(Value, null, null))
.AddInt32(exp.Length)
.AddUInt8Array(exp)
@@ -89,18 +96,18 @@ public class ConstantDef : MemberDef
.AddUInt8(hdr)
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(ValueType.Compose())
.AddUInt8Array(ValueType.Compose(connection))
.AddUInt8Array(Codec.Compose(Value, null, null))
.ToArray();
}
}
public static ConstantDef MakeConstantDef(Type type, FieldInfo ci, byte index = 0, string customName = null, TypeDef typeDef = null)
public static ConstantDef MakeConstantDef(Warehouse warehouse, Type type, FieldInfo ci, byte index = 0, string customName = null, TypeDef typeDef = null)
{
var annotationAttrs = ci.GetCustomAttributes<AnnotationAttribute>(true);
var valueType = Tru.FromType(ci.FieldType);
var valueType = Tru.FromType(ci.FieldType, warehouse);
if (valueType == null)
throw new Exception($"Unsupported type `{ci.FieldType}` in constant `{type.Name}.{ci.Name}`");
+13 -11
View File
@@ -1,5 +1,6 @@
using Esiur.Core;
using Esiur.Data;
using Esiur.Protocol;
using Esiur.Resource;
using System;
using System.Collections.Generic;
@@ -32,7 +33,7 @@ public class EventDef : MemberDef
public Tru ArgumentType { get; set; }
public static (uint, EventDef) Parse(byte[] data, uint offset, byte index, bool inherited)
public static async AsyncReply<ParseResult<EventDef>> ParseAsync(byte[] data, uint offset, byte index, bool inherited, EpConnection connection, ulong[] requestSequence)
{
var oOffset = offset;
@@ -42,9 +43,9 @@ public class EventDef : MemberDef
var name = data.GetString(offset + 1, data[offset]);
offset += (uint)data[offset] + 1;
var (dts, argType) = Tru.Parse(data, offset);
var argType = await Tru.ParseAsync(data, offset, connection, requestSequence);
offset += dts;
offset += argType.Size;
// Annotation ?
Map<string, string> annotations = null;
@@ -59,18 +60,18 @@ public class EventDef : MemberDef
offset += len;
}
return (offset - oOffset, new EventDef()
return new ParseResult<EventDef>(new EventDef()
{
Index = index,
Name = name,
Inherited = inherited,
ArgumentType = argType,
ArgumentType = argType.Value,
Subscribable = subscribable,
Annotations = annotations
});
}, offset - oOffset);
}
public byte[] Compose()
public byte[] Compose(EpConnection connection)
{
var name = Name.ToBytes();
@@ -87,7 +88,7 @@ public class EventDef : MemberDef
.AddUInt8(hdr)
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(ArgumentType.Compose())
.AddUInt8Array(ArgumentType.Compose(connection))
.AddInt32(exp.Length)
.AddUInt8Array(exp)
.ToArray();
@@ -99,12 +100,12 @@ public class EventDef : MemberDef
.AddUInt8(hdr)
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(ArgumentType.Compose())
.AddUInt8Array(ArgumentType.Compose(connection))
.ToArray();
}
public static EventDef MakeEventDef(Type type, EventInfo ei, byte index, string name, TypeDef schema)
public static EventDef MakeEventDef(Warehouse warehouse, Type type, EventInfo ei, byte index, string name, TypeDef schema)
{
if (!ei.EventHandlerType.IsGenericType)
@@ -116,7 +117,8 @@ public class EventDef : MemberDef
var argType = ei.EventHandlerType.GenericTypeArguments[0];
var evtType = Tru.FromType(argType);
// @TODO: need to check if the type is remote
var evtType = Tru.FromType(argType, warehouse);
if (evtType == null)
throw new Exception($"Unsupported type `{argType}` in event `{type.Name}.{ei.Name}`");
+22 -20
View File
@@ -40,7 +40,7 @@ public class FunctionDef : MemberDef
}
public static (uint, FunctionDef) Parse(byte[] data, uint offset, byte index, bool inherited)
public static async AsyncReply<ParseResult<FunctionDef>> ParseAsync(byte[] data, uint offset, byte index, bool inherited, EpConnection connection, ulong[] requestSequence)
{
var oOffset = offset;
@@ -51,19 +51,21 @@ public class FunctionDef : MemberDef
var name = data.GetString(offset + 1, data[offset]);
offset += (uint)data[offset] + 1;
// return type
var (rts, returnType) = Tru.Parse(data, offset);
offset += rts;
//Console.WriteLine("Parsing functionDef " + name);
// return type
var returnType = await Tru.ParseAsync(data, offset, connection, requestSequence);
offset += returnType.Size;
// arguments count
var argsCount = data[offset++];
List<ArgumentDef> arguments = new();
for (var a = 0; a < argsCount; a++)
{
var (cs, argType) = ArgumentDef.Parse(data, offset, a);
arguments.Add(argType);
offset += cs;
var argType = await ArgumentDef.ParseAsync(data, offset, a, connection, requestSequence);
arguments.Add(argType.Value);
offset += argType.Size;
}
Map<string, string> annotations = null;
@@ -79,7 +81,7 @@ public class FunctionDef : MemberDef
offset += len;
}
return (offset - oOffset, new FunctionDef()
return new ParseResult<FunctionDef>( new FunctionDef()
{
Index = index,
Name = name,
@@ -87,11 +89,11 @@ public class FunctionDef : MemberDef
IsStatic = isStatic,
Inherited = inherited,
Annotations = annotations,
ReturnType = returnType,
});
ReturnType = returnType.Value,
}, offset - oOffset);
}
public byte[] Compose()
public byte[] Compose(EpConnection connection)
{
var name = DC.ToBytes(Name);
@@ -99,11 +101,11 @@ public class FunctionDef : MemberDef
var bl = new BinaryList()
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(ReturnType.Compose())
.AddUInt8Array(ReturnType.Compose(connection))
.AddUInt8((byte)Arguments.Length);
for (var i = 0; i < Arguments.Length; i++)
bl.AddUInt8Array(Arguments[i].Compose());
bl.AddUInt8Array(Arguments[i].Compose(connection));
if (Annotations != null)
@@ -119,7 +121,7 @@ public class FunctionDef : MemberDef
}
public static FunctionDef MakeFunctionDef(Type type, MethodInfo mi, byte index, string name, TypeDef schema)
public static FunctionDef MakeFunctionDef(Warehouse warehouse, Type type, MethodInfo mi, byte index, string name, TypeDef schema)
{
var genericRtType = mi.ReturnType.IsGenericType ? mi.ReturnType.GetGenericTypeDefinition() : null;
@@ -128,23 +130,23 @@ public class FunctionDef : MemberDef
if (genericRtType == typeof(AsyncReply<>))
{
rtType = Tru.FromType(mi.ReturnType.GetGenericArguments()[0]);
rtType = Tru.FromType(mi.ReturnType.GetGenericArguments()[0], warehouse);
}
else if (genericRtType == typeof(Task<>))
{
rtType = Tru.FromType(mi.ReturnType.GetGenericArguments()[0]);
rtType = Tru.FromType(mi.ReturnType.GetGenericArguments()[0], warehouse);
}
else if (genericRtType == typeof(IEnumerable<>) || genericRtType == typeof(IAsyncEnumerable<>))
{
// get export
rtType = Tru.FromType(mi.ReturnType.GetGenericArguments()[0]);
rtType = Tru.FromType(mi.ReturnType.GetGenericArguments()[0], warehouse);
}
else
{
if (mi.ReturnType == typeof(Task))
rtType = Tru.FromType(null);
rtType = Tru.FromType(null, warehouse);
else
rtType = Tru.FromType(mi.ReturnType);
rtType = Tru.FromType(mi.ReturnType, warehouse);
}
if (rtType == null)
@@ -214,7 +216,7 @@ public class FunctionDef : MemberDef
var arguments = args.Select(x =>
{
var argType = Tru.FromType(x.ParameterType);
var argType = Tru.FromType(x.ParameterType, warehouse);
if (argType == null)
throw new Exception($"Unsupported type `{x.ParameterType}` in method `{type.Name}.{mi.Name}` parameter `{x.Name}`");
+579
View File
@@ -0,0 +1,579 @@
using Esiur.Core;
using Esiur.Data;
using Esiur.Misc;
using Esiur.Protocol;
using Esiur.Proxy;
using Esiur.Resource;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
namespace Esiur.Data.Types;
public class LocalTypeDef:TypeDef
{
Type _definedType { get; set; }
Type _parentDefinedType { get; set; }
public Type DefinedType => _definedType;
public Type ParentDefinedType => _parentDefinedType;
public TypeDef ParentTypeDef { get; private set; }
public static Uuid GetTypeUUID(Type type)
{
var attr = type.GetCustomAttribute<TypeIdAttribute>();
if (attr != null)
return attr.Id;
var tn = Encoding.UTF8.GetBytes(GetTypeName(type));
var hash = SHA256.Create().ComputeHash(tn).Clip(0, 16);
hash[6] = (byte)((hash[6] & 0xF) | 0x80);
hash[8] = (byte)((hash[8] & 0xF) | 0x80);
var rt = new Uuid(hash);
return rt;
}
static Type[] GetDistributedTypes(Type type)
{
if (type.IsArray)
return GetDistributedTypes(type.GetElementType());
else if (type.IsEnum)
return new Type[] { type };
else if (Codec.ImplementsInterface(type, typeof(IRecord))
|| Codec.ImplementsInterface(type, typeof(IResource)))
{
return new Type[] { type };
}
else if (type.IsGenericType)
{
var genericType = type.GetGenericTypeDefinition();
var genericTypeArgs = type.GetGenericArguments();
if (genericType == typeof(List<>)
|| genericType == typeof(PropertyContext<>)
|| genericType == typeof(AsyncReply<>)
|| genericType == typeof(ResourceLink<>))
{
return GetDistributedTypes(genericTypeArgs[0]);
}
else if (genericType == typeof(Tuple<>)
|| genericType == typeof(Map<,>))
{
var rt = new List<Type>();
for (var i = 0; i < genericTypeArgs.Length; i++)
{
var depTypes = GetDistributedTypes(genericTypeArgs[i]);
foreach (var depType in depTypes)
if (!rt.Contains(depType))
rt.Add(depType);
}
return rt.ToArray();
}
}
return new Type[0];
}
public static TypeDef[] GetDependencies(LocalTypeDef typeDef, Warehouse warehouse)
{
var list = new List<TypeDef>();
// Add self
list.Add(typeDef);
Action<LocalTypeDef> getDependenciesFunc = null;
getDependenciesFunc = (LocalTypeDef td) =>
{
if (td.DefinedType == null)
return;
// Add parents
var parentType = td.ParentDefinedType;
// Get parents
while (parentType != null)
{
var parentTypeDef = warehouse.GetLocalTypeDefByType(parentType);
if (parentTypeDef != null)
{
if (!list.Contains(parentTypeDef))
{
list.Add(parentTypeDef);
if (parentTypeDef is LocalTypeDef pltd)
{
parentType = pltd.DefinedType;
}
}
}
}
// functions
foreach (var f in td._functions)
{
var functionReturnTypes = GetDistributedTypes(f.MethodInfo.ReturnType);
foreach (var functionReturnType in functionReturnTypes)
{
var functionReturnTypeDef = warehouse.GetLocalTypeDefByType(functionReturnType);
if (functionReturnTypeDef != null)
{
if (!list.Contains(functionReturnTypeDef))
{
list.Add(functionReturnTypeDef);
if (functionReturnTypeDef is LocalTypeDef frtd)
getDependenciesFunc(frtd);
}
}
}
var args = f.MethodInfo.GetParameters();
for (var i = 0; i < args.Length - 1; i++)
{
var fpTypes = GetDistributedTypes(args[i].ParameterType);
foreach (var fpType in fpTypes)
{
var fpt = warehouse.GetLocalTypeDefByType(fpType);
if (fpt != null)
{
if (!list.Contains(fpt))
{
list.Add(fpt);
if (fpt is LocalTypeDef ltd)
getDependenciesFunc(ltd);
}
}
}
}
// skip EpConnection argument
if (args.Length > 0)
{
var last = args.Last();
if (last.ParameterType != typeof(EpConnection))
{
var fpTypes = GetDistributedTypes(last.ParameterType);
foreach (var fpType in fpTypes)
{
var fpt = warehouse.GetLocalTypeDefByType(fpType);
if (fpt != null)
{
if (!list.Contains(fpt))
{
list.Add(fpt);
if (fpt is LocalTypeDef ltd)
getDependenciesFunc(ltd);
}
}
}
}
}
}
// properties
foreach (var p in td._properties)
{
var propertyTypes = GetDistributedTypes(p.PropertyInfo.PropertyType);
foreach (var propertyType in propertyTypes)
{
var propertyTypeDef = warehouse.GetLocalTypeDefByType(propertyType);
if (propertyTypeDef != null)
{
if (!list.Contains(propertyTypeDef))
{
list.Add(propertyTypeDef);
if (propertyTypeDef is LocalTypeDef ltd)
getDependenciesFunc(ltd);
}
}
}
}
// events
foreach (var e in td._events)
{
var eventTypes = GetDistributedTypes(e.EventInfo.EventHandlerType.GenericTypeArguments[0]);
foreach (var eventType in eventTypes)
{
var eventTypeDef = warehouse.GetLocalTypeDefByType(eventType);
if (eventTypeDef != null)
{
if (!list.Contains(eventTypeDef))
{
list.Add(eventTypeDef);
if (eventTypeDef is LocalTypeDef ltd)
getDependenciesFunc(ltd);
}
}
}
}
};
getDependenciesFunc(typeDef);
return list.Distinct().ToArray();
}
public static string GetTypeName(Type type, char separator = '.')
{
if (type.IsGenericType)
{
var index = type.Name.IndexOf("`");
var name = $"{type.Namespace}{separator}{((index > -1) ? type.Name.Substring(0, index) : type.Name)}Of";
foreach (var t in type.GenericTypeArguments)
name += GetTypeName(t, '_');
return name;
}
else
return $"{type.Namespace?.Replace('.', separator) ?? "Global"}{separator}{type.Name}";
}
public LocalTypeDef(Type type, Warehouse warehouse)
{
if (Codec.ImplementsInterface(type, typeof(IResource)))
_typeDefKind = TypeDefKind.Resource;
else if (Codec.ImplementsInterface(type, typeof(IRecord)))
_typeDefKind = TypeDefKind.Record;
else if (type.IsEnum)
_typeDefKind = TypeDefKind.Enum;
else
throw new Exception("Type must implement IResource, IRecord or inherit from DistributedResource.");
//IsWrapper = Codec.InheritsClass(type, typeof(EpResource));
type = ResourceProxy.GetBaseType(type);
_definedType = type;
_typeName = GetTypeName(type);
warehouse.TryRegisterLocalTypeDef(this);
var hierarchy = GetHierarchy(type);
if (hierarchy.ContainsKey(MemberTypes.Field))
{
foreach (var cd in hierarchy[MemberTypes.Field])
{
_constants.Add(ConstantDef.MakeConstantDef
(warehouse, type, (FieldInfo)cd.GetMemberInfo(), cd.Index, cd.Name, this));
}
}
if (hierarchy.ContainsKey(MemberTypes.Property))
{
foreach (var pd in hierarchy[MemberTypes.Property])
{
_properties.Add(PropertyDef.MakePropertyDef
( warehouse, type, (PropertyInfo)pd.GetMemberInfo(), pd.Name, pd.Index, pd.PropertyPermission, this));
}
}
if (_typeDefKind == TypeDefKind.Resource)
{
if (hierarchy.ContainsKey(MemberTypes.Method))
{
foreach (var fd in hierarchy[MemberTypes.Method])
{
_functions.Add(FunctionDef.MakeFunctionDef
(warehouse, type, (MethodInfo)fd.GetMemberInfo(), fd.Index, fd.Name, this));
}
}
if (hierarchy.ContainsKey(MemberTypes.Event))
{
foreach (var ed in hierarchy[MemberTypes.Event])
{
_events.Add(EventDef.MakeEventDef
( warehouse, type, (EventInfo)ed.GetMemberInfo(), ed.Index, ed.Name, this));
}
}
}
// add attributes
var attrs = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.GetCustomAttribute<AttributeAttribute>() != null);
foreach (var attr in attrs)
{
var attrAttr = attr.GetCustomAttribute<AttributeAttribute>();
_attributes.Add(AttributeDef
.MakeAttributeDef(type, attr, 0, attrAttr?.Name ?? attr.Name, this));
}
// find the first parent type that implements IResource
var hasParent = HasParent(type);
var classAnnotations = type.GetCustomAttributes<AnnotationAttribute>(false);
var hasClassAnnotation = (classAnnotations != null) && (classAnnotations.Count() > 0);
if (hasParent)
{
// find the first parent type that implements IResource
_parentDefinedType = ResourceProxy.GetBaseType(type.BaseType);
var parentTypeDef = warehouse.GetLocalTypeDefByType(_parentDefinedType);
if (parentTypeDef == null)
throw new Exception("Can't find parent TypeDef.");
ParentTypeDef = parentTypeDef;
}
if (hasClassAnnotation)
{
Annotations = new Map<string, string>();
foreach (var ann in classAnnotations)
Annotations.Add(ann.Key, ann.Value);
}
}
public override byte[] Compose(EpConnection connection)
{
// bake it binarily
var b = new BinaryList();
// find the first parent type that implements IResource
var hasParent = ParentTypeDef != null;
var hasClassAnnotation = Annotations != null && Annotations.Count() > 0;
var typeNameBytes = DC.ToBytes(_typeName);
b.AddUInt8((byte)((hasParent ? 0x80 : 0) | (hasClassAnnotation ? 0x40 : 0x0) | (byte)_typeDefKind))
.AddUInt64(_typeId)
.AddUInt8((byte)typeNameBytes.Length)
.AddUInt8Array(typeNameBytes);
if (hasParent)
{
b.AddUInt64(ParentTypeDef.Id);
}
if (hasClassAnnotation)
{
//foreach (var ann in Annotations)
// Annotations.Add(ann.Key, ann.Value);
var classAnnotationBytes = Codec.Compose(Annotations, null, null);
b.AddUInt8Array(classAnnotationBytes);
}
b.AddInt32(_version)
.AddUInt16((ushort)(_functions.Count + _properties.Count + _events.Count + _constants.Count));
foreach (var ft in _functions)
b.AddUInt8Array(ft.Compose(connection ));
foreach (var pt in _properties)
b.AddUInt8Array(pt.Compose(connection));
foreach (var et in _events)
b.AddUInt8Array(et.Compose(connection));
foreach (var ct in _constants)
b.AddUInt8Array(ct.Compose(connection));
return b.ToArray();
}
public static bool HasParent(Type type)
{
var parent = type.BaseType;
while (parent != null)
{
if (parent == typeof(Esiur.Resource.Resource)
|| parent == typeof(Record)
|| parent == typeof(EntryPoint))
return false;
if (parent.GetInterfaces().Contains(typeof(IResource))
|| parent.GetInterfaces().Contains(typeof(IRecord)))
return true;
parent = parent.BaseType;
}
return false;
}
public static Dictionary<MemberTypes, List<MemberData>> GetHierarchy(Type type)
{
var members = new List<MemberData>();
var order = 0;
while (type != null)
{
var classIsPublic = type.IsEnum || (type.GetCustomAttribute<ExportAttribute>() != null);
if (classIsPublic)
{
// get public instance members only.
var mis = type.GetMembers(BindingFlags.Public | BindingFlags.Instance
| BindingFlags.DeclaredOnly | BindingFlags.Static)
.Where(x => x.MemberType == MemberTypes.Property || x.MemberType == MemberTypes.Field
|| x.MemberType == MemberTypes.Event || x.MemberType == MemberTypes.Method)
.Where(x => !(x is FieldInfo c && !c.IsStatic))
.Where(x => x.GetCustomAttribute<IgnoreAttribute>() == null)
.Where(x => x.Name != "Instance")
.Where(x => x.Name != "Trigger")
.Where(x => !(x is MethodInfo m && m.IsSpecialName))
.Where(x => !(x is EventInfo e &&
!(e.EventHandlerType.IsGenericType &&
(e.EventHandlerType.GetGenericTypeDefinition() == typeof(ResourceEventHandler<>)
|| e.EventHandlerType.GetGenericTypeDefinition() == typeof(CustomResourceEventHandler<>))
)
))
.Select(x => new MemberData(
info: x,
order: order
))
.OrderBy(x => x.Name);
members.AddRange(mis.ToArray());
}
else
{
// allow private and public members that are marked with [Export] attribute.
var mis = type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
| BindingFlags.DeclaredOnly | BindingFlags.Static)
.Where(x => x.MemberType == MemberTypes.Property || x.MemberType == MemberTypes.Field
|| x.MemberType == MemberTypes.Event || x.MemberType == MemberTypes.Method)
.Where(x => !(x is FieldInfo c && !c.IsStatic))
.Where(x => x.GetCustomAttribute<ExportAttribute>() != null)
.Where(x => !(x is MethodInfo m && m.IsSpecialName))
.Select(x => new MemberData(
info: x,
order: order
))
.OrderBy(x => x.Name);
members.AddRange(mis.ToArray());
}
type = type.BaseType;
if (type == null
|| type == typeof(Esiur.Resource.Resource)
|| type == typeof(Record)
|| type == typeof(EntryPoint))
break;
if (type.GetInterfaces().Contains(typeof(IResource))
|| type.GetInterfaces().Contains(typeof(IRecord)))
{
order++;
continue;
}
break;
}
// round 2: check for duplicates
for (var i = 0; i < members.Count; i++)
{
var mi = members[i];
for (var j = i + 1; j < members.Count; j++)
{
var pi = members[j];
if (pi.Info.MemberType != mi.Info.MemberType)
continue;
//if (ci.Info.Name == mi.Info.Name && ci.Order == mi.Order)
// throw new Exception($"Method overload is not supported. Method '{ci.Info.Name}'.");
if (pi.Name == mi.Name)
{
if (pi.Order == mi.Order)
throw new Exception($"Duplicate definitions for members public name '{mi.Info.DeclaringType.Name}:{mi.Info.Name}' and '{pi.Info.DeclaringType.Name}:{pi.Info.Name}'.");
else
{
// @TODO: check for return type and parameters they must match
if (pi.Info.Name != mi.Info.Name)
throw new Exception($"Duplicate definitions for members public name '{mi.Info.DeclaringType.Name}:{mi.Info.Name}' and '{pi.Info.DeclaringType.Name}:{pi.Info.Name}'.");
}
mi.Parent = pi;
pi.Child = mi;
}
}
}
// assign indexes
var groups = members.Where(x => x.Parent == null)
.OrderBy(x => x.Name).OrderByDescending(x => x.Order)
.GroupBy(x => x.Info.MemberType);
foreach (var group in groups)
{
byte index = 0;
foreach (var mi in group)
{
//if (mi.Parent == null)
mi.Index = index++;
}
}
var rt = groups.ToDictionary(g => g.Key, g => g.ToList());
return rt;
}
//public Map<byte, object> CastProperties(Map<string, object> properties)
//{
// var rt = new Map<byte, object>();
// foreach (var kv in properties)
// {
// var pt = GetPropertyDefByName(kv.Key);
// if (pt == null) continue;
// rt.Add(pt.Index, kv.Value);
// }
// return rt;
//}
}
+17 -13
View File
@@ -1,4 +1,5 @@
using Esiur.Data;
using Esiur.Core;
using Esiur.Data;
using Esiur.Protocol;
using Esiur.Resource;
using System;
@@ -80,7 +81,7 @@ public class PropertyDef : MemberDef
return $"{Name}: {ValueType}";
}
public static (uint, PropertyDef) Parse(byte[] data, uint offset, byte index, bool inherited)
public static async AsyncReply<ParseResult<PropertyDef>> ParseAsync(byte[] data, uint offset, byte index, bool inherited, EpConnection connection, ulong[] requestSequence)
{
var oOffset = offset;
@@ -91,11 +92,13 @@ public class PropertyDef : MemberDef
var permission = (PropertyPermission)((data[offset++] >> 1) & 0x3);
var name = data.GetString(offset + 1, data[offset]);
//Console.WriteLine("Parsing propdef " + name);
offset += (uint)data[offset] + 1;
var (dts, valueType) = Tru.Parse(data, offset);
var valueType = await Tru.ParseAsync(data, offset, connection, requestSequence);
offset += dts;
offset += valueType.Size;
Map<string, string> annotations = null;
@@ -110,20 +113,20 @@ public class PropertyDef : MemberDef
offset += len;
}
return (offset - oOffset, new PropertyDef()
return new ParseResult<PropertyDef>(new PropertyDef()
{
Index = index,
Name = name,
Inherited = inherited,
Permission = permission,
HasHistory = hasHistory,
ValueType = valueType,
ValueType = valueType.Value,
Annotations = annotations
});
}, offset - oOffset);
}
public byte[] Compose()
public byte[] Compose(EpConnection connection)
{
var name = DC.ToBytes(Name);
@@ -178,7 +181,7 @@ public class PropertyDef : MemberDef
.AddUInt8((byte)(0x28 | pv))
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(ValueType.Compose())
.AddUInt8Array(ValueType.Compose(connection))
.AddUInt8Array(rexp)
.ToArray();
}
@@ -188,20 +191,21 @@ public class PropertyDef : MemberDef
.AddUInt8((byte)(0x20 | pv))
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(ValueType.Compose())
.AddUInt8Array(ValueType.Compose(connection))
.ToArray();
}
}
public static PropertyDef MakePropertyDef(Type type, PropertyInfo pi, string name, byte index, PropertyPermission permission, TypeDef schema)
public static PropertyDef MakePropertyDef(Warehouse warehouse, Type type, PropertyInfo pi, string name, byte index, PropertyPermission permission, TypeDef schema)
{
var genericPropType = pi.PropertyType.IsGenericType ? pi.PropertyType.GetGenericTypeDefinition() : null;
// @TODO: need to check if the type is remote
var propType = genericPropType == typeof(PropertyContext<>) ?
Tru.FromType(pi.PropertyType.GetGenericArguments()[0]) :
Tru.FromType(pi.PropertyType);
Tru.FromType(pi.PropertyType.GetGenericArguments()[0], warehouse) :
Tru.FromType(pi.PropertyType, warehouse);
if (propType == null)
throw new Exception($"Unsupported type `{pi.PropertyType}` in property `{type.Name}.{pi.Name}`");
+130
View File
@@ -0,0 +1,130 @@
using Esiur.Core;
using Esiur.Data;
using Esiur.Misc;
using Esiur.Protocol;
using Esiur.Proxy;
using Esiur.Resource;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
namespace Esiur.Data.Types;
public class RemoteTypeDef:TypeDef
{
Type _proxyType { get; set; }
public Type ProxyType => _proxyType;
string _domain;
public string Domain => _domain;
public uint _localTypeDefId;
public uint LocalTypeDefId
{
get => _localTypeDefId;
internal set => _localTypeDefId = value;
}
public static AsyncReply<RemoteTypeDef> Parse(RemoteTypeDef od, string domain, byte[] data, EpConnection connection, ulong[] requestSequence)
{
return Parse(od, domain, data, 0, (uint)data.Length, connection, requestSequence);
}
public static async AsyncReply<RemoteTypeDef> Parse(RemoteTypeDef od, string domain, byte[] data, uint offset, uint contentLength, EpConnection connection, ulong[] requestSequence)
{
uint ends = offset + contentLength;
uint oOffset = offset;
// start parsing...
od._domain = domain;
od._content = data.Clip(offset, contentLength);
var hasParent = (data[offset] & 0x80) > 0;
var hasClassAnnotation = (data[offset] & 0x40) > 0;
od._typeDefKind = (TypeDefKind)(data[offset++] & 0xF);
od._typeId = data.GetUInt64(offset, Endian.Little);
offset += 8;
od._typeName = data.GetString(offset + 1, data[offset]);
offset += (uint)data[offset] + 1;
if (hasParent)
{
od._parentTypeId = data.GetUInt64(offset, Endian.Little);
offset += 8;
}
if (hasClassAnnotation)
{
var (len, anns) = Codec.ParseSync(data, offset, null);
if (anns is Map<string, string> annotations)
od.Annotations = annotations;
offset += len;
}
od._version = data.GetInt32(offset, Endian.Little);
offset += 4;
ushort methodsCount = data.GetUInt16(offset, Endian.Little);
offset += 2;
byte functionIndex = 0;
byte propertyIndex = 0;
byte eventIndex = 0;
byte constantIndex = 0;
for (int i = 0; i < methodsCount; i++)
{
var inherited = (data[offset] & 0x80) > 0;
var type = (data[offset] >> 5) & 0x3;
if (type == 0) // function
{
var ft = await FunctionDef.ParseAsync(data, offset, functionIndex++, inherited, connection, requestSequence);
offset += ft.Size;
od._functions.Add(ft.Value);
}
else if (type == 1) // property
{
var pt = await PropertyDef.ParseAsync(data, offset, propertyIndex++, inherited, connection, requestSequence);
offset += pt.Size;
od._properties.Add(pt.Value);
}
else if (type == 2) // Event
{
var et = await EventDef.ParseAsync(data, offset, eventIndex++, inherited, connection, requestSequence);
offset += et.Size;
od._events.Add(et.Value);
}
// constant
else if (type == 3)
{
var ct = await ConstantDef.ParseAsync(data, offset, constantIndex++, inherited, connection, requestSequence);
offset += ct.Size;
od._constants.Add(ct.Value);
}
}
return od;
}
}
+50 -636
View File
@@ -18,46 +18,51 @@ namespace Esiur.Data.Types;
public class TypeDef
{
protected Uuid typeId;
protected Uuid? parentId;
protected ulong _typeId;
protected ulong? _parentTypeId;
public Map<string, string> Annotations { get; set; }
string typeName;
List<FunctionDef> functions = new List<FunctionDef>();
List<EventDef> events = new List<EventDef>();
List<PropertyDef> properties = new List<PropertyDef>();
List<AttributeDef> attributes = new List<AttributeDef>();
List<ConstantDef> constants = new();
int version;
TypeDefKind typeDefKind;
protected string _typeName;
protected List<FunctionDef> _functions = new List<FunctionDef>();
protected List<EventDef> _events = new List<EventDef>();
protected List<PropertyDef> _properties = new List<PropertyDef>();
protected List<AttributeDef> _attributes = new List<AttributeDef>();
protected List<ConstantDef> _constants = new();
protected int _version;
protected TypeDefKind _typeDefKind;
//Type _definedType { get; set; }
//Type _proxyType { get; set; }
//Type _parentDefinedType { get; set; }
//bool _isLocal;
//public Type DefinedType => _definedType;
//public Type ParentDefinedType => _parentDefinedType;
//public Type ProxyType => _proxyType;
public override string ToString()
{
return typeName;
return _typeName;
}
protected byte[] _content;
protected byte[] content;
public ulong? ParentTypeId => _parentTypeId;
public Uuid? ParentId => parentId;
//public byte[] Content => _content;
public byte[] Content
{
get { return content; }
}
public TypeDefKind Kind => typeDefKind;
public TypeDefKind Kind => _typeDefKind;
public Type DefinedType { get; set; }
public Type ParentDefinedType { get; set; }
public EventDef GetEventDefByName(string eventName)
{
foreach (var i in events)
foreach (var i in _events)
if (i.Name == eventName)
return i;
return null;
@@ -65,7 +70,7 @@ public class TypeDef
public EventDef GetEventDefByIndex(byte index)
{
foreach (var i in events)
foreach (var i in _events)
if (i.Index == index)
return i;
return null;
@@ -73,14 +78,14 @@ public class TypeDef
public FunctionDef GetFunctionDefByName(string functionName)
{
foreach (var i in functions)
foreach (var i in _functions)
if (i.Name == functionName)
return i;
return null;
}
public FunctionDef GetFunctionDefByIndex(byte index)
{
foreach (var i in functions)
foreach (var i in _functions)
if (i.Index == index)
return i;
return null;
@@ -88,7 +93,7 @@ public class TypeDef
public PropertyDef GetPropertyDefByIndex(byte index)
{
foreach (var i in properties)
foreach (var i in _properties)
if (i.Index == index)
return i;
return null;
@@ -96,7 +101,7 @@ public class TypeDef
public PropertyDef GetPropertyDefByName(string propertyName)
{
foreach (var i in properties)
foreach (var i in _properties)
if (i.Name == propertyName)
return i;
return null;
@@ -104,645 +109,47 @@ public class TypeDef
public AttributeDef GetAttributeDef(string attributeName)
{
foreach (var i in attributes)
foreach (var i in _attributes)
if (i.Name == attributeName)
return i;
return null;
}
public Uuid Id
public ulong Id
{
get { return typeId; }
get { return _typeId; }
internal set { _typeId = value; }
}
public string Name
{
get { return typeName; }
get { return _typeName; }
}
public FunctionDef[] Functions
{
get { return functions.ToArray(); }
get { return _functions.ToArray(); }
}
public EventDef[] Events
{
get { return events.ToArray(); }
get { return _events.ToArray(); }
}
public PropertyDef[] Properties
{
get { return properties.ToArray(); }
get { return _properties.ToArray(); }
}
public ConstantDef[] Constants => constants.ToArray();
public ConstantDef[] Constants => _constants.ToArray();
public TypeDef()
{
}
public static Uuid GetTypeUUID(Type type)
{
var attr = type.GetCustomAttribute<TypeIdAttribute>();
if (attr != null)
return attr.Id;
var tn = Encoding.UTF8.GetBytes(GetTypeName(type));
var hash = SHA256.Create().ComputeHash(tn).Clip(0, 16);
hash[6] = (byte)((hash[6] & 0xF) | 0x80);
hash[8] = (byte)((hash[8] & 0xF) | 0x80);
var rt = new Uuid(hash);
return rt;
}
static Type[] GetDistributedTypes(Type type)
{
if (type.IsArray)
return GetDistributedTypes(type.GetElementType());
else if (type.IsEnum)
return new Type[] { type };
else if (Codec.ImplementsInterface(type, typeof(IRecord))
|| Codec.ImplementsInterface(type, typeof(IResource)))
{
return new Type[] { type };
}
else if (type.IsGenericType)
{
var genericType = type.GetGenericTypeDefinition();
var genericTypeArgs = type.GetGenericArguments();
if (genericType == typeof(List<>)
|| genericType == typeof(PropertyContext<>)
|| genericType == typeof(AsyncReply<>)
|| genericType == typeof(ResourceLink<>))
{
return GetDistributedTypes(genericTypeArgs[0]);
}
else if (genericType == typeof(Tuple<>)
|| genericType == typeof(Map<,>))
{
var rt = new List<Type>();
for (var i = 0; i < genericTypeArgs.Length; i++)
{
var depTypes = GetDistributedTypes(genericTypeArgs[i]);
foreach (var depType in depTypes)
if (!rt.Contains(depType))
rt.Add(depType);
}
return rt.ToArray();
}
}
return new Type[0];
}
public static TypeDef[] GetDependencies(TypeDef schema, Warehouse warehouse)
{
var list = new List<TypeDef>();
// Add self
list.Add(schema);
Action<TypeDef, List<TypeDef>> getDependenciesFunc = null;
getDependenciesFunc = (TypeDef sch, List<TypeDef> bag) =>
{
if (schema.DefinedType == null)
return;
// Add parents
var parentType = sch.ParentDefinedType;
// Get parents
while (parentType != null)
{
var parentTypeDef = warehouse.GetTypeDefByType(parentType);
if (parentTypeDef != null)
{
list.Add(parentTypeDef);
parentType = parentTypeDef.ParentDefinedType;
}
}
// functions
foreach (var f in sch.functions)
{
var functionReturnTypes = GetDistributedTypes(f.MethodInfo.ReturnType);
foreach (var functionReturnType in functionReturnTypes)
{
var functionReturnTypeDef = warehouse.GetTypeDefByType(functionReturnType);
if (functionReturnTypeDef != null)
{
if (!bag.Contains(functionReturnTypeDef))
{
list.Add(functionReturnTypeDef);
getDependenciesFunc(functionReturnTypeDef, bag);
}
}
}
var args = f.MethodInfo.GetParameters();
for (var i = 0; i < args.Length - 1; i++)
{
var fpTypes = GetDistributedTypes(args[i].ParameterType);
foreach (var fpType in fpTypes)
{
var fpt = warehouse.GetTypeDefByType(fpType);
if (fpt != null)
{
if (!bag.Contains(fpt))
{
bag.Add(fpt);
getDependenciesFunc(fpt, bag);
}
}
}
}
// skip EpConnection argument
if (args.Length > 0)
{
var last = args.Last();
if (last.ParameterType != typeof(EpConnection))
{
var fpTypes = GetDistributedTypes(last.ParameterType);
foreach (var fpType in fpTypes)
{
var fpt = warehouse.GetTypeDefByType(fpType);
if (fpt != null)
{
if (!bag.Contains(fpt))
{
bag.Add(fpt);
getDependenciesFunc(fpt, bag);
}
}
}
}
}
}
// properties
foreach (var p in sch.properties)
{
var propertyTypes = GetDistributedTypes(p.PropertyInfo.PropertyType);
foreach (var propertyType in propertyTypes)
{
var propertyTypeDef = warehouse.GetTypeDefByType(propertyType);
if (propertyTypeDef != null)
{
if (!bag.Contains(propertyTypeDef))
{
bag.Add(propertyTypeDef);
getDependenciesFunc(propertyTypeDef, bag);
}
}
}
}
// events
foreach (var e in sch.events)
{
var eventTypes = GetDistributedTypes(e.EventInfo.EventHandlerType.GenericTypeArguments[0]);
foreach (var eventType in eventTypes)
{
var eventTypeDef = warehouse.GetTypeDefByType(eventType);
if (eventTypeDef != null)
{
if (!bag.Contains(eventTypeDef))
{
bag.Add(eventTypeDef);
getDependenciesFunc(eventTypeDef, bag);
}
}
}
}
};
getDependenciesFunc(schema, list);
return list.Distinct().ToArray();
}
public static string GetTypeName(Type type, char separator = '.')
{
if (type.IsGenericType)
{
var index = type.Name.IndexOf("`");
var name = $"{type.Namespace}{separator}{((index > -1) ? type.Name.Substring(0, index) : type.Name)}Of";
foreach (var t in type.GenericTypeArguments)
name += GetTypeName(t, '_');
return name;
}
else
return $"{type.Namespace?.Replace('.', separator) ?? "Global"}{separator}{type.Name}";
}
public bool IsWrapper { get; private set; }
public TypeDef(Type type, Warehouse warehouse = null)
{
if (Codec.ImplementsInterface(type, typeof(IResource)))
typeDefKind = TypeDefKind.Resource;
else if (Codec.ImplementsInterface(type, typeof(IRecord)))
typeDefKind = TypeDefKind.Record;
else if (type.IsEnum)
typeDefKind = TypeDefKind.Enum;
else
throw new Exception("Type must implement IResource, IRecord or inherit from DistributedResource.");
IsWrapper = Codec.InheritsClass(type, typeof(EpResource));
type = ResourceProxy.GetBaseType(type);
DefinedType = type;
typeName = GetTypeName(type);
// set guid
typeId = GetTypeUUID(type);
if (warehouse != null)
warehouse.RegisterTypeDef(this);
var hierarchy = GetHierarchy(type);
if (hierarchy.ContainsKey(MemberTypes.Field))
{
foreach (var cd in hierarchy[MemberTypes.Field])
{
constants.Add(ConstantDef.MakeConstantDef
(type, (FieldInfo)cd.GetMemberInfo(), cd.Index, cd.Name, this));
}
}
if (hierarchy.ContainsKey(MemberTypes.Property))
{
foreach (var pd in hierarchy[MemberTypes.Property])
{
properties.Add(PropertyDef.MakePropertyDef
(type, (PropertyInfo)pd.GetMemberInfo(), pd.Name, pd.Index, pd.PropertyPermission, this));
}
}
if (typeDefKind == TypeDefKind.Resource)
{
if (hierarchy.ContainsKey(MemberTypes.Method))
{
foreach (var fd in hierarchy[MemberTypes.Method])
{
functions.Add(FunctionDef.MakeFunctionDef
(type, (MethodInfo)fd.GetMemberInfo(), fd.Index, fd.Name, this));
}
}
if (hierarchy.ContainsKey(MemberTypes.Event))
{
foreach (var ed in hierarchy[MemberTypes.Event])
{
events.Add(EventDef.MakeEventDef
(type, (EventInfo)ed.GetMemberInfo(), ed.Index, ed.Name, this));
}
}
}
// add attributes
var attrs = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.GetCustomAttribute<AttributeAttribute>() != null);
foreach (var attr in attrs)
{
var attrAttr = attr.GetCustomAttribute<AttributeAttribute>();
attributes.Add(AttributeDef
.MakeAttributeDef(type, attr, 0, attrAttr?.Name ?? attr.Name, this));
}
// bake it binarily
var b = new BinaryList();
// find the first parent type that implements IResource
var hasParent = HasParent(type);
var classAnnotations = type.GetCustomAttributes<AnnotationAttribute>(false);
var hasClassAnnotation = (classAnnotations != null) && (classAnnotations.Count() > 0);
var typeNameBytes = DC.ToBytes(typeName);
b.AddUInt8((byte)((hasParent ? 0x80 : 0) | (hasClassAnnotation ? 0x40 : 0x0) | (byte)typeDefKind))
.AddUUID(typeId)
.AddUInt8((byte)typeNameBytes.Length)
.AddUInt8Array(typeNameBytes);
if (hasParent)
{
// find the first parent type that implements IResource
ParentDefinedType = ResourceProxy.GetBaseType(type.BaseType);
var parentId = GetTypeUUID(ParentDefinedType);
b.AddUUID(parentId);
}
if (hasClassAnnotation)
{
Annotations = new Map<string, string>();
foreach (var ann in classAnnotations)
Annotations.Add(ann.Key, ann.Value);
var classAnnotationBytes = Codec.Compose (Annotations, null, null);
b.AddUInt8Array(classAnnotationBytes);
}
b.AddInt32(version)
.AddUInt16((ushort)(functions.Count + properties.Count + events.Count + constants.Count));
foreach (var ft in functions)
b.AddUInt8Array(ft.Compose());
foreach (var pt in properties)
b.AddUInt8Array(pt.Compose());
foreach (var et in events)
b.AddUInt8Array(et.Compose());
foreach (var ct in constants)
b.AddUInt8Array(ct.Compose());
content = b.ToArray();
}
public static bool HasParent(Type type)
{
var parent = type.BaseType;
while (parent != null)
{
if (parent == typeof(Esiur.Resource.Resource)
|| parent == typeof(Record)
|| parent == typeof(EntryPoint))
return false;
if (parent.GetInterfaces().Contains(typeof(IResource))
|| parent.GetInterfaces().Contains(typeof(IRecord)))
return true;
parent = parent.BaseType;
}
return false;
}
public static Dictionary<MemberTypes, List<MemberData>> GetHierarchy(Type type)
{
var members = new List<MemberData>();
var order = 0;
while (type != null)
{
var classIsPublic = type.IsEnum || (type.GetCustomAttribute<ExportAttribute>() != null);
if (classIsPublic)
{
// get public instance members only.
var mis = type.GetMembers(BindingFlags.Public | BindingFlags.Instance
| BindingFlags.DeclaredOnly | BindingFlags.Static)
.Where(x => x.MemberType == MemberTypes.Property || x.MemberType == MemberTypes.Field
|| x.MemberType == MemberTypes.Event || x.MemberType == MemberTypes.Method)
.Where(x => !(x is FieldInfo c && !c.IsStatic))
.Where(x => x.GetCustomAttribute<IgnoreAttribute>() == null)
.Where(x => x.Name != "Instance")
.Where(x => x.Name != "Trigger")
.Where(x => !(x is MethodInfo m && m.IsSpecialName))
.Where(x => !(x is EventInfo e &&
!(e.EventHandlerType.IsGenericType &&
(e.EventHandlerType.GetGenericTypeDefinition() == typeof(ResourceEventHandler<>)
|| e.EventHandlerType.GetGenericTypeDefinition() == typeof(CustomResourceEventHandler<>))
)
))
.Select(x => new MemberData(
info: x,
order: order
))
.OrderBy(x => x.Name);
members.AddRange(mis.ToArray());
}
else
{
// allow private and public members that are marked with [Export] attribute.
var mis = type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
| BindingFlags.DeclaredOnly | BindingFlags.Static)
.Where(x => x.MemberType == MemberTypes.Property || x.MemberType == MemberTypes.Field
|| x.MemberType == MemberTypes.Event || x.MemberType == MemberTypes.Method)
.Where(x => !(x is FieldInfo c && !c.IsStatic))
.Where(x => x.GetCustomAttribute<ExportAttribute>() != null)
.Where(x => !(x is MethodInfo m && m.IsSpecialName))
.Select(x => new MemberData(
info: x,
order: order
))
.OrderBy(x => x.Name);
members.AddRange(mis.ToArray());
}
type = type.BaseType;
if (type == null
|| type == typeof(Esiur.Resource.Resource)
|| type == typeof(Record)
|| type == typeof(EntryPoint))
break;
if (type.GetInterfaces().Contains(typeof(IResource))
|| type.GetInterfaces().Contains(typeof(IRecord)))
{
order++;
continue;
}
break;
}
// round 2: check for duplicates
for (var i = 0; i < members.Count; i++)
{
var mi = members[i];
for (var j = i + 1; j < members.Count; j++)
{
var pi = members[j];
if (pi.Info.MemberType != mi.Info.MemberType)
continue;
//if (ci.Info.Name == mi.Info.Name && ci.Order == mi.Order)
// throw new Exception($"Method overload is not supported. Method '{ci.Info.Name}'.");
if (pi.Name == mi.Name)
{
if (pi.Order == mi.Order)
throw new Exception($"Duplicate definitions for members public name '{mi.Info.DeclaringType.Name}:{mi.Info.Name}' and '{pi.Info.DeclaringType.Name}:{pi.Info.Name}'.");
else
{
// @TODO: check for return type and parameters they must match
if (pi.Info.Name != mi.Info.Name)
throw new Exception($"Duplicate definitions for members public name '{mi.Info.DeclaringType.Name}:{mi.Info.Name}' and '{pi.Info.DeclaringType.Name}:{pi.Info.Name}'.");
}
mi.Parent = pi;
pi.Child = mi;
}
}
}
// assign indexes
var groups = members.Where(x => x.Parent == null)
.OrderBy(x => x.Name).OrderByDescending(x => x.Order)
.GroupBy(x => x.Info.MemberType);
foreach (var group in groups)
{
byte index = 0;
foreach (var mi in group)
{
//if (mi.Parent == null)
mi.Index = index++;
}
}
var rt = groups.ToDictionary(g => g.Key, g => g.ToList());
return rt;
}
public static TypeDef Parse(byte[] data)
{
return Parse(data, 0, (uint)data.Length);
}
public static TypeDef Parse(byte[] data, uint offset, uint contentLength)
{
uint ends = offset + contentLength;
uint oOffset = offset;
// start parsing...
var od = new TypeDef();
od.content = data.Clip(offset, contentLength);
var hasParent = (data[offset] & 0x80) > 0;
var hasClassAnnotation = (data[offset] & 0x40) > 0;
od.typeDefKind = (TypeDefKind)(data[offset++] & 0xF);
od.typeId = data.GetUUID(offset);
offset += 16;
od.typeName = data.GetString(offset + 1, data[offset]);
offset += (uint)data[offset] + 1;
if (hasParent)
{
od.parentId = data.GetUUID(offset);
offset += 16;
}
if (hasClassAnnotation)
{
var (len, anns) = Codec.ParseSync(data, offset, null);
if (anns is Map<string, string> annotations)
od.Annotations = annotations;
offset += len;
}
od.version = data.GetInt32(offset, Endian.Little);
offset += 4;
ushort methodsCount = data.GetUInt16(offset, Endian.Little);
offset += 2;
byte functionIndex = 0;
byte propertyIndex = 0;
byte eventIndex = 0;
byte constantIndex = 0;
for (int i = 0; i < methodsCount; i++)
{
var inherited = (data[offset] & 0x80) > 0;
var type = (data[offset] >> 5) & 0x3;
if (type == 0) // function
{
var (len, ft) = FunctionDef.Parse(data, offset, functionIndex++, inherited);
offset += len;
od.functions.Add(ft);
}
else if (type == 1) // property
{
var (len, pt) = PropertyDef.Parse(data, offset, propertyIndex++, inherited);
offset += len;
od.properties.Add(pt);
}
else if (type == 2) // Event
{
var (len, et) = EventDef.Parse(data, offset, eventIndex++, inherited);
offset += len;
od.events.Add(et);
}
// constant
else if (type == 3)
{
var (len, ct) = ConstantDef.Parse(data, offset, constantIndex++, inherited);
offset += len;
od.constants.Add(ct);
}
}
return od;
}
public Map<byte, object> CastProperties(Map<string, object> properties)
{
var rt = new Map<byte, object>();
@@ -755,5 +162,12 @@ public class TypeDef
return rt;
}
public virtual byte[] Compose(EpConnection connection)
{
return null;
}
}
+10 -6
View File
@@ -59,33 +59,37 @@
<ItemGroup>
<Compile Remove="Data\IRemoteRecord.cs" />
<Compile Remove="Data\NullabilityInfo.cs" />
<Compile Remove="Data\NullabilityInfoContext.cs" />
<Compile Remove="Data\TypeDefId.cs" />
<Compile Remove="Net\Packets\EpAuthPacketAcknowledgement.cs" />
<Compile Remove="Net\Packets\EpAuthPacketAction.cs" />
<Compile Remove="Net\Packets\EpAuthPacketAuthMode.cs" />
<Compile Remove="Net\Packets\EpAuthPacketEvent.cs" />
<Compile Remove="Net\Sockets\TcpSocket.cs" />
<Compile Remove="Protocol\Authentication\HashAnonymousAuthenticator.cs" />
<Compile Remove="Security\Authority\Authentication.cs" />
<Compile Remove="Security\Authority\AuthenticationMethod.cs" />
<Compile Remove="Security\Authority\IAuthenticationInitiator.cs" />
<Compile Remove="Security\Authority\IAuthenticationResponder.cs" />
<Compile Remove="Security\Membership\SimpleMembership.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Data\IRemoteRecord.cs" />
<None Include="Data\NullabilityInfo.cs" />
<None Include="Data\NullabilityInfoContext.cs" />
<None Include="Data\TypeDefId.cs" />
<None Include="Data\Types\ArgumentDef.cs" />
<None Include="Data\Types\AttributeDef.cs" />
<None Include="LICENSE" Pack="true" PackagePath=""></None>
<None Include="Net\Packets\EpAuthPacketAcknowledgement.cs" />
<None Include="Net\Packets\EpAuthPacketAction.cs" />
<None Include="Net\Packets\EpAuthPacketAuthMode.cs" />
<None Include="Net\Packets\EpAuthPacketEvent.cs" />
<None Include="Net\Sockets\TcpSocket.cs" />
<None Include="Protocol\Authentication\HashAnonymousAuthenticator.cs" />
<None Include="README.md" Pack="true" PackagePath="" />
<None Include="Security\Authority\Authentication.cs" />
<None Include="Security\Authority\AuthenticationMethod.cs" />
<None Include="Security\Authority\IAuthenticationInitiator.cs" />
<None Include="Security\Authority\IAuthenticationResponder.cs" />
<None Include="Security\Membership\SimpleMembership.cs" />
</ItemGroup>
+36 -27
View File
@@ -23,6 +23,7 @@ SOFTWARE.
*/
using Esiur.Data;
using Esiur.Resource;
using Esiur.Security.Authority;
using Esiur.Security.Cryptography;
using System;
@@ -38,29 +39,39 @@ namespace Esiur.Net.Packets;
public class EpAuthPacket : Packet
{
Warehouse _warehouse;
public EpAuthPacket(Warehouse warehouse)
{
_warehouse = warehouse;
}
public EpAuthPacketCommand Command
{
get;
set;
}
public EpAuthPacketAction Action
{
get;
set;
}
public EpAuthPacketMethod Method { get; set; }
public EpAuthPacketEvent Event
{
get;
set;
}
//public EpAuthPacketAction Action
//{
// get;
// set;
//}
public EpAuthPacketAcknowledgement Acknowledgement
{
get;
set;
}
//public EpAuthPacketEvent Event
//{
// get;
// set;
//}
//public EpAuthPacketAcknowledgement Acknowledgement
//{
// get;
// set;
//}
public AuthenticationMode AuthMode
@@ -101,7 +112,7 @@ public class EpAuthPacket : Packet
}
public ParsedTdu? Tdu
public PlainTdu? Tdu
{
get;
set;
@@ -114,8 +125,6 @@ public class EpAuthPacket : Packet
set;
}
private uint dataLengthNeeded;
bool NotEnough(uint offset, uint ends, uint needed)
@@ -131,7 +140,7 @@ public class EpAuthPacket : Packet
public override string ToString()
{
return Command.ToString() + " " + Action.ToString();
return Command.ToString() + " " + Method.ToString();
}
public override long Parse(byte[] data, uint offset, uint ends)
@@ -146,23 +155,23 @@ public class EpAuthPacket : Packet
if (Command == EpAuthPacketCommand.Initialize)
{
AuthMode = (AuthenticationMode)(data[offset] >> 3 & 0x7);
AuthMode = (AuthenticationMode)(data[offset] >> 2 & 0x4);
EncryptionMode = (EncryptionMode)(data[offset++] & 0x7);
}
else if (Command == EpAuthPacketCommand.Acknowledge)
{
// remove last two reserved LSBs
Acknowledgement = (EpAuthPacketAcknowledgement)(data[offset++]);// & 0xFC);
// remove hasTdu
Method = (EpAuthPacketMethod)(data[offset++] & 0xDF);
}
else if (Command == EpAuthPacketCommand.Action)
{
// remove last two reserved LSBs
Action = (EpAuthPacketAction)(data[offset++]);// & 0xFC);
// remove hasTdu
Method = (EpAuthPacketMethod)(data[offset++] & 0xDF);
}
else if (Command == EpAuthPacketCommand.Event)
{
// remove last two reserved LSBs
Event = (EpAuthPacketEvent)(data[offset++]);// & 0xFC);
// remove hasTdu
Method = (EpAuthPacketMethod)(data[offset++] & 0xDF);
}
else
{
@@ -174,7 +183,7 @@ public class EpAuthPacket : Packet
if (NotEnough(offset, ends, 1))
return -dataLengthNeeded;
Tdu = ParsedTdu.Parse(data, offset, ends);
Tdu = PlainTdu.Parse(data, offset, ends);//, _warehouse);
if (Tdu.Value.Class == TduClass.Invalid)
return -(int)Tdu.Value.TotalLength;
@@ -19,6 +19,8 @@ namespace Esiur.Net.Packets
Referrer,
Time,
IPAddress,
Identity,
AuthenticationProtocol,
AuthenticationData,
}
}
@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Net.Packets
{
public enum EpAuthPacketMethod
{
// Initialize
Initialize = 0x00,
//InitializeEncrypted = 0x01,
//InitializeNoAuth = 0x02,
//InitializeNoAuthEncrypted = 0x3,
// Actions
Handshake = 0x80,
FinalHandshake = 0x81,
// Acks
Denied = 0x40, // no reason, terminate connection
NotSupported = 0x41, // auth not supported, terminate connection
TrySupported = 0x42, // auth not supported, but other auth methods in the reply are supported. connection is still open
Retry = 0x43, // try another auth method, connection is still open
ProceedToHandshake = 0x44, // auth method accepted, proceed to handshake, connection is still open
ProceedToFinalHandshake = 0x45, // auth method accepted, proceed to final handshake, connection is still open
ProceedToEstablishSession = 0x46, // auth method accepted, proceed to establish session, connection is still open
SessionEstablished = 0x47, // session established, session Id provided, switch to session mode, connection is still open
// Events
ErrorTerminate = 0xC0,
ErrorMustEncrypt = 0xC1,
ErrorRetry = 0xC2,
IndicationEstablished = 0xC8,
IAuthPlain = 0xD0,
IAuthHashed = 0xD1,
IAuthEncrypted = 0xD2
}
}
+20 -2
View File
@@ -30,6 +30,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Esiur.Resource;
namespace Esiur.Net.Packets;
class EpPacket : Packet
@@ -44,12 +45,22 @@ class EpPacket : Packet
public byte Extension { get; set; }
public ParsedTdu? Tdu { get; set; }
//public ParsedTdu? Tdu { get; set; }
//public byte[] TduPayload { get; set; }
public PlainTdu? Tdu { get; set; }
private uint dataLengthNeeded;
private uint originalOffset;
Warehouse _warehouse;
public EpPacket(Warehouse warehouse)
{
_warehouse = warehouse;
}
public override bool Compose()
{
return base.Compose();
@@ -124,15 +135,22 @@ class EpPacket : Packet
if (NotEnough(offset, ends, 1))
return -dataLengthNeeded;
Tdu = ParsedTdu.Parse(data, offset, ends);
Tdu = PlainTdu.Parse(data, offset, ends);
if (Tdu.Value.Class == TduClass.Invalid)
return -(int)Tdu.Value.TotalLength;
//Tdu = ParsedTdu.ParseSync(data, offset, ends, _warehouse);
//if (Tdu.Value.Class == TduClass.Invalid)
// return -(int)Tdu.Value.TotalLength;
//offset += (uint)Tdu.Value.TotalLength;
offset += (uint)Tdu.Value.TotalLength;
}
else
{
//Tdu = null;
Tdu = null;
}
File diff suppressed because it is too large Load Diff
@@ -1,35 +0,0 @@
using Esiur.Core;
using Esiur.Data;
using Esiur.Net.Packets;
using Esiur.Security.Membership;
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Protocol;
public class EpConnectionConfig
{
public ExceptionLevel ExceptionLevel { get; set; }
= ExceptionLevel.Code | ExceptionLevel.Message | ExceptionLevel.Source | ExceptionLevel.Trace;
public Func<AuthorizationRequest, AsyncReply<object>> Authenticator { get; set; }
public bool AutoReconnect { get; set; } = false;
public uint ReconnectInterval { get; set; } = 5;
public string Username { get; set; }
public bool UseWebSocket { get; set; }
public bool SecureWebSocket { get; set; }
public string Password { get; set; }
public string Token { get; set; }
public ulong TokenIndex { get; set; }
public string Domain { get; set; }
}
@@ -0,0 +1,58 @@
using Esiur.Core;
using Esiur.Data;
using Esiur.Net.Packets;
using Esiur.Resource;
using Esiur.Security.Membership;
using Esiur.Security.Permissions;
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Protocol;
public class EpConnectionContext : ResourceContext
{
public EpConnectionContext()
: base(0, new Map<string, object>(), null, null)
{
}
public override void Build()
{
Attributes["AutoConnect"] = AutoReconnect;
Attributes["ReconnectInterval"] = ReconnectInterval;
Attributes["UseWebSocket"] = UseWebSocket;
Attributes["SecureWebSocket"] = SecureWebSocket;
Attributes["Domain"] = SecureWebSocket;
Attributes["AuthenticationProtocol"] = SecureWebSocket;
Attributes["Identity"] = SecureWebSocket;
}
public ExceptionLevel ExceptionLevel { get; set; }
= ExceptionLevel.Code | ExceptionLevel.Message | ExceptionLevel.Source | ExceptionLevel.Trace;
//public Func<AuthorizationRequest, AsyncReply<object>> Authenticator { get; set; }
//public Func<AuthorizationRequest, AsyncReply<object>> Authenticator { get; set; }
public string Identity { get; set; }
public string AuthenticationProtocol { get; set; }
public bool AutoReconnect { get; set; } = false;
public uint ReconnectInterval { get; set; } = 5;
//public string Username { get; set; }
public bool UseWebSocket { get; set; }
public bool SecureWebSocket { get; set; }
// public string Password { get; set; }
//public string Token { get; set; }
//public ulong TokenIndex { get; set; }
public string Domain { get; set; }
}
File diff suppressed because it is too large Load Diff
+25 -9
View File
@@ -81,7 +81,7 @@ public class EpResource : DynamicObject, IResource, INotifyPropertyChanged, IDyn
/// <summary>
/// Connection responsible for the distributed resource.
/// </summary>
public EpConnection DistributedResourceConnection
public EpConnection ResourceConnection
{
get { return connection; }
}
@@ -89,7 +89,7 @@ public class EpResource : DynamicObject, IResource, INotifyPropertyChanged, IDyn
/// <summary>
/// Resource link
/// </summary>
public string DistributedResourceLink
public string ResourceLink
{
get { return link; }
}
@@ -97,7 +97,7 @@ public class EpResource : DynamicObject, IResource, INotifyPropertyChanged, IDyn
/// <summary>
/// Instance Id given by the other end.
/// </summary>
public uint DistributedResourceInstanceId
public uint ResourceInstanceId
{
get { return instanceId; }
internal set { instanceId = value; }
@@ -128,9 +128,9 @@ public class EpResource : DynamicObject, IResource, INotifyPropertyChanged, IDyn
/// <summary>
/// Resource is attached when all its properties are received.
/// </summary>
public bool DistributedResourceAttached => attached;
public bool ResourceAttached => attached;
public bool DistributedResourceSuspended => suspended;
public bool ResourceSuspended => suspended;
// public DistributedResourceStack Stack
@@ -216,10 +216,18 @@ public class EpResource : DynamicObject, IResource, INotifyPropertyChanged, IDyn
public AsyncReply Subscribe(EventDef et)
{
if (et == null)
return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.MethodNotFound, ""));
{
var rt = new AsyncReply();
rt.TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.MethodNotFound, ""));
return rt;
}
if (!et.Subscribable)
return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.NotSubscribable, ""));
{
var rt = new AsyncReply();
rt.TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.NotSubscribable, ""));
return rt;
}
return connection.SendSubscribeRequest(instanceId, et.Index);
}
@@ -235,10 +243,18 @@ public class EpResource : DynamicObject, IResource, INotifyPropertyChanged, IDyn
public AsyncReply Unsubscribe(EventDef et)
{
if (et == null)
return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.MethodNotFound, ""));
{
var rt = new AsyncReply();
rt.TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.MethodNotFound, ""));
return rt;
}
if (!et.Subscribable)
return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.NotSubscribable, ""));
{
var rt = new AsyncReply();
rt.TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.NotSubscribable, ""));
return rt;
}
return connection.SendUnsubscribeRequest(instanceId, et.Index);
}
@@ -6,12 +6,12 @@ using System.Text;
namespace Esiur.Protocol;
internal class EpResourceAttachRequestInfo
internal class FetchRequestInfo<TValue, TId>
{
public AsyncReply<EpResource> Reply { get; set; }
public uint[] RequestSequence { get; set; }
public AsyncReply<TValue> Reply { get; set; }
public TId[] RequestSequence { get; set; }
public EpResourceAttachRequestInfo(AsyncReply<EpResource> reply, uint[] requestSequence)
public FetchRequestInfo(AsyncReply<TValue> reply, TId[] requestSequence)
{
Reply = reply;
RequestSequence = requestSequence;
+1 -1
View File
@@ -184,7 +184,7 @@ public class EpServer : NetworkServer<EpConnection>, IResource
public EpServer MapCall(string call, Delegate handler)
{
var fd = FunctionDef.MakeFunctionDef(null, handler.Method, 0, call, null);
var fd = FunctionDef.MakeFunctionDef(Instance.Warehouse, null, handler.Method, 0, call, null);
Calls.Add(call, new CallInfo() { Delegate = handler, Definition = fd });
return this;
}
+1 -1
View File
@@ -250,7 +250,7 @@ namespace Esiur.Proxy
}
// === Emission helpers (ported from your original generator) ===
private static void EmitTypeDefs(SourceProductionContext spc, TypeDef[] typeDefs)
private static void EmitTypeDefs(SourceProductionContext spc, RemoteTypeDef[] typeDefs)
{
foreach (var typeDef in typeDefs)
{
+46 -31
View File
@@ -61,7 +61,7 @@ public static class TypeDefGenerator
}
internal static string GenerateRecord(TypeDef typeDef, TypeDef[] typeDefs)
internal static string GenerateRecord(RemoteTypeDef typeDef, TypeDef[] typeDefs)
{
var cls = typeDef.Name.Split('.');
@@ -82,7 +82,8 @@ public static class TypeDefGenerator
}
}
rt.AppendLine($"[TypeId(\"{typeDef.Id.Data.ToHex(0, 16, null)}\")]");
//rt.AppendLine($"[TypeId(\"{typeDef.Id.Data.ToHex(0, 16, null)}\")]");
rt.AppendLine($"[Remote(\"{typeDef.Name}\", \"{typeDef.Domain}\")]");
rt.AppendLine($"[Export] public class {className} : IRecord {{");
@@ -109,7 +110,7 @@ public static class TypeDefGenerator
return rt.ToString();
}
internal static string GenerateEnum(TypeDef typeDef, TypeDef[] typeDefs)
internal static string GenerateEnum(RemoteTypeDef typeDef, TypeDef[] typeDefs)
{
var cls = typeDef.Name.Split('.');
@@ -129,7 +130,9 @@ public static class TypeDefGenerator
}
}
rt.AppendLine($"[TypeId(\"{typeDef.Id.Data.ToHex(0, 16, null)}\")]");
//rt.AppendLine($"[TypeId(\"{typeDef.Id.Data.ToHex(0, 16, null)}\")]");
rt.AppendLine($"[Remote(\"{typeDef.Name}\", \"{typeDef.Domain}\")]");
rt.AppendLine($"[Export] public enum {className} {{");
rt.AppendLine(String.Join(",\r\n", typeDef.Constants.Select(x => $"{x.Name}={x.Value}")));
@@ -142,28 +145,36 @@ public static class TypeDefGenerator
static string GetTypeName(Tru tru, TypeDef[] typeDefs)
{
string name;
string name = null;
if (tru.Identifier == TruIdentifier.TypedResource)// == DataType.Resource)
name = typeDefs.First(x => x.Id == tru.UUID && (x.Kind == TypeDefKind.Resource)).Name;
else if (tru.Identifier == TruIdentifier.TypedRecord)
name = typeDefs.First(x => x.Id == tru.UUID && x.Kind == TypeDefKind.Record).Name;
else if (tru.Identifier == TruIdentifier.Enum)
name = typeDefs.First(x => x.Id == tru.UUID && x.Kind == TypeDefKind.Enum).Name;
else if (tru.Identifier == TruIdentifier.TypedList)
name = GetTypeName(tru.SubTypes[0], typeDefs) + "[]";
else if (tru.Identifier == TruIdentifier.TypedMap)
name = "Map<" + GetTypeName(tru.SubTypes[0], typeDefs)
+ "," + GetTypeName(tru.SubTypes[1], typeDefs)
+ ">";
else if (tru.Identifier == TruIdentifier.Tuple2 ||
tru.Identifier == TruIdentifier.Tuple3 ||
tru.Identifier == TruIdentifier.Tuple4 ||
tru.Identifier == TruIdentifier.Tuple5 ||
tru.Identifier == TruIdentifier.Tuple6 ||
tru.Identifier == TruIdentifier.Tuple7)
name = "(" + String.Join(",", tru.SubTypes.Select(x => GetTypeName(x, typeDefs)))
+ ")";
if (tru is TruTypeDef truTypeDef)
{
name = truTypeDef.TypeDef.Name;// typeDefs.First(x => x.Id == tru.TypeDef.Value.Value).Name;
}
//if (tru.Identifier == TruIdentifier.TypedResource)// == DataType.Resource)
// name = typeDefs.First(x => x.Id == tru.UUID && (x.Kind == TypeDefKind.Resource)).Name;
//else if (tru.Identifier == TruIdentifier.TypedRecord)
// name = typeDefs.First(x => x.Id == tru.UUID && x.Kind == TypeDefKind.Record).Name;
//else if (tru.Identifier == TruIdentifier.Enum)
// name = typeDefs.First(x => x.Id == tru.UUID && x.Kind == TypeDefKind.Enum).Name;
else if (tru is TruComposite truComposite)
{
if (tru.Identifier == TruIdentifier.TypedList)
name = GetTypeName(truComposite.SubTypes[0], typeDefs) + "[]";
else if (tru.Identifier == TruIdentifier.TypedMap)
name = "Map<" + GetTypeName(truComposite.SubTypes[0], typeDefs)
+ "," + GetTypeName(truComposite.SubTypes[1], typeDefs)
+ ">";
else if (tru.Identifier == TruIdentifier.Tuple2 ||
tru.Identifier == TruIdentifier.Tuple3 ||
tru.Identifier == TruIdentifier.Tuple4 ||
tru.Identifier == TruIdentifier.Tuple5 ||
tru.Identifier == TruIdentifier.Tuple6 ||
tru.Identifier == TruIdentifier.Tuple7)
name = "(" + String.Join(",", truComposite.SubTypes.Select(x => GetTypeName(x, typeDefs)))
+ ")";
}
else
{
@@ -193,6 +204,8 @@ public static class TypeDefGenerator
};
}
if (name == null) return "object";
return (tru.Nullable) ? name + "?" : name;
}
@@ -205,8 +218,9 @@ public static class TypeDefGenerator
throw new Exception("Invalid EP URL");
var path = urlRegex.Split(url);
var con = Warehouse.Default.Get<EpConnection>(path[1] + "://" + path[2],
!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password) ? new { Username = username, Password = password } : null
var con = Warehouse.Default.Get<EpConnection>(path[1] + "://" + path[2], new ResourceContext(0,
new Map<string, object> { ["username"] = username, ["password"] = password }, null, null)
// !string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password) ? new { Username = username, Password = password } : null
).Wait(20000);
if (con == null)
@@ -279,7 +293,7 @@ public static class TypeDefGenerator
}
}
internal static string GenerateClass(TypeDef typeDef, TypeDef[] typeDefs, bool asyncSetters)
internal static string GenerateClass(RemoteTypeDef typeDef, TypeDef[] typeDefs, bool asyncSetters)
{
var cls = typeDef.Name.Split('.');
@@ -302,13 +316,14 @@ public static class TypeDefGenerator
}
rt.AppendLine($"[TypeId(\"{typeDef.Id.Data.ToHex(0, 16, null)}\")]");
rt.AppendLine($"[Remote(\"{typeDef.Name}\", \"{typeDef.Domain}\")]");
//rt.AppendLine($"[TypeId(\"{typeDef.Id.Data.ToHex(0, 16, null)}\")]");
// extends
if (typeDef.ParentId == null)
if (typeDef.ParentTypeId == null)
rt.AppendLine($"public class {className} : EpResource {{");
else
rt.AppendLine($"public class {className} : {typeDefs.First(x => x.Id == typeDef.ParentId && x.Kind == TypeDefKind.Resource).Name} {{");
rt.AppendLine($"public class {className} : {typeDefs.First(x => x.Id == typeDef.ParentTypeId && x.Kind == TypeDefKind.Resource).Name} {{");
rt.AppendLine($"public {className}(EpConnection connection, uint instanceId, ulong age, string link) : base(connection, instanceId, age, link) {{}}");
+10 -2
View File
@@ -710,7 +710,15 @@ public class Instance
}
return Ruling.DontCare;
// Apply default permissions if no manager is applicable or if the resource is not available.
if (action == ActionType.GetProperty
|| action == ActionType.ViewTypeDef
|| action == ActionType.ReceiveEvent
|| action == ActionType.Attach
|| action == ActionType.Execute)
return Ruling.Allowed;
else
return Ruling.Denied;
}
@@ -753,7 +761,7 @@ public class Instance
}
else
{
this.definition = warehouse.GetTypeDefByType(resource.GetType());
this.definition = warehouse.GetLocalTypeDefByType(resource.GetType());
}
// set ages
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Resource
{
[AttributeUsage(AttributeTargets.Enum | AttributeTargets.Class, Inherited = false)]
public class RemoteAttribute:Attribute
{
public string Domain { get; private set; }
public string FullName { get; private set; }
public RemoteAttribute(string domain, string fullName)
{
Domain = domain;
FullName = fullName;
}
}
}
@@ -0,0 +1,31 @@
using Esiur.Data;
using Esiur.Security.Authority;
using Esiur.Security.Permissions;
using System;
using System.Collections.Generic;
using System.Security.Principal;
using System.Text;
namespace Esiur.Resource
{
public class ResourceContext
{
public ulong Age { get; }
public Map<string, object> Attributes { get; }
public Map<string, object> Properties { get; }
public IPermissionsManager PermissionsManager { get; }
public ResourceContext(ulong age, Map<string, object> attributes, Map<string, object> properties, IPermissionsManager permissionsManager)
{
Age = age;
Attributes = attributes;
Properties = properties;
PermissionsManager = permissionsManager;
}
public virtual void Build()
{
// update the context based on the current state of the resource and its environment
}
}
}
+312 -180
View File
@@ -29,13 +29,16 @@ using Esiur.Misc;
using Esiur.Net.Packets;
using Esiur.Protocol;
using Esiur.Proxy;
using Esiur.Security.Authority;
using Esiur.Security.Permissions;
using Org.BouncyCastle.Asn1.Cms;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Text;
using System.Text.RegularExpressions;
@@ -51,47 +54,83 @@ public class Warehouse
//static byte prefixCounter;
//static AutoList<IStore, Instance> stores = new AutoList<IStore, Instance>(null);
ConcurrentDictionary<uint, WeakReference<IResource>> resources = new ConcurrentDictionary<uint, WeakReference<IResource>>();
ConcurrentDictionary<IStore, List<WeakReference<IResource>>> stores = new ConcurrentDictionary<IStore, List<WeakReference<IResource>>>();
ConcurrentDictionary<uint, WeakReference<IResource>> _resources = new ConcurrentDictionary<uint, WeakReference<IResource>>();
ConcurrentDictionary<IStore, List<WeakReference<IResource>>> _stores = new ConcurrentDictionary<IStore, List<WeakReference<IResource>>>();
volatile int _resourceCounter = 0;
volatile int _typeDefsCounter = 0;
uint resourceCounter = 0;
//KeyList<TypeDefKind, KeyList<uint, LocalTypeDef>> _localTypeDefs
// = new KeyList<TypeDefKind, KeyList<uint, LocalTypeDef>>()
// {
// [TypeDefKind.Resource] = new KeyList<uint, LocalTypeDef>(),
// [TypeDefKind.Record] = new KeyList<uint, LocalTypeDef>(),
// [TypeDefKind.Enum] = new KeyList<uint, LocalTypeDef>(),
// };
KeyList<ulong, TypeDef> _localTypeDefs
= new KeyList<ulong, TypeDef>();
KeyList<TypeDefKind, KeyList<Uuid, TypeDef>> typeDefs
= new KeyList<TypeDefKind, KeyList<Uuid, TypeDef>>()
{
[TypeDefKind.Resource] = new KeyList<Uuid, TypeDef>(),
[TypeDefKind.Record] = new KeyList<Uuid, TypeDef>(),
[TypeDefKind.Enum] = new KeyList<Uuid, TypeDef>(),
};
KeyList<string, KeyList<ulong, RemoteTypeDef>> _remoteTypeDefs
= new KeyList<string, KeyList<ulong, RemoteTypeDef>>();
object typeDefsLock = new object();
//KeyList<string, KeyList<TypeDefKind, KeyList<uint, RemoteTypeDef>>> _remoteTypeDefs
// = new KeyList<string, KeyList<TypeDefKind, KeyList<uint, RemoteTypeDef>>>();
bool warehouseIsOpen = false;
Map<string, IAuthenticationProvider> _authenticationProviders = new Map<string, IAuthenticationProvider>();
List<IPermissionsManager> _permissionsManagers = new List<IPermissionsManager>();
object _typeDefsLock = new object();
bool _warehouseIsOpen = false;
public delegate void StoreEvent(IStore store);
public event StoreEvent StoreConnected;
public event StoreEvent StoreDisconnected;
public delegate AsyncReply<IStore> ProtocolInstance(string name, object properties);
public delegate AsyncReply<IStore> ProtocolInstance(string name, ResourceContext resourceContext);
public KeyList<string, ProtocolInstance> Protocols { get; } = new KeyList<string, ProtocolInstance>();
private Regex urlRegex = new Regex(@"^(?:([\S]*)://([^/]*)/?)");
public void RegisterAuthenticationProvider(IAuthenticationProvider provider)
{
RegisterAuthenticationProvider(provider.DefaultName, provider);
}
public void RegisterAuthenticationProvider(string name, IAuthenticationProvider provider)
{
_authenticationProviders.Add(name, provider);
}
public void RegisterPermissionsManager(IPermissionsManager manager)
{
_permissionsManagers.Add(manager);
}
public IAuthenticationProvider GetAuthenticationProvider(string name)
{
if (_authenticationProviders.ContainsKey(name))
return _authenticationProviders[name];
throw new Exception("Authentication provider not found.");
}
public Warehouse()
{
Protocols.Add("EP",
async (name, attributes)
=> await New<EpConnection>(name, null, attributes));
async (name, context)
=> await New<EpConnection>(name, context));
new TypeDef(typeof(EpAuthPacketIAuthHeader), this);
new TypeDef(typeof(EpAuthPacketIAuthDestination), this);
new TypeDef(typeof(EpAuthPacketIAuthFormat), this);
//new LocalTypeDef(typeof(EpAuthPacketIAuthHeader), this);
//new LocalTypeDef(typeof(EpAuthPacketIAuthDestination), this);
//new LocalTypeDef(typeof(EpAuthPacketIAuthFormat), this);
}
@@ -102,13 +141,13 @@ public class Warehouse
/// <returns></returns>
public IStore GetStore(string name)
{
foreach (var s in stores)
foreach (var s in _stores)
if (s.Key.Instance.Name == name)
return s.Key;
return null;
}
public WeakReference<IResource>[] Resources => resources.Values.ToArray();
public WeakReference<IResource>[] Resources => _resources.Values.ToArray();
/// <summary>
/// Get a resource by instance Id.
@@ -117,10 +156,10 @@ public class Warehouse
/// <returns></returns>
public AsyncReply<IResource> GetById(uint id)
{
if (resources.ContainsKey(id))
if (_resources.ContainsKey(id))
{
IResource r;
if (resources[id].TryGetTarget(out r))
if (_resources[id].TryGetTarget(out r))
return new AsyncReply<IResource>(r);
else
return new AsyncReply<IResource>(null);
@@ -129,33 +168,33 @@ public class Warehouse
return new AsyncReply<IResource>(null);
}
void LoadGenerated()
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
var generatedType = assembly.GetType("Esiur.Generated");
if (generatedType != null)
{
var resourceTypes = (Type[])generatedType.GetProperty("Resources").GetValue(null);
foreach (var t in resourceTypes)
{
RegisterTypeDef(new TypeDef(t));
}
//void LoadGenerated()
//{
// foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
// {
// var generatedType = assembly.GetType("Esiur.Generated");
// if (generatedType != null)
// {
// var resourceTypes = (Type[])generatedType.GetProperty("Resources").GetValue(null);
// foreach (var t in resourceTypes)
// {
// RegisterTypeDef(new TypeDef(t));
// }
var recordTypes = (Type[])generatedType.GetProperty("Records").GetValue(null);
foreach (var t in recordTypes)
{
RegisterTypeDef(new TypeDef(t));
}
// var recordTypes = (Type[])generatedType.GetProperty("Records").GetValue(null);
// foreach (var t in recordTypes)
// {
// RegisterTypeDef(new TypeDef(t));
// }
var enumsTypes = (Type[])generatedType.GetProperty("Enums").GetValue(null);
foreach (var t in enumsTypes)
{
RegisterTypeDef(new TypeDef(t));
}
}
}
}
// var enumsTypes = (Type[])generatedType.GetProperty("Enums").GetValue(null);
// foreach (var t in enumsTypes)
// {
// RegisterTypeDef(new TypeDef(t));
// }
// }
// }
//}
/// <summary>
/// Open the warehouse.
@@ -164,16 +203,16 @@ public class Warehouse
/// <returns>True, if no problem occurred.</returns>
public async AsyncReply<bool> Open()
{
if (warehouseIsOpen)
if (_warehouseIsOpen)
return false;
// Load generated models
LoadGenerated();
//LoadGenerated();
warehouseIsOpen = true;
_warehouseIsOpen = true;
var resSnap = resources.Select(x =>
var resSnap = _resources.Select(x =>
{
IResource r;
if (x.Value.TryGetTarget(out r))
@@ -222,7 +261,7 @@ public class Warehouse
var bag = new AsyncBag<bool>();
foreach (var resource in resources.Values)
foreach (var resource in _resources.Values)
{
IResource r;
if (resource.TryGetTarget(out r))
@@ -233,11 +272,11 @@ public class Warehouse
}
}
foreach (var store in stores)
foreach (var store in _stores)
bag.Add(store.Key.Trigger(ResourceTrigger.Terminate));
foreach (var resource in resources.Values)
foreach (var resource in _resources.Values)
{
IResource r;
if (resource.TryGetTarget(out r))
@@ -248,7 +287,7 @@ public class Warehouse
}
foreach (var store in stores)
foreach (var store in _stores)
bag.Add(store.Key.Trigger(ResourceTrigger.SystemTerminated));
bag.Seal();
@@ -274,7 +313,7 @@ public class Warehouse
{
var p = path.Trim().TrimStart('/').Split('/');
foreach (var store in stores.Keys)
foreach (var store in _stores.Keys)
{
if (p[0] == store.Instance.Name)
{
@@ -298,7 +337,7 @@ public class Warehouse
/// </summary>
/// <param name="path"></param>
/// <returns>Resource instance.</returns>
public async AsyncReply<T> Get<T>(string path, object attributes = null, IResource parent = null, IPermissionsManager manager = null)
public async AsyncReply<T> Get<T>(string path, ResourceContext resourceContext = null)
where T : IResource
{
@@ -309,11 +348,11 @@ public class Warehouse
if (Protocols.ContainsKey(url[1]))
{
if (!warehouseIsOpen)
if (!_warehouseIsOpen)
await Open();
var handler = Protocols[url[1]];
var store = await handler(url[2], attributes);
var store = await handler(url[2], resourceContext);
try
{
@@ -346,7 +385,7 @@ public class Warehouse
/// <param name="resource">Resource instance.</param>
/// <param name="store">IStore that manages the resource. Can be null if the resource is a store.</param>
/// <param name="parent">Parent resource. if not presented the store becomes the parent for the resource.</param>
public async AsyncReply<T> Put<T>(string path, T resource, ulong age = 0, IPermissionsManager manager = null, object attributes = null) where T : IResource
public async AsyncReply<T> Put<T>(string path, T resource, ResourceContext resourceContext = null) where T : IResource
{
if (resource.Instance != null)
throw new Exception("Resource already initialized.");
@@ -385,18 +424,17 @@ public class Warehouse
var resourceReference = new WeakReference<IResource>(resource);
resource.Instance = new Instance(this, resourceCounter++, instanceName, resource, store, age);
var resourceId = (uint)Interlocked.Increment(ref _resourceCounter);
if (attributes != null)
if (attributes is Map<string, object> attrs)
resource.Instance.SetAttributes(attrs);
else
resource.Instance.SetAttributes(Map<string, object>.FromObject(attributes));
resource.Instance = new Instance(this, resourceId, instanceName, resource, store, resourceContext?.Age ?? 0);
if (resourceContext?.Attributes != null)
resource.Instance.SetAttributes(resourceContext.Attributes);
try
{
if (resource is IStore)
stores.TryAdd(resource as IStore, new List<WeakReference<IResource>>());
_stores.TryAdd(resource as IStore, new List<WeakReference<IResource>>());
else if ((IResource)resource != store)
{
if (!await store.Put(resource, string.Join("/", location.Skip(1).ToArray())))
@@ -406,9 +444,9 @@ public class Warehouse
var t = resource.GetType();
Global.Counters["T-" + t.Namespace + "." + t.Name]++;
resources.TryAdd(resource.Instance.Id, resourceReference);
if (warehouseIsOpen)
_resources.TryAdd(resource.Instance.Id, resourceReference);
if (_warehouseIsOpen)
{
await resource.Trigger(ResourceTrigger.Initialize);
if (resource is IStore)
@@ -429,60 +467,81 @@ public class Warehouse
}
public T Create<T>(object properties = null)
public T Create<T>(Map<string, object> properties = null)
{
return (T)Create(typeof(T), properties);
}
public IResource Create(Type type, object properties = null)
public IResource CreateFromIndexedProperties(Type type, Map<byte, object> properties)
{
type = ResourceProxy.GetProxy(type);
var res = Activator.CreateInstance(type) as IResource;
if (properties != null)
{
if (properties is Map<byte, object> map)
var typeDef = GetLocalTypeDefByType(type);
foreach (var p in properties)
{
var typeDef = GetTypeDefByType(type);
foreach (var kvp in map)
typeDef.GetPropertyDefByIndex(kvp.Key).PropertyInfo.SetValue(res, kvp.Value);
}
else
{
var ps = Map<string, object>.FromObject(properties);
var pi = typeDef.GetPropertyDefByIndex(p.Key).PropertyInfo;
foreach (var p in ps)
if (pi != null)
{
var pi = type.GetProperty(p.Key, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
if (pi != null)
if (pi.CanWrite)
{
if (pi.CanWrite)
try
{
try
{
pi.SetValue(res, p.Value);
}
catch (Exception ex)
{
Global.Log(ex);
}
pi.SetValue(res, p.Value);
}
catch (Exception ex)
{
Global.Log(ex);
}
}
else
}
}
}
return res;
}
public IResource Create(Type type, Map<string, object> properties)
{
type = ResourceProxy.GetProxy(type);
var res = Activator.CreateInstance(type) as IResource;
if (properties != null)
{
foreach (var p in properties)
{
var pi = type.GetProperty(p.Key, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
if (pi != null)
{
if (pi.CanWrite)
{
var fi = type.GetField(p.Key, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
if (fi != null)
try
{
try
{
fi.SetValue(res, p.Value);
}
catch (Exception ex)
{
Global.Log(ex);
}
pi.SetValue(res, p.Value);
}
catch (Exception ex)
{
Global.Log(ex);
}
}
}
else
{
var fi = type.GetField(p.Key, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
if (fi != null)
{
try
{
fi.SetValue(res, p.Value);
}
catch (Exception ex)
{
Global.Log(ex);
}
}
}
@@ -492,59 +551,130 @@ public class Warehouse
return res;
}
public async AsyncReply<IResource> New(Type type, string path, IPermissionsManager manager = null, object attributes = null, object properties = null)
public async AsyncReply<IResource> New(Type type, string path, ResourceContext resourceContext)
{
var res = Create(type, properties);
return await Put(path, res, 0, manager, attributes);
var res = Create(type, resourceContext?.Properties);
return await Put(path, res, resourceContext);
}
public async AsyncReply<T> New<T>(string path, IPermissionsManager manager = null, object attributes = null, object properties = null)
public async AsyncReply<T> New<T>(string path, ResourceContext resourceContext = null)
where T : IResource
{
return (T)(await New(typeof(T), path, manager, attributes, properties));
return (T)(await New(typeof(T), path, resourceContext));
}
public void IsProxyType(Type type)
{
}
public void RegisterProxyType(Type type)
{
}
/// <summary>
/// Put a resource schema in the schemas warehouse.
/// Register TypeDef.
/// </summary>
/// <param name="typeDef">Resource type definition.</param>
public void RegisterTypeDef(TypeDef typeDef)
public uint RegisterLocalTypeDef(LocalTypeDef typeDef)
{
lock (typeDefsLock)
lock (_typeDefsLock)
{
if (typeDefs[typeDef.Kind].ContainsKey(typeDef.Id))
throw new Exception($"TypeDef with same class Id already exists. {typeDefs[typeDef.Kind][typeDef.Id].Name} -> {typeDef.Name}");
//if (_localTypeDefs[typeDef.Kind].ContainsKey(typeDef.Id))
if (_localTypeDefs.ContainsKey(typeDef.Id))
throw new Exception($"TypeDef with same class Id already exists. {_localTypeDefs[typeDef.Id].Name} -> {typeDef.Name}");
//throw new Exception($"TypeDef with same class Id already exists. {_localTypeDefs[typeDef.Kind][typeDef.Id].Name} -> {typeDef.Name}");
typeDefs[typeDef.Kind][typeDef.Id] = typeDef;
var typeDefId = (uint)Interlocked.Increment(ref _typeDefsCounter);
typeDef.Id = typeDefId;
//_localTypeDefs[typeDef.Kind][typeDef.Id] = typeDef;
_localTypeDefs[typeDef.Id] = typeDef;
return typeDefId;
}
}
public bool TryRegisterTypeDef(TypeDef typeDef)
public bool TryRegisterLocalTypeDef(LocalTypeDef typeDef)
{
lock (typeDefsLock)
lock (_typeDefsLock)
{
if (typeDefs[typeDef.Kind].ContainsKey(typeDef.Id))
if (_localTypeDefs.ContainsKey(typeDef.Id))
return false;
typeDefs[typeDef.Kind][typeDef.Id] = typeDef;
var typeDefId = (uint)Interlocked.Increment(ref _typeDefsCounter);
typeDef.Id = typeDefId;
_localTypeDefs[typeDef.Id] = typeDef;
return true;
}
}
public bool TryRegisterRemoteTypeDef(string domain, RemoteTypeDef typeDef)
{
lock (_typeDefsLock)
{
if (!_remoteTypeDefs.ContainsKey(domain))
{
_remoteTypeDefs.Add(domain, new KeyList<ulong, RemoteTypeDef>());
}
if (_remoteTypeDefs[domain].ContainsKey(typeDef.Id))
return false;
// @TODO: Try to find a proxy type for the remote type def, if not found, create a new proxy type and register it in the warehouse.
_remoteTypeDefs[domain][typeDef.Id] = typeDef;
var localTypeDefId = (uint)Interlocked.Increment(ref _typeDefsCounter);
typeDef.LocalTypeDefId = localTypeDefId;
_localTypeDefs[localTypeDefId] = typeDef;
return true;
}
}
//public TypeDef FindTypeDefByType(Type type)
//{
// var remote = type.GetCustomAttribute<RemoteAttribute>();
// if (remote != null)
// {
// return GetRemoteTypeDefByName(remote.Domain, remote.FullName);
// }
// else
// {
// return GetLocalTypeDefByType(type);
// }
//}
//public TypeDef FindTypeDefByTypeDefId(TypeDefId typeDefId, string domain)
//{
// if (typeDefId.Remote)
// {
// return GetRemoteTypeDefById(domain, typeDefId.Value);
// }
// else
// {
// return GetLocalTypeDefById(typeDefId.Value);
// }
//}
/// <summary>
/// Get a TypeDef by type from the warehouse. If not in the warehouse, a new TypeDef is created and added to the warehouse.
/// </summary>
/// <param name="type">.Net type.</param>
/// <returns>Resource TypeDef.</returns>
public TypeDef GetTypeDefByType(Type type)
public TypeDef GetLocalTypeDefByType(Type type)
{
if (!(type.IsClass || type.IsEnum))
return null;
//if (!(type.IsClass || type.IsEnum))
// return null;
var baseType = ResourceProxy.GetBaseType(type);
if (baseType == typeof(IResource)
@@ -561,16 +691,33 @@ public class Warehouse
else
return null;
lock (typeDefsLock)
lock (_typeDefsLock)
{
var typeDef = typeDefs[typeDefKind].Values.FirstOrDefault(x => x.DefinedType == baseType);
if (typeDef != null)
return typeDef;
//var typeDef = _localTypeDefs.Values.FirstOrDefault(x => x is LocalTypeDef ltd
// && ltd.DefinedType == baseType);
foreach (var td in _localTypeDefs.Values)
{
if (td is LocalTypeDef ltd && ltd.DefinedType == baseType)
{
return td;
}
else if (td is RemoteTypeDef rtd && rtd.ProxyType == baseType)
{
return td;
}
}
//if (typeDef != null)
// return typeDef;
// create new TypeDef for type
typeDef = new TypeDef(baseType, this);
TypeDef.GetDependencies(typeDef, this);
return typeDef;
//Console.WriteLine($"Creating {baseType.Name}");
var ntd = new LocalTypeDef(baseType, this);
//LocalTypeDef.GetDependencies(ntd, this);
return ntd;
}
}
@@ -579,71 +726,56 @@ public class Warehouse
/// </summary>
/// <param name="typeId">typeId.</param>
/// <returns>TypeDef.</returns>
public TypeDef GetTypeDefById(Uuid typeId, TypeDefKind? typeDefKind = null)
public TypeDef GetLocalTypeDefById(ulong typeId)
{
if (typeDefKind == null)
{
// look into resources
var typeDef = typeDefs[TypeDefKind.Resource][typeId];
if (typeDef != null)
return typeDef;
return _localTypeDefs[typeId];
}
// look into records
typeDef = typeDefs[TypeDefKind.Record][typeId];
if (typeDef != null)
return typeDef;
// look into enums
typeDef = typeDefs[TypeDefKind.Enum][typeId];
return typeDef;
}
else
return typeDefs[typeDefKind.Value][typeId];
public RemoteTypeDef GetRemoteTypeDefById(string domain, ulong typeId)
{
if (string.IsNullOrEmpty(domain) || !_remoteTypeDefs.ContainsKey(domain))
return null;
return _remoteTypeDefs[domain][typeId];
}
public TypeDef GetRemoteTypeDefByName(string domain, string typeName, TypeDefKind? typeDefKind = null)
{
if (!string.IsNullOrEmpty(domain) || !_remoteTypeDefs.ContainsKey(domain))
return null;
return _remoteTypeDefs[domain].Values.FirstOrDefault(x => x.Name == typeName);
}
public TypeDef GetRemoteTypeDefByType(Type type)
{
var remoteAttr = type.GetCustomAttribute<RemoteAttribute>();
if (remoteAttr == null) return null;
return GetRemoteTypeDefByName(remoteAttr.Domain, remoteAttr.FullName);
}
/// <summary>
/// Get a TypeDef by type name . If not in the warehouse, a new TypeDef is created and added to the warehouse.
/// </summary>
/// <param name="typeName">Class full name.</param>
/// <returns>TypeDef.</returns>
public TypeDef GetTypeDefByName(string typeName, TypeDefKind? typeDefKind = null)
public TypeDef GetLocalTypeDefByName(string typeName)
{
if (typeDefKind == null)
{
// look into resources
var typeDef = typeDefs[TypeDefKind.Resource].Values.FirstOrDefault(x => x.Name == typeName);
if (typeDef != null)
return typeDef;
// look into records
typeDef = typeDefs[TypeDefKind.Record].Values.FirstOrDefault(x => x.Name == typeName);
if (typeDef != null)
return typeDef;
// look into enums
typeDef = typeDefs[TypeDefKind.Enum].Values.FirstOrDefault(x => x.Name == typeName);
return typeDef;
}
else
{
return typeDefs[typeDefKind.Value].Values.FirstOrDefault(x => x.Name == typeName);
}
return _localTypeDefs.Values.FirstOrDefault(x => x.Name == typeName);
}
public bool Remove(IResource resource)
{
if (resource.Instance == null)
return false;
WeakReference<IResource> resourceReference;
if (resources.ContainsKey(resource.Instance.Id))
resources.TryRemove(resource.Instance.Id, out resourceReference);
if (_resources.ContainsKey(resource.Instance.Id))
_resources.TryRemove(resource.Instance.Id, out resourceReference);
else
return false;
@@ -651,7 +783,7 @@ public class Warehouse
if (resource != resource.Instance.Store)
{
List<WeakReference<IResource>> list;
if (stores.TryGetValue(resource.Instance.Store, out list))
if (_stores.TryGetValue(resource.Instance.Store, out list))
{
lock (((ICollection)list).SyncRoot)
@@ -665,7 +797,7 @@ public class Warehouse
List<WeakReference<IResource>> toBeRemoved;
stores.TryRemove(store, out toBeRemoved);
_stores.TryRemove(store, out toBeRemoved);
foreach (var o in toBeRemoved)
@@ -1,64 +0,0 @@
/*
Copyright (c) 2017 Ahmed Kh. Zamil
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Security.Authority;
public class Authentication
{
AuthenticationMode type;
public AuthenticationMethod Method { get; set; }
public ulong TokenIndex { get; set; }
public string Username { get; set; }
public Certificate Certificate { get; set; }
public string Domain { get; set; }
public string FullName => Username + "@" + Domain;
public Source Source { get; } = new Source();
public AuthenticationState State
{
get;
set;
}
public AuthenticationMode Type
{
get => type;
}
public Authentication(AuthenticationMode type)
{
this.type = type;
}
}
@@ -6,16 +6,14 @@ namespace Esiur.Security.Authority
{
public class AuthenticationContext
{
public AuthenticationMode Mode { get; }
public AuthenticationDirection Direction { get; set; }
public AuthenticationMode Mode { get; set; }
public string? LocalDomain { get; }
public string? RemoteDomain { get; }
public string? LocalHost { get; }
public string? RemoteHost { get; }
//public AuthenticationComponentContext LocalToRemote { get; } = new();
//public AuthenticationComponentContext RemoteToLocal { get; } = new();
public string Domain { get; set; }
public string? InitiatorIdentity { get; set; }
public string? ResponderIdentity { get; set; }
public AuthenticationMaterial[] Materials { get; set; }
public string? HostName { get; set; }
}
}
@@ -4,9 +4,9 @@ using System.Text;
namespace Esiur.Security.Authority
{
public interface IAuthenticationResponder
public enum AuthenticationDirection
{
public AuthenticationResult Process(Session session);
Initiator,
Responder
}
}
@@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Security.Authority
{
internal class AuthenticationHandler
{
}
}
@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Security.Authority
{
public class AuthenticationMaterial
{
public AuthenticationMaterialType Type { get; set; }
public object Value { get; set; }
}
}
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Security.Authority
{
public enum AuthenticationMaterialType: byte
{
Secret,
Key,
Identity,
Data,
}
}
@@ -8,20 +8,24 @@ namespace Esiur.Security.Authority
public class AuthenticationResult
{
public AuthenticationRuling Ruling { get; internal set; }
public string Identity { get; internal set; }
public string LocalIdentity { get; internal set; }
public string RemoteIdentity { get; internal set; }
public object HandshakePayload { get; internal set; }
//public object HandshakePayload { get; internal set; }
public byte[] SessionKey { get; internal set; }
public ExceptionCode? ExceptionCode { get; internal set; }
public string ExceptionMessage { get; internal set; }
public AuthenticationResult(AuthenticationRuling ruling, string identity, object handshakePayload, byte[] sessionKey)
public object AuthenticationData { get; internal set; }
public AuthenticationResult(AuthenticationRuling ruling, object authenticationData, string localIdentity = null, string remoteIdentity = null, byte[] sessionKey = null)
{
Ruling = ruling;
Identity = identity;
HandshakePayload = handshakePayload;
LocalIdentity = localIdentity;
RemoteIdentity = remoteIdentity;
AuthenticationData = authenticationData;
SessionKey = sessionKey;
}
}
@@ -8,13 +8,12 @@ namespace Esiur.Security.Authority
public interface IAuthenticationHandler
{
public AuthenticationMode Mode { get; }
public AuthenticationResult Initialize(Session session, object authenticationData);
public string Protocol { get; }
//public AuthenticationMode Mode { get; }
//public AuthenticationResult Initialize(object authData);
public AuthenticationResult Process(object authenticationData);
public AuthenticationResult Process(object authData);
public void Terminate(Session session);
public void Update(Session session, object authData);
//public AuthenticationResult? Result { get; }
}
}
@@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Security.Authority
{
public interface IAuthenticationInitiator
{
public AuthenticationResult Initiate(Session session);
public AuthenticationResult Process(object handshakePayload);
}
}
@@ -1,16 +0,0 @@
//using System;
//using System.Collections.Generic;
//using System.Text;
//namespace Esiur.Security.Authority
//{
// public interface IAuthenticationMethodProvider
// {
// string Method { get; }
// bool CanHandle(AuthenticationCreationContext context);
// IAuthenticator CreateAuthenticator(AuthenticationCreationContext context);
// }
//}
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Security.Authority
{
public interface IAuthenticationProvider
{
public IAuthenticationHandler
CreateAuthenticationHandler(AuthenticationContext context);
public string DefaultName { get; }
}
}
@@ -1,19 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Security.Authority
{
public sealed class InitiatorAuthenticationContext
{
public string LocalIdentity { get; } = string.Empty;
public string RemoteIdentity { get; } = string.Empty;
public string? RemoteDomain { get; }
public string? LocalDomain { get; }
public string? RemoteIpAddress { get; }
public AuthenticationMode Mode { get; }
}
}
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Security.Authority
{
internal class PpapAuthenticationProvider : IAuthenticationProvider
{
public string DefaultName => "PPAP";
public IAuthenticationHandler CreateAuthenticationHandler(AuthenticationContext context)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,606 @@
using Esiur.Misc;
using Esiur.Security.Permissions;
using Org.BouncyCastle.Crypto.Digests;
using System;
using System.Collections.Generic;
using System.Text;
using Esiur.Data;
using Esiur.Data.Types;
namespace Esiur.Security.Authority.Providers
{
internal class PasswordAuthenticationHandler : IAuthenticationHandler
{
public string Protocol => "hash";
byte[] localNonce, remoteNonce;
byte[] localSalt, remoteSalt;
string initiatorIdentity, responderIdentity;
byte[] initiatorPassword, responderPassword;
string hostName, domain;
int step = 0;
AuthenticationMode mode;
AuthenticationDirection direction;
PasswordAuthenticationProvider provider;
public byte[] ComputeSha3(byte[] data, int bitLength = 256)
{
// 1. Initialize the digest (supports 224, 256, 384, 512)
var digest = new Sha3Digest(bitLength);
// 3. Update the digest with data
digest.BlockUpdate(data, 0, data.Length);
// 4. Retrieve the final hash
byte[] result = new byte[digest.GetDigestSize()];
digest.DoFinal(result, 0);
return result;
}
public AuthenticationResult Process(object authData)
{
var remoteAuthData = (object[])authData;
var localAuthData = new List<object>();
if (direction == AuthenticationDirection.Initiator)
{
if (mode == AuthenticationMode.None)
{
step = -1;
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
else if (mode == AuthenticationMode.InitializerIdentity)
{
if (step == 0)
{
// step 0: send local nonce and initiator identity.
if (initiatorIdentity == null)
(initiatorIdentity, initiatorPassword) = provider.GetSelfIdentityAndCredential(domain, hostName);
else
initiatorPassword = provider.GetSelfCredential(initiatorIdentity, domain, hostName);
if (initiatorPassword == null || initiatorIdentity == null)
return new AuthenticationResult(AuthenticationRuling.Failed, null);
// send local nonce and initiator identity
localAuthData.Add(localNonce);
localAuthData.Add(initiatorIdentity);
return new AuthenticationResult(AuthenticationRuling.InProgress, localAuthData);
}
else if (step == 1)
{
// expect remote nonce, salt and challenge.
remoteNonce = (byte[])remoteAuthData[0];
remoteSalt = (byte[])remoteAuthData[1];
var remoteChallenge = (byte[])remoteAuthData[2];
// prevent reply attack by checking if remote nonce is same as local nonce.
if (remoteNonce.SequenceEqual(localNonce))
{
step = -1;
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
// make salted hash of password.
var hashedPassword = ComputeSha3(initiatorPassword.Concat(remoteSalt).ToArray());
var expectedRemoteChallenge = ComputeSha3(remoteNonce.Concat(hashedPassword)
.Concat(localNonce)
.ToArray());
// compare remote challenge
if (!remoteChallenge.SequenceEqual(expectedRemoteChallenge))
{
step = -1;
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
// make hash challenge response.
var localChallenge = ComputeSha3(localNonce.Concat(hashedPassword)
.Concat(remoteNonce)
.ToArray());
localAuthData.Add(localChallenge);
step = -1;
// derive a session key from nonces and password.
// initiator identity + initiator password + initiator nonce + responder nonce
var sessionKey = ComputeSha3(initiatorIdentity.ToBytes()
.Concat(hashedPassword)
.Concat(localNonce)
.Concat(remoteNonce)
.ToArray(), 512);
return new AuthenticationResult(AuthenticationRuling.Succeeded, localAuthData, initiatorIdentity, null, sessionKey);
}
else
{
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
}
else if (mode == AuthenticationMode.ResponderIdentity)
{
if (step == 0)
{
// just send local nonce.
localAuthData.Add(localNonce);
return new AuthenticationResult(AuthenticationRuling.InProgress, localAuthData);
}
else if (step == 1)
{
// expect responder identity and nonce.
remoteNonce = (byte[])remoteAuthData[0];
responderIdentity = (string)remoteAuthData[1];
// prevent reply attack by checking if remote nonce is same as local nonce.
if (remoteNonce.SequenceEqual(localNonce))
{
step = -1;
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
// check if responder identity is valid and get password.
(localSalt, responderPassword) = provider.GetHostedAccountCredential(responderIdentity, domain);
if (responderPassword == null)
{
step = -1;
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
// make hash challenge response.
var localChallenge = ComputeSha3(localNonce.Concat(responderPassword)
.Concat(remoteNonce)
.ToArray());
// send localSalt and challenge
localAuthData.Add(localSalt);
localAuthData.Add(localChallenge);
step = 2;
return new AuthenticationResult(AuthenticationRuling.InProgress, localAuthData);
}
else if (step == 2)
{
// expect remote challenge.
var remoteChallenge = (byte[])remoteAuthData[0];
// compare remote challenge
var expectedRemoteChallenge = ComputeSha3(remoteNonce.Concat(responderPassword)
.Concat(localNonce)
.ToArray());
if (!remoteChallenge.SequenceEqual(expectedRemoteChallenge))
{
step = -1;
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
// derive a session key from nonces and password.
// responder identity + responder hashed password + initiator nonce + responder nonce
var sessionKey = ComputeSha3(responderIdentity.ToBytes()
.Concat(responderPassword)
.Concat(localNonce)
.Concat(remoteNonce)
.ToArray(), 512);
step = -1;
return new AuthenticationResult(AuthenticationRuling.Succeeded, null, initiatorIdentity, responderIdentity, sessionKey);
}
else
{
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
}
else if (mode == AuthenticationMode.DualIdentity)
{
if (step == 0)
{
// step 0: send local nonce and initiator identity.
if (initiatorIdentity == null)
(initiatorIdentity, initiatorPassword) = provider.GetSelfIdentityAndCredential(domain, hostName);
else
initiatorPassword = provider.GetSelfCredential(initiatorIdentity, domain, hostName);
if (initiatorPassword == null || initiatorIdentity == null)
{
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
localAuthData.Add(localNonce);
localAuthData.Add(initiatorIdentity);
return new AuthenticationResult(AuthenticationRuling.InProgress, localAuthData);
}
else if (step == 1)
{
// expect responder identity, nonce and salt.
remoteNonce = (byte[])remoteAuthData[0];
responderIdentity = (string)remoteAuthData[1];
remoteSalt = (byte[])remoteAuthData[2];
// prevent reply attack by checking if remote nonce is same as local nonce.
if (remoteNonce.SequenceEqual(localNonce))
{
step = -1;
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
// check if responder identity is valid and get password.
(localSalt, responderPassword) = provider.GetHostedAccountCredential(responderIdentity, domain);
if (responderPassword == null)
{
step = -1;
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
// make salted hash of password.
var hashedPassword = ComputeSha3(initiatorPassword.Concat(remoteSalt).ToArray());
// make hash challenge response.
var localChallenge = ComputeSha3(localNonce.Concat(hashedPassword)
.Concat(responderPassword)
.Concat(remoteNonce)
.ToArray());
// send localSalt and challenge
localAuthData.Add(localSalt);
localAuthData.Add(localChallenge);
step = 2;
return new AuthenticationResult(AuthenticationRuling.InProgress, localAuthData);
}
else if (step == 2)
{
// expect remote challenge.
var remoteChallenge = (byte[])remoteAuthData[0];
// make salted hash of password.
var hashedPassword = ComputeSha3(initiatorPassword.Concat(remoteSalt).ToArray());
// compare remote challenge
var expectedRemoteChallenge = ComputeSha3(remoteNonce.Concat(hashedPassword)
.Concat(responderPassword)
.Concat(localNonce)
.ToArray());
if (!remoteChallenge.SequenceEqual(expectedRemoteChallenge))
{
step = -1;
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
// derive a session key from nonces and password.
// responder identity + responder password + initiator nonce + responder nonce
var sessionKey = ComputeSha3(initiatorIdentity.ToBytes()
.Concat(responderIdentity.ToBytes())
.Concat(hashedPassword)
.Concat(responderPassword)
.Concat(localNonce)
.Concat(remoteNonce)
.ToArray(), 512);
step = -1;
return new AuthenticationResult(AuthenticationRuling.Succeeded, null, initiatorIdentity, responderIdentity, sessionKey);
}
else
{
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
}
}
else if (direction == AuthenticationDirection.Responder)
{
if (mode == AuthenticationMode.None)
{
step = -1;
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
else if (mode == AuthenticationMode.InitializerIdentity)
{
if (step == 0)
{
if (remoteAuthData.Length < 2)
return new AuthenticationResult(AuthenticationRuling.Failed, null);
// step 0: expect remote nonce and initiator identity.
remoteNonce = (byte[])remoteAuthData[0];
initiatorIdentity = (string)remoteAuthData[1];
// prevent reply attack by checking if remote nonce is same as local nonce.
// @TODO: We can change our localNonce then send it
if (remoteNonce.SequenceEqual(localNonce))
{
step = -1;
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
// get initiator password from provider.
(localSalt, initiatorPassword) = provider.GetHostedAccountCredential(initiatorIdentity, domain);
// account not found or no password for this account.
if (initiatorPassword == null || initiatorIdentity == null)
{
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
var localChallenge = ComputeSha3(localNonce.Concat(initiatorPassword)
.Concat(remoteNonce)
.ToArray());
// send local nonce, salt and challenge.
localAuthData.Add(localNonce);
localAuthData.Add(localSalt);
localAuthData.Add(localChallenge);
step = 1;
return new AuthenticationResult(AuthenticationRuling.InProgress,
localAuthData);
}
else if (step == 1)
{
// expect challenge response.
var remoteChallenge = (byte[])remoteAuthData[0];
var expectedRemoteChallenge = ComputeSha3(remoteNonce.Concat(initiatorPassword)
.Concat(localNonce)
.ToArray());
// compare remote challenge
if (!expectedRemoteChallenge.SequenceEqual(remoteChallenge))
{
step = -1;
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
// compute session key.
// derive a session key from nonces and password.
// initiator identity + initiator password + initiator nonce + responder nonce
var sessionKey = ComputeSha3(initiatorIdentity.ToBytes()
.Concat(initiatorPassword)
.Concat(remoteNonce)
.Concat(localNonce)
.ToArray(), 512);
step = -1;
return new AuthenticationResult(AuthenticationRuling.Succeeded, null, initiatorIdentity, responderIdentity, sessionKey);
}
else
{
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
}
else if (mode == AuthenticationMode.ResponderIdentity)
{
if (step == 0)
{
if (remoteAuthData.Length < 1)
return new AuthenticationResult(AuthenticationRuling.Failed, null);
// step 0: receive remote nonce.
remoteNonce = (byte[])remoteAuthData[0];
// prevent reply attack by checking if remote nonce is same as local nonce.
// @TODO: We can change our localNonce then send it
if (remoteNonce.SequenceEqual(localNonce))
{
step = -1;
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
// get responder identity from provider.
if (responderIdentity == null)
(responderIdentity, responderPassword) = provider.GetSelfIdentityAndCredential(domain, hostName);
else
responderPassword = provider.GetSelfCredential(responderIdentity, domain, hostName);
if (responderPassword == null || responderIdentity == null)
{
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
localAuthData.Add(localNonce);
localAuthData.Add(responderIdentity);
step = 1;
// send local nonce and identity.
return new AuthenticationResult(AuthenticationRuling.InProgress,
localAuthData
);
}
else if (step == 1)
{
// expect remote salt and challenge.
remoteSalt = (byte[])remoteAuthData[0];
var remoteChallenge = (byte[])remoteAuthData[1];
// compute expected challenge response.
var hashedPassword = ComputeSha3(responderPassword.Concat(remoteSalt).ToArray());
var expectedRemoteChallenge = ComputeSha3(remoteNonce.Concat(hashedPassword)
.Concat(localNonce)
.ToArray());
// compare remote challenge
if (!expectedRemoteChallenge.SequenceEqual(remoteChallenge))
{
step = -1;
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
// compute our challenge response.
var localChallenge = ComputeSha3(localNonce.Concat(hashedPassword)
.Concat(remoteNonce)
.ToArray());
// derive a session key from nonces and password.
// responder identity + responder hashed password + initiator nonce + responder nonce
var sessionKey = ComputeSha3(responderIdentity.ToBytes()
.Concat(hashedPassword)
.Concat(remoteNonce)
.Concat(localNonce)
.ToArray(), 512);
localAuthData.Add(localChallenge);
step = -1;
return new AuthenticationResult(AuthenticationRuling.Succeeded, localAuthData, responderIdentity, null, sessionKey);
}
else
{
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
}
else if (mode == AuthenticationMode.DualIdentity)
{
if (step == 0)
{
if (remoteAuthData.Length < 2)
return new AuthenticationResult(AuthenticationRuling.Failed, null);
// step 0: receive remote nonce and initiator identity.
remoteNonce = (byte[])remoteAuthData[0];
initiatorIdentity = (string)remoteAuthData[1];
// prevent reply attack by checking if remote nonce is same as local nonce.
// @TODO: We can change our localNonce then send it
if (remoteNonce.SequenceEqual(localNonce))
{
step = -1;
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
// get responder identity from provider.
if (responderIdentity == null)
(responderIdentity, responderPassword) = provider.GetSelfIdentityAndCredential(domain, hostName);
else
responderPassword = provider.GetSelfCredential(responderIdentity, domain, hostName);
if (responderPassword == null || responderIdentity == null)
{
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
// get initiator password from provider.
(localSalt, initiatorPassword) = provider.GetHostedAccountCredential(initiatorIdentity, domain);
// account not found or no password for this account.
if (initiatorPassword == null || initiatorIdentity == null)
{
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
// send local nonce, salt and responder identity.
localAuthData.Add(localNonce);
localAuthData.Add(localSalt);
localAuthData.Add(responderIdentity);
step = 1;
// send local nonce and identity.
return new AuthenticationResult(AuthenticationRuling.InProgress,
localAuthData
);
}
else if (step == 1)
{
// expect initiator salt and challenge.
var remoteSalt = (byte[])remoteAuthData[0];
var remoteChallenge = (byte[])remoteAuthData[1];
// compute expected challenge response.
var hashedPassword = ComputeSha3(responderPassword.Concat(remoteSalt).ToArray());
// compare remote challenge
var expectedRemoteChallenge = ComputeSha3(remoteNonce.Concat(initiatorPassword)
.Concat(hashedPassword)
.Concat(localNonce)
.ToArray());
// compare remote challenge
if (!expectedRemoteChallenge.SequenceEqual(remoteChallenge))
{
step = -1;
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
// compute our challenge
var localChallenge = ComputeSha3(localNonce.Concat(hashedPassword)
.Concat(initiatorPassword)
.Concat(remoteNonce)
.ToArray());
localAuthData.Add(localChallenge);
// derive a session key from nonces and password.
// responder identity + responder password + initiator nonce + responder nonce
var sessionKey = ComputeSha3(initiatorIdentity.ToBytes()
.Concat(responderIdentity.ToBytes())
.Concat(initiatorPassword)
.Concat(hashedPassword)
.Concat(remoteNonce)
.Concat(localNonce)
.ToArray(), 512);
step = -1;
return new AuthenticationResult(AuthenticationRuling.Succeeded, localAuthData, initiatorIdentity, responderIdentity, sessionKey);
}
else
{
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
}
}
step = -1;
return new AuthenticationResult(AuthenticationRuling.Failed, null);
}
public PasswordAuthenticationHandler(AuthenticationMode mode,
AuthenticationDirection direction,
string initiatorIdentity,
string responderIdentity,
string hostName,
string domain,
PasswordAuthenticationProvider provider)
{
localNonce = Global.GenerateBytes(20);
this.provider = provider;
this.initiatorIdentity = initiatorIdentity;
this.responderIdentity = responderIdentity;
this.mode = mode;
this.direction = direction;
this.domain = domain;
this.hostName = hostName;
}
}
}
@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Security.Authority.Providers
{
internal class PasswordAuthenticationProvider : IAuthenticationProvider
{
public string DefaultName => "hash";
public IAuthenticationHandler CreateAuthenticationHandler(AuthenticationContext context)
{
var authHandler = new PasswordAuthenticationHandler(context.Mode,
context.Direction,
context.InitiatorIdentity,
context.ResponderIdentity,
context.HostName,
context.Domain,
this);
return authHandler;
}
public virtual (byte[], byte[]) GetHostedAccountCredential(string identity, string domain)
{
return (null, null);
}
public virtual (string, byte[]) GetSelfIdentityAndCredential(string domain, string hostname)
{
return (null, null);
}
public virtual byte[] GetSelfCredential(string identity, string domain, string hostname)
{
return null;
}
}
}
@@ -1,18 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Security.Authority
{
public sealed class ResponderAuthenticationContext
{
public string? RemoteIpAddress { get; }
public string? LocalDomain { get; }
public AuthenticationMode Mode { get; }
public IReadOnlyDictionary<string, string> Headers { get; }
= new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
}
@@ -59,5 +59,6 @@ public class Session
public IAuthenticationHandler AuthenticationHandler { get; set; }
//public IAuthenticationHandler AuthenticationResponder { get; set; }
public string AuthorizedIdentity { get; set; }
public string LocalIdentity { get; set; }
public string RemoteIdentity { get; set; }
}
@@ -52,4 +52,5 @@ public interface IPermissionsManager
bool Initialize(Map<string, object> settings, IResource resource);
Map<string, object> Settings { get; }
}
@@ -44,8 +44,8 @@ public class UserPermissionsManager : IPermissionsManager
{
Map<string,object> userPermissions = null;
if (settings.ContainsKey(session.AuthorizedIdentity))
userPermissions = settings[session.AuthorizedIdentity] as Map<string, object>;
if (settings.ContainsKey(session.RemoteIdentity))
userPermissions = settings[session.RemoteIdentity] as Map<string, object>;
else if (settings.ContainsKey("public"))
userPermissions = settings["public"] as Map<string,object>;
else