3c1f479b9d
Former-commit-id: 806294f5ded97629b74c85c09952f2a74fe182d9
1059 lines
26 KiB
C#
1059 lines
26 KiB
C#
//
|
|
// System.Web.UI.ObjectStateFormatter
|
|
//
|
|
// Authors:
|
|
// Ben Maurer (bmaurer@users.sourceforge.net)
|
|
// Gonzalo Paniagua (gonzalo@ximian.com)
|
|
//
|
|
// (C) 2003 Ben Maurer
|
|
// (c) Copyright 2004-2010 Novell, Inc. (http://www.novell.com)
|
|
//
|
|
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
|
|
//#define TRACE
|
|
|
|
using System.Collections;
|
|
using System.ComponentModel;
|
|
using System.Globalization;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Runtime.Serialization.Formatters.Binary;
|
|
using System.Runtime.Serialization;
|
|
using System.Text;
|
|
using System.Web.UI.WebControls;
|
|
using System.Web.Util;
|
|
using System.Diagnostics;
|
|
using System.Web.Configuration;
|
|
|
|
namespace System.Web.UI
|
|
{
|
|
public sealed class ObjectStateFormatter : IFormatter, IStateFormatter
|
|
{
|
|
const ushort SERIALIZED_STREAM_MAGIC = 0x01FF;
|
|
|
|
Page page;
|
|
MachineKeySection section;
|
|
|
|
public ObjectStateFormatter ()
|
|
{
|
|
}
|
|
|
|
internal ObjectStateFormatter (Page page)
|
|
{
|
|
this.page = page;
|
|
}
|
|
|
|
bool EnableMac {
|
|
get {
|
|
return (page == null) ? (section != null) : page.EnableViewStateMac;
|
|
}
|
|
}
|
|
|
|
bool NeedViewStateEncryption {
|
|
get {
|
|
return (page == null) ? false : page.NeedViewStateEncryption;
|
|
}
|
|
}
|
|
|
|
internal MachineKeySection Section {
|
|
get {
|
|
if (section == null)
|
|
section = (MachineKeySection) WebConfigurationManager.GetWebApplicationSection ("system.web/machineKey");
|
|
return section;
|
|
}
|
|
set {
|
|
section = value;
|
|
}
|
|
}
|
|
|
|
// There's no need to implement encryption support in this overload. Encryption is
|
|
// performed only when ObjectStateFormatter is created in the Page context, and that
|
|
// can happen only internally to System.Web. Since System.Web doesn't use this
|
|
// overload, the encryption code in here would be effectively dead.
|
|
public object Deserialize (Stream inputStream)
|
|
{
|
|
if (inputStream == null)
|
|
throw new ArgumentNullException ("inputStream");
|
|
|
|
BinaryReader reader = new BinaryReader (inputStream);
|
|
short magic = reader.ReadInt16 ();
|
|
if (magic != SERIALIZED_STREAM_MAGIC)
|
|
throw new ArgumentException ("The serialized data is invalid");
|
|
|
|
return DeserializeObject (reader);
|
|
}
|
|
|
|
public object Deserialize (string inputString)
|
|
{
|
|
if (inputString == null)
|
|
throw new ArgumentNullException ("inputString");
|
|
if (inputString.Length == 0)
|
|
throw new ArgumentNullException ("inputString");
|
|
|
|
byte [] data = Convert.FromBase64String (inputString);
|
|
if (data == null || (data.Length) == 0)
|
|
throw new ArgumentNullException ("inputString");
|
|
|
|
if (NeedViewStateEncryption) {
|
|
if (EnableMac) {
|
|
data = MachineKeySectionUtils.VerifyDecrypt (Section, data);
|
|
} else {
|
|
data = MachineKeySectionUtils.Decrypt (Section, data);
|
|
}
|
|
} else if (EnableMac) {
|
|
data = MachineKeySectionUtils.Verify (Section, data);
|
|
}
|
|
|
|
if (data == null)
|
|
throw new HttpException ("Unable to validate data.");
|
|
|
|
using (MemoryStream ms = new MemoryStream (data)) {
|
|
return Deserialize (ms);
|
|
}
|
|
}
|
|
|
|
public string Serialize (object stateGraph)
|
|
{
|
|
if (stateGraph == null)
|
|
return String.Empty;
|
|
|
|
byte[] data = null;
|
|
using (MemoryStream ms = new MemoryStream ()) {
|
|
Serialize (ms, stateGraph);
|
|
data = ms.GetBuffer ();
|
|
}
|
|
|
|
if (NeedViewStateEncryption) {
|
|
if (EnableMac) {
|
|
data = MachineKeySectionUtils.EncryptSign (Section, data);
|
|
} else {
|
|
data = MachineKeySectionUtils.Encrypt (Section, data);
|
|
}
|
|
} else if (EnableMac) {
|
|
data = MachineKeySectionUtils.Sign (Section, data);
|
|
}
|
|
|
|
return Convert.ToBase64String (data, 0, data.Length);
|
|
}
|
|
|
|
// There's no need to implement encryption support in this overload. Encryption is
|
|
// performed only when ObjectStateFormatter is created in the Page context, and that
|
|
// can happen only internally to System.Web. Since System.Web doesn't use this
|
|
// overload, the encryption code in here would be effectively dead.
|
|
public void Serialize (Stream outputStream, object stateGraph)
|
|
{
|
|
if (outputStream == null)
|
|
throw new ArgumentNullException ("outputStream");
|
|
|
|
if (stateGraph == null)
|
|
throw new ArgumentNullException ("stateGraph");
|
|
|
|
BinaryWriter writer = new BinaryWriter (outputStream);
|
|
writer.Write (SERIALIZED_STREAM_MAGIC);
|
|
|
|
SerializeValue (writer, stateGraph);
|
|
}
|
|
|
|
void SerializeValue (BinaryWriter w, object o)
|
|
{
|
|
ObjectFormatter.WriteObject (w, o, new WriterContext ());
|
|
}
|
|
|
|
object DeserializeObject (BinaryReader r)
|
|
{
|
|
return ObjectFormatter.ReadObject (r, new ReaderContext ());
|
|
}
|
|
|
|
#region IFormatter
|
|
|
|
object IFormatter.Deserialize (Stream serializationStream)
|
|
{
|
|
return Deserialize (serializationStream);
|
|
}
|
|
|
|
void IFormatter.Serialize (Stream serializationStream, object stateGraph)
|
|
{
|
|
Serialize (serializationStream, stateGraph);
|
|
}
|
|
|
|
SerializationBinder IFormatter.Binder {
|
|
get { return null; }
|
|
set { }
|
|
}
|
|
|
|
StreamingContext IFormatter.Context {
|
|
get { return new StreamingContext (StreamingContextStates.All); }
|
|
set { }
|
|
}
|
|
|
|
ISurrogateSelector IFormatter.SurrogateSelector {
|
|
get { return null; }
|
|
set { }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Object Readers/Writers
|
|
|
|
sealed class WriterContext
|
|
{
|
|
Hashtable cache;
|
|
short nextKey = 0;
|
|
short key = 0;
|
|
|
|
public short Key {
|
|
get { return key; }
|
|
}
|
|
|
|
public bool RegisterCache (object o)
|
|
{
|
|
if (nextKey == short.MaxValue)
|
|
return false;
|
|
|
|
if (cache == null) {
|
|
cache = new Hashtable ();
|
|
cache.Add (o, key = nextKey++);
|
|
return false;
|
|
}
|
|
|
|
object posKey = cache [o];
|
|
if (posKey == null) {
|
|
cache.Add (o, key = nextKey++);
|
|
return false;
|
|
}
|
|
|
|
key = (short) posKey;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
sealed class ReaderContext
|
|
{
|
|
ArrayList cache;
|
|
|
|
public void CacheItem (object o)
|
|
{
|
|
if (cache == null)
|
|
cache = new ArrayList ();
|
|
|
|
cache.Add (o);
|
|
}
|
|
|
|
public object GetCache (short key)
|
|
{
|
|
return cache [key];
|
|
}
|
|
}
|
|
|
|
abstract class ObjectFormatter
|
|
{
|
|
static readonly Hashtable writeMap = new Hashtable ();
|
|
static ObjectFormatter [] readMap = new ObjectFormatter [256];
|
|
static BinaryObjectFormatter binaryObjectFormatter;
|
|
static TypeFormatter typeFormatter;
|
|
static EnumFormatter enumFormatter;
|
|
static SingleRankArrayFormatter singleRankArrayFormatter;
|
|
static TypeConverterFormatter typeConverterFormatter;
|
|
|
|
static ObjectFormatter ()
|
|
{
|
|
new StringFormatter ().Register ();
|
|
new Int64Formatter ().Register ();
|
|
new Int32Formatter ().Register ();
|
|
new Int16Formatter ().Register ();
|
|
new ByteFormatter ().Register ();
|
|
new BooleanFormatter ().Register ();
|
|
new CharFormatter ().Register ();
|
|
new DateTimeFormatter ().Register ();
|
|
new PairFormatter ().Register ();
|
|
new TripletFormatter ().Register ();
|
|
new ArrayListFormatter ().Register ();
|
|
new HashtableFormatter ().Register ();
|
|
new ObjectArrayFormatter ().Register ();
|
|
new UnitFormatter ().Register ();
|
|
new FontUnitFormatter ().Register ();
|
|
new IndexedStringFormatter ().Register ();
|
|
new ColorFormatter ().Register ();
|
|
|
|
enumFormatter = new EnumFormatter ();
|
|
enumFormatter.Register ();
|
|
|
|
typeFormatter = new TypeFormatter ();
|
|
typeFormatter.Register ();
|
|
|
|
singleRankArrayFormatter = new SingleRankArrayFormatter ();
|
|
singleRankArrayFormatter.Register ();
|
|
|
|
typeConverterFormatter = new TypeConverterFormatter ();
|
|
typeConverterFormatter.Register ();
|
|
|
|
binaryObjectFormatter = new BinaryObjectFormatter ();
|
|
binaryObjectFormatter.Register ();
|
|
}
|
|
|
|
// 0 == null
|
|
static byte nextId = 1;
|
|
|
|
public ObjectFormatter ()
|
|
{
|
|
PrimaryId = nextId ++;
|
|
if (NumberOfIds == 1)
|
|
return;
|
|
|
|
SecondaryId = nextId ++;
|
|
if (NumberOfIds == 2)
|
|
return;
|
|
|
|
TertiaryId = nextId ++;
|
|
if (NumberOfIds == 3)
|
|
return;
|
|
|
|
throw new Exception ();
|
|
}
|
|
|
|
protected readonly byte PrimaryId, SecondaryId = 255, TertiaryId = 255;
|
|
|
|
protected abstract void Write (BinaryWriter w, object o, WriterContext ctx);
|
|
protected abstract object Read (byte token, BinaryReader r, ReaderContext ctx);
|
|
protected abstract Type Type { get; }
|
|
protected virtual int NumberOfIds { get { return 1; } }
|
|
|
|
public virtual void Register ()
|
|
{
|
|
writeMap [Type] = this;
|
|
readMap [PrimaryId] = this;
|
|
if (SecondaryId != 255) {
|
|
readMap [SecondaryId] = this;
|
|
if (TertiaryId != 255)
|
|
readMap [TertiaryId] = this;
|
|
}
|
|
}
|
|
|
|
public static void WriteObject (BinaryWriter w, object o, WriterContext ctx)
|
|
{
|
|
#if TRACE
|
|
if (o != null) {
|
|
Trace.WriteLine (String.Format ("Writing {0} (type: {1})", o, o.GetType ()));
|
|
Trace.Indent ();
|
|
} else {
|
|
Trace.WriteLine ("Writing null");
|
|
}
|
|
long pos = w.BaseStream.Position;
|
|
#endif
|
|
|
|
if (o == null) {
|
|
w.Write ((byte) 0);
|
|
return;
|
|
}
|
|
|
|
Type t = o.GetType ();
|
|
#if TRACE
|
|
Trace.WriteLine (String.Format ("Looking up formatter for type {0}", t));
|
|
#endif
|
|
|
|
ObjectFormatter fmt = writeMap [t] as ObjectFormatter;
|
|
#if TRACE
|
|
Trace.WriteLine (String.Format ("Formatter from writeMap: '{0}'", fmt));
|
|
#endif
|
|
if (fmt == null) {
|
|
// Handle abstract types here
|
|
|
|
if (o is Type)
|
|
fmt = typeFormatter;
|
|
else if (t.IsEnum)
|
|
fmt = enumFormatter;
|
|
else if (t.IsArray && ((Array) o).Rank == 1)
|
|
fmt = singleRankArrayFormatter;
|
|
else {
|
|
TypeConverter converter;
|
|
converter = TypeDescriptor.GetConverter (o);
|
|
#if TRACE
|
|
Trace.WriteLine (String.Format ("Type converter: '{0}' (to string: {1}; from {2}: {3})",
|
|
converter,
|
|
converter != null ? converter.CanConvertTo (typeof (string)) : false,
|
|
t,
|
|
converter != null ? converter.CanConvertFrom (t) : false));
|
|
#endif
|
|
// Do not use the converter if it's an instance of
|
|
// TypeConverter itself - it reports it is able to
|
|
// convert to string, but it's only a conversion
|
|
// consisting of a call to ToString() with no
|
|
// reverse conversion supported. This leads to
|
|
// problems when deserializing the object.
|
|
if (converter == null || converter.GetType () == typeof (TypeConverter) ||
|
|
!converter.CanConvertTo (typeof (string)) || !converter.CanConvertFrom (typeof (string)))
|
|
fmt = binaryObjectFormatter;
|
|
else {
|
|
typeConverterFormatter.Converter = converter;
|
|
fmt = typeConverterFormatter;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if TRACE
|
|
Trace.WriteLine (String.Format ("Writing with formatter '{0}'", fmt.GetType ()));
|
|
#endif
|
|
fmt.Write (w, o, ctx);
|
|
#if TRACE
|
|
Trace.Unindent ();
|
|
Trace.WriteLine (String.Format ("Wrote {0} (type: {1}) {2} bytes", o, o.GetType (), w.BaseStream.Position - pos));
|
|
#endif
|
|
}
|
|
|
|
public static object ReadObject (BinaryReader r, ReaderContext ctx)
|
|
{
|
|
byte sig = r.ReadByte ();
|
|
|
|
if (sig == 0)
|
|
return null;
|
|
|
|
return readMap [sig].Read (sig, r, ctx);
|
|
}
|
|
|
|
protected void Write7BitEncodedInt (BinaryWriter w, int value)
|
|
{
|
|
do {
|
|
int high = (value >> 7) & 0x01ffffff;
|
|
byte b = (byte)(value & 0x7f);
|
|
|
|
if (high != 0)
|
|
b = (byte)(b | 0x80);
|
|
|
|
w.Write(b);
|
|
value = high;
|
|
} while(value != 0);
|
|
}
|
|
|
|
protected int Read7BitEncodedInt (BinaryReader r)
|
|
{
|
|
int ret = 0;
|
|
int shift = 0;
|
|
byte b;
|
|
|
|
do {
|
|
b = r.ReadByte();
|
|
|
|
ret = ret | ((b & 0x7f) << shift);
|
|
shift += 7;
|
|
} while ((b & 0x80) == 0x80);
|
|
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
#region Primitive Formatters
|
|
class StringFormatter : ObjectFormatter
|
|
{
|
|
protected override void Write (BinaryWriter w, object o, WriterContext ctx)
|
|
{
|
|
if (ctx.RegisterCache (o)) {
|
|
w.Write (SecondaryId);
|
|
w.Write (ctx.Key);
|
|
} else {
|
|
w.Write (PrimaryId);
|
|
w.Write ((string)o);
|
|
}
|
|
}
|
|
|
|
protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
|
|
{
|
|
if (token == PrimaryId) {
|
|
string s = r.ReadString ();
|
|
ctx.CacheItem (s);
|
|
return s;
|
|
} else {
|
|
return ctx.GetCache (r.ReadInt16 ());
|
|
}
|
|
}
|
|
protected override Type Type {
|
|
get { return typeof (string); }
|
|
}
|
|
|
|
protected override int NumberOfIds {
|
|
get { return 2; }
|
|
}
|
|
}
|
|
|
|
class IndexedStringFormatter : StringFormatter
|
|
{
|
|
protected override void Write (BinaryWriter w, object o, WriterContext ctx)
|
|
{
|
|
IndexedString s = o as IndexedString;
|
|
|
|
if (s == null)
|
|
throw new InvalidOperationException ("object is not of the IndexedString type");
|
|
|
|
base.Write (w, s.Value, ctx);
|
|
}
|
|
|
|
protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
|
|
{
|
|
string s = base.Read (token, r, ctx) as string;
|
|
if (String.IsNullOrEmpty (s))
|
|
throw new InvalidOperationException ("string must not be null or empty.");
|
|
|
|
return new IndexedString (s);
|
|
}
|
|
|
|
protected override Type Type {
|
|
get { return typeof (IndexedString); }
|
|
}
|
|
|
|
protected override int NumberOfIds {
|
|
get { return 2; }
|
|
}
|
|
}
|
|
|
|
class Int64Formatter : ObjectFormatter
|
|
{
|
|
protected override void Write (BinaryWriter w, object o, WriterContext ctx)
|
|
{
|
|
w.Write (PrimaryId);
|
|
w.Write ((long)o);
|
|
}
|
|
|
|
protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
|
|
{
|
|
return r.ReadInt64 ();
|
|
}
|
|
protected override Type Type {
|
|
get { return typeof (long); }
|
|
}
|
|
}
|
|
|
|
class Int32Formatter : ObjectFormatter
|
|
{
|
|
protected override void Write (BinaryWriter w, object o, WriterContext ctx)
|
|
{
|
|
int i = (int) o;
|
|
if ((int)(byte) i == i) {
|
|
w.Write (SecondaryId);
|
|
w.Write ((byte) i);
|
|
} else {
|
|
w.Write (PrimaryId);
|
|
w.Write (i);
|
|
}
|
|
}
|
|
|
|
protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
|
|
{
|
|
if (token == PrimaryId)
|
|
return r.ReadInt32 ();
|
|
else
|
|
return (int) r.ReadByte ();
|
|
}
|
|
|
|
protected override Type Type {
|
|
get { return typeof (int); }
|
|
}
|
|
|
|
protected override int NumberOfIds {
|
|
get { return 2; }
|
|
}
|
|
}
|
|
|
|
class Int16Formatter : ObjectFormatter
|
|
{
|
|
protected override void Write (BinaryWriter w, object o, WriterContext ctx)
|
|
{
|
|
w.Write (PrimaryId);
|
|
w.Write ((short)o);
|
|
}
|
|
|
|
protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
|
|
{
|
|
return r.ReadInt16 ();
|
|
}
|
|
|
|
protected override Type Type {
|
|
get { return typeof (short); }
|
|
}
|
|
}
|
|
|
|
class ByteFormatter : ObjectFormatter
|
|
{
|
|
protected override void Write (BinaryWriter w, object o, WriterContext ctx)
|
|
{
|
|
w.Write (PrimaryId);
|
|
w.Write ((byte)o);
|
|
}
|
|
|
|
protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
|
|
{
|
|
return r.ReadByte ();
|
|
}
|
|
|
|
protected override Type Type {
|
|
get { return typeof (byte); }
|
|
}
|
|
}
|
|
|
|
class BooleanFormatter : ObjectFormatter
|
|
{
|
|
protected override void Write (BinaryWriter w, object o, WriterContext ctx)
|
|
{
|
|
if ((bool)o == true)
|
|
w.Write (PrimaryId);
|
|
else
|
|
w.Write (SecondaryId);
|
|
}
|
|
|
|
protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
|
|
{
|
|
return token == PrimaryId;
|
|
}
|
|
|
|
protected override Type Type {
|
|
get { return typeof (bool); }
|
|
}
|
|
|
|
protected override int NumberOfIds {
|
|
get { return 2; }
|
|
}
|
|
}
|
|
|
|
class CharFormatter : ObjectFormatter
|
|
{
|
|
protected override void Write (BinaryWriter w, object o, WriterContext ctx)
|
|
{
|
|
w.Write (PrimaryId);
|
|
w.Write ((char) o);
|
|
}
|
|
|
|
protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
|
|
{
|
|
return r.ReadChar ();
|
|
}
|
|
|
|
protected override Type Type {
|
|
get { return typeof (char); }
|
|
}
|
|
}
|
|
|
|
class DateTimeFormatter : ObjectFormatter
|
|
{
|
|
protected override void Write (BinaryWriter w, object o, WriterContext ctx)
|
|
{
|
|
w.Write (PrimaryId);
|
|
w.Write (((DateTime) o).Ticks);
|
|
}
|
|
|
|
protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
|
|
{
|
|
return new DateTime (r.ReadInt64 ());
|
|
}
|
|
|
|
protected override Type Type {
|
|
get { return typeof (DateTime); }
|
|
}
|
|
}
|
|
|
|
class PairFormatter : ObjectFormatter
|
|
{
|
|
protected override void Write (BinaryWriter w, object o, WriterContext ctx)
|
|
{
|
|
Pair p = (Pair) o;
|
|
w.Write (PrimaryId);
|
|
WriteObject (w, p.First, ctx);
|
|
WriteObject (w, p.Second, ctx);
|
|
}
|
|
|
|
protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
|
|
{
|
|
Pair p = new Pair ();
|
|
p.First = ReadObject (r, ctx);
|
|
p.Second = ReadObject (r, ctx);
|
|
return p;
|
|
}
|
|
|
|
protected override Type Type {
|
|
get { return typeof (Pair); }
|
|
}
|
|
}
|
|
|
|
class TripletFormatter : ObjectFormatter
|
|
{
|
|
protected override void Write (BinaryWriter w, object o, WriterContext ctx)
|
|
{
|
|
Triplet t = (Triplet) o;
|
|
w.Write (PrimaryId);
|
|
WriteObject (w, t.First, ctx);
|
|
WriteObject (w, t.Second, ctx);
|
|
WriteObject (w, t.Third, ctx);
|
|
}
|
|
|
|
protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
|
|
{
|
|
Triplet t = new Triplet ();
|
|
t.First = ReadObject (r, ctx);
|
|
t.Second = ReadObject (r, ctx);
|
|
t.Third = ReadObject (r, ctx);
|
|
return t;
|
|
}
|
|
|
|
protected override Type Type {
|
|
get { return typeof (Triplet); }
|
|
}
|
|
}
|
|
|
|
class ArrayListFormatter : ObjectFormatter
|
|
{
|
|
protected override void Write (BinaryWriter w, object o, WriterContext ctx)
|
|
{
|
|
ArrayList l = (ArrayList) o;
|
|
|
|
w.Write (PrimaryId);
|
|
Write7BitEncodedInt (w, l.Count);
|
|
for (int i = 0; i < l.Count; i++)
|
|
WriteObject (w, l [i], ctx);
|
|
}
|
|
|
|
protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
|
|
{
|
|
int len = Read7BitEncodedInt (r);
|
|
ArrayList l = new ArrayList (len);
|
|
|
|
for (int i = 0; i < len; i++)
|
|
l.Add (ReadObject (r, ctx));
|
|
|
|
return l;
|
|
}
|
|
|
|
protected override Type Type {
|
|
get { return typeof (ArrayList); }
|
|
}
|
|
}
|
|
|
|
class HashtableFormatter : ObjectFormatter
|
|
{
|
|
protected override void Write (BinaryWriter w, object o, WriterContext ctx)
|
|
{
|
|
Hashtable ht = (Hashtable) o;
|
|
|
|
w.Write (PrimaryId);
|
|
Write7BitEncodedInt (w, ht.Count);
|
|
foreach (DictionaryEntry de in ht) {
|
|
WriteObject (w, de.Key, ctx);
|
|
WriteObject (w, de.Value, ctx);
|
|
}
|
|
}
|
|
|
|
protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
|
|
{
|
|
int len = Read7BitEncodedInt (r);
|
|
Hashtable ht = new Hashtable (len);
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
object key = ReadObject (r, ctx);
|
|
object val = ReadObject (r, ctx);
|
|
|
|
ht.Add (key, val);
|
|
}
|
|
|
|
return ht;
|
|
}
|
|
|
|
protected override Type Type {
|
|
get { return typeof (Hashtable); }
|
|
}
|
|
}
|
|
|
|
class ObjectArrayFormatter : ObjectFormatter
|
|
{
|
|
protected override void Write (BinaryWriter w, object o, WriterContext ctx)
|
|
{
|
|
object [] val = (object []) o;
|
|
|
|
w.Write (PrimaryId);
|
|
Write7BitEncodedInt (w, val.Length);
|
|
for (int i = 0; i < val.Length; i++)
|
|
WriteObject (w, val [i], ctx);
|
|
}
|
|
|
|
protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
|
|
{
|
|
int len = Read7BitEncodedInt (r);
|
|
object [] ret = new object [len];
|
|
|
|
for (int i = 0; i < len; i++)
|
|
ret [i] = ReadObject (r, ctx);
|
|
|
|
return ret;
|
|
}
|
|
|
|
protected override Type Type {
|
|
get { return typeof (object []); }
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region System.Web Optimizations
|
|
class ColorFormatter : ObjectFormatter
|
|
{
|
|
protected override void Write (BinaryWriter w, object o, WriterContext ctx)
|
|
{
|
|
Color c = (Color) o;
|
|
|
|
if (c.IsEmpty || c.IsKnownColor) {
|
|
w.Write (SecondaryId);
|
|
if (c.IsEmpty)
|
|
w.Write (-1); //isempty marker
|
|
else
|
|
w.Write ((int) c.ToKnownColor ());
|
|
} else {
|
|
w.Write (PrimaryId);
|
|
w.Write (c.ToArgb ());
|
|
}
|
|
}
|
|
|
|
protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
|
|
{
|
|
int value = r.ReadInt32 ();
|
|
if (token == PrimaryId)
|
|
return Color.FromArgb (value);
|
|
else {
|
|
if (value == -1) //isempty marker
|
|
return Color.Empty;
|
|
return Color.FromKnownColor ((KnownColor)value);
|
|
}
|
|
}
|
|
|
|
protected override Type Type {
|
|
get { return typeof (Color); }
|
|
}
|
|
|
|
protected override int NumberOfIds {
|
|
get { return 2; }
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Special Formatters
|
|
class EnumFormatter : ObjectFormatter
|
|
{
|
|
protected override void Write (BinaryWriter w, object o, WriterContext ctx)
|
|
{
|
|
object value = Convert.ChangeType (o, ((Enum) o).GetTypeCode ());
|
|
w.Write (PrimaryId);
|
|
WriteObject (w, o.GetType (), ctx);
|
|
WriteObject (w, value, ctx);
|
|
}
|
|
|
|
protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
|
|
{
|
|
Type t = (Type) ReadObject (r, ctx);
|
|
object value = ReadObject (r, ctx);
|
|
|
|
return Enum.ToObject (t, value);
|
|
}
|
|
|
|
protected override Type Type {
|
|
get { return typeof (Enum); }
|
|
}
|
|
}
|
|
|
|
class TypeFormatter : ObjectFormatter
|
|
{
|
|
protected override void Write (BinaryWriter w, object o, WriterContext ctx)
|
|
{
|
|
if (ctx.RegisterCache (o)) {
|
|
w.Write (SecondaryId);
|
|
w.Write (ctx.Key);
|
|
} else {
|
|
w.Write (PrimaryId);
|
|
w.Write (((Type) o).FullName);
|
|
|
|
// We should cache the name of the assembly
|
|
w.Write (((Type) o).Assembly.FullName);
|
|
}
|
|
}
|
|
|
|
protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
|
|
{
|
|
if (token == PrimaryId) {
|
|
string type = r.ReadString ();
|
|
string assembly = r.ReadString ();
|
|
Type t = Assembly.Load (assembly).GetType (type);
|
|
ctx.CacheItem (t);
|
|
return t;
|
|
} else {
|
|
return ctx.GetCache (r.ReadInt16 ());
|
|
}
|
|
}
|
|
|
|
protected override Type Type {
|
|
get { return typeof (Type); }
|
|
}
|
|
|
|
protected override int NumberOfIds {
|
|
get { return 2; }
|
|
}
|
|
}
|
|
|
|
class SingleRankArrayFormatter : ObjectFormatter
|
|
{
|
|
readonly BinaryFormatter _binaryFormatter = new BinaryFormatter ();
|
|
|
|
protected override void Write (BinaryWriter w, object o, WriterContext ctx)
|
|
{
|
|
Array val = (Array) o;
|
|
if (val.GetType ().GetElementType ().IsPrimitive) {
|
|
w.Write (SecondaryId);
|
|
_binaryFormatter.Serialize (w.BaseStream, o);
|
|
return;
|
|
}
|
|
|
|
w.Write (PrimaryId);
|
|
WriteObject (w, val.GetType ().GetElementType (), ctx);
|
|
|
|
Write7BitEncodedInt (w, val.Length);
|
|
for (int i = 0; i < val.Length; i++)
|
|
WriteObject (w, val.GetValue (i), ctx);
|
|
}
|
|
|
|
protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
|
|
{
|
|
if (token == SecondaryId)
|
|
return _binaryFormatter.Deserialize (r.BaseStream);
|
|
Type t = (Type) ReadObject (r, ctx);
|
|
int len = Read7BitEncodedInt (r);
|
|
Array val = Array.CreateInstance (t, len);
|
|
|
|
for (int i = 0; i < len; i++)
|
|
val.SetValue (ReadObject (r, ctx), i);
|
|
|
|
return val;
|
|
}
|
|
|
|
protected override Type Type {
|
|
get { return typeof (Array); }
|
|
}
|
|
|
|
protected override int NumberOfIds {
|
|
get { return 2; }
|
|
}
|
|
}
|
|
|
|
class FontUnitFormatter : StringFormatter
|
|
{
|
|
protected override void Write (BinaryWriter w, object o, WriterContext ctx)
|
|
{
|
|
base.Write (w, o.ToString (), ctx);
|
|
}
|
|
|
|
protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
|
|
{
|
|
return FontUnit.Parse ((string) base.Read (token, r, ctx));
|
|
}
|
|
|
|
protected override Type Type {
|
|
get { return typeof (FontUnit); }
|
|
}
|
|
}
|
|
|
|
class UnitFormatter : StringFormatter
|
|
{
|
|
protected override void Write (BinaryWriter w, object o, WriterContext ctx)
|
|
{
|
|
base.Write (w, o.ToString (), ctx);
|
|
}
|
|
|
|
protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
|
|
{
|
|
return Unit.Parse ((string) base.Read (token, r, ctx));
|
|
}
|
|
|
|
protected override Type Type {
|
|
get { return typeof (Unit); }
|
|
}
|
|
}
|
|
|
|
class TypeConverterFormatter : StringFormatter
|
|
{
|
|
TypeConverter converter;
|
|
|
|
protected override void Write (BinaryWriter w, object o, WriterContext ctx)
|
|
{
|
|
w.Write (PrimaryId);
|
|
ObjectFormatter.WriteObject (w, o.GetType (), ctx);
|
|
string v = (string) converter.ConvertTo (null, Helpers.InvariantCulture,
|
|
o, typeof (string));
|
|
base.Write (w, v, ctx);
|
|
}
|
|
|
|
protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
|
|
{
|
|
Type t = (Type) ObjectFormatter.ReadObject (r, ctx);
|
|
converter = TypeDescriptor.GetConverter (t);
|
|
token = r.ReadByte ();
|
|
string v = (string) base.Read (token, r, ctx);
|
|
return converter.ConvertFrom (null, Helpers.InvariantCulture, v);
|
|
}
|
|
|
|
protected override Type Type {
|
|
get { return typeof (TypeConverter); }
|
|
}
|
|
|
|
public TypeConverter Converter {
|
|
set { converter = value; }
|
|
}
|
|
}
|
|
|
|
class BinaryObjectFormatter : ObjectFormatter
|
|
{
|
|
protected override void Write (BinaryWriter w, object o, WriterContext ctx)
|
|
{
|
|
w.Write (PrimaryId);
|
|
|
|
MemoryStream ms = new MemoryStream (128);
|
|
new BinaryFormatter ().Serialize (ms, o);
|
|
|
|
byte [] buf = ms.GetBuffer ();
|
|
Write7BitEncodedInt (w, buf.Length);
|
|
w.Write (buf, 0, buf.Length);
|
|
}
|
|
|
|
protected override object Read (byte token, BinaryReader r, ReaderContext ctx)
|
|
{
|
|
int len = Read7BitEncodedInt (r);
|
|
byte [] buf = r.ReadBytes (len);
|
|
if (buf.Length != len)
|
|
throw new Exception ();
|
|
|
|
return new BinaryFormatter ().Deserialize (new MemoryStream (buf));
|
|
}
|
|
|
|
protected override Type Type {
|
|
get { return typeof (object); }
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
}
|
|
}
|