From 1c6059116f38cf1c929a48ef816c53366ed956d4 Mon Sep 17 00:00:00 2001
From: Dan <12039578+optimus-code@users.noreply.github.com>
Date: Mon, 19 Aug 2024 00:13:10 +0100
Subject: [PATCH] Experimental support for dynamic/object
---
RmSharp.Tests/Converters/Double.cs | 6 +-
RmSharp.Tests/Converters/Dynamic.cs | 41 ++++++++++++++
RmSharp.Tests/Converters/DynamicArray.cs | 55 +++++++++++++++++++
RmSharp.Tests/RmSharp.Tests.csproj | 16 ++----
RmSharp/Converters/DynamicConverter.cs | 39 +++++++++++++
RmSharp/Converters/RmConverterFactory.cs | 54 +++++++++++++++++-
RmSharp/Converters/Types/ArrayConverter.cs | 2 +-
.../Converters/Types/DictionaryConverter.cs | 4 +-
RmSharp/Converters/Types/ListConverter.cs | 2 +-
RmSharp/RmSharp.csproj | 2 +-
10 files changed, 199 insertions(+), 22 deletions(-)
create mode 100644 RmSharp.Tests/Converters/Dynamic.cs
create mode 100644 RmSharp.Tests/Converters/DynamicArray.cs
create mode 100644 RmSharp/Converters/DynamicConverter.cs
diff --git a/RmSharp.Tests/Converters/Double.cs b/RmSharp.Tests/Converters/Double.cs
index d525e4b..86c54ae 100644
--- a/RmSharp.Tests/Converters/Double.cs
+++ b/RmSharp.Tests/Converters/Double.cs
@@ -8,11 +8,7 @@ public class Double
{
private readonly byte[] _rubyMarshalData = new byte[]
{
- (byte)RubyMarshalToken.Double, // Token indicating a double
- 0x08, // Length of the string "1.2\0\0\0\0" (8 bytes total)
- 0x31, // '1' in ASCII
- 0x2E, // '.' in ASCII
- 0x32, // '2' in ASCII
+ ( byte ) RubyMarshalToken.Double, 0x08, 0x31, 0x2E, 0x32
};
private readonly double _expectedValue = 1.2;
diff --git a/RmSharp.Tests/Converters/Dynamic.cs b/RmSharp.Tests/Converters/Dynamic.cs
new file mode 100644
index 0000000..799153a
--- /dev/null
+++ b/RmSharp.Tests/Converters/Dynamic.cs
@@ -0,0 +1,41 @@
+using RmSharp.Converters;
+using RmSharp.Tokens;
+
+namespace RmSharp.Tests.Converters
+{
+ [TestClass]
+ public class Dynamic
+ {
+ private readonly byte[] _rubyMarshalData = new byte[] { ( byte ) RubyMarshalToken.Fixnum, 0x02, 0xD2, 0x04 };
+ private readonly dynamic _expectedValue = 1234;
+
+ [TestMethod]
+ public void Read( )
+ {
+ var converter = RmConverterFactory.GetConverter( _expectedValue.GetType( ) );
+ using ( var memoryStream = new MemoryStream( _rubyMarshalData ) )
+ using ( var reader = new BinaryReader( memoryStream ) )
+ {
+ var result = converter.Read( reader );
+
+ Assert.IsNotNull( result );
+ Assert.IsInstanceOfType( result, _expectedValue.GetType( ) );
+ Assert.AreEqual( _expectedValue, result );
+ }
+ }
+
+ [TestMethod]
+ public void Write( )
+ {
+ var converter = RmConverterFactory.GetConverter( _expectedValue.GetType( ) );
+ using ( var memoryStream = new MemoryStream( ) )
+ using ( var writer = new BinaryWriter( memoryStream ) )
+ {
+ converter.Write( writer, _expectedValue );
+
+ byte[] writtenData = memoryStream.ToArray( );
+ CollectionAssert.AreEqual( _rubyMarshalData, writtenData );
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/RmSharp.Tests/Converters/DynamicArray.cs b/RmSharp.Tests/Converters/DynamicArray.cs
new file mode 100644
index 0000000..0ae2b46
--- /dev/null
+++ b/RmSharp.Tests/Converters/DynamicArray.cs
@@ -0,0 +1,55 @@
+using RmSharp.Converters;
+using RmSharp.Tokens;
+
+namespace RmSharp.Tests.Converters
+{
+ [TestClass]
+ public class DyamicArray
+ {
+ private readonly byte[] _rubyMarshalData = new byte[] {
+
+ ( byte ) RubyMarshalToken.Array, 0x0A,
+ ( byte ) RubyMarshalToken.Fixnum, 0x02, 0xD2, 0x04,
+ ( byte ) RubyMarshalToken.String, 0x10, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64,
+ ( byte ) RubyMarshalToken.Fixnum, 0x02, 0xD2, 0x04,
+ ( byte ) RubyMarshalToken.Fixnum, 0x02, 0xD2, 0x04,
+ ( byte ) RubyMarshalToken.Double, 0x08, 0x31, 0x2E, 0x32
+ };
+
+ private readonly dynamic[] _expectedValue = [1234, "Hello World", 1234, 1234, 1.2];
+
+ [TestMethod]
+ public void Read( )
+ {
+ var converter = RmConverterFactory.GetConverter( _expectedValue.GetType() );
+ using ( var memoryStream = new MemoryStream( _rubyMarshalData ) )
+ using ( var reader = new BinaryReader( memoryStream ) )
+ {
+ var result = ( dynamic[] ) converter.Read( reader );
+
+ Assert.IsNotNull( result );
+ Assert.IsInstanceOfType( result, _expectedValue.GetType( ) );
+
+ for ( var i = 0; i < result.Length; i++ )
+ {
+ if ( result[i] != _expectedValue[i] )
+ Assert.Fail( $"Item at index '{i}' is not equal to '{_expectedValue[i]}' got '{result[i]}'." );
+ }
+ }
+ }
+
+ [TestMethod]
+ public void Write( )
+ {
+ var converter = RmConverterFactory.GetConverter( _expectedValue.GetType( ) );
+ using ( var memoryStream = new MemoryStream( ) )
+ using ( var writer = new BinaryWriter( memoryStream ) )
+ {
+ converter.Write( writer, _expectedValue );
+
+ byte[] writtenData = memoryStream.ToArray( );
+ CollectionAssert.AreEqual( _rubyMarshalData, writtenData );
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/RmSharp.Tests/RmSharp.Tests.csproj b/RmSharp.Tests/RmSharp.Tests.csproj
index bbb52fc..d9c55ed 100644
--- a/RmSharp.Tests/RmSharp.Tests.csproj
+++ b/RmSharp.Tests/RmSharp.Tests.csproj
@@ -9,6 +9,12 @@
true
+
+
+
+
+
+
@@ -24,14 +30,4 @@
-
-
-
-
-
-
- PreserveNewest
-
-
-
diff --git a/RmSharp/Converters/DynamicConverter.cs b/RmSharp/Converters/DynamicConverter.cs
new file mode 100644
index 0000000..e2f4c0a
--- /dev/null
+++ b/RmSharp/Converters/DynamicConverter.cs
@@ -0,0 +1,39 @@
+using RmSharp.Extensions;
+using RmSharp.Tokens;
+using System;
+using System.IO;
+
+namespace RmSharp.Converters
+{
+ public class DynamicConverter : RmConverter
+ {
+ public override object Read( BinaryReader reader )
+ {
+ // Peek the byte to find out a token to process
+ if ( reader.PeekByte( out var byteValue ) )
+ {
+ if ( Enum.IsDefined( typeof( RubyMarshalToken ), byteValue ) )
+ {
+ var tokenConverter = RmConverterFactory.GetConverter( ( RubyMarshalToken ) byteValue );
+
+ if ( tokenConverter != null )
+ {
+ return tokenConverter.Read( reader );
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public override void Write( BinaryWriter writer, object instance )
+ {
+ var converter = RmConverterFactory.GetConverter( instance.GetType() );
+
+ if ( converter != null )
+ {
+ converter.Write( writer, instance );
+ }
+ }
+ }
+}
diff --git a/RmSharp/Converters/RmConverterFactory.cs b/RmSharp/Converters/RmConverterFactory.cs
index f62c14b..aab7c08 100644
--- a/RmSharp/Converters/RmConverterFactory.cs
+++ b/RmSharp/Converters/RmConverterFactory.cs
@@ -1,7 +1,9 @@
using RmSharp.Converters.Types;
using RmSharp.Converters.Types.Basic;
+using RmSharp.Tokens;
using System;
using System.Collections.Generic;
+using System.Dynamic;
using System.Numerics;
using System.Text.RegularExpressions;
@@ -32,11 +34,58 @@ public static SymbolConverter SymbolConverter
{ typeof( ulong ), new UInt64Converter( ) },
};
+
+ private static readonly Dictionary _tokenMap =
+ new Dictionary
+ {
+ { RubyMarshalToken.Bignum, _typeConverters[typeof( BigInteger )]},
+ { RubyMarshalToken.True, _typeConverters[typeof( bool )]},
+ { RubyMarshalToken.False, _typeConverters[typeof( bool )]},
+ { RubyMarshalToken.Double, _typeConverters[typeof( double )]},
+ { RubyMarshalToken.Fixnum, _typeConverters[typeof( long )]},
+ { RubyMarshalToken.String, _typeConverters[typeof( string )]}
+ };
+
private static readonly Dictionary _classConverters = [];
+ private static readonly DynamicConverter _dynamicConverter = new( );
+
+ public static RmTypeConverter GetConverter( RubyMarshalToken token )
+ {
+ if ( _tokenMap.TryGetValue( token, out var converter ) )
+ return converter;
+
+ if ( token == RubyMarshalToken.Array )
+ {
+ return new ListConverter( typeof( List ) );
+ }
+ else if ( token == RubyMarshalToken.Hash )
+ {
+ return new DictionaryConverter( typeof( Dictionary ) );
+ }
+ else if ( token == RubyMarshalToken.Object )
+ {
+ // Handle this, it'll need to find the appropriate type based on name...
+
+ //if ( _classConverters.TryGetValue( type, out converter ) )
+ //{
+ // return converter;
+ //}
- public static RmTypeConverter GetConverter( Type type )
+ //converter = new ClassConverter( type );
+ //_classConverters.Add( type, converter );
+ //return converter;
+ }
+
+ return null;
+ }
+
+ public static RmConverter GetConverter( Type type )
{
- if ( _typeConverters.TryGetValue( type, out var converter ) )
+ if ( typeof( object ) == type || typeof( IDynamicMetaObjectProvider ).IsAssignableFrom( type ) )
+ {
+ return _dynamicConverter;
+ }
+ else if ( _typeConverters.TryGetValue( type, out var converter ) )
{
return converter;
}
@@ -63,6 +112,7 @@ public static RmTypeConverter GetConverter( Type type )
_classConverters.Add( type, converter );
return converter;
}
+
return null;
}
diff --git a/RmSharp/Converters/Types/ArrayConverter.cs b/RmSharp/Converters/Types/ArrayConverter.cs
index c42c827..fdffb41 100644
--- a/RmSharp/Converters/Types/ArrayConverter.cs
+++ b/RmSharp/Converters/Types/ArrayConverter.cs
@@ -8,7 +8,7 @@ namespace RmSharp.Converters.Types
{
public class ArrayConverter : RmTypeConverter
{
- private readonly RmTypeConverter _elementConverter;
+ private readonly RmConverter _elementConverter;
public ArrayConverter( Type type ) : base( type )
{
diff --git a/RmSharp/Converters/Types/DictionaryConverter.cs b/RmSharp/Converters/Types/DictionaryConverter.cs
index 10a334e..20cae46 100644
--- a/RmSharp/Converters/Types/DictionaryConverter.cs
+++ b/RmSharp/Converters/Types/DictionaryConverter.cs
@@ -8,8 +8,8 @@ namespace RmSharp.Converters.Types
{
public class DictionaryConverter : RmTypeConverter
{
- private readonly RmTypeConverter _keyConverter;
- private readonly RmTypeConverter _valueConverter;
+ private readonly RmConverter _keyConverter;
+ private readonly RmConverter _valueConverter;
public DictionaryConverter( Type type ) : base( type )
{
diff --git a/RmSharp/Converters/Types/ListConverter.cs b/RmSharp/Converters/Types/ListConverter.cs
index 88bebc1..31e84a8 100644
--- a/RmSharp/Converters/Types/ListConverter.cs
+++ b/RmSharp/Converters/Types/ListConverter.cs
@@ -10,7 +10,7 @@ namespace RmSharp.Converters.Types
{
public class ListConverter : RmTypeConverter
{
- private readonly RmTypeConverter _elementConverter;
+ private readonly RmConverter _elementConverter;
public ListConverter( Type type ) : base( type )
{
diff --git a/RmSharp/RmSharp.csproj b/RmSharp/RmSharp.csproj
index 5dc94da..bf8eee5 100644
--- a/RmSharp/RmSharp.csproj
+++ b/RmSharp/RmSharp.csproj
@@ -9,7 +9,7 @@
RmSharp
- 0.0.1
+ 0.0.2
optimus-code;HNIdesu
RmSharp
RmSharp is a .NET library for handling Ruby Marshal data structures, providing essential functionalities for reading, writing, and manipulating Ruby Marshal files.