/*
OpenNxSerialization Framework
Copyright (C) 2006 - 2008 "NeXtreme Innovations"
[The Next Xtreme in ingenuity]
This program is free software, distributed under the terms of
the GNU General Public License Version 2. See the License file
at the top of the source tree.
*/
//#define DIAGNOSTIC_SIGNATURE
using System;
using System.Collections;
using System.IO;
using System.Text;
using NeXtreme.OpenNxSerialization.IO;
using NeXtreme.OpenNxSerialization.Surrogates;
using System.Runtime.Serialization;
using System.Runtime.Remoting.Messaging;
using System.Diagnostics;
using NeXtreme.OpenNxSerialization.Native.IO;
namespace NeXtreme.OpenNxSerialization.Native.Formatters
{
/// <summary>
/// Serializes and deserializes an object, or an entire graph of connected objects, in binary format.
/// Uses the compact serialization framework to achieve better stream size and cpu time utlization.
/// </summary>
/// <remarks>
/// <para>
/// The basic idea behind space conservation is that every 'known type' is assigned a 2-byte
/// type handle by the system. Native .NET serialization stores the complete type information
/// with serialized object data, which includes assembly, version and tokens etc. Instead of storing
/// such information only a type handle is stored, which lets the system uniquely identify 'known types'.
///
/// A known type is a type that is registered with the <see cref="INxTypeSurrogateSelector"/>. Moreover
/// surrogate types take care of serializing only the required information. Information related to fields
/// and attributes is not stored as in case of native serialization.
/// </para>
/// <para>
/// From performance's perspective reflection is avoided by using surrogates for types. A type surrogate
/// is intimate with the internals of a type and therefore does not need reflection to guess
/// object schema.
/// </para>
/// For types that are not known to the system the formatter reverts to the default .NET
/// serialization scheme.
/// </remarks>
public class NxBinaryFormatter : IFormatter, IRemotingFormatter
{
private static NxBinaryFormatter msDefault;
private INxTypeSurrogateSelector mSurrogateSelector;
private NxSerializationContext mContext;
/// <summary>
/// Constructor
/// </summary>
public NxBinaryFormatter()
{
mSurrogateSelector = NxTypeSurrogateSelectorNative.Default;
this.mContext = new NxSerializationContext(mSurrogateSelector);
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="surrogateSelector"></param>
public NxBinaryFormatter(INxTypeSurrogateSelector surrogateSelector)
{
if (surrogateSelector == null)
throw new ArgumentNullException("surrogateSelector");
mSurrogateSelector = surrogateSelector;
this.mContext = new NxSerializationContext(mSurrogateSelector);
}
/// <summary>
/// returns the default instance of <see cref="NxBinaryFormatter"/>
/// </summary>
/// <value>The default instance of <see cref="NxBinaryFormatter"/></value>
public static NxBinaryFormatter Default
{
get
{
if (msDefault == null)
msDefault = new NxBinaryFormatter();
return msDefault;
}
}
/// <summary>
/// Get/Set the associated <see cref="INxTypeSurrogateSelector"/> instance.
/// </summary>
/// <value>The surrogate selector</value>
public INxTypeSurrogateSelector SurrogateSelector
{
get { return mSurrogateSelector; }
set { mSurrogateSelector = value; }
}
#region / IFormatter Members /
/// <summary>
/// Gets or sets the <see cref="SerializationBinder"/> that performs type lookups during deserialization
/// </summary>
public SerializationBinder Binder
{
get { return mContext.Binder; }
set { mContext.Binder = value; }
}
/// <summary>
/// Gets or sets the <see cref="StreamingContext"/> used for serialization and deserialization
/// </summary>
public StreamingContext Context
{
get { return mContext.StreamingContext; }
set { mContext.StreamingContext = value; }
}
/// <summary>
/// Gets or sets the SurrogateSelector used by the current formatter
/// </summary>
ISurrogateSelector IFormatter.SurrogateSelector
{
get { return mContext.NativeSurrogateSelector; }
set { mContext.NativeSurrogateSelector = value; }
}
/// <summary>
/// Serializes an object, or graph of objects with the given root to the provided stream.
/// </summary>
/// <param name="stream">The stream where the formatter puts the serialized data.
/// This stream can reference a variety of backing stores (such as files, network, memory, and so on)</param>
/// <param name="graph">The object, or root of the object graph, to serialize.
/// All child objects of this root object are automatically serialized</param>
public void Serialize(Stream stream, object graph)
{
using (NxBinaryWriter writer = new NxBinaryWriter(stream, Encoding.Default, mContext))
{
writer.WriteObject(graph);
}
}
/// <summary>
/// Deserializes the data on the provided stream and reconstitutes the graph of objects.
/// </summary>
/// <param name="stream">The stream that contains the data to deserialize</param>
/// <returns>The top object of the deserialized graph</returns>
public object Deserialize(Stream stream)
{
using (NxBinaryReader reader = new NxBinaryReader(stream, Encoding.Default, mContext))
{
return reader.ReadObject();
}
}
#endregion
#region / IRemotingFormatter Members /
/// <summary>
/// Serializes an object, or graph of objects with the given root to the provided stream.
/// </summary>
/// <param name="stream">The stream where the formatter puts the serialized data.
/// This stream can reference a variety of backing stores (such as files, network, memory, and so on)</param>
/// <param name="graph">The object, or root of the object graph, to serialize.
/// All child objects of this root object are automatically serialized</param>
/// <param name="headers">The array of <see cref="Header"/> objects to transmit with the graph specified by the graph parameter.
/// Can be a null reference </param>
public void Serialize(Stream stream, object graph, Header[] headers)
{
using (NxBinaryWriter writer = new NxBinaryWriter(stream, Encoding.Default, mContext))
{
writer.WriteObject(graph, headers);
}
}
/// <summary>
/// Deserializes the data on the provided stream and reconstitutes the graph of objects.
/// </summary>
/// <param name="stream">The stream that contains the data to deserialize</param>
/// <param name="handler">The delegate designed to handle <see cref="Header"/> objects. Can be a null reference</param>
/// <returns>The top object of the deserialized graph</returns>
public object Deserialize(Stream stream, HeaderHandler handler)
{
using (NxBinaryReader reader = new NxBinaryReader(stream, Encoding.Default, mContext))
{
return reader.ReadObject(handler);
}
}
#endregion
/// <summary>
/// Resets this instanc of <see cref="NxBinaryFormatter"/> object.
/// </summary>
/// <remarks>
/// Use this method to reset context state if you want to reuse the same <see cref="NxBinaryFormatter"/> instance
/// for different streams.
/// </remarks>
public void Reset()
{
mContext.Clear();
}
/// <summary>
/// Serializes an object, or graph of objects with the given root to the provided stream.
/// </summary>
/// <param name="stream">The stream where the formatter puts the serialized data.
/// This stream can reference a variety of backing stores (such as files, network, memory, and so on)</param>
/// <param name="graph">The object, or root of the object graph, to serialize.
/// All child objects of this root object are automatically serialized</param>
/// <param name="type">Serializes the object as an instance of type <paramref name="type"/></param>
public void SerializeAs(Stream stream, object graph, Type type)
{
using (NxBinaryWriter writer = new NxBinaryWriter(stream, Encoding.Default, mContext))
{
writer.WriteObjectAs(graph, type);
}
}
/// <summary>
/// Serializes an object, or graph of objects with the given root to the provided stream.
/// </summary>
/// <param name="stream">The stream where the formatter puts the serialized data.
/// This stream can reference a variety of backing stores (such as files, network, memory, and so on)</param>
/// <param name="graph">The object, or root of the object graph, to serialize.
/// All child objects of this root object are automatically serialized</param>
/// <param name="type">Serializes the object as an instance of type <paramref name="type"/></param>
/// <param name="headers">The array of <see cref="Header"/> objects to transmit with the graph specified by the graph parameter.
/// Can be a null reference </param>
public void SerializeAs(Stream stream, object graph, Type type, Header[] headers)
{
using (NxBinaryWriter writer = new NxBinaryWriter(stream, Encoding.Default, mContext))
{
writer.WriteObjectAs(graph, type, headers);
}
}
/// <summary>
/// Deserializes the data on the provided stream and reconstitutes the graph of objects.
/// </summary>
/// <param name="stream">The stream that contains the data to deserialize</param>
/// <param name="type">Deserializes the object as an instance of type <paramref name="type"/></param>
/// <returns>The top object of the deserialized graph</returns>
public object DeserializeAs(Stream stream, Type type)
{
using (NxBinaryReader reader = new NxBinaryReader(stream, Encoding.Default, mContext))
{
return reader.ReadObjectAs(type);
}
}
/// <summary>
/// Deserializes the data on the provided stream and reconstitutes the graph of objects.
/// </summary>
/// <param name="stream">The stream that contains the data to deserialize</param>
/// <param name="type">Deserializes the object as an instance of type <paramref name="type"/></param>
/// <param name="handler">The delegate designed to handle <see cref="Header"/> objects. Can be a null reference</param>
/// <returns>The top object of the deserialized graph</returns>
public object DeserializeAs(Stream stream, Type type, HeaderHandler handler)
{
using (NxBinaryReader reader = new NxBinaryReader(stream, Encoding.Default, mContext))
{
return reader.ReadObjectAs(type, handler);
}
}
/// <summary>
/// Serializes an object, or graph of objects with the given root to the provided stream.
/// </summary>
/// <param name="stream">The stream where the formatter puts the serialized data.
/// This stream can reference a variety of backing stores (such as files, network, memory, and so on)</param>
/// <param name="graph">The object, or root of the object graph, to serialize.
/// All child objects of this root object are automatically serialized</param>
/// <param name="context">The context to use for serialization of this root object</param>
private void Serialize(Stream stream, object graph, NxSerializationContext context)
{
using (NxBinaryWriter writer = new NxBinaryWriter(stream, Encoding.Default, context))
{
writer.WriteObject(graph);
}
}
/// <summary>
/// Deserializes the data on the provided stream and reconstitutes the graph of objects.
/// </summary>
/// <param name="stream">The stream that contains the data to deserialize</param>
/// <param name="context">The context to use for deserialization of this root object</param>
/// <returns>The top object of the deserialized graph</returns>
private object Deserialize(Stream stream, NxSerializationContext context)
{
using (NxBinaryReader reader = new NxBinaryReader(stream, Encoding.Default, context))
{
return reader.ReadObject();
}
}
/// <summary>
/// Serializes an object, or graph of objects with the given root to the provided stream.
/// </summary>
/// <param name="graph">The object, or root of the object graph, to serialize.
/// All child objects of this root object are automatically serialized</param>
/// <returns>Serialized byte array</returns>
public byte[] ToByteArray(object graph)
{
using (MemoryStream stream = new MemoryStream())
{
Serialize(stream, graph);
return stream.ToArray();
}
}
/// <summary>
/// Deserializes the data on the provided stream and reconstitutes the graph of objects.
/// </summary>
/// <param name="buffer">binary representation of the object</param>
/// <returns>The byte array to deserialize</returns>
public object FromByteArray(byte[] buffer)
{
using (MemoryStream stream = new MemoryStream(buffer))
{
return Deserialize(stream);
}
}
}
}