Click here to Skip to main content
15,886,199 members
Articles / Programming Languages / XML

Optimizing Serialization in .NET

Rate me:
Please Sign up or sign in to vote.
4.87/5 (88 votes)
16 May 2010Public Domain31 min read 644.5K   5K   333  
Provides code and techniques to enable developers to optimize serialization of data
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Reflection;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace SimmoTech.Utils.Serialization
{
	public class WebFastSerializationHelper: IFastSerializationTypeSurrogate
	{
		#region Static
		public const double Epsilon = 2.2204460492503131e-016;

		internal static readonly int StateBagIsIgnoreCase = BitVector32.CreateMask();
		internal static readonly int StateBagHasDirtyEntries = BitVector32.CreateMask(StateBagIsIgnoreCase);
		internal static readonly int StateBagHasCleanEntries = BitVector32.CreateMask(StateBagHasDirtyEntries);

		internal static readonly BitVector32.Section UnitType = BitVector32.CreateSection(9); // 4 bits
		internal static readonly BitVector32.Section UnitIsZeroValue = BitVector32.CreateSection(1, UnitType);
		internal static readonly BitVector32.Section UnitIsNegativeValue = BitVector32.CreateSection(1, UnitIsZeroValue);
		internal static readonly BitVector32.Section UnitIsOptimizedValue = BitVector32.CreateSection(1, UnitIsNegativeValue);

		// Since Zero and Negative are mutually exclusive, we can use their combined masks and offsets 
		// as a Pseudo-BitVector32.Section rather than use an explicit UnitIsDouble BitVector32.Section which would 
		// require an extra bit and cause the BitVector32 to be written as 2 bytes depending on the value
		internal static readonly int UnitIsDoubleValue = UnitIsZeroValue.Mask << UnitIsZeroValue.Offset |
		                                                 UnitIsNegativeValue.Mask << UnitIsNegativeValue.Offset;

		internal static readonly FieldInfo StateBagIgnoreCaseField =
			typeof(StateBag).GetField("ignoreCase", BindingFlags.Instance | BindingFlags.NonPublic);
		#endregion Static

		#region IFastSerializationTypeSurrogate
		public bool SupportsType(Type type)
		{
			if (type == typeof(Pair))
				return true;

			if (type == typeof(Triplet)) 
				return true;

			if (type == typeof(StateBag))
				return true;

			if (type == typeof(Unit))
				return true;

			if (type == typeof(Hashtable))
				return true;

			return false;
		}

		public void Serialize(SerializationWriter writer, object value)
		{
			Type type = value.GetType();
			if (type == typeof(Pair))
				Serialize(writer, (Pair) value);

			else if (type == typeof(Triplet))
				Serialize(writer, (Triplet) value);

			else if (type == typeof(StateBag))
				Serialize(writer, (StateBag) value);

			else if (type == typeof(Unit))
				Serialize(writer, (Unit) value);

			else if (type == typeof(Hashtable))
				Serialize(writer, (Hashtable) value);

			else
			{
				throw new InvalidOperationException(string.Format("{0} does not support Type: {1}", GetType(), type));
			}
		}

		public object Deserialize(SerializationReader reader, Type type)
		{
			if (type == typeof(Pair))
				return DeserializePair(reader);

			else if (type == typeof(Triplet))
				return DeserializeTriplet(reader);

			else if (type == typeof(StateBag))
				return DeserializeStateBag(reader);

			else if (type == typeof(Unit))
				return DeserializeUnit(reader);

			else if (type == typeof(Hashtable))
				return DeserializeHashtable(reader);

			else
			{
				throw new InvalidOperationException(string.Format("{0} does not support Type: {1}", GetType(), type));
			}
		}
		#endregion IFastSerializationTypeSurrogate

		#region Unit
		public static void Serialize(SerializationWriter writer, Unit unit)
		{
			BitVector32 state = new BitVector32();

			if (unit.IsEmpty)
			{
				writer.WriteOptimized(state);
				return;
			}

			state[UnitType] = (int) unit.Type;

			int integerValue = (int) Math.Round(unit.Value);
			bool writeAsDouble = unit.Type != System.Web.UI.WebControls.UnitType.Pixel &&
			                     Math.Abs(unit.Value - integerValue) > Epsilon;

			if (writeAsDouble)
			{
				state[UnitIsDoubleValue] = true;
				writer.WriteOptimized(state);
				writer.Write(unit.Value);
			}
			else if (integerValue == 0)
			{
				state[UnitIsZeroValue] = 1;
				writer.WriteOptimized(state);
			}
			else
			{
				int complementedValue = integerValue > 0 ? integerValue : -(integerValue + 1);

				if (complementedValue > SerializationWriter.HighestOptimizable16BitValue)
				{
					writer.WriteOptimized(state);
					writer.Write((short) integerValue);
				}
				else
				{
					state[UnitIsOptimizedValue] = 1;
					state[UnitIsNegativeValue] = integerValue > 0 ? 0 : 1;
					writer.WriteOptimized(state);
					writer.WriteOptimized(complementedValue);
				}
			}
		}

		public static Unit DeserializeUnit(SerializationReader reader)
		{
			BitVector32 state = reader.ReadOptimizedBitVector32();

			int unitType = state[UnitType];

			if(unitType == 0)
				return System.Web.UI.WebControls.Unit.Empty;
			else if(state[UnitIsDoubleValue])
				return new Unit(reader.ReadDouble(), (UnitType) unitType);
			else if(state[UnitIsZeroValue] == 1)
				return new Unit(0, (UnitType) unitType);
			else
			{
				int integerValue = state[UnitIsOptimizedValue] == 1 ? reader.ReadOptimizedInt32() : reader.ReadInt16();
				if(state[UnitIsNegativeValue] == 1) integerValue = -integerValue - 1;
				return new Unit(integerValue, (UnitType) unitType);
			}
		}
		#endregion Unit

		#region StateBag
		public static void Serialize(SerializationWriter writer, StateBag stateBag)
		{
			List<DictionaryEntry> dirtyEntries = new List<DictionaryEntry>();
			List<DictionaryEntry> cleanEntries = new List<DictionaryEntry>();

			BitVector32 flags = new BitVector32();
			flags[StateBagIsIgnoreCase] = (bool) StateBagIgnoreCaseField.GetValue(stateBag);

			foreach(DictionaryEntry entry in stateBag)
			{
				StateItem stateItem = (StateItem) entry.Value;
				if (stateItem.IsDirty)
					dirtyEntries.Add(entry);
				else
				{
					cleanEntries.Add(entry);
				}
			}

			flags[StateBagHasDirtyEntries] = dirtyEntries.Count != 0;
			flags[StateBagHasCleanEntries] = cleanEntries.Count != 0;

			writer.WriteOptimized(flags);

			if (dirtyEntries.Count != 0)
			{
				writer.WriteOptimized(dirtyEntries.Count);
				foreach(DictionaryEntry entry in dirtyEntries)
				{
					writer.WriteOptimized((string) entry.Key);
					writer.WriteObject((entry.Value as StateItem).Value);
				}
			}

			if (cleanEntries.Count != 0)
			{
				writer.WriteOptimized(cleanEntries.Count);
				foreach(DictionaryEntry entry in cleanEntries)
				{
					writer.WriteOptimized((string) entry.Key);
					writer.WriteObject((entry.Value as StateItem).Value);
				}
			}
		}

		public static StateBag DeserializeStateBag(SerializationReader reader)
		{
			BitVector32 flags = reader.ReadOptimizedBitVector32();
			StateBag stateBag = new StateBag(flags[StateBagIsIgnoreCase]);

			if (flags[StateBagHasDirtyEntries])
			{
				int count = reader.ReadOptimizedInt32();
				for(int i = 0; i < count; i++)
				{
					string key = reader.ReadOptimizedString();
					object value = reader.ReadObject();
					stateBag.Add(key, value).IsDirty = true;
				}
			}

			if (flags[StateBagHasCleanEntries]) 
			{
				int count = reader.ReadOptimizedInt32();
				for(int i = 0; i < count; i++)
				{
					string key = reader.ReadOptimizedString();
					object value = reader.ReadObject();
					stateBag.Add(key, value);
				}
			}
			return stateBag;
		}

		#endregion StateBag

		#region Triplet
		public static void Serialize(SerializationWriter writer, Triplet triplet)
		{
			writer.WriteObject(triplet.First);
			writer.WriteObject(triplet.Second);
			writer.WriteObject(triplet.Third);
		}

		public static Triplet DeserializeTriplet(SerializationReader reader)
		{
			return new Triplet(reader.ReadObject(), reader.ReadObject(), reader.ReadObject());
		}
		#endregion Triplet

		#region Pair
		public static void Serialize(SerializationWriter writer, Pair pair)
		{
			writer.WriteObject(pair.First);
			writer.WriteObject(pair.Second);
		}

		public static Pair DeserializePair(SerializationReader reader)
		{
			return new Pair(reader.ReadObject(), reader.ReadObject());
		}
		#endregion Pair

		#region Hashtable
		// Note this is a simplistic version as it assumes defaults for comparer, hashcodeprovider, loadfactor etc.
		public static void Serialize(SerializationWriter writer, Hashtable hashtable) 
		{
			object[] keys = new object[hashtable.Count];
			object[] values = new object[hashtable.Count];

			hashtable.Keys.CopyTo(keys, 0);
			hashtable.Values.CopyTo(values, 0);

			writer.WriteOptimized(keys);
			writer.WriteOptimized(values);
		}

		// Note this is a simplistic version as it assumes defaults for comparer, hashcodeprovider, loadfactor etc.
		public static Hashtable DeserializeHashtable(SerializationReader reader)
		{
			object[] keys = reader.ReadOptimizedObjectArray();
			object[] values = reader.ReadOptimizedObjectArray();

			Hashtable result = new Hashtable(keys.Length);
			for(int i = 0; i < keys.Length; i++)
			{
				result[keys[i]] = values[i];
			}
			return result;
		}
		#endregion Hashtable

	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication


Written By
Software Developer (Senior) Hunton Information Systems Ltd.
United Kingdom United Kingdom
Simon Hewitt is a freelance IT consultant and is MD of Hunton Information Systems Ltd.

He is currently looking for contract work in London.

He is happily married to Karen (originally from Florida, US), has a lovely daughter Bailey, and they live in Kings Langley, Hertfordshire, UK.

Comments and Discussions