2025-02-24 12:48:31 +03:00
using System ;
using System.Collections ;
using System.Collections.Generic ;
2025-06-30 11:45:59 +03:00
using System.Diagnostics ;
2025-02-24 12:48:31 +03:00
using System.Linq ;
2025-02-25 08:43:01 +03:00
using System.Reflection ;
2025-07-15 17:10:12 +03:00
using Micial.Cbor.Reader ;
using Micial.Cbor.Writer ;
2025-02-24 12:48:31 +03:00
using mROA.Abstract ;
using mROA.Implementation ;
2025-02-25 08:43:01 +03:00
using mROA.Implementation.Attributes ;
2025-07-09 17:15:55 +03:00
using mROA.Implementation.CommandExecution ;
2025-02-24 12:48:31 +03:00
namespace mROA.Cbor
{
2025-02-25 17:09:04 +03:00
public class CborSerializationToolkit : IContextualSerializationToolKit
2025-02-24 12:48:31 +03:00
{
2025-07-10 10:25:40 +03:00
private readonly IOrdinaryStructureParser [] _parsers = {
new NetworkMessageHeaderParser (), new DefaultCallRequestParser (), new FinalCommandExecutionParser (),
new FinalCommandExecutionResultlessParser ()
};
// private Dictionary<Type, IOrdinaryStructureParser> _parsers = new(){{typeof(NetworkMessageHeader), new NetworkMessageHeaderParser()}, {typeof(DefaultCallRequest), new DefaultCallRequestParser()}, {typeof(FinalCommandExecution<object>), new FinalCommandExecutionParser()}, {typeof(FinalCommandExecution), new FinalCommandExecutionResultlessParser()}}; //
2025-07-08 23:33:37 +03:00
private Dictionary < Type , List < PropertyInfo >> _propertiesCache = new ();
2025-06-30 11:45:59 +03:00
public static TimeSpan SerializationTime = TimeSpan . Zero ;
2025-07-10 10:25:40 +03:00
private bool FindParser ( Type t , out IOrdinaryStructureParser parser )
{
if ( t == typeof ( NetworkMessageHeader ))
{
parser = _parsers [ 0 ];
return true ;
}
if ( t == typeof ( DefaultCallRequest ))
{
parser = _parsers [ 1 ];
return true ;
}
if ( t == typeof ( FinalCommandExecution < object >))
{
parser = _parsers [ 2 ];
return true ;
}
if ( t == typeof ( FinalCommandExecution ))
{
parser = _parsers [ 3 ];
return true ;
}
parser = null ;
return false ;
}
2025-02-25 08:43:01 +03:00
public byte [] Serialize ( object objectToSerialize , IEndPointContext context )
2025-02-24 12:48:31 +03:00
{
2025-06-30 11:45:59 +03:00
var sw = Stopwatch . StartNew ();
2025-07-09 12:01:08 +03:00
var writer = new CborWriter ( initialCapacity : 64 );
2025-02-25 08:43:01 +03:00
WriteData ( objectToSerialize , writer , context );
2025-07-09 12:01:08 +03:00
var result = writer . Encode ();
2025-06-30 11:45:59 +03:00
sw . Stop ();
SerializationTime = SerializationTime . Add ( sw . Elapsed );
return result ;
2025-02-24 12:48:31 +03:00
}
2025-05-31 19:46:29 +03:00
public int Serialize ( object objectToSerialize , Span < byte > destination , IEndPointContext context )
2025-02-24 12:48:31 +03:00
{
2025-07-09 12:01:08 +03:00
var writer = new CborWriter ( initialCapacity : 64 );
2025-02-25 08:43:01 +03:00
WriteData ( objectToSerialize , writer , context );
2025-05-31 19:46:29 +03:00
return writer . Encode ( destination );
2025-02-24 12:48:31 +03:00
}
2025-02-27 00:12:52 +03:00
public T Deserialize < T >( byte [] rawData , IEndPointContext ? context )
2025-02-24 12:48:31 +03:00
{
2025-02-27 00:12:52 +03:00
return ( T ) Deserialize ( rawData , typeof ( T ), context ) ?? default ;
2025-02-24 12:48:31 +03:00
}
2025-02-27 00:12:52 +03:00
public object? Deserialize ( byte [] rawData , Type type , IEndPointContext ? context )
2025-02-24 12:48:31 +03:00
{
2025-02-25 08:43:01 +03:00
return Deserialize ( rawData . AsMemory (), type , context );
2025-02-24 12:48:31 +03:00
}
2025-02-27 00:12:52 +03:00
public T Deserialize < T >( ReadOnlyMemory < byte > rawMemory , IEndPointContext ? context )
2025-02-24 12:48:31 +03:00
{
2025-02-27 00:12:52 +03:00
return ( T ) Deserialize ( rawMemory , typeof ( T ), context );
2025-02-24 12:48:31 +03:00
}
2025-02-27 00:12:52 +03:00
public object? Deserialize ( ReadOnlyMemory < byte > rawMemory , Type type , IEndPointContext ? context )
2025-02-24 12:48:31 +03:00
{
2025-02-27 00:12:52 +03:00
var reader = new CborReader ( rawMemory );
2025-02-25 08:43:01 +03:00
return ReadData ( reader , type , context );
2025-02-24 12:48:31 +03:00
}
2025-02-27 00:12:52 +03:00
public T Cast < T >( object nonCasted , IEndPointContext ? context )
2025-02-24 12:48:31 +03:00
{
return ( T ) Cast ( nonCasted , typeof ( T ), context );
}
2025-03-06 14:10:52 +03:00
public object? Cast ( object? nonCasted , Type type , IEndPointContext ? context )
2025-02-24 12:48:31 +03:00
{
2025-03-06 14:10:52 +03:00
if ( nonCasted == null )
return null ;
2025-03-08 23:22:59 +03:00
2025-02-27 00:12:52 +03:00
if ( nonCasted . GetType () == type )
return nonCasted ;
if ( nonCasted is PreParsedValue preParsed )
return preParsed . ToObject ( type , context );
2025-03-13 08:43:48 +03:00
if ( type == typeof ( Guid ))
{
return new Guid (( byte []) nonCasted );
}
2025-03-08 23:22:59 +03:00
return Convert . ChangeType ( nonCasted , type );
2025-02-24 12:48:31 +03:00
}
2025-07-10 17:45:19 +03:00
public void Inject ( object dependency )
2025-03-10 21:56:22 +03:00
{
}
public byte [] Serialize < T >( T objectToSerialize )
{
return Serialize ( objectToSerialize , typeof ( T ));
}
public byte [] Serialize ( object objectToSerialize , Type type )
{
return Serialize ( objectToSerialize , context : null );
}
public T Deserialize < T >( byte [] rawData )
{
return Deserialize < T >( rawData : rawData , context : null );
}
public object? Deserialize ( byte [] rawData , Type type )
{
return Deserialize ( rawData : rawData , type , context : null );
}
public T Deserialize < T >( Span < byte > rawData )
{
return Deserialize < T >( rawData . ToArray (). AsMemory (), context : null );
}
public object? Deserialize ( Span < byte > rawData , Type type )
{
return Deserialize ( rawData : rawData . ToArray (), type : type );
}
public T Cast < T >( object nonCasted )
{
return Cast < T >( nonCasted : nonCasted , context : null );
}
public object Cast ( object nonCasted , Type type )
{
return Cast ( nonCasted : nonCasted , type : type , context : null );
}
2025-07-09 17:15:55 +03:00
public void WriteData ( object? obj , CborWriter writer , IEndPointContext ? context )
2025-02-24 12:48:31 +03:00
{
2025-07-10 10:25:40 +03:00
if ( obj is not null && FindParser ( obj . GetType (), out var parser ))
2025-07-09 17:15:55 +03:00
{
parser . Write ( writer , obj , context , this );
return ;
}
2025-02-24 12:48:31 +03:00
switch ( obj )
{
case int i :
writer . WriteInt32 ( i );
break ;
case long l :
writer . WriteInt64 ( l );
break ;
case float f :
writer . WriteSingle ( f );
break ;
case double d :
writer . WriteDouble ( d );
break ;
case bool b :
writer . WriteBoolean ( b );
break ;
case string s :
writer . WriteTextString ( s );
break ;
case null :
writer . WriteNull ();
break ;
case uint ui :
writer . WriteUInt32 ( ui );
break ;
case ulong ul :
writer . WriteUInt64 ( ul );
break ;
case DateTimeOffset dto :
writer . WriteDateTimeOffset ( dto );
break ;
2025-02-27 00:12:52 +03:00
case Guid g :
writer . WriteByteString ( g . ToByteArray ());
break ;
2025-02-24 12:48:31 +03:00
case byte [] bytes :
writer . WriteByteString ( bytes );
break ;
case IDictionary dictionary :
2025-02-25 08:43:01 +03:00
WriteDictionary ( dictionary , writer , context );
2025-02-24 12:48:31 +03:00
break ;
2025-02-25 08:43:01 +03:00
case IList enumerable :
WriteList ( enumerable , writer , context );
2025-02-24 12:48:31 +03:00
break ;
2025-03-01 19:06:18 +03:00
case ISharedObjectShell sharedObject :
2025-02-27 00:12:52 +03:00
if ( context != null )
sharedObject . EndPointContext = context ;
2025-02-25 08:43:01 +03:00
WriteObject ( sharedObject , writer , context );
2025-02-24 12:48:31 +03:00
break ;
default :
2025-02-25 08:43:01 +03:00
if ( obj . GetType (). IsEnum )
{
2025-02-27 00:12:52 +03:00
writer . WriteInt32 (( int ) obj );
2025-02-25 08:43:01 +03:00
break ;
}
WriteObject ( obj , writer , context );
2025-02-24 12:48:31 +03:00
break ;
}
}
2025-07-09 17:15:55 +03:00
public void WriteList ( IList list , CborWriter writer , IEndPointContext ? context )
2025-02-24 12:48:31 +03:00
{
writer . WriteStartArray ( list . Count );
2025-07-09 17:29:57 +03:00
for ( var index = 0 ; index < list . Count ; index ++)
{
var element = list [ index ];
2025-02-25 08:43:01 +03:00
WriteData ( element , writer , context );
2025-07-09 17:29:57 +03:00
}
2025-02-24 12:48:31 +03:00
writer . WriteEndArray ();
}
2025-07-09 17:15:55 +03:00
public void WriteDictionary ( IDictionary dictionary , CborWriter writer , IEndPointContext ? context )
2025-02-24 12:48:31 +03:00
{
writer . WriteStartMap ( dictionary . Count );
var keysEnumerator = dictionary . Keys . GetEnumerator ();
var valuesEnumerator = dictionary . Values . GetEnumerator ();
for ( int i = 0 ; i < dictionary . Count ; i ++)
{
keysEnumerator . MoveNext ();
valuesEnumerator . MoveNext ();
2025-02-25 08:43:01 +03:00
WriteData ( keysEnumerator . Current , writer , context );
WriteData ( valuesEnumerator . Current , writer , context );
2025-02-24 12:48:31 +03:00
}
2025-02-25 08:43:01 +03:00
2025-02-24 12:48:31 +03:00
writer . WriteEndMap ();
2025-02-25 08:43:01 +03:00
( keysEnumerator as IDisposable )?. Dispose ();
( valuesEnumerator as IDisposable )?. Dispose ();
2025-02-24 12:48:31 +03:00
}
2025-07-09 17:15:55 +03:00
public void WriteObject ( object obj , CborWriter writer , IEndPointContext ? context )
2025-02-24 12:48:31 +03:00
{
var type = obj . GetType ();
2025-03-01 19:06:18 +03:00
if ( obj is IShared )
{
2025-05-07 09:55:15 +03:00
var sharedShell = typeof ( SharedObjectShellShell < object >);
2025-03-01 19:06:18 +03:00
var so =
2025-05-05 16:30:23 +03:00
Activator . CreateInstance ( sharedShell , obj , context ) as
2025-03-01 19:06:18 +03:00
ISharedObjectShell ;
writer . WriteStartArray ( 1 );
writer . WriteUInt64 ( so . Identifier . Flat );
writer . WriteEndArray ();
return ;
}
2025-07-08 23:33:37 +03:00
var properties = GetAvailableProperties ( type );
2025-02-25 08:43:01 +03:00
var values = properties . Select ( property => property . GetValue ( obj )). ToList ();
WriteList ( values , writer , context );
}
2025-07-09 17:15:55 +03:00
public object? ReadData ( CborReader reader , Type ? type , IEndPointContext ? context )
2025-02-25 08:43:01 +03:00
{
2025-07-10 10:25:40 +03:00
if ( type is not null && FindParser ( type , out var parser ))
2025-07-09 17:15:55 +03:00
{
return parser . Read ( reader , context , this );
}
2025-02-25 08:43:01 +03:00
var state = reader . PeekState ();
switch ( state )
{
case CborReaderState . Boolean :
return reader . ReadBoolean ();
case CborReaderState . UnsignedInteger :
case CborReaderState . NegativeInteger :
2025-02-27 00:12:52 +03:00
if ( type == typeof ( int ) || type is { IsEnum : true })
2025-02-25 08:43:01 +03:00
return reader . ReadInt32 ();
if ( type == typeof ( long ))
return reader . ReadInt64 ();
2025-02-27 00:12:52 +03:00
if ( type == typeof ( uint ))
2025-02-25 08:43:01 +03:00
return reader . ReadUInt32 ();
if ( type == typeof ( ulong ))
return reader . ReadUInt64 ();
2025-02-27 00:12:52 +03:00
2025-03-01 16:40:40 +03:00
return reader . ReadUInt64 ();
2025-02-25 08:43:01 +03:00
case CborReaderState . ByteString :
2025-02-27 00:12:52 +03:00
if ( type == typeof ( Guid ))
return new Guid ( reader . ReadByteString ());
2025-02-25 08:43:01 +03:00
return reader . ReadByteString ();
case CborReaderState . TextString :
return reader . ReadTextString ();
case CborReaderState . Null :
2025-02-27 00:12:52 +03:00
reader . ReadNull ();
2025-02-25 08:43:01 +03:00
return null ;
case CborReaderState . DoublePrecisionFloat :
return reader . ReadDouble ();
case CborReaderState . SinglePrecisionFloat :
return reader . ReadSingle ();
2025-03-08 23:22:59 +03:00
case CborReaderState . HalfPrecisionFloat :
return type == typeof ( float ) ? reader . ReadSingle () : reader . ReadDouble ();
2025-02-25 08:43:01 +03:00
case CborReaderState . StartArray :
if ( type == null )
return ReadList ( reader , null , context );
2025-03-01 19:06:18 +03:00
if ( type . IsSubclassOf ( typeof ( ISharedObjectShell )))
2025-02-25 08:43:01 +03:00
return ReadSharedObject ( reader , type , context );
2025-02-25 17:09:04 +03:00
if ( typeof ( IList ). IsAssignableFrom ( type ) || type . IsArray )
2025-02-25 08:43:01 +03:00
return ReadList ( reader , type , context );
2025-02-25 17:09:04 +03:00
2025-02-25 08:43:01 +03:00
return ReadObject ( reader , type , context );
case CborReaderState . StartMap :
return ReadDictionary ( reader , type , context );
}
if ( type == typeof ( DateTimeOffset ))
return reader . ReadDateTimeOffset ();
return null ;
}
2025-07-09 17:15:55 +03:00
public IList ReadList ( CborReader reader , Type ? type , IEndPointContext ? context )
2025-02-25 08:43:01 +03:00
{
var length = reader . ReadStartArray ();
if ( length != null )
{
2025-02-25 17:09:04 +03:00
var elementType = typeof ( object );
2025-02-25 08:43:01 +03:00
if ( type is { IsArray : true })
2025-02-27 00:12:52 +03:00
{
2025-02-25 08:57:58 +03:00
elementType = type . GetElementType ();
2025-02-27 00:12:52 +03:00
Array values = Array . CreateInstance ( elementType , length . Value );
2025-02-25 17:09:04 +03:00
2025-02-27 00:12:52 +03:00
for ( int i = 0 ; i < length ; i ++)
{
values . SetValue ( ReadData ( reader , elementType , context ), i );
}
reader . ReadEndArray ();
return values ;
}
2025-02-25 08:43:01 +03:00
2025-02-27 00:12:52 +03:00
if ( typeof ( IList ). IsAssignableFrom ( type ))
elementType = type . GetGenericArguments ()[ 0 ];
else elementType = typeof ( object );
Type genericListType = typeof ( List <>). MakeGenericType ( elementType );
var list = ( IList ) Activator . CreateInstance ( genericListType , length );
2025-02-25 08:43:01 +03:00
for ( int i = 0 ; i < length ; i ++)
{
2025-02-27 00:12:52 +03:00
list . Add ( ReadData ( reader , elementType , context ));
2025-02-25 08:43:01 +03:00
}
2025-02-25 17:09:04 +03:00
reader . ReadEndArray ();
2025-02-25 08:43:01 +03:00
2025-02-25 17:09:04 +03:00
2025-02-27 00:12:52 +03:00
return list ;
return null ;
}
return null ;
2025-02-25 08:43:01 +03:00
}
2025-07-09 17:15:55 +03:00
public IDictionary ReadDictionary ( CborReader reader , Type type , IEndPointContext ? context )
2025-02-25 08:43:01 +03:00
{
var dictionaryInstance = ( Activator . CreateInstance ( type ) as IDictionary )!;
var length = reader . ReadStartArray ();
if ( length != null )
{
for ( int i = 0 ; i < length ; i ++)
{
var key = ReadData ( reader , type , context );
var value = ReadData ( reader , type , context );
dictionaryInstance . Add ( key , value );
}
}
return dictionaryInstance ;
}
2025-07-09 17:15:55 +03:00
public object ReadObject ( CborReader reader , Type type , IEndPointContext ? context )
2025-02-25 08:43:01 +03:00
{
2025-02-27 00:12:52 +03:00
if ( type == typeof ( object ))
{
return new PreParsedValue ( ReadList ( reader , null , context ) as List < object >);
}
2025-03-01 19:06:18 +03:00
if ( type . IsInterface )
{
var sharedShell = typeof ( SharedObjectShellShell <>). MakeGenericType ( type );
var so =
Activator . CreateInstance ( sharedShell ) as
ISharedObjectShell ;
if ( context != null )
so . EndPointContext = context ;
reader . ReadStartArray ();
var identifier = reader . ReadUInt64 ();
reader . ReadEndArray ();
2025-03-10 21:56:22 +03:00
so . Identifier = ComplexObjectIdentifier . FromFlat ( identifier );
2025-03-01 19:06:18 +03:00
return so . UniversalValue ;
}
2025-02-25 08:43:01 +03:00
var instance = Activator . CreateInstance ( type )!;
FillObject ( instance , type , reader , context );
return instance ;
}
2025-03-01 19:06:18 +03:00
private ISharedObjectShell ReadSharedObject ( CborReader reader , Type type , IEndPointContext ? context )
2025-02-25 08:43:01 +03:00
{
2025-03-01 19:06:18 +03:00
var sharedObject = ( Activator . CreateInstance ( type ) as ISharedObjectShell )!;
2025-02-27 00:12:52 +03:00
if ( context != null )
{
sharedObject . EndPointContext = context ;
}
2025-02-25 08:43:01 +03:00
FillObject ( sharedObject , type , reader , context );
return sharedObject ;
}
2025-07-09 17:15:55 +03:00
public void FillObject ( object obj , Type type , CborReader reader , IEndPointContext ? context )
2025-02-25 08:43:01 +03:00
{
2025-07-08 23:33:37 +03:00
var properties = GetAvailableProperties ( type );
2025-03-01 16:40:40 +03:00
var length = reader . ReadStartArray ();
2025-02-27 00:12:52 +03:00
try
{
#if TRACE
2025-03-01 19:06:18 +03:00
Console . WriteLine ( $"Reading list of {length} objects, {properties.Count} properties found" );
2025-02-27 00:12:52 +03:00
#endif
2025-02-25 08:43:01 +03:00
2025-02-27 00:12:52 +03:00
for ( var index = 0 ; index < length ; index ++)
{
var property = properties [ index ];
var value = ReadData ( reader , property . PropertyType , context );
property . SetValue ( obj , value );
}
2025-07-09 12:01:08 +03:00
if ( reader . PeekState () == CborReaderState . EndArray )
reader . ReadEndArray ();
2025-02-25 08:43:01 +03:00
}
2025-02-27 00:12:52 +03:00
catch ( Exception e )
{
Console . WriteLine ( e );
reader . ReadEndArray ();
2025-02-25 08:43:01 +03:00
2025-02-27 00:12:52 +03:00
throw ;
}
2025-02-25 08:43:01 +03:00
}
2025-02-27 00:12:52 +03:00
public static List < PropertyInfo > FilterProperties ( PropertyInfo [] properties )
2025-02-25 08:43:01 +03:00
{
var finalProperties = new List < PropertyInfo >( properties . Length );
2025-07-09 17:29:57 +03:00
for ( int i = 0 ; i < properties . Length ; i ++)
2025-02-25 08:43:01 +03:00
{
2025-07-09 17:29:57 +03:00
var property = properties [ i ];
if ( property is not { CanRead : true , CanWrite : true } || property . GetCustomAttribute < SerializationIgnoreAttribute >() != null )
continue ;
finalProperties . Add ( property );
2025-02-25 08:43:01 +03:00
}
return finalProperties ;
2025-02-24 12:48:31 +03:00
}
2025-07-03 18:12:04 +03:00
2025-07-08 23:33:37 +03:00
private List < PropertyInfo > GetAvailableProperties ( Type type )
2025-07-03 18:12:04 +03:00
{
if ( _propertiesCache . TryGetValue ( type , out var properties ))
{
return properties ;
}
2025-07-09 12:01:08 +03:00
2025-07-03 18:12:04 +03:00
var propertiesList = FilterProperties ( type . GetProperties ());
_propertiesCache [ type ] = propertiesList ;
return propertiesList ;
}
2025-02-24 12:48:31 +03:00
}
}