Click here to Skip to main content
15,897,187 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 649.3K   5K   332  
Provides code and techniques to enable developers to optimize serialization of data
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections.Generic;

namespace Framework.Serialization
{
	/// <summary>
	/// A SerializationReader instance is used to read stored values and objects from a byte array.
	///
	/// Once an instance is created, use the various methods to read the required data.
	/// The data read MUST be exactly the same type and in the same order as it was written.
	/// </summary>
	public sealed class SerializationReader: BinaryReader
	{
		// Marker to denote that all elements in a value array are optimizable
		static readonly BitArray FullyOptimizableTypedArray = new BitArray(0);

		readonly int startPosition;
		readonly int endPosition;
		List<string> stringTokenList;
		List<object> objectTokenList;

		#region Debug Related
		/// <summary>
		/// Dumps the string tables.
		/// </summary>
		/// <param name="list">The list.</param>
		[Conditional("DEBUG")]
		public void DumpStringTables(ArrayList list)
		{
			list.AddRange(stringTokenList);
		}
		#endregion

		/// <summary>
		/// Creates a SerializationReader using a byte[] previous created by SerializationWriter
		/// 
		/// A MemoryStream is used to access the data without making a copy of it.
		/// </summary>
		/// <param name="data">The byte[] containining serialized data.</param>
		public SerializationReader(byte[] data): this(new MemoryStream(data)) 
		{
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="SerializationReader"/> class.
		/// </summary>
		/// <param name="stream">The stream.</param>
		public SerializationReader(Stream stream): base(stream)
		{
			// Store the start position of the stream if seekable
			startPosition = stream.CanSeek ? (int) stream.Position : 0;

			// Always read the first 4 bytes
			endPosition = startPosition + ReadInt32();

			// If the first four bytes are zero
			if (startPosition == endPosition)
			{
				// then there is no token table presize info
				InitializeTokenTables(0, 0);
			}
			else
			{
				// Use the correct token table sizes
				InitializeTokenTables(ReadInt32(), ReadInt32());
			}
		}

		/// <summary>
		/// Creates a SerializationReader based around the passed Stream.
		/// Allows the string and object token tables to be presized using
		/// the specified values.
		/// </summary>
		/// <param name="stream">The stream containing the serialized data</param>
		/// <param name="stringTokenTablePresize">Number of string tokens to presize</param>
		/// <param name="objectTokenTablePresize">Number of object tokens to presize</param>
		public SerializationReader(Stream stream, int stringTokenTablePresize, int objectTokenTablePresize): base(stream)
		{
			if (stringTokenTablePresize < 0) throw new ArgumentOutOfRangeException("stringTokenTablePresize", "Cannot be negative");
			if (objectTokenTablePresize < 0) throw new ArgumentOutOfRangeException("objectTokenTablePresize", "Cannot be negative");

			// Store the start position of the stream if seekable
			startPosition = stream.CanSeek ? (int) stream.Position : 0;

			// Always read the first 4 bytes
			endPosition = startPosition + ReadInt32();

			// Need to ignore token table size if present (unlikely)
			if (startPosition != endPosition)
			{
				ReadInt32();
				ReadInt32();
			}

			InitializeTokenTables(stringTokenTablePresize, objectTokenTablePresize);
		}

		/// <summary>
		/// Returns an ArrayList or null from the stream.
		/// </summary>
		/// <returns>An ArrayList instance.</returns>
		public ArrayList ReadArrayList()
		{
			if (ReadTypeCode() == SerializedType.NullType) return null;

			return new ArrayList(ReadOptimizedObjectArray());
		}

		/// <summary>
		/// Returns a BitArray or null from the stream.
		/// </summary>
		/// <returns>A BitArray instance.</returns>
		public BitArray ReadBitArray()
		{
			if (ReadTypeCode() == SerializedType.NullType) return null;

			return ReadOptimizedBitArray();
		}

		/// <summary>
		/// Returns a BitVector32 value from the stream.
		/// </summary>
		/// <returns>A BitVector32 value.</returns>
		public BitVector32 ReadBitVector32()
		{
			return new BitVector32(ReadInt32());
		}
		
		/// <summary>
		/// Reads the specified number of bytes directly from the stream.
		/// </summary>
		/// <param name="count">The number of bytes to read</param>
		/// <returns>A byte[] containing the read bytes</returns>
		public byte[] ReadBytesDirect(int count)
		{
			return ReadBytes(count);
		}

		/// <summary>
		/// Returns a DateTime value from the stream.
		/// </summary>
		/// <returns>A DateTime value.</returns>
		public DateTime ReadDateTime()
		{
			return DateTime.FromBinary(ReadInt64());
		}

		/// <summary>
		/// Returns a Guid value from the stream.
		/// </summary>
		/// <returns>A DateTime value.</returns>
		public Guid ReadGuid()
		{
			return new Guid(ReadBytes(16));
		}

		/// <summary>
		/// Returns an object based on the SerializedType read next from the stream.
		/// </summary>
		/// <returns>An object instance.</returns>
		public object ReadObject()
		{
			return ProcessObject((SerializedType) ReadByte());
		}
		/// <summary>
		/// Called ReadOptimizedString().
		/// This override to hide base BinaryReader.ReadString().
		/// </summary>
		/// <returns>A string value.</returns>
		public override string ReadString()
		{
			return ReadOptimizedString();
		}

		/// <summary>
		/// Returns a string value from the stream.
		/// </summary>
		/// <returns>A string value.</returns>
		public string ReadStringDirect()
		{
			return base.ReadString();
		}

		/// <summary>
		/// Returns a TimeSpan value from the stream.
		/// </summary>
		/// <returns>A TimeSpan value.</returns>
		public TimeSpan ReadTimeSpan()
		{
			return new TimeSpan(ReadInt64());
		}

		/// <summary>
		/// Returns a Type or null from the stream.
		/// 
		/// Throws an exception if the Type cannot be found.
		/// </summary>
		/// <returns>A Type instance.</returns>
		public Type ReadType()
		{
			return ReadType(true);
		}

		/// <summary>
		/// Returns a Type or null from the stream.
		/// 
		/// Throws an exception if the Type cannot be found and throwOnError is true.
		/// </summary>
		/// <returns>A Type instance.</returns>
		public Type ReadType(bool throwOnError)
		{
			if (ReadTypeCode() == SerializedType.NullType) return null;

			return Type.GetType(ReadOptimizedString(), throwOnError);
		}

		/// <summary>
		/// Returns an ArrayList from the stream that was stored optimized.
		/// </summary>
		/// <returns>An ArrayList instance.</returns>
		public ArrayList ReadOptimizedArrayList()
		{
			return new ArrayList(ReadOptimizedObjectArray());
		}

		/// <summary>
		/// Returns a BitArray from the stream that was stored optimized.
		/// </summary>
		/// <returns>A BitArray instance.</returns>
		public BitArray ReadOptimizedBitArray()
		{
			var length = ReadOptimizedInt32();
			if (length == 0) return FullyOptimizableTypedArray;

			return new BitArray(ReadBytes((length + 7) / 8)) { Length = length };
		}

		/// <summary>
		/// Returns a BitVector32 value from the stream that was stored optimized.
		/// </summary>
		/// <returns>A BitVector32 value.</returns>
		public BitVector32 ReadOptimizedBitVector32()
		{
			return new BitVector32(Read7BitEncodedInt());
		}

		/// <summary>
		/// Returns a DateTime value from the stream that was stored optimized.
		/// </summary>
		/// <returns>A DateTime value.</returns>
		public DateTime ReadOptimizedDateTime()
		{
			// Read date information from first three bytes
			var dateMask = new BitVector32(ReadByte() | (ReadByte() << 8) | (ReadByte() << 16));
			var result = new DateTime(
					dateMask[SerializationWriter.DateYearMask],
					dateMask[SerializationWriter.DateMonthMask],
					dateMask[SerializationWriter.DateDayMask]
			);

			if (dateMask[SerializationWriter.DateHasTimeOrKindMask] == 1)
			{
				var initialByte = ReadByte();
				var dateTimeKind = (DateTimeKind) (initialByte & 0x03);

				// Remove the IsNegative and HasDays flags which are never true for a DateTime
				initialByte &= 0xfc; 
				if (dateTimeKind != DateTimeKind.Unspecified)
				{
					result = DateTime.SpecifyKind(result, dateTimeKind);
				}

				if (initialByte == 0)
				{
					// No need to call decodeTimeSpan if there is no time information
					ReadByte(); 
				}
				else
				{
					result = result.Add(DecodeTimeSpan(initialByte));
				}
			}

			return result;
		}

		/// <summary>
		/// Returns a Decimal value from the stream that was stored optimized.
		/// </summary>
		/// <returns>A Decimal value.</returns>
		public Decimal ReadOptimizedDecimal()
		{
			var flags = ReadByte();
			var lo = 0;
			var mid = 0;
			var hi = 0;
			byte scale = 0;

			if ((flags & 0x02) != 0)
			{
				scale = ReadByte();
			}

			if ((flags & 4) == 0)
			{
				lo = (flags & 32) != 0 ? ReadOptimizedInt32() : ReadInt32();
			}

			if ((flags & 8) == 0)
			{
				mid = (flags & 64) != 0 ? ReadOptimizedInt32() : ReadInt32();
			}

			if ((flags & 16) == 0)
			{
				hi = (flags & 128) != 0 ? ReadOptimizedInt32() : ReadInt32();
			}

			return new decimal(lo, mid, hi, (flags & 0x01) != 0, scale);
		}

		/// <summary>
		/// Returns an Int32 value from the stream that was stored optimized.
		/// </summary>
		/// <returns>An Int32 value.</returns>
		public int ReadOptimizedInt32()
		{
			var result = 0;
			var bitShift = 0;

			while(true)
			{
				var nextByte = ReadByte();

				result |= (nextByte & 0x7f) << bitShift;
				bitShift += 7;

				if ((nextByte & 0x80) == 0) return result;
			}
		}

		/// <summary>
		/// Returns an Int16 value from the stream that was stored optimized.
		/// </summary>
		/// <returns>An Int16 value.</returns>
		public short ReadOptimizedInt16()
		{
			return (short) ReadOptimizedInt32();
		}

		/// <summary>
		/// Returns an Int64 value from the stream that was stored optimized.
		/// </summary>
		/// <returns>An Int64 value.</returns>
		public long ReadOptimizedInt64()
		{
			long result = 0;
			var bitShift = 0;

			while(true)
			{
				var nextByte = ReadByte();

				result |= ((long) nextByte & 0x7f) << bitShift;
				bitShift += 7;

				if ((nextByte & 0x80) == 0) return result;
			}
		}

		/// <summary>
		/// Returns an object[] from the stream that was stored optimized.
		/// </summary>
		/// <returns>An object[] instance.</returns>
		public object[] ReadOptimizedObjectArray()
		{
			return ReadOptimizedObjectArray(null);
		}

		/// <summary>
		/// Returns an object[] from the stream that was stored optimized.
		/// The returned array will be typed according to the specified element type
		/// and the resulting array can be cast to the expected type.
		/// e.g.
		/// string[] myStrings = (string[]) reader.ReadOptimizedObjectArray(typeof(string));
		/// 
		/// An exception will be thrown if any of the deserialized values cannot be
		/// cast to the specified elementType.
		/// 
		/// </summary>
		/// <param name="elementType">The Type of the expected array elements. null will return a plain object[].</param>
		/// <returns>An object[] instance.</returns>
		public object[] ReadOptimizedObjectArray(Type elementType)
		{
			var length = ReadOptimizedInt32();
			var result = (object[]) (elementType == null ? new object[length] : Array.CreateInstance(elementType, length));

			for (var i = 0; i < result.Length; i++)
			{
				var serializedType = (SerializedType) ReadByte();

				switch (serializedType)
				{
					case SerializedType.NullSequenceType:
						i += ReadOptimizedInt32();

						break;

					case SerializedType.DuplicateValueSequenceType:
						var target = result[i] = ReadObject();
						var duplicateValueCount = ReadOptimizedInt32();

						while (duplicateValueCount-- > 0)
						{
							result[++i] = target;
						}

						break;

					case SerializedType.DBNullSequenceType:
						result[i] = DBNull.Value;
						var duplicateDBNullCount = ReadOptimizedInt32();

						while (duplicateDBNullCount-- > 0)
						{
							result[++i] = DBNull.Value;
						}

						break;

					default:
						if (serializedType != SerializedType.NullType)
						{
							result[i] = ProcessObject(serializedType);
						}

						break;
				}
			}
			return result;
		}

		/// <summary>
		/// Returns a pair of object[] arrays from the stream that were stored optimized.
		/// </summary>
		/// <returns>A pair of object[] arrays.</returns>
		public void ReadOptimizedObjectArrayPair(out object[] values1, out object[] values2)
		{
			values1 = ReadOptimizedObjectArray(null);
			values2 = new object[values1.Length];

			for(var i = 0; i < values2.Length; i++)
			{
				var serializedType = (SerializedType) ReadByte();

				switch (serializedType)
				{
					case SerializedType.DuplicateValueSequenceType:
						values2[i] = values1[i];
						var duplicateValueCount = ReadOptimizedInt32();

						while (duplicateValueCount-- > 0)
						{
							values2[++i] = values1[i];
						}

						break;

					case SerializedType.DuplicateValueType:
						values2[i] = values1[i];

						break;

					case SerializedType.NullSequenceType:
						i += ReadOptimizedInt32();

						break;

					case SerializedType.DBNullSequenceType:
						values2[i] = DBNull.Value;
						var duplicates = ReadOptimizedInt32();

						while (duplicates-- > 0)
						{
							values2[++i] = DBNull.Value;
						}

						break;

					default:
						if (serializedType != SerializedType.NullType)
						{
							values2[i] = ProcessObject(serializedType);
						}

						break;
				}
			}
		}

		/// <summary>
		/// Returns a string value from the stream that was stored optimized.
		/// </summary>
		/// <returns>A string value.</returns>
		public string ReadOptimizedString()
		{
			var typeCode = ReadTypeCode();

			if (typeCode < SerializedType.NullType)
			{
				return ReadTokenizedString((int) typeCode);
			}

			switch (typeCode)
			{
				case SerializedType.NullType: return null;
				case SerializedType.YStringType: return "Y";
				case SerializedType.NStringType: return "N";
				case SerializedType.SingleCharStringType: return Char.ToString(ReadChar());
				case SerializedType.SingleSpaceType: return " ";
				case SerializedType.EmptyStringType: return string.Empty;

				default: throw new InvalidOperationException("Unrecognized TypeCode");
			}
		}

		/// <summary>
		/// Returns a TimeSpan value from the stream that was stored optimized.
		/// </summary>
		/// <returns>A TimeSpan value.</returns>
		public TimeSpan ReadOptimizedTimeSpan()
		{
			return DecodeTimeSpan(ReadByte());
		}

		/// <summary>
		/// Returns a Type from the stream.
		/// 
		/// Throws an exception if the Type cannot be found.
		/// </summary>
		/// <returns>A Type instance.</returns>
		public Type ReadOptimizedType()
		{
			return ReadOptimizedType(true);
		}
		/// <summary>
		/// Returns a Type from the stream.
		/// 
		/// Throws an exception if the Type cannot be found and throwOnError is true.
		/// </summary>
		/// <returns>A Type instance.</returns>
		public Type ReadOptimizedType(bool throwOnError)
		{
			return Type.GetType(ReadOptimizedString(), throwOnError);
		}

		/// <summary>
		/// Returns a UInt16 value from the stream that was stored optimized.
		/// </summary>
		/// <returns>A UInt16 value.</returns>
		[CLSCompliant(false)]
		public ushort ReadOptimizedUInt16()
		{
			return (ushort) ReadOptimizedUInt32();	
		}

		/// <summary>
		/// Returns a UInt32 value from the stream that was stored optimized.
		/// </summary>
		/// <returns>A UInt32 value.</returns>
		[CLSCompliant(false)]
		public uint ReadOptimizedUInt32()
		{
			uint result = 0;
			var bitShift = 0;

			while(true)
			{
				var nextByte = ReadByte();

				result |= ((uint) nextByte & 0x7f) << bitShift;
				bitShift += 7;

				if ((nextByte & 0x80) == 0) return result;
			}
		}

		/// <summary>
		/// Returns a UInt64 value from the stream that was stored optimized.
		/// </summary>
		/// <returns>A UInt64 value.</returns>
		[CLSCompliant(false)]
		public ulong ReadOptimizedUInt64()
		{
			ulong result = 0;
			var bitShift = 0;

			while(true)
			{
				var nextByte = ReadByte();

				result |= ((ulong) nextByte & 0x7f) << bitShift;
				bitShift += 7;

				if ((nextByte & 0x80) == 0) return result;
			}
		}

		/// <summary>
		/// Returns a typed array from the stream.
		/// </summary>
		/// <returns>A typed array.</returns>
		public Array ReadTypedArray()
		{
			return (Array) ProcessArrayTypes(ReadTypeCode(), null);
		}

		/// <summary>
		/// Returns a new, simple generic dictionary populated with keys and values from the stream.
		/// </summary>
		/// <typeparam name="K">The key Type.</typeparam>
		/// <typeparam name="V">The value Type.</typeparam>
		/// <returns>A new, simple, populated generic Dictionary.</returns>
		public Dictionary<K, V> ReadDictionary<K, V>()
		{
			var result = new Dictionary<K, V>();

			ReadDictionary(result);

			return result;
		}

		
		/// <summary>
		/// Populates a pre-existing generic dictionary with keys and values from the stream.
		/// This allows a generic dictionary to be created without using the default constructor.
		/// </summary>
		/// <typeparam name="K">The key Type.</typeparam>
		/// <typeparam name="V">The value Type.</typeparam>
		public void ReadDictionary<K, V>(Dictionary<K, V> dictionary)
		{
			var keys = (K[]) ProcessArrayTypes(ReadTypeCode(), typeof(K));
			var values = (V[]) ProcessArrayTypes(ReadTypeCode(), typeof(V));

			if (dictionary == null) 
			{
				dictionary = new Dictionary<K, V>(keys.Length);
			}

			for (var i = 0; i < keys.Length; i++)
			{
				dictionary.Add(keys[i], values[i]);
			}
		}

		/// <summary>
		/// Returns a generic List populated with values from the stream.
		/// </summary>
		/// <typeparam name="T">The list Type.</typeparam>
		/// <returns>A new generic List.</returns>
		public List<T> ReadList<T>()
		{
			return new List<T>((T[]) ProcessArrayTypes(ReadTypeCode(), typeof(T)));
		}
		
		/// <summary>
		/// Returns a Nullable struct from the stream.
		/// The value returned must be cast to the correct Nullable type.
		/// Synonym for ReadObject();
		/// </summary>
		/// <returns>A struct value or null</returns>
		public ValueType ReadNullable()
		{
			return (ValueType) ReadObject();
		}
		
		/// <summary>
		/// Returns a Nullable Boolean from the stream.
		/// </summary>
		/// <returns>A Nullable Boolean.</returns>
		public Boolean? ReadNullableBoolean()
		{
			return (bool?) ReadObject();
		}

		/// <summary>
		/// Returns a Nullable Byte from the stream.
		/// </summary>
		/// <returns>A Nullable Byte.</returns>
		public Byte? ReadNullableByte()
		{
			return (byte?) ReadObject();
		}

		/// <summary>
		/// Returns a Nullable Char from the stream.
		/// </summary>
		/// <returns>A Nullable Char.</returns>
		public Char? ReadNullableChar()
		{
			return (char?) ReadObject();
		}

		/// <summary>
		/// Returns a Nullable DateTime from the stream.
		/// </summary>
		/// <returns>A Nullable DateTime.</returns>
		public DateTime? ReadNullableDateTime()
		{
			return (DateTime?) ReadObject();
		}

		/// <summary>
		/// Returns a Nullable Decimal from the stream.
		/// </summary>
		/// <returns>A Nullable Decimal.</returns>
		public Decimal? ReadNullableDecimal()
		{
			return (decimal?) ReadObject();
		}

		/// <summary>
		/// Returns a Nullable Double from the stream.
		/// </summary>
		/// <returns>A Nullable Double.</returns>
		public Double? ReadNullableDouble()
		{
			return (double?) ReadObject();
		}

		/// <summary>
		/// Returns a Nullable Guid from the stream.
		/// </summary>
		/// <returns>A Nullable Guid.</returns>
		public Guid? ReadNullableGuid()
		{
			return (Guid?) ReadObject();
		}

		/// <summary>
		/// Returns a Nullable Int16 from the stream.
		/// </summary>
		/// <returns>A Nullable Int16.</returns>
		public Int16? ReadNullableInt16()
		{
			return (short?) ReadObject();
		}
		/// <summary>
		/// Returns a Nullable Int32 from the stream.
		/// </summary>
		/// <returns>A Nullable Int32.</returns>
		public Int32? ReadNullableInt32()
		{
			return (int?) ReadObject();
		}

		/// <summary>
		/// Returns a Nullable Int64 from the stream.
		/// </summary>
		/// <returns>A Nullable Int64.</returns>
		public Int64? ReadNullableInt64()
		{
			return (long?) ReadObject();
		}

		/// <summary>
		/// Returns a Nullable SByte from the stream.
		/// </summary>
		/// <returns>A Nullable SByte.</returns>
		[CLSCompliant(false)]
		public SByte? ReadNullableSByte()
		{
			return (sbyte?) ReadObject();
		}

		/// <summary>
		/// Returns a Nullable Single from the stream.
		/// </summary>
		/// <returns>A Nullable Single.</returns>
		public Single? ReadNullableSingle()
		{
			return (float?) ReadObject();
		}

		/// <summary>
		/// Returns a Nullable TimeSpan from the stream.
		/// </summary>
		/// <returns>A Nullable TimeSpan.</returns>
		public TimeSpan? ReadNullableTimeSpan()
		{
			return (TimeSpan?) ReadObject();
		}

		/// <summary>
		/// Returns a Nullable UInt16 from the stream.
		/// </summary>
		/// <returns>A Nullable UInt16.</returns>
		[CLSCompliant(false)]
		public UInt16? ReadNullableUInt16()
		{
			return (ushort?) ReadObject();
		}

		/// <summary>
		/// Returns a Nullable UInt32 from the stream.
		/// </summary>
		/// <returns>A Nullable UInt32.</returns>
		[CLSCompliant(false)]
		public UInt32? ReadNullableUInt32()
		{
			return (uint?) ReadObject();
		}

		/// <summary>
		/// Returns a Nullable UInt64 from the stream.
		/// </summary>
		/// <returns>A Nullable UInt64.</returns>
		[CLSCompliant(false)]
		public UInt64? ReadNullableUInt64()
		{
			return (ulong?) ReadObject();
		}

		/// <summary>
		/// Returns a Byte[] from the stream.
		/// </summary>
		/// <returns>A Byte instance; or null.</returns>
		public byte[] ReadByteArray()
		{
			switch (ReadTypeCode())
			{
				case SerializedType.NullType: return null;
				case SerializedType.EmptyTypedArrayType: return new byte[0];

				default: return ReadByteArrayInternal();
			}
		}

		/// <summary>
		/// Returns a Char[] from the stream.
		/// </summary>
		/// <returns>A Char[] value; or null.</returns>
		public char[] ReadCharArray()
		{
			switch (ReadTypeCode())
			{
				case SerializedType.NullType: return null;
				case SerializedType.EmptyTypedArrayType: return new char[0];

				default: return ReadCharArrayInternal();
			}
		}

		/// <summary>
		/// Returns a Double[] from the stream.
		/// </summary>
		/// <returns>A Double[] instance; or null.</returns>
		public double[] ReadDoubleArray()
		{
			switch (ReadTypeCode())
			{
				case SerializedType.NullType: return null;
				case SerializedType.EmptyTypedArrayType: return new double[0];
				
				default: return ReadDoubleArrayInternal();
			}
		}

		/// <summary>
		/// Returns a Guid[] from the stream.
		/// </summary>
		/// <returns>A Guid[] instance; or null.</returns>
		public Guid[] ReadGuidArray()
		{
			switch (ReadTypeCode())
			{
				case SerializedType.NullType: return null;
				case SerializedType.EmptyTypedArrayType: return new Guid[0];

				default: return ReadGuidArrayInternal();
			}
		}

		/// <summary>
		/// Returns an Int16[] from the stream.
		/// </summary>
		/// <returns>An Int16[] instance; or null.</returns>
		public short[] ReadInt16Array()
		{
			var t = ReadTypeCode();

			switch (t)
			{
				case SerializedType.NullType: return null;
				case SerializedType.EmptyTypedArrayType: return new short[0];

				default: 
					var optimizeFlags = ReadTypedArrayOptimizeFlags(t);
					var result = new short[ReadOptimizedInt32()];

					for (var i = 0; i < result.Length; i++)
					{
						if ((optimizeFlags == null) || ((optimizeFlags != FullyOptimizableTypedArray) && !optimizeFlags[i]))
						{
							result[i] = ReadInt16();
						}
						else
						{
							result[i] = ReadOptimizedInt16();
						}
					}

					return result;
			}
		}

		/// <summary>
		/// Returns an object[] or null from the stream.
		/// </summary>
		/// <returns>A DateTime value.</returns>
		public object[] ReadObjectArray()
		{
			return ReadObjectArray(null);
		}

		/// <summary>
		/// Returns an object[] or null from the stream.
		/// The returned array will be typed according to the specified element type
		/// and the resulting array can be cast to the expected type.
		/// e.g.
		/// string[] myStrings = (string[]) reader.ReadObjectArray(typeof(string));
		/// 
		/// An exception will be thrown if any of the deserialized values cannot be
		/// cast to the specified elementType.
		/// 
		/// </summary>
		/// <param name="elementType">The Type of the expected array elements. null will return a plain object[].</param>
		/// <returns>An object[] instance.</returns>
		public object[] ReadObjectArray(Type elementType)
		{
			switch (ReadTypeCode())
			{
				case SerializedType.NullType: return null;
				case SerializedType.EmptyObjectArrayType: return elementType == null ? new object[0] : (object[]) Array.CreateInstance(elementType, 0);
				case SerializedType.EmptyTypedArrayType: throw new Exception(string.Format("None of the deserialized values can be casted to the specified elementType: '{0}'", elementType.FullName));

				default: return ReadOptimizedObjectArray(elementType);
			}
		}

		/// <summary>
		/// Returns a Single[] from the stream.
		/// </summary>
		/// <returns>A Single[] instance; or null.</returns>
		public float[] ReadSingleArray()
		{
			switch (ReadTypeCode())
			{
				case SerializedType.NullType: return null;
				case SerializedType.EmptyTypedArrayType: return new float[0];

				default: return ReadSingleArrayInternal();
			}
		}

		/// <summary>
		/// Returns an SByte[] from the stream.
		/// </summary>
		/// <returns>An SByte[] instance; or null.</returns>
		[CLSCompliant(false)]
		public sbyte[] ReadSByteArray()
		{
			switch (ReadTypeCode())
			{
				case SerializedType.NullType: return null;
				case SerializedType.EmptyTypedArrayType: return new sbyte[0];

				default: return ReadSByteArrayInternal();
			}
		}

		/// <summary>
		/// Returns a string[] or null from the stream.
		/// </summary>
		/// <returns>An string[] instance.</returns>
		public string[] ReadStringArray()
		{
			return (string[]) ReadObjectArray(typeof(string));
		}

		/// <summary>
		/// Returns a UInt16[] from the stream.
		/// </summary>
		/// <returns>A UInt16[] instance; or null.</returns>
		[CLSCompliant(false)]
		public ushort[] ReadUInt16Array()
		{
			var typeCode = ReadTypeCode();

			switch (typeCode)
			{
				case SerializedType.NullType: return null;
				case SerializedType.EmptyTypedArrayType: return new ushort[0];

				default:
					var optimizeFlags = ReadTypedArrayOptimizeFlags(typeCode);
					var result = new ushort[ReadOptimizedUInt32()];

					for (var i = 0; i < result.Length; i++)
					{
						if ((optimizeFlags == null) || ((optimizeFlags != FullyOptimizableTypedArray) && !optimizeFlags[i]))
						{
							result[i] = ReadUInt16();
						}
						else
						{
							result[i] = ReadOptimizedUInt16();
						}
					}

					return result;
			}
		}

		/// <summary>
		/// Returns a Boolean[] from the stream.
		/// </summary>
		/// <returns>A Boolean[] instance; or null.</returns>
		public bool[] ReadBooleanArray()
		{
			switch (ReadTypeCode())
			{
				case SerializedType.NullType: return null;
				case SerializedType.EmptyTypedArrayType: return new bool[0];

				default: return ReadBooleanArrayInternal();
			}
		}

		/// <summary>
		/// Returns a DateTime[] from the stream.
		/// </summary>
		/// <returns>A DateTime[] instance; or null.</returns>
		public DateTime[] ReadDateTimeArray()
		{
			var typeCode = ReadTypeCode();
			switch (typeCode)
			{
				case SerializedType.NullType: return null;
				case SerializedType.EmptyTypedArrayType: return new DateTime[0];

				default:
					var optimizeFlags = ReadTypedArrayOptimizeFlags(typeCode);
					var result = new DateTime[ReadOptimizedInt32()];

					for (var i = 0; i < result.Length; i++)
					{
						if ((optimizeFlags == null) || ((optimizeFlags != FullyOptimizableTypedArray) && !optimizeFlags[i]))
						{
							result[i] = ReadDateTime();
						}
						else
						{
							result[i] = ReadOptimizedDateTime();
						}
					}

					return result;
			}
		}

		/// <summary>
		/// Returns a Decimal[] from the stream.
		/// </summary>
		/// <returns>A Decimal[] instance; or null.</returns>
		public decimal[] ReadDecimalArray()
		{
			switch (ReadTypeCode())
			{
				case SerializedType.NullType: return null;
				case SerializedType.EmptyTypedArrayType: return new decimal[0];

				default: return ReadDecimalArrayInternal();
			}
		}

		/// <summary>
		/// Returns an Int32[] from the stream.
		/// </summary>
		/// <returns>An Int32[] instance; or null.</returns>
		public int[] ReadInt32Array()
		{
			var typeCode = ReadTypeCode();

			switch (typeCode)
			{
				case SerializedType.NullType: return null;
				case SerializedType.EmptyTypedArrayType: return new int[0];

				default:
					var optimizeFlags = ReadTypedArrayOptimizeFlags(typeCode);
					var result = new int[ReadOptimizedInt32()];

					for (var i = 0; i < result.Length; i++)
					{
						if ((optimizeFlags == null) || ((optimizeFlags != FullyOptimizableTypedArray) && !optimizeFlags[i]))
						{
							result[i] = ReadInt32();
						}
						else
						{
							result[i] = ReadOptimizedInt32();
						}
					}

					return result;
			}
		}

		/// <summary>
		/// Returns an Int64[] from the stream.
		/// </summary>
		/// <returns>An Int64[] instance; or null.</returns>
		public long[] ReadInt64Array()
		{
			var typeCode = ReadTypeCode();

			switch (typeCode)
			{
				case SerializedType.NullType: return null;
				case SerializedType.EmptyTypedArrayType: return new long[0];

				default:
					var optimizeFlags = ReadTypedArrayOptimizeFlags(typeCode);
					var result = new long[ReadOptimizedInt64()];

					for (var i = 0; i < result.Length; i++)
					{
						if ((optimizeFlags == null) || ((optimizeFlags != FullyOptimizableTypedArray) && !optimizeFlags[i]))
						{
							result[i] = ReadInt64();
						}
						else
						{
							result[i] = ReadOptimizedInt64();
						}
					}

					return result;
			}
		}

		/// <summary>
		/// Returns a string[] from the stream that was stored optimized.
		/// </summary>
		/// <returns>An string[] instance.</returns>
		public string[] ReadOptimizedStringArray()
		{
			return (string[]) ReadOptimizedObjectArray(typeof(string));
		}

		/// <summary>
		/// Returns a TimeSpan[] from the stream.
		/// </summary>
		/// <returns>A TimeSpan[] instance; or null.</returns>
		public TimeSpan[] ReadTimeSpanArray()
		{
			var typeCode = ReadTypeCode();

			switch (typeCode)
			{
				case SerializedType.NullType: return null;
				case SerializedType.EmptyTypedArrayType: return new TimeSpan[0];

				default:
					var optimizeFlags = ReadTypedArrayOptimizeFlags(typeCode);
					var result = new TimeSpan[ReadOptimizedInt32()];

					for (var i = 0; i < result.Length; i++)
					{
						if ((optimizeFlags == null) || ((optimizeFlags != FullyOptimizableTypedArray) && !optimizeFlags[i]))
						{
							result[i] = ReadTimeSpan();
						}
						else
						{
							result[i] = ReadOptimizedTimeSpan();
						}
					}

					return result;
			}
		}

		/// <summary>
		/// Returns a UInt[] from the stream.
		/// </summary>
		/// <returns>A UInt[] instance; or null.</returns>
		[CLSCompliant(false)]
		public uint[] ReadUInt32Array()
		{
			var typeCode = ReadTypeCode();

			switch (typeCode)
			{
				case SerializedType.NullType: return null;
				case SerializedType.EmptyTypedArrayType: return new uint[0];

				default:
					var optimizeFlags = ReadTypedArrayOptimizeFlags(typeCode);
					var result = new uint[ReadOptimizedUInt32()];

					for (var i = 0; i < result.Length; i++)
					{
						if ((optimizeFlags == null) || ((optimizeFlags != FullyOptimizableTypedArray) && !optimizeFlags[i]))
						{
							result[i] = ReadUInt32();
						}
						else
						{
							result[i] = ReadOptimizedUInt32();
						}
					}

					return result;
			}
		}

		/// <summary>
		/// Returns a UInt64[] from the stream.
		/// </summary>
		/// <returns>A UInt64[] instance; or null.</returns>
		[CLSCompliant((false))]
		public ulong[] ReadUInt64Array()
		{
			var typeCode = ReadTypeCode();

			switch (typeCode)
			{
				case SerializedType.NullType: return null;
				case SerializedType.EmptyTypedArrayType: return new ulong[0];

				default:
					var optimizeFlags = ReadTypedArrayOptimizeFlags(typeCode);
					var result = new ulong[ReadOptimizedInt64()];

					for (var i = 0; i < result.Length; i++)
					{
						if ((optimizeFlags == null) || ((optimizeFlags != FullyOptimizableTypedArray) && !optimizeFlags[i]))
						{
							result[i] = ReadUInt64();
						}
						else
						{
							result[i] = ReadOptimizedUInt64();
						}
					}

					return result;
			}
		}

		/// <summary>
		/// Returns a Boolean[] from the stream.
		/// </summary>
		/// <returns>A Boolean[] instance; or null.</returns>
		public bool[] ReadOptimizedBooleanArray()
		{
			return ReadBooleanArray();
		}

		/// <summary>
		/// Returns a DateTime[] from the stream.
		/// </summary>
		/// <returns>A DateTime[] instance; or null.</returns>
		public DateTime[] ReadOptimizedDateTimeArray()
		{
			return ReadDateTimeArray();
		}

		/// <summary>
		/// Returns a Decimal[] from the stream.
		/// </summary>
		/// <returns>A Decimal[] instance; or null.</returns>
		public decimal[] ReadOptimizedDecimalArray()
		{
			return ReadDecimalArray();
		}

		/// <summary>
		/// Returns a Int16[] from the stream.
		/// </summary>
		/// <returns>An Int16[] instance; or null.</returns>
		public short[] ReadOptimizedInt16Array()
		{
			return ReadInt16Array();
		}

		/// <summary>
		/// Returns a Int32[] from the stream.
		/// </summary>
		/// <returns>An Int32[] instance; or null.</returns>
		public int[] ReadOptimizedInt32Array()
		{
			return ReadInt32Array();
		}

		/// <summary>
		/// Returns a Int64[] from the stream.
		/// </summary>
		/// <returns>A Int64[] instance; or null.</returns>
		public long[] ReadOptimizedInt64Array()
		{
			return ReadInt64Array();
		}

		/// <summary>
		/// Returns a TimeSpan[] from the stream.
		/// </summary>
		/// <returns>A TimeSpan[] instance; or null.</returns>
		public TimeSpan[] ReadOptimizedTimeSpanArray()
		{
			return ReadTimeSpanArray();
		}

		/// <summary>
		/// Returns a UInt16[] from the stream.
		/// </summary>
		/// <returns>A UInt16[] instance; or null.</returns>
		[CLSCompliant(false)]
		public ushort[] ReadOptimizedUInt16Array()
		{
			return ReadUInt16Array();
		}

		/// <summary>
		/// Returns a UInt32[] from the stream.
		/// </summary>
		/// <returns>A UInt32[] instance; or null.</returns>
		[CLSCompliant(false)]
		public uint[] ReadOptimizedUInt32Array()
		{
			return ReadUInt32Array();
		}

		/// <summary>
		/// Returns a UInt64[] from the stream.
		/// </summary>
		/// <returns>A UInt64[] instance; or null.</returns>
		[CLSCompliant(false)]
		public ulong[] ReadOptimizedUInt64Array()
		{
			return ReadUInt64Array();
		}

		/// <summary>
		/// Allows an existing object, implementing IOwnedDataSerializable, to 
		/// retrieve its owned data from the stream.
		/// </summary>
		/// <param name="target">Any IOwnedDataSerializable object.</param>
		/// <param name="context">An optional, arbitrary object to allow context to be provided.</param>
		public void ReadOwnedData(IOwnedDataSerializable target, object context)
		{
			target.DeserializeOwnedData(this, context);
		}

		/// <summary>
		/// Returns the object associated with the object token read next from the stream.
		/// </summary>
		/// <returns>An object.</returns>
		public object ReadTokenizedObject()
		{
			var token = ReadOptimizedInt32();

			if (token >= objectTokenList.Count)
			{
				var tokenizedObject = ReadObject();

				objectTokenList.Add(tokenizedObject);

				return tokenizedObject;
			}

			return objectTokenList[token];
		}

		/// <summary>
		/// Initializes the token tables.
		/// </summary>
		/// <param name="stringTokenTablePresize">The string token table presize.</param>
		/// <param name="objectTokenTablePresize">The object token table presize.</param>
		void InitializeTokenTables(int stringTokenTablePresize, int objectTokenTablePresize)
		{
			stringTokenList = new List<string>(stringTokenTablePresize);
			objectTokenList = new List<object>(objectTokenTablePresize);
		}

		/// <summary>
		/// Returns a TimeSpan decoded from packed data.
		/// This routine is called from ReadOptimizedDateTime() and ReadOptimizedTimeSpan().
		/// <remarks>
		/// This routine uses a parameter to allow ReadOptimizedDateTime() to 'peek' at the
		/// next byte and extract the DateTimeKind from bits one and two (IsNegative and HasDays)
		/// which are never set for a Time portion of a DateTime.
		/// </remarks>
		/// </summary>
		/// <param name="initialByte">The first of two always-present bytes.</param>
		/// <returns>A decoded TimeSpan</returns>
		TimeSpan DecodeTimeSpan(byte initialByte)
		{
			var packedData = new BitVector32(initialByte | (ReadByte() << 8)); // Read first two bytes
			var hasTime = packedData[SerializationWriter.HasTimeSection] == 1;
			var hasSeconds = packedData[SerializationWriter.HasSecondsSection] == 1;
			var hasMilliseconds = packedData[SerializationWriter.HasMillisecondsSection] == 1;
			long ticks = 0;

			if (hasMilliseconds)
			{
				packedData = new BitVector32(packedData.Data | (ReadByte() << 16) | (ReadByte() << 24));
			}
			else if (hasTime && hasSeconds)
			{
				packedData = new BitVector32(packedData.Data | (ReadByte() << 16));
			}

			if (hasTime)
			{
				ticks += packedData[SerializationWriter.HoursSection] * TimeSpan.TicksPerHour;
				ticks += packedData[SerializationWriter.MinutesSection] * TimeSpan.TicksPerMinute;
			}

			if (hasSeconds)
			{
				ticks += packedData[(!hasTime && !hasMilliseconds)
				                    	? SerializationWriter.MinutesSection
				                    	: SerializationWriter.SecondsSection] * TimeSpan.TicksPerSecond;
			}

			if (hasMilliseconds)
			{
				ticks += packedData[SerializationWriter.MillisecondsSection] * TimeSpan.TicksPerMillisecond;
			}

			if (packedData[SerializationWriter.HasDaysSection] == 1)
			{
				ticks += ReadOptimizedInt32() * TimeSpan.TicksPerDay;
			}

			if (packedData[SerializationWriter.IsNegativeSection] == 1)
			{
				ticks = -ticks;
			}

			return new TimeSpan(ticks);
		}

		/// <summary>
		/// Creates a BitArray representing which elements of a typed array
		/// are serializable.
		/// </summary>
		/// <param name="serializedType">The type of typed array.</param>
		/// <returns>A BitArray denoting which elements are serializable.</returns>
		BitArray ReadTypedArrayOptimizeFlags(SerializedType serializedType)
		{
			switch (serializedType)
			{
				case SerializedType.FullyOptimizedTypedArrayType: return FullyOptimizableTypedArray;
				case SerializedType.PartiallyOptimizedTypedArrayType: return ReadOptimizedBitArray();

				default:
					return null;
			}
		}

		/// <summary>
		/// Returns an object based on supplied SerializedType.
		/// </summary>
		/// <returns>An object instance.</returns>
		object ProcessObject(SerializedType typeCode)
		{
			if (typeCode < SerializedType.NullType) return ReadTokenizedString((int) typeCode);

			switch (typeCode)
			{
				case SerializedType.NullType: return null;
				case SerializedType.Int32Type: return ReadInt32();
				case SerializedType.EmptyStringType: return string.Empty;
				case SerializedType.BooleanFalseType: return false;
				case SerializedType.ZeroInt32Type: return 0;
				case SerializedType.OptimizedInt32Type: return ReadOptimizedInt32();
				case SerializedType.OptimizedInt32NegativeType: return -ReadOptimizedInt32() - 1;
				case SerializedType.DecimalType: return ReadOptimizedDecimal();
				case SerializedType.ZeroDecimalType: return (Decimal) 0;
				case SerializedType.YStringType: return "Y";
				case SerializedType.DateTimeType: return ReadDateTime();
				case SerializedType.OptimizedDateTimeType: return ReadOptimizedDateTime();
				case SerializedType.SingleCharStringType: return Char.ToString(ReadChar());
				case SerializedType.SingleSpaceType: return " ";
				case SerializedType.OneInt32Type: return 1;
				case SerializedType.OptimizedInt16Type: return ReadOptimizedInt16();
				case SerializedType.OptimizedInt16NegativeType: return (short)(-ReadOptimizedInt16() - 1);
				case SerializedType.OneDecimalType: return (Decimal) 1;
				case SerializedType.BooleanTrueType: return true;
				case SerializedType.NStringType: return "N";
				case SerializedType.DBNullType: return DBNull.Value;
				case SerializedType.ObjectArrayType: return ReadOptimizedObjectArray();
				case SerializedType.EmptyObjectArrayType: return new object[0];
				case SerializedType.MinusOneInt32Type: return -1;
				case SerializedType.MinusOneInt64Type: return (Int64) (-1);
				case SerializedType.MinusOneInt16Type: return (Int16) (-1);
				case SerializedType.MinDateTimeType: return DateTime.MinValue;
				case SerializedType.GuidType: return ReadGuid();
				case SerializedType.EmptyGuidType: return Guid.Empty;
				case SerializedType.TimeSpanType: return ReadTimeSpan();
				case SerializedType.MaxDateTimeType: return DateTime.MaxValue;
				case SerializedType.ZeroTimeSpanType: return TimeSpan.Zero;
				case SerializedType.OptimizedTimeSpanType: return ReadOptimizedTimeSpan();
				case SerializedType.DoubleType: return ReadDouble();
				case SerializedType.ZeroDoubleType: return (Double) 0;
				case SerializedType.Int64Type: return ReadInt64();
				case SerializedType.ZeroInt64Type: return (Int64) 0;
				case SerializedType.OptimizedInt64Type: return ReadOptimizedInt64();
				case SerializedType.OptimizedInt64NegativeType: return -ReadOptimizedInt64() - 1;
				case SerializedType.Int16Type: return ReadInt16();
				case SerializedType.ZeroInt16Type: return (Int16) 0;
				case SerializedType.SingleType: return ReadSingle();
				case SerializedType.ZeroSingleType: return (Single) 0;
				case SerializedType.ByteType: return ReadByte();
				case SerializedType.ZeroByteType: return (Byte) 0;
				case SerializedType.OtherType: return new BinaryFormatter().Deserialize(BaseStream);
				case SerializedType.UInt16Type: return ReadUInt16();
				case SerializedType.ZeroUInt16Type: return (UInt16) 0;
				case SerializedType.UInt32Type: return ReadUInt32();
				case SerializedType.ZeroUInt32Type: return (UInt32) 0;
				case SerializedType.OptimizedUInt32Type: return ReadOptimizedUInt32();
				case SerializedType.UInt64Type: return ReadUInt64();
				case SerializedType.ZeroUInt64Type: return (UInt64) 0;
				case SerializedType.OptimizedUInt64Type: return ReadOptimizedUInt64();
				case SerializedType.BitVector32Type: return ReadBitVector32();
				case SerializedType.CharType: return ReadChar();
				case SerializedType.ZeroCharType: return (Char) 0;
				case SerializedType.SByteType: return ReadSByte();
				case SerializedType.ZeroSByteType: return (SByte) 0;
				case SerializedType.OneByteType: return (Byte) 1;
				case SerializedType.OneDoubleType: return (Double) 1;
				case SerializedType.OneCharType: return (Char) 1;
				case SerializedType.OneInt16Type: return (Int16) 1;
				case SerializedType.OneInt64Type: return (Int64) 1;
				case SerializedType.OneUInt16Type: return (UInt16) 1;
				case SerializedType.OptimizedUInt16Type: return ReadOptimizedUInt16();
				case SerializedType.OneUInt32Type: return (UInt32) 1;
				case SerializedType.OneUInt64Type: return (UInt64) 1;
				case SerializedType.OneSByteType: return (SByte) 1;
				case SerializedType.OneSingleType: return (Single) 1;
				case SerializedType.BitArrayType: return ReadOptimizedBitArray();
				case SerializedType.TypeType: return Type.GetType(ReadOptimizedString(), false);
				case SerializedType.ArrayListType: return ReadOptimizedArrayList();
				case SerializedType.SingleInstanceType:
					try
					{
						return Activator.CreateInstance(Type.GetType(ReadStringDirect()), true);
					}
					catch
					{
						// cannot recover from this, swallow.
						return null;
					}

				case SerializedType.OwnedDataSerializableAndRecreatableType:
					{
						var result = Activator.CreateInstance(ReadOptimizedType());

						ReadOwnedData((IOwnedDataSerializable) result, null);

						return result;
					}

				case SerializedType.OptimizedEnumType:
					{
						var enumType = ReadOptimizedType();
						var underlyingType = Enum.GetUnderlyingType(enumType);

						if ((underlyingType == typeof(int)) || (underlyingType == typeof(uint)) ||
						    (underlyingType == typeof(long)) || (underlyingType == typeof(ulong)))
						{
							return Enum.ToObject(enumType, ReadOptimizedUInt64());
						}
						
						return Enum.ToObject(enumType, ReadUInt64());
					}

				case SerializedType.EnumType:
					{
						var enumType = ReadOptimizedType();
						var underlyingType = Enum.GetUnderlyingType(enumType);

						if (underlyingType == typeof(Int32)) return Enum.ToObject(enumType, ReadInt32());
						if (underlyingType == typeof(Byte)) return Enum.ToObject(enumType, ReadByte());
						if (underlyingType == typeof(Int16)) return Enum.ToObject(enumType, ReadInt16());
						if (underlyingType == typeof(UInt32)) return Enum.ToObject(enumType, ReadUInt32());
						if (underlyingType == typeof(Int64)) return Enum.ToObject(enumType, ReadInt64());
						if (underlyingType == typeof(SByte)) return Enum.ToObject(enumType, ReadSByte());
						if (underlyingType == typeof(UInt16)) return Enum.ToObject(enumType, ReadUInt16());
					
						return Enum.ToObject(enumType, ReadUInt64()); 
					}

				case SerializedType.SurrogateHandledType:
					{
						var serializedType = ReadOptimizedType();
						var typeSurrogate = SerializationWriter.FindSurrogateForType(serializedType);

						return typeSurrogate.Deserialize(this, serializedType);
					}

				default:
					{
						var result = ProcessArrayTypes(typeCode, null);
						if (result != null) return result;

						throw new InvalidOperationException("Unrecognized TypeCode: " + typeCode);
					}
			}
		}

		/// <summary>
		/// Determine whether the passed-in type code refers to an array type
		/// and deserializes the array if it is.
		/// Returns null if not an array type.
		/// </summary>
		/// <param name="typeCode">The SerializedType to check.</param>
		/// <param name="defaultElementType">The Type of array element; null if to be read from stream.</param>
		/// <returns></returns>
		object ProcessArrayTypes(SerializedType typeCode, Type defaultElementType)
		{
			switch (typeCode)
			{
				case SerializedType.StringArrayType: return ReadOptimizedStringArray();
				case SerializedType.Int32ArrayType: return ReadInt32Array();
				case SerializedType.Int64ArrayType: return ReadInt64Array();
				case SerializedType.DecimalArrayType: return ReadDecimalArrayInternal();
				case SerializedType.TimeSpanArrayType: return ReadTimeSpanArray();
				case SerializedType.UInt32ArrayType: return ReadUInt32Array();
				case SerializedType.UInt64ArrayType: return ReadUInt64Array();
				case SerializedType.DateTimeArrayType: return ReadDateTimeArray();
				case SerializedType.BooleanArrayType: return ReadBooleanArrayInternal();
				case SerializedType.ByteArrayType: return ReadByteArrayInternal();
				case SerializedType.CharArrayType: return ReadCharArrayInternal();
				case SerializedType.DoubleArrayType: return ReadDoubleArrayInternal();
				case SerializedType.SingleArrayType: return ReadSingleArrayInternal();
				case SerializedType.GuidArrayType: return ReadGuidArrayInternal();
				case SerializedType.SByteArrayType: return ReadSByteArrayInternal();
				case SerializedType.Int16ArrayType: return ReadInt16Array();
				case SerializedType.UInt16ArrayType: return ReadUInt16Array();
				case SerializedType.EmptyTypedArrayType: return Array.CreateInstance(defaultElementType ?? ReadOptimizedType(), 0);
				case SerializedType.OtherTypedArrayType: return ReadOptimizedObjectArray(ReadOptimizedType());
				case SerializedType.ObjectArrayType: return ReadOptimizedObjectArray(defaultElementType);
				case SerializedType.NonOptimizedTypedArrayType:
				case SerializedType.PartiallyOptimizedTypedArrayType:
				case SerializedType.FullyOptimizedTypedArrayType:
					var optimizeFlags = ReadTypedArrayOptimizeFlags(typeCode);
					var length = ReadOptimizedInt32();

					if (defaultElementType == null)
					{
						defaultElementType = ReadOptimizedType();
					}

					var result = Array.CreateInstance(defaultElementType, length);

					for (var i = 0; i < length; i++)
					{
						if (optimizeFlags == null)
						{
							result.SetValue(ReadObject(), i);
						}
						else if ((optimizeFlags == FullyOptimizableTypedArray) || !optimizeFlags[i])
						{
							var value = (IOwnedDataSerializable) Activator.CreateInstance(defaultElementType);

							ReadOwnedData(value, null);
							result.SetValue(value, i);
						}
					}

					return result;
			}

			return null;
		}

		/// <summary>
		/// Returns the string value associated with the string token read next from the stream.
		/// </summary>
		/// <returns>A DateTime value.</returns>
		string ReadTokenizedString(int bucket)
		{
			var stringTokenIndex = (ReadOptimizedInt32() << 7) + bucket;

			if (stringTokenIndex >= stringTokenList.Count)
			{
				stringTokenList.Add(base.ReadString());
			}

			return stringTokenList[stringTokenIndex];
		}
		/// <summary>
		/// Returns the SerializedType read next from the stream.
		/// </summary>
		/// <returns>A SerializedType value.</returns>
		SerializedType ReadTypeCode()
		{
			return (SerializedType) ReadByte();
		}

		/// <summary>
		/// Internal implementation returning a Bool[].
		/// </summary>
		/// <returns>A Bool[].</returns>
		bool[] ReadBooleanArrayInternal() 
		{
			var bitArray = ReadOptimizedBitArray();
			var result = new bool[bitArray.Count];

			for(var i = 0; i < result.Length; i++)
			{
				result[i] = bitArray[i];
			}

			return result;
		}

		/// <summary>
		/// Internal implementation returning a Byte[].
		/// </summary>
		/// <returns>A Byte[].</returns>
		byte[] ReadByteArrayInternal()
		{
			return ReadBytes(ReadOptimizedInt32());
		}

		/// <summary>
		/// Internal implementation returning a Char[].
		/// </summary>
		/// <returns>A Char[].</returns>
		char[] ReadCharArrayInternal()
		{
			return ReadChars(ReadOptimizedInt32());
		}

		/// <summary>
		/// Internal implementation returning a Decimal[].
		/// </summary>
		/// <returns>A Decimal[].</returns>
		decimal[] ReadDecimalArrayInternal()
		{
			var result = new decimal[ReadOptimizedInt32()];

			for(var i = 0; i < result.Length; i++)
			{
				result[i] = ReadOptimizedDecimal();
			}

			return result;
		}

		/// <summary>
		/// Internal implementation returning a Double[].
		/// </summary>
		/// <returns>A Double[].</returns>
		double[] ReadDoubleArrayInternal()
		{
			var result = new double[ReadOptimizedInt32()];

			for(var i = 0; i < result.Length; i++)
			{
				result[i] = ReadDouble();
			}

			return result;
		}

		/// <summary>
		/// Internal implementation returning a Guid[].
		/// </summary>
		/// <returns>A Guid[].</returns>
		Guid[] ReadGuidArrayInternal()
		{
			var result = new Guid[ReadOptimizedInt32()];

			for(var i = 0; i < result.Length; i++)
			{
				result[i] = ReadGuid();
			}

			return result;
		}

		/// <summary>
		/// Internal implementation returning an SByte[].
		/// </summary>
		/// <returns>An SByte[].</returns>
		sbyte[] ReadSByteArrayInternal()
		{
			var result = new sbyte[ReadOptimizedInt32()];

			for(var i = 0; i < result.Length; i++)
			{
				result[i] = ReadSByte();
			}

			return result;
		}

		/// <summary>
		/// Internal implementation returning a Single[].
		/// </summary>
		/// <returns>A Single[].</returns>
		float[] ReadSingleArrayInternal()
		{
			var result = new float[ReadOptimizedInt32()];

			for(var i = 0; i < result.Length; i++)
			{
				result[i] = ReadSingle();
			}

			return result;
		}

		#region Class Property declarations
		/// <summary>
		/// Returns the number of bytes or serialized remaining to be processed.
		/// Useful for checking that deserialization is complete.
		/// 
		/// Warning: Retrieving the Position in certain stream types can be expensive,
		/// e.g. a FileStream, so use sparingly unless known to be a MemoryStream.
		/// </summary>
		public int BytesRemaining
		{
			get { return endPosition == 0 ? -1 : endPosition - (int) BaseStream.Position; }
		}
		#endregion
	}
}

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