678 lines
12 KiB
C#
678 lines
12 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Collections;
|
|
using System.Diagnostics;
|
|
//using System.Globalization;
|
|
//using System.ComponentModel;
|
|
using System.Runtime.Serialization;
|
|
|
|
namespace lib
|
|
{
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public class VersionFormatter: IFormatter
|
|
{
|
|
public enum ETypes
|
|
{
|
|
Array,
|
|
Int32,
|
|
Ref,
|
|
Object,
|
|
EndObject,
|
|
Single,
|
|
Double,
|
|
Char,
|
|
String,
|
|
Boolean,
|
|
EndStream,
|
|
}
|
|
|
|
|
|
public VersionFormatter()
|
|
{
|
|
//
|
|
// TODO: Add constructor logic here
|
|
//
|
|
}
|
|
|
|
|
|
#region Useless
|
|
public ISurrogateSelector SurrogateSelector
|
|
{
|
|
get
|
|
{
|
|
return null;
|
|
}
|
|
|
|
set
|
|
{
|
|
}
|
|
}
|
|
|
|
public SerializationBinder Binder
|
|
{
|
|
get
|
|
{
|
|
return null;
|
|
}
|
|
|
|
set
|
|
{
|
|
}
|
|
}
|
|
|
|
public StreamingContext Context
|
|
{
|
|
get
|
|
{
|
|
return new StreamingContext();
|
|
}
|
|
|
|
set
|
|
{
|
|
}
|
|
}
|
|
#endregion Useless
|
|
|
|
Queue m_objectsToBeDeserialized = new Queue();
|
|
Hashtable m_alreadyDeserialzied = new Hashtable();
|
|
//int m_GUID = 0;
|
|
|
|
#region Serialize
|
|
public void Serialize( Stream stream, object obj )
|
|
{
|
|
//Default is 4k
|
|
//BufferedStream bufStream = new BufferedStream( stream );
|
|
|
|
BinaryWriter writer = new BinaryWriter( stream );
|
|
|
|
writeObject( writer, obj );
|
|
|
|
while( m_objectsToBeDeserialized.Count != 0 )
|
|
{
|
|
object objToDes = m_objectsToBeDeserialized.Dequeue();
|
|
|
|
writeObject( writer, objToDes );
|
|
}
|
|
|
|
writer.Write( (char)ETypes.EndStream );
|
|
}
|
|
|
|
void writeRefAndSched( BinaryWriter writer, object obj )
|
|
{
|
|
//if( m_alreadyDeserialzied[ obj.GetType().GetArrayRank(
|
|
|
|
if( obj == null )
|
|
{
|
|
writer.Write( 0 );
|
|
return;
|
|
}
|
|
|
|
|
|
//Now write the address.
|
|
//Bad bad. Need to do this correctly.
|
|
int objRef = obj.GetHashCode();
|
|
writer.Write( objRef );
|
|
|
|
if( m_alreadyDeserialzied[obj] == null )
|
|
{
|
|
m_alreadyDeserialzied[obj] = obj;
|
|
m_objectsToBeDeserialized.Enqueue( obj );
|
|
}
|
|
}
|
|
|
|
void dispatchWrite( BinaryWriter writer, object parentObj, FieldInfo fi )
|
|
{
|
|
string typeName = fi.FieldType.Name;
|
|
|
|
string name = fi.Name;
|
|
|
|
if( fi.IsNotSerialized )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( fi.FieldType.IsArray )
|
|
{
|
|
writer.Write( (char)ETypes.Array );
|
|
writer.Write( name.GetHashCode() );
|
|
|
|
writeArray( writer, (Array)fi.GetValue( parentObj ) );
|
|
}
|
|
else if( ( fi.FieldType.IsClass || fi.FieldType.IsInterface ) && typeName != "String" )
|
|
{
|
|
writer.Write( (char)ETypes.Ref );
|
|
writer.Write( name.GetHashCode() );
|
|
|
|
writeRefAndSched( writer, fi.GetValue( parentObj ) );
|
|
}
|
|
else if( fi.FieldType.IsEnum )
|
|
{
|
|
writer.Write( (char)ETypes.Int32 );
|
|
writer.Write( name.GetHashCode() );
|
|
|
|
write( writer, Convert.ToInt32( fi.GetValue( parentObj ) ) );
|
|
}
|
|
else
|
|
{
|
|
switch( typeName )
|
|
{
|
|
case "Int32":
|
|
writer.Write( (char)ETypes.Int32 );
|
|
writer.Write( name.GetHashCode() );
|
|
|
|
write( writer, Convert.ToInt32( fi.GetValue( parentObj ) ) );
|
|
break;
|
|
case "Single":
|
|
writer.Write( (char)ETypes.Single );
|
|
writer.Write( name.GetHashCode() );
|
|
|
|
write( writer, Convert.ToSingle( fi.GetValue( parentObj ) ) );
|
|
break;
|
|
case "Double":
|
|
writer.Write( (char)ETypes.Double );
|
|
writer.Write( name.GetHashCode() );
|
|
|
|
write( writer, Convert.ToDouble( fi.GetValue( parentObj ) ) );
|
|
break;
|
|
case "Char":
|
|
writer.Write( (char)ETypes.Char );
|
|
writer.Write( name.GetHashCode() );
|
|
|
|
write( writer, Convert.ToChar( fi.GetValue( parentObj ) ) );
|
|
break;
|
|
case "String":
|
|
writer.Write( (char)ETypes.String );
|
|
writer.Write( name.GetHashCode() );
|
|
|
|
write( writer, Convert.ToString( fi.GetValue( parentObj ) ) );
|
|
break;
|
|
case "Boolean":
|
|
writer.Write( (char)ETypes.Boolean );
|
|
writer.Write( name.GetHashCode() );
|
|
|
|
writer.Write( Convert.ToBoolean( fi.GetValue( parentObj ) ) );
|
|
break;
|
|
default:
|
|
Console.WriteLine( "VersionFormatter does not understand type " + typeName );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void writeArray( BinaryWriter writer, Array array )
|
|
{
|
|
if( array == null )
|
|
{
|
|
writer.Write( (int)-1 );
|
|
return;
|
|
}
|
|
|
|
writer.Write( array.Length );
|
|
|
|
foreach( object obj in array )
|
|
{
|
|
writeRefAndSched( writer, obj );
|
|
}
|
|
}
|
|
|
|
void getAllFields( object obj, ArrayList list )
|
|
{
|
|
Type t = obj.GetType();
|
|
|
|
while( t != null )
|
|
{
|
|
FieldInfo[] fiArr = t.GetFields( BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly );
|
|
list.AddRange( fiArr );
|
|
|
|
t = t.BaseType;
|
|
}
|
|
}
|
|
|
|
|
|
void writeObject( BinaryWriter writer, object obj )
|
|
{
|
|
Type objType = obj.GetType();
|
|
|
|
writer.Write( (char)ETypes.Object );
|
|
writer.Write( objType.FullName );
|
|
|
|
int objRef = obj.GetHashCode();
|
|
writer.Write( objRef );
|
|
|
|
ArrayList list = new ArrayList();
|
|
|
|
getAllFields( obj, list );
|
|
|
|
foreach( FieldInfo fi in list )
|
|
{
|
|
dispatchWrite( writer, obj, fi );
|
|
}
|
|
|
|
writer.Write( (char)ETypes.EndObject );
|
|
}
|
|
|
|
void write<TType>( BinaryWriter wr, TType val )
|
|
{
|
|
//wr.Write( val );
|
|
}
|
|
|
|
/*
|
|
void writeInt( BinaryWriter writer, int val )
|
|
{
|
|
writer.Write( val );
|
|
}
|
|
|
|
void writeSingle( BinaryWriter writer, float val )
|
|
{
|
|
writer.Write( val );
|
|
}
|
|
|
|
void writeDouble( BinaryWriter writer, double val )
|
|
{
|
|
writer.Write( val );
|
|
}
|
|
|
|
void writeChar( BinaryWriter writer, char val )
|
|
{
|
|
writer.Write( val );
|
|
}
|
|
|
|
void writeString( BinaryWriter writer, string val )
|
|
{
|
|
writer.Write( val );
|
|
}
|
|
|
|
void writeBool( BinaryWriter writer, bool val )
|
|
{
|
|
writer.Write( val );
|
|
}
|
|
*/
|
|
#endregion Serialize
|
|
|
|
|
|
#region Deserialize
|
|
|
|
class Fixup
|
|
{
|
|
public Fixup( int guid, object obj, FieldInfo fi )
|
|
{
|
|
m_guid = guid;
|
|
m_obj = obj;
|
|
m_fi = fi;
|
|
}
|
|
|
|
public Fixup( int guid, object obj, int index )
|
|
{
|
|
m_guid = guid;
|
|
m_obj = obj;
|
|
m_index = index;
|
|
}
|
|
|
|
public readonly int m_guid = 0;
|
|
public readonly object m_obj = null;
|
|
|
|
public readonly FieldInfo m_fi = null;
|
|
public readonly int m_index= -1;
|
|
|
|
}
|
|
|
|
Hashtable m_mapGUIDToObject = new Hashtable();
|
|
ArrayList m_fixupList = new ArrayList();
|
|
|
|
ArrayList m_desObjects = new ArrayList();
|
|
|
|
public object Deserialize( Stream stream )
|
|
{
|
|
BinaryReader reader = new BinaryReader( stream );
|
|
|
|
object objRoot = null;
|
|
|
|
//Read in the first object.
|
|
{
|
|
ETypes type = (ETypes)reader.ReadChar();
|
|
|
|
Debug.Assert( type == ETypes.Object );
|
|
|
|
objRoot = readObject( reader );
|
|
|
|
m_desObjects.Add( objRoot );
|
|
}
|
|
|
|
bool readObjects = true;
|
|
|
|
while( readObjects )
|
|
{
|
|
ETypes type = (ETypes)reader.ReadChar();
|
|
|
|
Debug.Assert( type == ETypes.Object || type == ETypes.EndStream );
|
|
|
|
if( type == ETypes.Object )
|
|
{
|
|
object obj = readObject( reader );
|
|
|
|
m_desObjects.Add( obj );
|
|
}
|
|
else
|
|
{
|
|
Debug.Assert( type == ETypes.EndStream );
|
|
|
|
readObjects = false;
|
|
}
|
|
}
|
|
|
|
foreach( Fixup fu in m_fixupList )
|
|
{
|
|
//Fixup fix = m_fixups[
|
|
|
|
object obj = m_mapGUIDToObject[ fu.m_guid ];
|
|
|
|
if( obj != null )
|
|
{
|
|
if( fu.m_fi != null )
|
|
{
|
|
fu.m_fi.SetValue( fu.m_obj, obj );
|
|
}
|
|
else
|
|
{
|
|
Debug.Assert( fu.m_index >= 0 );
|
|
|
|
object []array = (object [])fu.m_obj;
|
|
|
|
array[fu.m_index] = obj;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine( "Obj to ref is null." );
|
|
}
|
|
}
|
|
|
|
foreach( object obj in m_desObjects )
|
|
{
|
|
if( typeof( IDeserializationCallback ).IsAssignableFrom( obj.GetType() ) )
|
|
{
|
|
IDeserializationCallback desCB = (IDeserializationCallback)obj;
|
|
|
|
if( desCB != null )
|
|
{
|
|
desCB.OnDeserialization( this );
|
|
}
|
|
}
|
|
}
|
|
|
|
return objRoot;
|
|
}
|
|
|
|
|
|
|
|
bool dispatchRead( BinaryReader reader, object obj, Hashtable ht )
|
|
{
|
|
|
|
//Read the type
|
|
ETypes type = (ETypes)reader.ReadChar();
|
|
|
|
if( type == ETypes.EndObject )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int nameHash = reader.ReadInt32();
|
|
|
|
FieldInfo fi = (FieldInfo)ht[ nameHash ];
|
|
|
|
if( fi == null )
|
|
{
|
|
Console.WriteLine( "Field no longer exists" );
|
|
}
|
|
|
|
try
|
|
{
|
|
switch( type )
|
|
{
|
|
case ETypes.Array:
|
|
readArray( reader, obj, fi );
|
|
break;
|
|
case ETypes.Int32:
|
|
readInt( reader, obj, fi );
|
|
break;
|
|
case ETypes.Single:
|
|
readSingle( reader, obj, fi );
|
|
break;
|
|
case ETypes.Double:
|
|
readDouble( reader, obj, fi );
|
|
break;
|
|
case ETypes.Char:
|
|
readChar( reader, obj, fi );
|
|
break;
|
|
case ETypes.Boolean:
|
|
readBool( reader, obj, fi );
|
|
break;
|
|
case ETypes.String:
|
|
readString( reader, obj, fi );
|
|
break;
|
|
case ETypes.Ref:
|
|
readRef( reader, obj, fi );
|
|
break;
|
|
case ETypes.Object:
|
|
readObject( reader );
|
|
break;
|
|
default:
|
|
Debug.Fail( "Unknown type on read." );
|
|
break;
|
|
}
|
|
}
|
|
catch( Exception ex )
|
|
{
|
|
Console.WriteLine( "Exception: " + ex.Message );
|
|
Console.WriteLine( "Stack: " + ex.StackTrace );
|
|
}
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
object createObject( string objTypeName )
|
|
{
|
|
Assembly[] ass = AppDomain.CurrentDomain.GetAssemblies();
|
|
|
|
foreach( Assembly a in ass )
|
|
{
|
|
Type t = a.GetType( objTypeName );
|
|
|
|
if( t != null )
|
|
{
|
|
object obj = FormatterServices.GetUninitializedObject( t );
|
|
|
|
if( obj != null )
|
|
{
|
|
return obj;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
object readObject( BinaryReader reader )
|
|
{
|
|
//ETypes type = (ETypes)reader.ReadChar();
|
|
|
|
//Debug.Assert( type == ETypes.Object, "Expecting type Object" );
|
|
|
|
string objTypeName = reader.ReadString();
|
|
int objGUID = reader.ReadInt32();
|
|
|
|
try
|
|
{
|
|
object obj = createObject( objTypeName );
|
|
|
|
m_mapGUIDToObject[objGUID] = obj;
|
|
|
|
ArrayList list = new ArrayList();
|
|
Hashtable ht = new Hashtable();
|
|
|
|
if( obj != null )
|
|
{
|
|
getAllFields( obj, list );
|
|
|
|
foreach( FieldInfo fi in list )
|
|
{
|
|
ht[fi.Name.GetHashCode()] = fi;
|
|
}
|
|
}
|
|
|
|
while( dispatchRead( reader, obj, ht ) )
|
|
{
|
|
}
|
|
|
|
return obj;
|
|
}
|
|
catch( Exception ex )
|
|
{
|
|
Console.WriteLine( "Exception: " + ex.Message );
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
void readArray( BinaryReader reader, object obj, FieldInfo fi )
|
|
{
|
|
int length = reader.ReadInt32();
|
|
|
|
if( length < 0 )
|
|
{
|
|
if( fi == null )
|
|
return;
|
|
|
|
fi.SetValue( obj, null );
|
|
|
|
return;
|
|
}
|
|
|
|
object[] array = new object[length];
|
|
|
|
if( fi != null )
|
|
{
|
|
fi.SetValue( obj, array );
|
|
}
|
|
|
|
for( int i = 0; i < length; ++i )
|
|
{
|
|
int val = reader.ReadInt32();
|
|
|
|
//m_fixups[ val ] = new Fixup( obj, fi );
|
|
|
|
if( fi != null )
|
|
{
|
|
m_fixupList.Add( new Fixup( val, array, i ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
void readRef( BinaryReader reader, object obj, FieldInfo fi )
|
|
{
|
|
int val = reader.ReadInt32();
|
|
|
|
//m_fixups[ val ] = new Fixup( obj, fi );
|
|
|
|
m_fixupList.Add( new Fixup( val, obj, fi ) );
|
|
}
|
|
|
|
void readInt( BinaryReader reader, object obj, FieldInfo fi )
|
|
{
|
|
int val = reader.ReadInt32();
|
|
|
|
if( fi == null )
|
|
return;
|
|
|
|
if( !fi.FieldType.IsEnum )
|
|
{
|
|
fi.SetValue( obj, val );
|
|
}
|
|
else
|
|
{
|
|
object enumVal = Enum.Parse( fi.FieldType, val.ToString() );
|
|
fi.SetValue( obj, Convert.ChangeType( enumVal, fi.FieldType ) );
|
|
}
|
|
|
|
}
|
|
|
|
void readSingle( BinaryReader reader, object obj, FieldInfo fi )
|
|
{
|
|
float val = reader.ReadSingle();
|
|
|
|
if( fi == null )
|
|
return;
|
|
|
|
fi.SetValue( obj, val );
|
|
}
|
|
|
|
void readDouble( BinaryReader reader, object obj, FieldInfo fi )
|
|
{
|
|
double val = reader.ReadDouble();
|
|
|
|
if( fi == null )
|
|
return;
|
|
|
|
fi.SetValue( obj, val );
|
|
}
|
|
|
|
void readChar( BinaryReader reader, object obj, FieldInfo fi )
|
|
{
|
|
char val = reader.ReadChar();
|
|
|
|
if( fi == null )
|
|
return;
|
|
|
|
fi.SetValue( obj, val );
|
|
}
|
|
|
|
void readString( BinaryReader reader, object obj, FieldInfo fi )
|
|
{
|
|
string val = reader.ReadString();
|
|
|
|
if( fi == null )
|
|
return;
|
|
|
|
fi.SetValue( obj, val );
|
|
}
|
|
|
|
void readBool( BinaryReader reader, object obj, FieldInfo fi )
|
|
{
|
|
bool val = reader.ReadBoolean();
|
|
|
|
if( fi == null )
|
|
return;
|
|
|
|
fi.SetValue( obj, val );
|
|
}
|
|
|
|
#endregion Deserialize
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|