mirror of
https://github.com/esiur/esiur-dotnet.git
synced 2025-12-13 16:30:24 +00:00
Async serialization
This commit is contained in:
@@ -22,6 +22,7 @@ SOFTWARE.
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using Esiur.Data;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -102,6 +103,9 @@ public class AsyncBag<T> : AsyncReply, IAsyncBag
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (ArrayType != null)
|
||||||
|
replies[i] = RuntimeCaster.Cast(replies[i], ArrayType);
|
||||||
|
|
||||||
results.SetValue(replies[i], index);
|
results.SetValue(replies[i], index);
|
||||||
count++;
|
count++;
|
||||||
if (count == replies.Count)
|
if (count == replies.Count)
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public static class DC // Data Converter
|
|||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
public static object CastConvert(object value, Type destinationType)
|
public static object CastConvertOld(object value, Type destinationType)
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (value == null)
|
||||||
return null;
|
return null;
|
||||||
@@ -68,7 +68,7 @@ public static class DC // Data Converter
|
|||||||
|
|
||||||
for (var i = 0; i < rt.Length; i++)
|
for (var i = 0; i < rt.Length; i++)
|
||||||
{
|
{
|
||||||
rt.SetValue(CastConvert(v.GetValue(i), destinationType), i);
|
rt.SetValue(CastConvertOld(v.GetValue(i), destinationType), i);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rt;
|
return rt;
|
||||||
|
|||||||
@@ -376,34 +376,74 @@ public static class DataDeserializer
|
|||||||
|
|
||||||
public static unsafe object RecordParserAsync(ParsedTDU tdu, DistributedConnection connection, uint[] requestSequence)
|
public static unsafe object RecordParserAsync(ParsedTDU tdu, DistributedConnection connection, uint[] requestSequence)
|
||||||
{
|
{
|
||||||
|
|
||||||
var reply = new AsyncReply<IRecord>();
|
|
||||||
|
|
||||||
var classId = tdu.Metadata.GetUUID(0);
|
var classId = tdu.Metadata.GetUUID(0);
|
||||||
|
var template = connection.Instance.Warehouse.GetTemplateByClassId(classId,
|
||||||
|
TemplateType.Record);
|
||||||
|
var rt = new AsyncReply<IRecord>();
|
||||||
|
|
||||||
var template = connection.Instance.Warehouse.GetTemplateByClassId(classId, TemplateType.Record);
|
|
||||||
|
var list = new AsyncBag<object>();
|
||||||
|
|
||||||
|
ParsedTDU current;
|
||||||
|
ParsedTDU? previous = null;
|
||||||
|
|
||||||
|
var offset = tdu.Offset;
|
||||||
|
var length = tdu.ContentLength;
|
||||||
|
var ends = offset + (uint)length;
|
||||||
|
|
||||||
var initRecord = (TypeTemplate template) =>
|
var initRecord = (TypeTemplate template) =>
|
||||||
{
|
{
|
||||||
ListParserAsync(tdu, connection, requestSequence).Then(r =>
|
for (var i = 0; i < template.Properties.Length; i++)
|
||||||
{
|
{
|
||||||
var ar = (object[])r;
|
current = ParsedTDU.Parse(tdu.Data, offset, ends);
|
||||||
|
|
||||||
if (template == null)
|
if (current.Class == TDUClass.Invalid)
|
||||||
|
throw new Exception("Unknown type.");
|
||||||
|
|
||||||
|
|
||||||
|
if (current.Identifier == TDUIdentifier.TypeContinuation)
|
||||||
{
|
{
|
||||||
// @TODO: add parse if no template settings
|
current.Class = previous.Value.Class;
|
||||||
reply.TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.TemplateNotFound,
|
current.Identifier = previous.Value.Identifier;
|
||||||
"Template not found for record."));
|
current.Metadata = previous.Value.Metadata;
|
||||||
}
|
}
|
||||||
else if (template.DefinedType != null)
|
else if (current.Identifier == TDUIdentifier.TypeOfTarget)
|
||||||
{
|
{
|
||||||
|
var (idf, mt) = template.Properties[i].ValueType.GetMetadata();
|
||||||
|
current.Class = TDUClass.Typed;
|
||||||
|
current.Identifier = idf;
|
||||||
|
current.Metadata = mt;
|
||||||
|
current.Index = (int)idf & 0x7;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (cs, reply) = Codec.ParseAsync(current, connection, requestSequence);
|
||||||
|
|
||||||
|
list.Add(reply);
|
||||||
|
|
||||||
|
if (cs > 0)
|
||||||
|
{
|
||||||
|
offset += (uint)current.TotalLength;
|
||||||
|
length -= (uint)current.TotalLength;
|
||||||
|
previous = current;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new Exception("Error while parsing structured data");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
list.Seal();
|
||||||
|
|
||||||
|
list.Then(results =>
|
||||||
|
{
|
||||||
|
if (template.DefinedType != null)
|
||||||
|
{
|
||||||
|
|
||||||
var record = Activator.CreateInstance(template.DefinedType) as IRecord;
|
var record = Activator.CreateInstance(template.DefinedType) as IRecord;
|
||||||
for (var i = 0; i < template.Properties.Length; i++)
|
for (var i = 0; i < template.Properties.Length; i++)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//var v = Convert.ChangeType(ar[i], template.Properties[i].PropertyInfo.PropertyType);
|
var v = RuntimeCaster.Cast(results[i], template.Properties[i].PropertyInfo.PropertyType);
|
||||||
var v = DC.CastConvert(ar[i], template.Properties[i].PropertyInfo.PropertyType);
|
|
||||||
template.Properties[i].PropertyInfo.SetValue(record, v);
|
template.Properties[i].PropertyInfo.SetValue(record, v);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -412,21 +452,23 @@ public static class DataDeserializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reply.Trigger(record);
|
rt.Trigger(record);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var record = new Record();
|
var record = new Record();
|
||||||
|
|
||||||
for (var i = 0; i < template.Properties.Length; i++)
|
for (var i = 0; i < template.Properties.Length; i++)
|
||||||
record.Add(template.Properties[i].Name, ar[i]);
|
record.Add(template.Properties[i].Name, results[i]);
|
||||||
|
|
||||||
reply.Trigger(record);
|
rt.Trigger(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
if (template != null)
|
if (template != null)
|
||||||
{
|
{
|
||||||
initRecord(template);
|
initRecord(template);
|
||||||
@@ -437,14 +479,83 @@ public static class DataDeserializer
|
|||||||
connection.GetTemplate(classId).Then(tmp =>
|
connection.GetTemplate(classId).Then(tmp =>
|
||||||
{
|
{
|
||||||
initRecord(tmp);
|
initRecord(tmp);
|
||||||
}).Error(x => reply.TriggerError(x));
|
}).Error(x => rt.TriggerError(x));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
initRecord(null);
|
initRecord(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return reply;
|
return rt;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//var classId = tdu.Metadata.GetUUID(0);
|
||||||
|
|
||||||
|
//var template = connection.Instance.Warehouse.GetTemplateByClassId(classId, TemplateType.Record);
|
||||||
|
|
||||||
|
//var initRecord = (TypeTemplate template) =>
|
||||||
|
//{
|
||||||
|
// ListParserAsync(tdu, connection, requestSequence).Then(r =>
|
||||||
|
// {
|
||||||
|
// var ar = (object[])r;
|
||||||
|
|
||||||
|
// if (template == null)
|
||||||
|
// {
|
||||||
|
// // @TODO: add parse if no template settings
|
||||||
|
// reply.TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.TemplateNotFound,
|
||||||
|
// "Template not found for record."));
|
||||||
|
// }
|
||||||
|
// else if (template.DefinedType != null)
|
||||||
|
// {
|
||||||
|
// var record = Activator.CreateInstance(template.DefinedType) as IRecord;
|
||||||
|
// for (var i = 0; i < template.Properties.Length; i++)
|
||||||
|
// {
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// //var v = Convert.ChangeType(ar[i], template.Properties[i].PropertyInfo.PropertyType);
|
||||||
|
// var v = RuntimeCaster.Cast(ar[i], template.Properties[i].PropertyInfo.PropertyType);
|
||||||
|
// template.Properties[i].PropertyInfo.SetValue(record, v);
|
||||||
|
// }
|
||||||
|
// catch (Exception ex)
|
||||||
|
// {
|
||||||
|
// Global.Log(ex);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// reply.Trigger(record);
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// var record = new Record();
|
||||||
|
|
||||||
|
// for (var i = 0; i < template.Properties.Length; i++)
|
||||||
|
// record.Add(template.Properties[i].Name, ar[i]);
|
||||||
|
|
||||||
|
// reply.Trigger(record);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// });
|
||||||
|
//};
|
||||||
|
|
||||||
|
//if (template != null)
|
||||||
|
//{
|
||||||
|
// initRecord(template);
|
||||||
|
//}
|
||||||
|
//else if (connection != null)
|
||||||
|
//{
|
||||||
|
// // try to get the template from the other end
|
||||||
|
// connection.GetTemplate(classId).Then(tmp =>
|
||||||
|
// {
|
||||||
|
// initRecord(tmp);
|
||||||
|
// }).Error(x => reply.TriggerError(x));
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// initRecord(null);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//return reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -453,13 +564,6 @@ public static class DataDeserializer
|
|||||||
var classId = tdu.Metadata.GetUUID(0);
|
var classId = tdu.Metadata.GetUUID(0);
|
||||||
var template = warehouse.GetTemplateByClassId(classId, TemplateType.Record);
|
var template = warehouse.GetTemplateByClassId(classId, TemplateType.Record);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//var r = ListParser(tdu, warehouse);
|
|
||||||
|
|
||||||
//var ar = (object[])r;
|
|
||||||
|
|
||||||
if (template == null)
|
if (template == null)
|
||||||
{
|
{
|
||||||
// @TODO: add parse if no template settings
|
// @TODO: add parse if no template settings
|
||||||
@@ -515,9 +619,6 @@ public static class DataDeserializer
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (template.DefinedType != null)
|
if (template.DefinedType != null)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -526,8 +627,7 @@ public static class DataDeserializer
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//var v = Convert.ChangeType(ar[i], template.Properties[i].PropertyInfo.PropertyType);
|
var v = RuntimeCaster.Cast(list[i], template.Properties[i].PropertyInfo.PropertyType);
|
||||||
var v = DC.CastConvert(list[i], template.Properties[i].PropertyInfo.PropertyType);
|
|
||||||
template.Properties[i].PropertyInfo.SetValue(record, v);
|
template.Properties[i].PropertyInfo.SetValue(record, v);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -567,7 +667,8 @@ public static class DataDeserializer
|
|||||||
|
|
||||||
var index = tdu.Data[tdu.Offset];
|
var index = tdu.Data[tdu.Offset];
|
||||||
|
|
||||||
var template = connection.Instance.Warehouse.GetTemplateByClassId(classId, TemplateType.Enum);
|
var template = connection.Instance.Warehouse.GetTemplateByClassId(classId,
|
||||||
|
TemplateType.Enum);
|
||||||
|
|
||||||
if (template != null)
|
if (template != null)
|
||||||
{
|
{
|
||||||
@@ -839,52 +940,90 @@ public static class DataDeserializer
|
|||||||
|
|
||||||
public static AsyncReply TypedMapParserAsync(ParsedTDU tdu, DistributedConnection connection, uint[] requestSequence)
|
public static AsyncReply TypedMapParserAsync(ParsedTDU tdu, DistributedConnection connection, uint[] requestSequence)
|
||||||
{
|
{
|
||||||
// get key type
|
|
||||||
|
|
||||||
var (keyCs, keyRepType) = TRU.Parse(tdu.Metadata, 0);
|
|
||||||
var (valueCs, valueRepType) = TRU.Parse(tdu.Metadata, keyCs);
|
|
||||||
|
|
||||||
var wh = connection.Instance.Warehouse;
|
|
||||||
|
|
||||||
var map = (IMap)Activator.CreateInstance(typeof(Map<,>).MakeGenericType(keyRepType.GetRuntimeType(wh), valueRepType.GetRuntimeType(wh)));
|
|
||||||
|
|
||||||
var rt = new AsyncReply();
|
var rt = new AsyncReply();
|
||||||
|
|
||||||
var results = new AsyncBag<object>();
|
// get key type
|
||||||
|
|
||||||
var offset = tdu.Offset;
|
var (keyCs, keysTru) = TRU.Parse(tdu.Metadata, 0);
|
||||||
var length = tdu.ContentLength;
|
var (valueCs, valuesTru) = TRU.Parse(tdu.Metadata, keyCs);
|
||||||
|
|
||||||
while (length > 0)
|
var map = (IMap)Activator.CreateInstance(typeof(Map<,>).MakeGenericType(
|
||||||
|
keysTru.GetRuntimeType(connection.Instance.Warehouse),
|
||||||
|
valuesTru.GetRuntimeType(connection.Instance.Warehouse)));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var keysTdu = ParsedTDU.Parse(tdu.Data, tdu.Offset,
|
||||||
|
(uint)(tdu.Offset + tdu.ContentLength));
|
||||||
|
|
||||||
|
var valuesTdu = ParsedTDU.Parse(tdu.Data,
|
||||||
|
(uint)(keysTdu.Offset + keysTdu.ContentLength),
|
||||||
|
tdu.Ends);
|
||||||
|
|
||||||
|
var keysReply = TypedArrayParserAsync(keysTdu, keysTru, connection, requestSequence);
|
||||||
|
var valuesReply = TypedArrayParserAsync(valuesTdu, valuesTru, connection, requestSequence);
|
||||||
|
|
||||||
|
|
||||||
|
keysReply.Then(keys =>
|
||||||
{
|
{
|
||||||
var (cs, reply) = Codec.ParseAsync(tdu.Data, offset, connection, requestSequence);
|
valuesReply.Then(values =>
|
||||||
|
|
||||||
|
|
||||||
results.Add(reply);
|
|
||||||
|
|
||||||
if (cs > 0)
|
|
||||||
{
|
{
|
||||||
offset += (uint)cs;
|
for (var i = 0; i < ((Array)keys).Length; i++)
|
||||||
length -= (uint)cs;
|
map.Add(((Array)keys).GetValue(i), ((Array)values).GetValue(i));
|
||||||
}
|
});
|
||||||
else
|
|
||||||
throw new Exception("Error while parsing structured data");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
results.Seal();
|
|
||||||
|
|
||||||
results.Then(ar =>
|
|
||||||
{
|
|
||||||
for (var i = 0; i < ar.Length; i += 2)
|
|
||||||
map.Add(ar[i], ar[i + 1]);
|
|
||||||
|
|
||||||
rt.Trigger(map);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
return rt;
|
return rt;
|
||||||
|
|
||||||
|
|
||||||
|
//// get key type
|
||||||
|
|
||||||
|
//var (keyCs, keyRepType) = TRU.Parse(tdu.Metadata, 0);
|
||||||
|
//var (valueCs, valueRepType) = TRU.Parse(tdu.Metadata, keyCs);
|
||||||
|
|
||||||
|
//var wh = connection.Instance.Warehouse;
|
||||||
|
|
||||||
|
//var map = (IMap)Activator.CreateInstance(typeof(Map<,>).MakeGenericType(keyRepType.GetRuntimeType(wh), valueRepType.GetRuntimeType(wh)));
|
||||||
|
|
||||||
|
//var rt = new AsyncReply();
|
||||||
|
|
||||||
|
//var results = new AsyncBag<object>();
|
||||||
|
|
||||||
|
//var offset = tdu.Offset;
|
||||||
|
//var length = tdu.ContentLength;
|
||||||
|
|
||||||
|
//while (length > 0)
|
||||||
|
//{
|
||||||
|
// var (cs, reply) = Codec.ParseAsync(tdu.Data, offset, connection, requestSequence);
|
||||||
|
|
||||||
|
|
||||||
|
// results.Add(reply);
|
||||||
|
|
||||||
|
// if (cs > 0)
|
||||||
|
// {
|
||||||
|
// offset += (uint)cs;
|
||||||
|
// length -= (uint)cs;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// throw new Exception("Error while parsing structured data");
|
||||||
|
|
||||||
|
//}
|
||||||
|
|
||||||
|
//results.Seal();
|
||||||
|
|
||||||
|
//results.Then(ar =>
|
||||||
|
//{
|
||||||
|
// for (var i = 0; i < ar.Length; i += 2)
|
||||||
|
// map.Add(ar[i], ar[i + 1]);
|
||||||
|
|
||||||
|
// rt.Trigger(map);
|
||||||
|
//});
|
||||||
|
|
||||||
|
|
||||||
|
//return rt;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Array TypedArrayParser(ParsedTDU tdu, TRU tru, Warehouse warehouse)
|
public static Array TypedArrayParser(ParsedTDU tdu, TRU tru, Warehouse warehouse)
|
||||||
@@ -927,7 +1066,7 @@ public static class DataDeserializer
|
|||||||
|
|
||||||
if (current.Class == TDUClass.Invalid)
|
if (current.Class == TDUClass.Invalid)
|
||||||
throw new Exception("Unknown type.");
|
throw new Exception("Unknown type.");
|
||||||
|
|
||||||
|
|
||||||
if (current.Identifier == TDUIdentifier.TypeContinuation)
|
if (current.Identifier == TDUIdentifier.TypeContinuation)
|
||||||
{
|
{
|
||||||
@@ -979,11 +1118,11 @@ public static class DataDeserializer
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
var keysTdu = ParsedTDU.Parse(tdu.Data, tdu.Offset,
|
var keysTdu = ParsedTDU.Parse(tdu.Data, tdu.Offset,
|
||||||
(uint)(tdu.Offset + tdu.ContentLength));
|
(uint)(tdu.Offset + tdu.ContentLength));
|
||||||
|
|
||||||
var valuesTdu = ParsedTDU.Parse(tdu.Data,
|
var valuesTdu = ParsedTDU.Parse(tdu.Data,
|
||||||
(uint)(keysTdu.Offset+keysTdu.ContentLength),
|
(uint)(keysTdu.Offset + keysTdu.ContentLength),
|
||||||
tdu.Ends);
|
tdu.Ends);
|
||||||
|
|
||||||
var keys = TypedArrayParser(keysTdu, keysTru, warehouse);
|
var keys = TypedArrayParser(keysTdu, keysTru, warehouse);
|
||||||
@@ -1168,17 +1307,9 @@ public static class DataDeserializer
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AsyncReply TypedArrayParserAsync(byte[] data, uint offset, TRU tru, DistributedConnection connection, uint[] requestSequence)
|
public static AsyncReply TypedArrayParserAsync(ParsedTDU tdu, TRU tru, DistributedConnection connection, uint[] requestSequence)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
switch (tru.Identifier)
|
||||||
}
|
|
||||||
|
|
||||||
public static AsyncReply TypedListParserAsync(ParsedTDU tdu, DistributedConnection connection, uint[] requestSequence)
|
|
||||||
{
|
|
||||||
// get the type
|
|
||||||
var (hdrCs, rep) = TRU.Parse(tdu.Metadata, 0);
|
|
||||||
|
|
||||||
switch (rep.Identifier)
|
|
||||||
{
|
{
|
||||||
case TRUIdentifier.Int32:
|
case TRUIdentifier.Int32:
|
||||||
return new AsyncReply(GroupInt32Codec.Decode(tdu.Data.AsSpan(
|
return new AsyncReply(GroupInt32Codec.Decode(tdu.Data.AsSpan(
|
||||||
@@ -1200,11 +1331,10 @@ public static class DataDeserializer
|
|||||||
(int)tdu.Offset, (int)tdu.ContentLength)));
|
(int)tdu.Offset, (int)tdu.ContentLength)));
|
||||||
default:
|
default:
|
||||||
|
|
||||||
var rt = new AsyncBag<object>();
|
|
||||||
|
|
||||||
var runtimeType = rep.GetRuntimeType(connection.Instance.Warehouse);
|
var list = new AsyncBag<object>();
|
||||||
|
|
||||||
rt.ArrayType = runtimeType;
|
list.ArrayType = tru.GetRuntimeType(connection.Instance.Warehouse);
|
||||||
|
|
||||||
ParsedTDU current;
|
ParsedTDU current;
|
||||||
ParsedTDU? previous = null;
|
ParsedTDU? previous = null;
|
||||||
@@ -1215,7 +1345,6 @@ public static class DataDeserializer
|
|||||||
|
|
||||||
while (length > 0)
|
while (length > 0)
|
||||||
{
|
{
|
||||||
|
|
||||||
current = ParsedTDU.Parse(tdu.Data, offset, ends);
|
current = ParsedTDU.Parse(tdu.Data, offset, ends);
|
||||||
|
|
||||||
if (current.Class == TDUClass.Invalid)
|
if (current.Class == TDUClass.Invalid)
|
||||||
@@ -1228,24 +1357,111 @@ public static class DataDeserializer
|
|||||||
current.Identifier = previous.Value.Identifier;
|
current.Identifier = previous.Value.Identifier;
|
||||||
current.Metadata = previous.Value.Metadata;
|
current.Metadata = previous.Value.Metadata;
|
||||||
}
|
}
|
||||||
|
else if (current.Identifier == TDUIdentifier.TypeOfTarget)
|
||||||
|
{
|
||||||
|
var (idf, mt) = tru.GetMetadata();
|
||||||
|
current.Class = TDUClass.Typed;
|
||||||
|
current.Identifier = idf;
|
||||||
|
current.Metadata = mt;
|
||||||
|
current.Index = (int)idf & 0x7;
|
||||||
|
}
|
||||||
|
|
||||||
var (cs, reply) = Codec.ParseAsync(tdu.Data, offset, connection, requestSequence);
|
var (cs, reply) = Codec.ParseAsync(current, connection, requestSequence);
|
||||||
|
|
||||||
rt.Add(reply);
|
list.Add(reply);
|
||||||
|
|
||||||
if (cs > 0)
|
if (cs > 0)
|
||||||
{
|
{
|
||||||
offset += (uint)cs;
|
offset += (uint)current.TotalLength;
|
||||||
length -= (uint)cs;
|
length -= (uint)current.TotalLength;
|
||||||
|
previous = current;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw new Exception("Error while parsing structured data");
|
throw new Exception("Error while parsing structured data");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rt.Seal();
|
list.Seal();
|
||||||
return rt;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AsyncReply TypedListParserAsync(ParsedTDU tdu, DistributedConnection connection, uint[] requestSequence)
|
||||||
|
{
|
||||||
|
// get the type
|
||||||
|
var (hdrCs, tru) = TRU.Parse(tdu.Metadata, 0);
|
||||||
|
|
||||||
|
return TypedArrayParserAsync(tdu, tru, connection, requestSequence);
|
||||||
|
|
||||||
|
//switch (rep.Identifier)
|
||||||
|
//{
|
||||||
|
// case TRUIdentifier.Int32:
|
||||||
|
// return new AsyncReply(GroupInt32Codec.Decode(tdu.Data.AsSpan(
|
||||||
|
// (int)tdu.Offset, (int)tdu.ContentLength)));
|
||||||
|
// case TRUIdentifier.Int64:
|
||||||
|
// return new AsyncReply(GroupInt64Codec.Decode(tdu.Data.AsSpan(
|
||||||
|
// (int)tdu.Offset, (int)tdu.ContentLength)));
|
||||||
|
// case TRUIdentifier.Int16:
|
||||||
|
// return new AsyncReply(GroupInt16Codec.Decode(tdu.Data.AsSpan(
|
||||||
|
// (int)tdu.Offset, (int)tdu.ContentLength)));
|
||||||
|
// case TRUIdentifier.UInt32:
|
||||||
|
// return new AsyncReply(GroupUInt32Codec.Decode(tdu.Data.AsSpan(
|
||||||
|
// (int)tdu.Offset, (int)tdu.ContentLength)));
|
||||||
|
// case TRUIdentifier.UInt64:
|
||||||
|
// return new AsyncReply(GroupUInt64Codec.Decode(tdu.Data.AsSpan(
|
||||||
|
// (int)tdu.Offset, (int)tdu.ContentLength)));
|
||||||
|
// case TRUIdentifier.UInt16:
|
||||||
|
// return new AsyncReply(GroupUInt16Codec.Decode(tdu.Data.AsSpan(
|
||||||
|
// (int)tdu.Offset, (int)tdu.ContentLength)));
|
||||||
|
// default:
|
||||||
|
|
||||||
|
// var rt = new AsyncBag<object>();
|
||||||
|
|
||||||
|
// var runtimeType = rep.GetRuntimeType(connection.Instance.Warehouse);
|
||||||
|
|
||||||
|
// rt.ArrayType = runtimeType;
|
||||||
|
|
||||||
|
// ParsedTDU current;
|
||||||
|
// ParsedTDU? previous = null;
|
||||||
|
|
||||||
|
// var offset = tdu.Offset;
|
||||||
|
// var length = tdu.ContentLength;
|
||||||
|
// var ends = offset + (uint)length;
|
||||||
|
|
||||||
|
// while (length > 0)
|
||||||
|
// {
|
||||||
|
|
||||||
|
// current = ParsedTDU.Parse(tdu.Data, offset, ends);
|
||||||
|
|
||||||
|
// if (current.Class == TDUClass.Invalid)
|
||||||
|
// throw new Exception("Unknown type.");
|
||||||
|
|
||||||
|
|
||||||
|
// if (current.Identifier == TDUIdentifier.TypeContinuation)
|
||||||
|
// {
|
||||||
|
// current.Class = previous.Value.Class;
|
||||||
|
// current.Identifier = previous.Value.Identifier;
|
||||||
|
// current.Metadata = previous.Value.Metadata;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var (cs, reply) = Codec.ParseAsync(tdu.Data, offset, connection, requestSequence);
|
||||||
|
|
||||||
|
// rt.Add(reply);
|
||||||
|
|
||||||
|
// if (cs > 0)
|
||||||
|
// {
|
||||||
|
// offset += (uint)cs;
|
||||||
|
// length -= (uint)cs;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// throw new Exception("Error while parsing structured data");
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
// rt.Seal();
|
||||||
|
// return rt;
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static object TypedListParser(ParsedTDU tdu, Warehouse warehouse)
|
public static object TypedListParser(ParsedTDU tdu, Warehouse warehouse)
|
||||||
@@ -1268,7 +1484,7 @@ public static class DataDeserializer
|
|||||||
var pvs = new List<PropertyValue>();
|
var pvs = new List<PropertyValue>();
|
||||||
|
|
||||||
for (var i = 0; i < ar.Length; i += 3)
|
for (var i = 0; i < ar.Length; i += 3)
|
||||||
pvs.Add(new PropertyValue(ar[2], (ulong?)ar[0], (DateTime?)ar[1]));
|
pvs.Add(new PropertyValue(ar[2], Convert.ToUInt64(ar[0]), (DateTime?)ar[1]));
|
||||||
|
|
||||||
|
|
||||||
rt.Trigger(pvs.ToArray());
|
rt.Trigger(pvs.ToArray());
|
||||||
|
|||||||
@@ -568,7 +568,7 @@ public static class DataSerializer
|
|||||||
{
|
{
|
||||||
var tdu = Codec.ComposeInternal(i, warehouse, connection);
|
var tdu = Codec.ComposeInternal(i, warehouse, connection);
|
||||||
|
|
||||||
var currentTru = TRU.FromType(i.GetType());
|
var currentTru = TRU.FromType(i?.GetType());
|
||||||
|
|
||||||
if (isTyped && tru.Match(currentTru))
|
if (isTyped && tru.Match(currentTru))
|
||||||
{
|
{
|
||||||
|
|||||||
370
Esiur/Data/RuntimeCaster.cs
Normal file
370
Esiur/Data/RuntimeCaster.cs
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
namespace Esiur.Data;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
|
||||||
|
public enum NaNInfinityPolicy
|
||||||
|
{
|
||||||
|
Throw, // Default: throw on NaN/∞ when converting to non-floating types
|
||||||
|
NullIfNullable, // If target is Nullable<decimal>, return null; otherwise throw
|
||||||
|
CoerceZero // Replace NaN/∞ with 0
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class RuntimeCastOptions
|
||||||
|
{
|
||||||
|
public bool CheckedNumeric { get; set; } = true;
|
||||||
|
|
||||||
|
// For DateTime/DateTimeOffset parsing
|
||||||
|
public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture;
|
||||||
|
public DateTimeStyles DateTimeStyles { get; set; } = DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeLocal;
|
||||||
|
|
||||||
|
// For enums
|
||||||
|
public bool EnumIgnoreCase { get; set; } = true;
|
||||||
|
public bool EnumMustBeDefined { get; set; } = false;
|
||||||
|
|
||||||
|
// float/double → decimal behavior
|
||||||
|
public NaNInfinityPolicy NaNInfinityPolicy { get; set; } = NaNInfinityPolicy.Throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class RuntimeCaster
|
||||||
|
{
|
||||||
|
|
||||||
|
public static readonly RuntimeCastOptions Default = new RuntimeCastOptions();
|
||||||
|
|
||||||
|
// (fromType, toType) -> converter(value, options) (options captured at call time)
|
||||||
|
private static readonly ConcurrentDictionary<(Type from, Type to), Func<object, RuntimeCastOptions, object>> _cache = new();
|
||||||
|
|
||||||
|
// Numeric-only compiled converters (fast path), keyed by checked/unchecked
|
||||||
|
private static readonly ConcurrentDictionary<(Type from, Type to, bool @checked), Func<object, object>> _numericCache = new();
|
||||||
|
|
||||||
|
public static object Cast(object value, Type toType, RuntimeCastOptions? options = null)
|
||||||
|
{
|
||||||
|
options ??= Default;
|
||||||
|
|
||||||
|
if (toType is null) throw new ArgumentNullException(nameof(toType));
|
||||||
|
if (value is null)
|
||||||
|
{
|
||||||
|
if (IsNonNullableValueType(toType))
|
||||||
|
throw new InvalidCastException($"Cannot cast null to non-nullable {toType}.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fromType = value.GetType();
|
||||||
|
if (toType.IsAssignableFrom(fromType)) return value; // already compatible
|
||||||
|
|
||||||
|
var fn = _cache.GetOrAdd((fromType, toType), k => BuildConverter(k.from, k.to));
|
||||||
|
return fn(value, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------ Builder ------------------------
|
||||||
|
private static Func<object, RuntimeCastOptions, object> BuildConverter(Type fromType, Type toType)
|
||||||
|
{
|
||||||
|
// Nullable handling is done inside ConvertCore.
|
||||||
|
return (value, opts) => ConvertCore(value, fromType, toType, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------ Core Routing ------------------------
|
||||||
|
private static object ConvertCore(object value, Type fromType, Type toType, RuntimeCastOptions opts)
|
||||||
|
{
|
||||||
|
var toUnderlying = Nullable.GetUnderlyingType(toType) ?? toType;
|
||||||
|
var fromUnderlying = Nullable.GetUnderlyingType(fromType) ?? fromType;
|
||||||
|
|
||||||
|
// If converting nullable source and it's null → result is null if target nullable, else throw
|
||||||
|
if (!fromType.IsValueType && value is null)
|
||||||
|
{
|
||||||
|
if (IsNonNullableValueType(toType))
|
||||||
|
throw new InvalidCastException($"Cannot cast null to non-nullable {toType}.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special cases first
|
||||||
|
// 1) Enum targets
|
||||||
|
if (toUnderlying.IsEnum)
|
||||||
|
return ConvertToEnum(value, fromUnderlying, toType, toUnderlying, opts);
|
||||||
|
|
||||||
|
// 2) Guid targets
|
||||||
|
if (toUnderlying == typeof(Guid))
|
||||||
|
return ConvertToGuid(value, fromUnderlying, toType);
|
||||||
|
|
||||||
|
// 3) DateTime / DateTimeOffset targets
|
||||||
|
if (toUnderlying == typeof(DateTime))
|
||||||
|
return ConvertToDateTime(value, fromUnderlying, toType, opts);
|
||||||
|
if (toUnderlying == typeof(DateTimeOffset))
|
||||||
|
return ConvertToDateTimeOffset(value, fromUnderlying, toType, opts);
|
||||||
|
|
||||||
|
// 4) decimal from float/double with NaN/∞ policy
|
||||||
|
if (toUnderlying == typeof(decimal) && (fromUnderlying == typeof(float) || fromUnderlying == typeof(double)))
|
||||||
|
{
|
||||||
|
var dec = ConvertFloatDoubleToDecimal(value, fromUnderlying, opts, out bool useNull);
|
||||||
|
if (toType != toUnderlying) // wrap in Nullable<decimal>
|
||||||
|
return useNull ? null : (decimal?)dec;
|
||||||
|
if (useNull) throw new OverflowException("NaN/Infinity cannot be converted to decimal.");
|
||||||
|
return dec;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5) General numeric conversions via compiled expression
|
||||||
|
if (IsNumeric(fromUnderlying) && IsNumeric(toUnderlying))
|
||||||
|
{
|
||||||
|
var nc = _numericCache.GetOrAdd((fromUnderlying, toUnderlying, opts.CheckedNumeric),
|
||||||
|
k => BuildNumericConverter(k.from, k.to, k.@checked));
|
||||||
|
var result = nc(value);
|
||||||
|
// Wrap into nullable if needed
|
||||||
|
if (toType != toUnderlying) return BoxNullable(result, toUnderlying);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6) String <-> other basics (Use TypeConverter first; if no path, fall through)
|
||||||
|
if (toUnderlying == typeof(string))
|
||||||
|
return value?.ToString();
|
||||||
|
|
||||||
|
// 7) Last-resort: TypeConverter or ChangeType once, inside this compiled path
|
||||||
|
// Try TypeConverter(target)
|
||||||
|
var tc = System.ComponentModel.TypeDescriptor.GetConverter(toUnderlying);
|
||||||
|
if (tc.CanConvertFrom(fromUnderlying))
|
||||||
|
{
|
||||||
|
var r = tc.ConvertFrom(null, opts.Culture, value);
|
||||||
|
if (toType != toUnderlying) return BoxNullable(r, toUnderlying);
|
||||||
|
return r!;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try TypeConverter(source)
|
||||||
|
var tc2 = System.ComponentModel.TypeDescriptor.GetConverter(fromUnderlying);
|
||||||
|
if (tc2.CanConvertTo(toUnderlying))
|
||||||
|
{
|
||||||
|
var r = tc2.ConvertTo(null, opts.Culture, value, toUnderlying);
|
||||||
|
if (toType != toUnderlying) return BoxNullable(r, toUnderlying);
|
||||||
|
return r!;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try Convert.ChangeType for IConvertible fallbacks
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var r = Convert.ChangeType(value, toUnderlying, opts.Culture);
|
||||||
|
if (toType != toUnderlying) return BoxNullable(r, toUnderlying);
|
||||||
|
return r!;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Final attempt: assignable cast via reflection (e.g., interfaces)
|
||||||
|
if (toUnderlying.IsInstanceOfType(value))
|
||||||
|
{
|
||||||
|
if (toType != toUnderlying) return BoxNullable(value, toUnderlying);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
throw new InvalidCastException($"Cannot cast {fromType} to {toType}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------ Helpers: Numeric ------------------------
|
||||||
|
private static Func<object, object> BuildNumericConverter(Type from, Type to, bool @checked)
|
||||||
|
{
|
||||||
|
var p = Expression.Parameter(typeof(object), "v");
|
||||||
|
|
||||||
|
Expression val = from.IsValueType
|
||||||
|
? Expression.Unbox(p, from)
|
||||||
|
: Expression.Convert(p, from);
|
||||||
|
|
||||||
|
Expression body;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
body = @checked ? Expression.ConvertChecked(val, to) : Expression.Convert(val, to);
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException)
|
||||||
|
{
|
||||||
|
// Non-legal numeric convert — should be rare given IsNumeric check.
|
||||||
|
throw new InvalidCastException($"Numeric conversion not supported: {from} -> {to}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Box result to object
|
||||||
|
Expression boxed = to.IsValueType ? Expression.Convert(body, typeof(object)) : body;
|
||||||
|
return Expression.Lambda<Func<object, object>>(boxed, p).Compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsNumeric(Type t)
|
||||||
|
{
|
||||||
|
t = Nullable.GetUnderlyingType(t) ?? t;
|
||||||
|
return t == typeof(byte) || t == typeof(sbyte) ||
|
||||||
|
t == typeof(short) || t == typeof(ushort) ||
|
||||||
|
t == typeof(int) || t == typeof(uint) ||
|
||||||
|
t == typeof(long) || t == typeof(ulong) ||
|
||||||
|
t == typeof(float) || t == typeof(double) ||
|
||||||
|
t == typeof(decimal);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsNonNullableValueType(Type t)
|
||||||
|
=> t.IsValueType && Nullable.GetUnderlyingType(t) is null;
|
||||||
|
|
||||||
|
private static object BoxNullable(object value, Type underlying)
|
||||||
|
{
|
||||||
|
// Create Nullable<T>(value) then box
|
||||||
|
var nt = typeof(Nullable<>).MakeGenericType(underlying);
|
||||||
|
var ctor = nt.GetConstructor(new[] { underlying })!;
|
||||||
|
return ctor.Invoke(new[] { value });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------ Helpers: NaN/∞ to decimal ------------------------
|
||||||
|
private static decimal ConvertFloatDoubleToDecimal(object value, Type fromUnderlying, RuntimeCastOptions opts, out bool useNull)
|
||||||
|
{
|
||||||
|
useNull = false;
|
||||||
|
|
||||||
|
if (fromUnderlying == typeof(float))
|
||||||
|
{
|
||||||
|
var f = (float)value;
|
||||||
|
if (float.IsNaN(f) || float.IsInfinity(f))
|
||||||
|
{
|
||||||
|
switch (opts.NaNInfinityPolicy)
|
||||||
|
{
|
||||||
|
case NaNInfinityPolicy.NullIfNullable: useNull = true; return default;
|
||||||
|
case NaNInfinityPolicy.CoerceZero: return 0m;
|
||||||
|
default: throw new OverflowException("Cannot convert NaN/Infinity to decimal.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return opts.CheckedNumeric ? checked((decimal)f) : (decimal)f;
|
||||||
|
}
|
||||||
|
else // double
|
||||||
|
{
|
||||||
|
var d = (double)value;
|
||||||
|
if (double.IsNaN(d) || double.IsInfinity(d))
|
||||||
|
{
|
||||||
|
switch (opts.NaNInfinityPolicy)
|
||||||
|
{
|
||||||
|
case NaNInfinityPolicy.NullIfNullable: useNull = true; return default;
|
||||||
|
case NaNInfinityPolicy.CoerceZero: return 0m;
|
||||||
|
default: throw new OverflowException("Cannot convert NaN/Infinity to decimal.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return opts.CheckedNumeric ? checked((decimal)d) : (decimal)d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------ Helpers: Enum ------------------------
|
||||||
|
private static object ConvertToEnum(object value, Type fromUnderlying, Type toType, Type enumType, RuntimeCastOptions opts)
|
||||||
|
{
|
||||||
|
// Nullable<Enum> wrapping
|
||||||
|
bool wrapNullable = toType != enumType;
|
||||||
|
|
||||||
|
// String → Enum
|
||||||
|
if (fromUnderlying == typeof(string))
|
||||||
|
{
|
||||||
|
var parsed = Enum.Parse(enumType, (string)value, opts.EnumIgnoreCase);
|
||||||
|
|
||||||
|
if (opts.EnumMustBeDefined && !Enum.IsDefined(enumType, parsed!))
|
||||||
|
throw new InvalidCastException($"Value '{value}' is not a defined member of {enumType.Name}.");
|
||||||
|
|
||||||
|
return wrapNullable ? BoxNullable(parsed!, enumType) : parsed!;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Numeric → Enum
|
||||||
|
if (IsNumeric(fromUnderlying))
|
||||||
|
{
|
||||||
|
// Convert numeric to enum’s underlying integral type first (checked/unchecked handled by compiled numeric path)
|
||||||
|
var et = Enum.GetUnderlyingType(enumType);
|
||||||
|
var numConv = _numericCache.GetOrAdd((fromUnderlying, et, true), k => BuildNumericConverter(k.from, k.to, k.@checked));
|
||||||
|
var integral = numConv(value);
|
||||||
|
|
||||||
|
var enumObj = Enum.ToObject(enumType, integral);
|
||||||
|
if (opts.EnumMustBeDefined && !Enum.IsDefined(enumType, enumObj))
|
||||||
|
throw new InvalidCastException($"Numeric value {integral} is not a defined member of {enumType.Name}.");
|
||||||
|
|
||||||
|
return wrapNullable ? BoxNullable(enumObj, enumType) : enumObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: not supported
|
||||||
|
throw new InvalidCastException($"Cannot cast {fromUnderlying} to enum {enumType.Name}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------ Helpers: Guid ------------------------
|
||||||
|
private static object ConvertToGuid(object value, Type fromUnderlying, Type toType)
|
||||||
|
{
|
||||||
|
bool wrapNullable = toType != typeof(Guid);
|
||||||
|
|
||||||
|
if (fromUnderlying == typeof(string))
|
||||||
|
{
|
||||||
|
if (!Guid.TryParse((string)value, out var g))
|
||||||
|
throw new InvalidCastException($"Cannot parse '{value}' to Guid.");
|
||||||
|
return wrapNullable ? (Guid?)g : g;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fromUnderlying == typeof(byte[]))
|
||||||
|
{
|
||||||
|
var bytes = (byte[])value;
|
||||||
|
if (bytes.Length != 16)
|
||||||
|
throw new InvalidCastException("Guid requires a 16-byte array.");
|
||||||
|
var g = new Guid(bytes);
|
||||||
|
return wrapNullable ? (Guid?)g : g;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidCastException($"Cannot cast {fromUnderlying} to Guid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------ Helpers: DateTime / DateTimeOffset ------------------------
|
||||||
|
private static object ConvertToDateTime(object value, Type fromUnderlying, Type toType, RuntimeCastOptions opts)
|
||||||
|
{
|
||||||
|
bool wrapNullable = toType != typeof(DateTime);
|
||||||
|
|
||||||
|
if (fromUnderlying == typeof(string))
|
||||||
|
{
|
||||||
|
if (!DateTime.TryParse((string)value, opts.Culture, opts.DateTimeStyles, out var dt))
|
||||||
|
throw new InvalidCastException($"Cannot parse '{value}' to DateTime.");
|
||||||
|
return wrapNullable ? (DateTime?)dt : dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fromUnderlying == typeof(long))
|
||||||
|
{
|
||||||
|
// Treat as ticks
|
||||||
|
var dt = new DateTime((long)value, DateTimeKind.Unspecified);
|
||||||
|
return wrapNullable ? (DateTime?)dt : dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fromUnderlying == typeof(double))
|
||||||
|
{
|
||||||
|
// Treat as OADate if finite
|
||||||
|
var d = (double)value;
|
||||||
|
if (double.IsNaN(d) || double.IsInfinity(d))
|
||||||
|
throw new InvalidCastException("Cannot convert NaN/Infinity to DateTime.");
|
||||||
|
var dt = DateTime.FromOADate(d);
|
||||||
|
return wrapNullable ? (DateTime?)dt : dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidCastException($"Cannot cast {fromUnderlying} to DateTime.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object ConvertToDateTimeOffset(object value, Type fromUnderlying, Type toType, RuntimeCastOptions opts)
|
||||||
|
{
|
||||||
|
bool wrapNullable = toType != typeof(DateTimeOffset);
|
||||||
|
|
||||||
|
if (fromUnderlying == typeof(string))
|
||||||
|
{
|
||||||
|
if (!DateTimeOffset.TryParse((string)value, opts.Culture, opts.DateTimeStyles, out var dto))
|
||||||
|
throw new InvalidCastException($"Cannot parse '{value}' to DateTimeOffset.");
|
||||||
|
return wrapNullable ? (DateTimeOffset?)dto : dto;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fromUnderlying == typeof(long))
|
||||||
|
{
|
||||||
|
// Treat as ticks since 0001-01-01
|
||||||
|
var dto = new DateTimeOffset(new DateTime((long)value, DateTimeKind.Unspecified));
|
||||||
|
return wrapNullable ? (DateTimeOffset?)dto : dto;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fromUnderlying == typeof(double))
|
||||||
|
{
|
||||||
|
var d = (double)value;
|
||||||
|
if (double.IsNaN(d) || double.IsInfinity(d))
|
||||||
|
throw new InvalidCastException("Cannot convert NaN/Infinity to DateTimeOffset.");
|
||||||
|
var dt = DateTime.FromOADate(d);
|
||||||
|
var dto = new DateTimeOffset(dt);
|
||||||
|
return wrapNullable ? (DateTimeOffset?)dto : dto;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidCastException($"Cannot cast {fromUnderlying} to DateTimeOffset.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -194,6 +194,9 @@ namespace Esiur.Data
|
|||||||
if (Identifier == TRUIdentifier.TypedList && SubTypes[0].Identifier == TRUIdentifier.UInt8)
|
if (Identifier == TRUIdentifier.TypedList && SubTypes[0].Identifier == TRUIdentifier.UInt8)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (Identifier == TRUIdentifier.TypedResource)
|
||||||
|
return false;
|
||||||
|
|
||||||
return (UUID != null) || (SubTypes != null && SubTypes.Length > 0);
|
return (UUID != null) || (SubTypes != null && SubTypes.Length > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,6 +237,7 @@ namespace Esiur.Data
|
|||||||
SubTypes[0].Compose().Concat(SubTypes[1].Compose()).ToArray());
|
SubTypes[0].Compose().Concat(SubTypes[1].Compose()).ToArray());
|
||||||
case TRUIdentifier.Enum:
|
case TRUIdentifier.Enum:
|
||||||
return (TDUIdentifier.TypedEnum, UUID?.Data);
|
return (TDUIdentifier.TypedEnum, UUID?.Data);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ public class HTTPServer : NetworkServer<HTTPConnection>, IResource
|
|||||||
foreach (var kv in ParameterIndex)
|
foreach (var kv in ParameterIndex)
|
||||||
{
|
{
|
||||||
var g = match.Groups[kv.Key];
|
var g = match.Groups[kv.Key];
|
||||||
args[kv.Value.Position] = DC.CastConvert(g.Value, kv.Value.ParameterType);
|
args[kv.Value.Position] = RuntimeCaster.Cast(g.Value, kv.Value.ParameterType);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SenderIndex != null)
|
if (SenderIndex != null)
|
||||||
|
|||||||
@@ -384,7 +384,7 @@ public partial class DistributedConnection : NetworkConnection, IStore
|
|||||||
SendRequest(IIPPacketRequest.KeepAlive, now, interval)
|
SendRequest(IIPPacketRequest.KeepAlive, now, interval)
|
||||||
.Then(x =>
|
.Then(x =>
|
||||||
{
|
{
|
||||||
Jitter = (uint)((object[])x)[1];
|
Jitter =Convert.ToUInt32(((object[])x)[1]);
|
||||||
keepAliveTimer.Start();
|
keepAliveTimer.Start();
|
||||||
}).Error(ex =>
|
}).Error(ex =>
|
||||||
{
|
{
|
||||||
@@ -429,7 +429,8 @@ public partial class DistributedConnection : NetworkConnection, IStore
|
|||||||
if (packet.DataType == null)
|
if (packet.DataType == null)
|
||||||
return offset;
|
return offset;
|
||||||
|
|
||||||
//Console.WriteLine("Incoming: " + packet);
|
Console.WriteLine("Incoming: " + packet);
|
||||||
|
|
||||||
if (packet.Method == IIPPacketMethod.Notification)
|
if (packet.Method == IIPPacketMethod.Notification)
|
||||||
{
|
{
|
||||||
var dt = packet.DataType.Value;
|
var dt = packet.DataType.Value;
|
||||||
@@ -934,7 +935,7 @@ public partial class DistributedConnection : NetworkConnection, IStore
|
|||||||
|
|
||||||
SendParams()
|
SendParams()
|
||||||
.AddUInt8((byte)IIPAuthPacketAcknowledge.NoAuthCredentials)
|
.AddUInt8((byte)IIPAuthPacketAcknowledge.NoAuthCredentials)
|
||||||
.AddUInt8Array(Codec.Compose(localHeaders, this.Instance.Warehouse, this))
|
.AddUInt8Array(Codec.Compose(localHeaders, Server.Instance.Warehouse, this))
|
||||||
.Done();
|
.Done();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1321,7 +1322,7 @@ public partial class DistributedConnection : NetworkConnection, IStore
|
|||||||
|
|
||||||
SendParams()
|
SendParams()
|
||||||
.AddUInt8((byte)IIPAuthPacketEvent.IAuthHashed)
|
.AddUInt8((byte)IIPAuthPacketEvent.IAuthHashed)
|
||||||
.AddUInt8Array(Codec.Compose(args, this.Instance.Warehouse, this))
|
.AddUInt8Array(Codec.Compose(args, Server.Instance.Warehouse, this))
|
||||||
.Done();
|
.Done();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -402,7 +402,7 @@ partial class DistributedConnection
|
|||||||
{
|
{
|
||||||
var (size, rt) = Codec.ParseSync(dataType, Instance.Warehouse);
|
var (size, rt) = Codec.ParseSync(dataType, Instance.Warehouse);
|
||||||
|
|
||||||
var resourceId = (uint)rt;
|
var resourceId = Convert.ToUInt32(rt);
|
||||||
|
|
||||||
if (attachedResources.Contains(resourceId))
|
if (attachedResources.Contains(resourceId))
|
||||||
{
|
{
|
||||||
@@ -475,7 +475,7 @@ partial class DistributedConnection
|
|||||||
DataDeserializer.LimitedCountListParser(data, dataType.Offset,
|
DataDeserializer.LimitedCountListParser(data, dataType.Offset,
|
||||||
dataType.ContentLength, Instance.Warehouse, 2);
|
dataType.ContentLength, Instance.Warehouse, 2);
|
||||||
|
|
||||||
var resourceId = (uint)args[0];
|
var resourceId = Convert.ToUInt32(args[0]);
|
||||||
var index = (byte)args[1];
|
var index = (byte)args[1];
|
||||||
|
|
||||||
Fetch(resourceId, null).Then(r =>
|
Fetch(resourceId, null).Then(r =>
|
||||||
@@ -522,7 +522,7 @@ partial class DistributedConnection
|
|||||||
|
|
||||||
var (_, value) = Codec.ParseSync(dataType, Instance.Warehouse);
|
var (_, value) = Codec.ParseSync(dataType, Instance.Warehouse);
|
||||||
|
|
||||||
var resourceId = (uint)value;
|
var resourceId = Convert.ToUInt32(value);
|
||||||
|
|
||||||
Instance.Warehouse.GetById(resourceId).Then((res) =>
|
Instance.Warehouse.GetById(resourceId).Then((res) =>
|
||||||
{
|
{
|
||||||
@@ -579,7 +579,8 @@ partial class DistributedConnection
|
|||||||
DataDeserializer.LimitedCountListParser(data, dataType.Offset,
|
DataDeserializer.LimitedCountListParser(data, dataType.Offset,
|
||||||
dataType.ContentLength, Instance.Warehouse, 2);
|
dataType.ContentLength, Instance.Warehouse, 2);
|
||||||
|
|
||||||
var resourceId = (uint)args[0];
|
var resourceId = Convert.ToUInt32(args[0]);
|
||||||
|
|
||||||
var age = (ulong)args[1];
|
var age = (ulong)args[1];
|
||||||
|
|
||||||
Instance.Warehouse.GetById(resourceId).Then((res) =>
|
Instance.Warehouse.GetById(resourceId).Then((res) =>
|
||||||
@@ -635,7 +636,7 @@ partial class DistributedConnection
|
|||||||
|
|
||||||
var (_, value) = Codec.ParseSync(dataType, Instance.Warehouse);
|
var (_, value) = Codec.ParseSync(dataType, Instance.Warehouse);
|
||||||
|
|
||||||
var resourceId = (uint)value;
|
var resourceId = Convert.ToUInt32(value);
|
||||||
|
|
||||||
Instance.Warehouse.GetById(resourceId).Then((res) =>
|
Instance.Warehouse.GetById(resourceId).Then((res) =>
|
||||||
{
|
{
|
||||||
@@ -727,7 +728,7 @@ partial class DistributedConnection
|
|||||||
|
|
||||||
var (_, value) = Codec.ParseSync(dataType, Instance.Warehouse);
|
var (_, value) = Codec.ParseSync(dataType, Instance.Warehouse);
|
||||||
|
|
||||||
var resourceId = (uint)value;
|
var resourceId = Convert.ToUInt32(value);
|
||||||
|
|
||||||
Instance.Warehouse.GetById(resourceId).Then(r =>
|
Instance.Warehouse.GetById(resourceId).Then(r =>
|
||||||
{
|
{
|
||||||
@@ -757,7 +758,9 @@ partial class DistributedConnection
|
|||||||
var (offset, length, args) = DataDeserializer.LimitedCountListParser(data, dataType.Offset,
|
var (offset, length, args) = DataDeserializer.LimitedCountListParser(data, dataType.Offset,
|
||||||
dataType.ContentLength, Instance.Warehouse);
|
dataType.ContentLength, Instance.Warehouse);
|
||||||
|
|
||||||
var resourceId = (uint)args[0];
|
|
||||||
|
var resourceId = Convert.ToUInt32(args[0]);
|
||||||
|
|
||||||
var name = (string)args[1];
|
var name = (string)args[1];
|
||||||
|
|
||||||
if (name.Contains("/"))
|
if (name.Contains("/"))
|
||||||
@@ -874,7 +877,7 @@ partial class DistributedConnection
|
|||||||
|
|
||||||
var (_, value) = Codec.ParseSync(dataType, Instance.Warehouse);
|
var (_, value) = Codec.ParseSync(dataType, Instance.Warehouse);
|
||||||
|
|
||||||
var resourceId = (uint)value;
|
var resourceId = Convert.ToUInt32(value);
|
||||||
|
|
||||||
Instance.Warehouse.GetById(resourceId).Then((r) =>
|
Instance.Warehouse.GetById(resourceId).Then((r) =>
|
||||||
{
|
{
|
||||||
@@ -1120,7 +1123,7 @@ partial class DistributedConnection
|
|||||||
var (offset, length, args) = DataDeserializer.LimitedCountListParser(data, dataType.Offset,
|
var (offset, length, args) = DataDeserializer.LimitedCountListParser(data, dataType.Offset,
|
||||||
dataType.ContentLength, Instance.Warehouse, 2);
|
dataType.ContentLength, Instance.Warehouse, 2);
|
||||||
|
|
||||||
var resourceId = (uint)args[0];
|
var resourceId = Convert.ToUInt32(args[0]);
|
||||||
var index = (byte)args[1];
|
var index = (byte)args[1];
|
||||||
|
|
||||||
Instance.Warehouse.GetById(resourceId).Then((r) =>
|
Instance.Warehouse.GetById(resourceId).Then((r) =>
|
||||||
@@ -1239,7 +1242,7 @@ partial class DistributedConnection
|
|||||||
for (byte i = 0; i < pis.Length - 1; i++)
|
for (byte i = 0; i < pis.Length - 1; i++)
|
||||||
{
|
{
|
||||||
if (arguments.ContainsKey(i))
|
if (arguments.ContainsKey(i))
|
||||||
args[i] = DC.CastConvert(arguments[i], pis[i].ParameterType);
|
args[i] = RuntimeCaster.Cast(arguments[i], pis[i].ParameterType);
|
||||||
else if (ft.Arguments[i].Type.Nullable)
|
else if (ft.Arguments[i].Type.Nullable)
|
||||||
args[i] = null;
|
args[i] = null;
|
||||||
else
|
else
|
||||||
@@ -1256,7 +1259,7 @@ partial class DistributedConnection
|
|||||||
for (byte i = 0; i < pis.Length - 1; i++)
|
for (byte i = 0; i < pis.Length - 1; i++)
|
||||||
{
|
{
|
||||||
if (arguments.ContainsKey(i))
|
if (arguments.ContainsKey(i))
|
||||||
args[i] = DC.CastConvert(arguments[i], pis[i].ParameterType);
|
args[i] = RuntimeCaster.Cast(arguments[i], pis[i].ParameterType);
|
||||||
else if (ft.Arguments[i].Type.Nullable)
|
else if (ft.Arguments[i].Type.Nullable)
|
||||||
args[i] = null;
|
args[i] = null;
|
||||||
else
|
else
|
||||||
@@ -1272,7 +1275,7 @@ partial class DistributedConnection
|
|||||||
for (byte i = 0; i < pis.Length; i++)
|
for (byte i = 0; i < pis.Length; i++)
|
||||||
{
|
{
|
||||||
if (arguments.ContainsKey(i))
|
if (arguments.ContainsKey(i))
|
||||||
args[i] = DC.CastConvert(arguments[i], pis[i].ParameterType);
|
args[i] = RuntimeCaster.Cast(arguments[i], pis[i].ParameterType);
|
||||||
else if (ft.Arguments[i].Type.Nullable) //Nullable.GetUnderlyingType(pis[i].ParameterType) != null)
|
else if (ft.Arguments[i].Type.Nullable) //Nullable.GetUnderlyingType(pis[i].ParameterType) != null)
|
||||||
args[i] = null;
|
args[i] = null;
|
||||||
else
|
else
|
||||||
@@ -1377,7 +1380,7 @@ partial class DistributedConnection
|
|||||||
var (offset, length, args) = DataDeserializer.LimitedCountListParser(data, dataType.Offset,
|
var (offset, length, args) = DataDeserializer.LimitedCountListParser(data, dataType.Offset,
|
||||||
dataType.ContentLength, Instance.Warehouse);
|
dataType.ContentLength, Instance.Warehouse);
|
||||||
|
|
||||||
var resourceId = (uint)args[0];
|
var resourceId = Convert.ToUInt32(args[0]);
|
||||||
var index = (byte)args[1];
|
var index = (byte)args[1];
|
||||||
|
|
||||||
Instance.Warehouse.GetById(resourceId).Then((r) =>
|
Instance.Warehouse.GetById(resourceId).Then((r) =>
|
||||||
@@ -1436,7 +1439,7 @@ partial class DistributedConnection
|
|||||||
var (offset, length, args) = DataDeserializer.LimitedCountListParser(data, dataType.Offset,
|
var (offset, length, args) = DataDeserializer.LimitedCountListParser(data, dataType.Offset,
|
||||||
dataType.ContentLength, Instance.Warehouse);
|
dataType.ContentLength, Instance.Warehouse);
|
||||||
|
|
||||||
var resourceId = (uint)args[0];
|
var resourceId = Convert.ToUInt32(args[0]);
|
||||||
var index = (byte)args[1];
|
var index = (byte)args[1];
|
||||||
|
|
||||||
Instance.Warehouse.GetById(resourceId).Then((r) =>
|
Instance.Warehouse.GetById(resourceId).Then((r) =>
|
||||||
@@ -1576,7 +1579,7 @@ partial class DistributedConnection
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// cast new value type to property type
|
// cast new value type to property type
|
||||||
value = DC.CastConvert(value, pi.PropertyType);
|
value = RuntimeCaster.Cast(value, pi.PropertyType);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -1601,7 +1604,7 @@ partial class DistributedConnection
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// cast new value type to property type
|
// cast new value type to property type
|
||||||
parsed = DC.CastConvert(parsed, pi.PropertyType);
|
parsed = RuntimeCaster.Cast(parsed, pi.PropertyType);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -1805,7 +1808,7 @@ partial class DistributedConnection
|
|||||||
// ClassId, Age, Link, Hops, PropertyValue[]
|
// ClassId, Age, Link, Hops, PropertyValue[]
|
||||||
var args = (object[])result;
|
var args = (object[])result;
|
||||||
var classId = (UUID)args[0];
|
var classId = (UUID)args[0];
|
||||||
var age = (ulong)args[1];
|
var age = Convert.ToUInt64(args[1]);
|
||||||
var link = (string)args[2];
|
var link = (string)args[2];
|
||||||
var hops = (byte)args[3];
|
var hops = (byte)args[3];
|
||||||
var pvData = (byte[])args[4];
|
var pvData = (byte[])args[4];
|
||||||
@@ -1820,7 +1823,7 @@ partial class DistributedConnection
|
|||||||
if (template?.DefinedType != null && template.IsWrapper)
|
if (template?.DefinedType != null && template.IsWrapper)
|
||||||
dr = Activator.CreateInstance(template.DefinedType, this, id, (ulong)args[1], (string)args[2]) as DistributedResource;
|
dr = Activator.CreateInstance(template.DefinedType, this, id, (ulong)args[1], (string)args[2]) as DistributedResource;
|
||||||
else
|
else
|
||||||
dr = new DistributedResource(this, id, (ulong)args[1], (string)args[2]);
|
dr = new DistributedResource(this, id, Convert.ToUInt64( args[1]), (string)args[2]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2063,7 +2066,7 @@ partial class DistributedConnection
|
|||||||
dataType.ContentLength, Instance.Warehouse);
|
dataType.ContentLength, Instance.Warehouse);
|
||||||
|
|
||||||
var peerTime = (DateTime)args[0];
|
var peerTime = (DateTime)args[0];
|
||||||
var interval = (uint)args[1];
|
var interval = Convert.ToUInt32(args[1]);
|
||||||
|
|
||||||
uint jitter = 0;
|
uint jitter = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ public class Instance
|
|||||||
|
|
||||||
if (at != null)
|
if (at != null)
|
||||||
if (at.PropertyInfo.CanWrite)
|
if (at.PropertyInfo.CanWrite)
|
||||||
at.PropertyInfo.SetValue(res, DC.CastConvert(kv.Value, at.PropertyInfo.PropertyType));
|
at.PropertyInfo.SetValue(res, RuntimeCaster.Cast(kv.Value, at.PropertyInfo.PropertyType));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -363,7 +363,7 @@ public class Instance
|
|||||||
{
|
{
|
||||||
loading = true;
|
loading = true;
|
||||||
|
|
||||||
pt.PropertyInfo.SetValue(res, DC.CastConvert(value, pt.PropertyInfo.PropertyType));
|
pt.PropertyInfo.SetValue(res, RuntimeCaster.Cast(value, pt.PropertyInfo.PropertyType));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user