forked from HNIdesu/RubyMarshal
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Experimental version, can read and write a Ruby Marshal list of class…
…es, needs more testing and more ruby token support as we go
- Loading branch information
1 parent
1c571b7
commit 3d3d300
Showing
18 changed files
with
430 additions
and
915 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,76 @@ | ||
### All marshal types | ||
| Token | Type | | ||
| --- | --- | | ||
| " | String | | ||
| [ | Array | | ||
| i | Fixnum/long | | ||
| 0 | Nil | | ||
| o | Object | | ||
| : | Symbol | | ||
| ; | Symbol Link | | ||
| I | Instance Variable | | ||
| T | True | | ||
| F | False | | ||
| f | Float | | ||
| { | Hash | | ||
| @ | Object References | | ||
| } | Default Hash | | ||
| / | Regexp | | ||
| l | Bignum | | ||
| S | Struct | | ||
| c | Class | | ||
| m | Module | | ||
| C | User Class | | ||
| e | Extended | | ||
| U | User Marshal | | ||
| d | Data | | ||
| u | User Defined | | ||
| e | Extended | | ||
|
||
### Not supported types | ||
| Token | Type | | ||
| --- | --- | | ||
| U | User Marshal | | ||
| d | Data | | ||
# RmSharp | ||
|
||
**RmSharp** is a C# .NET class library designed for deserializing and serializing Ruby Marshal files. It enables seamless translation between C# models and Ruby Marshal files, allowing you to work with Ruby data structures directly in your C# applications. | ||
|
||
This library is built upon the work done in [HNIdesu's RubyMarshal project](https://github.com/HNIdesu/RubyMarshal). Please note that not all Ruby Marshal tokens are supported in the current version, making it unsuitable for production use at this time. | ||
|
||
## Features | ||
|
||
- **Deserialize** Ruby Marshal files directly into C# objects. | ||
- **Serialize** C# objects into Ruby Marshal files. | ||
- Support for Arrays, basic types, and objects. | ||
- Attribute-based mapping to maintain Ruby naming conventions for classes and properties. | ||
|
||
## Installation | ||
|
||
You can install RmSharp via NuGet: | ||
|
||
```shell | ||
dotnet add package RmSharp | ||
``` | ||
|
||
## Usage | ||
|
||
### Deserializing Ruby Marshal Files | ||
|
||
You can deserialize a Ruby Marshal file into your C# model as shown below: | ||
|
||
```csharp | ||
using (var stream = File.OpenRead(file)) | ||
{ | ||
var instance = RmSerialiser.Deserialise<YourType>(stream); | ||
} | ||
``` | ||
|
||
### Serializing C# Objects to Ruby Marshal Files | ||
|
||
To serialize your C# object into a Ruby Marshal file: | ||
|
||
```csharp | ||
using (var stream = File.OpenWrite(file)) | ||
{ | ||
RmSerialiser.Serialise(stream, instance); | ||
} | ||
``` | ||
|
||
### Attribute-Based Mapping | ||
|
||
To ensure that Ruby naming conventions are maintained in your C# classes, you need to use the `[RmName]` attribute: | ||
|
||
```csharp | ||
[RmName("ModuleName::ClassName")] | ||
public class YourClass | ||
{ | ||
[RmName("ruby_name")] | ||
public string YourProperty { get; set; } | ||
} | ||
``` | ||
|
||
This will map your C# class and properties to the corresponding Ruby class and properties during serialization and deserialization. | ||
|
||
## Limitations | ||
|
||
- **Not Production-Ready**: This library is still in its early stages and is not ready for production use. | ||
- **Limited Token Support**: Currently, only a subset of Ruby Marshal tokens is supported, including arrays, basic types, and objects. | ||
|
||
## Credits | ||
|
||
This library is based on the work done in the [RubyMarshal project by HNIdesu](https://github.com/HNIdesu/RubyMarshal). We are deeply grateful for their contributions to the Ruby Marshal serialization format. | ||
|
||
## Contributing | ||
|
||
Contributions are welcome! Feel free to open issues or submit pull requests to improve the library. | ||
|
||
## License | ||
|
||
RmSharp is licensed under the MIT License. See the [LICENSE](LICENSE) file for more details. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
using System; | ||
|
||
namespace RmSharp.Attributes | ||
{ | ||
public class RmNameAttribute( string name ) : Attribute | ||
{ | ||
public string Name | ||
{ | ||
get; | ||
private set; | ||
} = name; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
using RmSharp.Attributes; | ||
using RmSharp.Exceptions; | ||
using RmSharp.Extensions; | ||
using RmSharp.Tokens; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Reflection; | ||
|
||
namespace RmSharp.Converters | ||
{ | ||
public class ClassConverter : RmTypeConverter | ||
{ | ||
private readonly List<string> _readSymbols = new List<string>(); | ||
private readonly List<string> _writeSymbols = new List<string>( ); | ||
private readonly RmNameAttribute _typeName; | ||
private readonly List<(PropertyInfo, RmNameAttribute)> _instanceVariables = []; | ||
private readonly SymbolConverter _symbolConverter = RmConverterFactory.SymbolConverter; | ||
|
||
public ClassConverter( Type type ) | ||
: base( type ) | ||
{ | ||
_typeName = type.GetCustomAttribute<RmNameAttribute>( ); | ||
|
||
if ( _typeName == null ) | ||
throw new RmException( $"No RmName attribute on type '{type.FullName}'." ); | ||
|
||
var properties = type.GetProperties( BindingFlags.Instance | BindingFlags.Public ); | ||
|
||
if ( properties == null || !properties.Any() ) | ||
throw new RmException( $"No properties accessible on type '{type.FullName}'." ); | ||
|
||
foreach ( var property in properties ) | ||
{ | ||
var nameAttribute = property.GetCustomAttribute<RmNameAttribute>( ); | ||
|
||
if ( nameAttribute == null ) | ||
continue; | ||
|
||
_instanceVariables.Add((property, nameAttribute)); | ||
} | ||
} | ||
|
||
public override object Read( BinaryReader reader ) | ||
{ | ||
return reader.ReadValue( ( token ) => | ||
{ | ||
var className = ( string ) _symbolConverter.Read( reader ); | ||
|
||
if ( className != _typeName.Name ) | ||
throw new RmException( $"Class name '{className}' does not match expected type '{_typeName.Name}'." ); | ||
|
||
var instance = Activator.CreateInstance( Type ); | ||
var count = reader.ReadFixNum<int>( ); | ||
|
||
for ( var i = 0; i < count; i++ ) | ||
{ | ||
var symbolName = ( string ) _symbolConverter.Read( reader ); | ||
|
||
var property = _instanceVariables | ||
.Where( i => symbolName == "@" + i.Item2.Name ) | ||
.Select( i => i.Item1 ) | ||
.FirstOrDefault( ); | ||
|
||
if ( property == null ) | ||
throw new RmException( $"Instance variable '{symbolName}' not found in type '{Type.FullName}'." ); | ||
|
||
var typeConverter = RmConverterFactory.GetConverter( property.PropertyType ); | ||
|
||
if ( typeConverter == null ) | ||
throw new RmException( $"No type converter found for '{property.PropertyType.FullName}'." ); | ||
|
||
object value = typeConverter.Read( reader ); | ||
property.SetValue( instance, value ); | ||
} | ||
|
||
return instance; | ||
}, RubyMarshalToken.Object ); | ||
} | ||
|
||
public override void Write( BinaryWriter writer, object instance ) | ||
{ | ||
writer.WriteValue( instance, RubyMarshalToken.Object, ( ) => | ||
{ | ||
_symbolConverter.Write( writer, _typeName.Name ); | ||
|
||
writer.WriteFixNum( _instanceVariables.Count ); | ||
|
||
foreach ( var instanceVariable in _instanceVariables ) | ||
{ | ||
var rubyName = "@" + instanceVariable.Item2.Name; | ||
|
||
_symbolConverter.Write( writer, rubyName ); | ||
|
||
var typeConverter = RmConverterFactory.GetConverter( instanceVariable.Item1.PropertyType ); | ||
|
||
if ( typeConverter == null ) | ||
throw new RmException( $"No type converter found for '{instanceVariable.Item1.PropertyType.FullName}'." ); | ||
|
||
typeConverter.Write( writer, instanceVariable.Item1.GetValue( instance ) ); | ||
} | ||
} ); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
using RmSharp.Exceptions; | ||
using RmSharp.Extensions; | ||
using RmSharp.Tokens; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
|
||
namespace RmSharp.Converters | ||
{ | ||
public class SymbolConverter : RmConverter | ||
{ | ||
private readonly List<string> _readSymbols = []; | ||
private readonly List<string> _writeSymbols = []; | ||
|
||
public override object Read( BinaryReader reader ) | ||
{ | ||
var token = reader.ReadToken( RubyMarshalToken.Symbol, RubyMarshalToken.SymbolLink ); | ||
var symbol = ""; | ||
|
||
if ( token == RubyMarshalToken.Symbol ) | ||
{ | ||
symbol = reader.ReadRubyString( false ); | ||
_readSymbols.Add( symbol ); | ||
} | ||
else | ||
{ | ||
var symbolID = reader.ReadFixNum<int>( ); | ||
|
||
if ( symbolID >= _readSymbols.Count ) | ||
throw new RmException( $"No valid Symbol Link '{symbolID}''." ); | ||
|
||
symbol = _readSymbols[symbolID]; | ||
} | ||
|
||
return symbol; | ||
} | ||
|
||
public override void Write( BinaryWriter writer, object instance ) | ||
{ | ||
var symbol = ( string ) instance; | ||
|
||
if ( HasWriteSymbol( symbol, out var id ) ) | ||
{ | ||
writer.Write( RubyMarshalToken.SymbolLink ); | ||
writer.WriteFixNum( id ); | ||
} | ||
else | ||
{ | ||
writer.Write( RubyMarshalToken.Symbol ); | ||
writer.WriteRubyString( symbol, false ); | ||
_writeSymbols.Add( symbol ); | ||
} | ||
} | ||
|
||
private bool HasWriteSymbol( string symbol, out int id ) | ||
{ | ||
id = _writeSymbols.IndexOf( symbol ); | ||
|
||
return id != -1; | ||
} | ||
|
||
public void Reset( ) | ||
{ | ||
_writeSymbols.Clear( ); | ||
_readSymbols.Clear( ); | ||
} | ||
} | ||
} |
Oops, something went wrong.