Skip to content

Instantly share code, notes, and snippets.

@chakrit
Created August 25, 2011 10:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chakrit/1170373 to your computer and use it in GitHub Desktop.
Save chakrit/1170373 to your computer and use it in GitHub Desktop.
WP7 almost-work binary serializer
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace MyApp.Models.Serialization
{
// TODO: Support for circular references
// TODO: Actually not thread-safe due to requiring the _context
// on .Write<T> and .Read<T>() not sure how to properly remove it yet.
public class Serializer : ISerializer
{
private delegate void TypeWriter(Context ctx);
private delegate object TypeReader(Context ctx);
private class TypeInfo
{
public Type Type;
public TypeWriter WriteAction;
public TypeReader ReadFunc;
}
private class Context
{
public ISerializer Serializer;
public Stream Stream;
public BinaryWriter Writer;
public BinaryReader Reader;
public Type PerceivedType;
public object Value;
}
private IDictionary<Type, TypeInfo> _infos;
private Context _context; // remove this stateful var
public Serializer()
{
_infos = new Dictionary<Type, TypeInfo>();
registerCommonTypes();
}
// primary serialization entry point
public void Serialize(Stream outgoing, object obj)
{
var ctx = new Context {
Serializer = this,
Writer = new BinaryWriter(outgoing),
Reader = null,
PerceivedType = obj.GetType(),
Value = obj,
};
serialize(_context = ctx, obj, obj.GetType());
ctx.Writer.Dispose();
_context = null;
}
public T Deserialize<T>(Stream incoming)
{
var ctx = new Context {
Serializer = this,
Writer = null,
Reader = new BinaryReader(incoming),
PerceivedType = typeof(T),
Value = null,
};
var result = deserialize(_context = ctx, ctx.PerceivedType);
ctx.Reader.Dispose();
_context = null;
return (T)result;
}
// object-level serialization interface
void ISerializer.Write<T>(T obj)
{ serialize(_context, obj, typeof(T)); }
T ISerializer.Read<T>()
{ return (T)deserialize(_context, typeof(T)); }
// internal recursive serialization access points
private void serialize(Context ctx, object obj, Type perceivedType)
{
var info = getTypeInfo(ctx.PerceivedType = perceivedType);
ctx.Value = obj;
info.WriteAction.Invoke(ctx);
}
private object deserialize(Context ctx, Type type)
{
var info = getTypeInfo(ctx.PerceivedType = type);
return info.ReadFunc.Invoke(ctx);
}
private void registerCommonTypes()
{
// simple value types
register(typeof(string),
c =>
{
if (c.Value == null) { // null flag
c.Writer.Write(true);
}
else {
c.Writer.Write(false);
c.Writer.Write((string)c.Value);
}
}, c => c.Reader.ReadBoolean() ? // null flag
null :
c.Reader.ReadString());
register(typeof(int),
c => c.Writer.Write((int)c.Value),
c => c.Reader.ReadInt32());
register(typeof(long),
c => c.Writer.Write((long)c.Value),
c => c.Reader.ReadInt64());
register(typeof(bool),
c => c.Writer.Write((bool)c.Value),
c => c.Reader.ReadBoolean());
register(typeof(DateTime),
c => c.Writer.Write(((DateTime)c.Value).Ticks),
c => new DateTime(c.Reader.ReadInt64()));
}
private void registerEnum(Type type)
{
register(type,
c => c.Writer.Write(c.Value.ToString()),
c => Enum.Parse(type, (string)c.Reader.ReadString(), false));
}
private void registerEnumerable(Type type)
{
var itemType = type.GetGenericArguments()[0];
register(type,
c => // writes
{
var items = ((IEnumerable)c.Value).Cast<object>();
var count = items.Count();
c.Writer.Write(count);
foreach (var item in items)
serialize(c, item, itemType);
}, c => // reads
{
var count = c.Reader.ReadInt32();
var items = Array.CreateInstance(itemType, count);
for (var i = 0; i < items.Length; i++)
items.SetValue(deserialize(c, itemType), i);
return items;
});
}
private void registerComplex(Type type)
{
// pre-extract ctor to speed things up a bit
var ctor = type.GetConstructor(new Type[] { typeof(ISerializer) });
if (ctor == null)
throw new PopCornException("Cannot serialize type: " + type +
" there is no parameterless constructor defined, or I can't access it.");
// define writers and readers which use the extracted information
register(type,
c => // writes
{
if (c.Value == null) {
c.Writer.Write(true); // null flag
}
else {
c.Writer.Write(false);
((ISerializable)c.Value).Write(c.Serializer);
}
}, c => // reads
{
if (c.Reader.ReadBoolean() == true) // null flag check
return null;
return ctor.Invoke(new object[] { c.Serializer });
});
}
private void register(Type type, TypeWriter writer, TypeReader reader)
{
_infos.Add(type, new TypeInfo {
Type = type,
WriteAction = writer,
ReadFunc = reader,
});
}
private TypeInfo getTypeInfo(Type type)
{
// TODO: Add Index to speed up search?
TypeInfo result;
if (_infos.TryGetValue(type, out result))
return result;
if (type.IsEnum)
registerEnum(type);
else if (type.IsGenericType &&
type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
registerEnumerable(type);
else
registerComplex(type);
return getTypeInfo(type);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment