using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.IO; using System.Runtime.Serialization.Formatters.Binary; namespace DelftTools.Utils.Serialization { /// /// 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. /// 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 stringTokenList; List objectTokenList; #region Debug Related /// /// Dumps the string tables. /// /// The list. [Conditional("DEBUG")] public void DumpStringTables(ArrayList list) { list.AddRange(stringTokenList); } #endregion /// /// Creates a SerializationReader using a byte[] previous created by SerializationWriter /// /// A MemoryStream is used to access the data without making a copy of it. /// /// The byte[] containining serialized data. public SerializationReader(byte[] data): this(new MemoryStream(data)) { } /// /// Initializes a new instance of the class. /// /// The stream. 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()); } } /// /// Creates a SerializationReader based around the passed Stream. /// Allows the string and object token tables to be presized using /// the specified values. /// /// The stream containing the serialized data /// Number of string tokens to presize /// Number of object tokens to presize 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); } /// /// Returns an ArrayList or null from the stream. /// /// An ArrayList instance. public ArrayList ReadArrayList() { if (ReadTypeCode() == SerializedType.NullType) return null; return new ArrayList(ReadOptimizedObjectArray()); } /// /// Returns a BitArray or null from the stream. /// /// A BitArray instance. public BitArray ReadBitArray() { if (ReadTypeCode() == SerializedType.NullType) return null; return ReadOptimizedBitArray(); } /// /// Returns a BitVector32 value from the stream. /// /// A BitVector32 value. public BitVector32 ReadBitVector32() { return new BitVector32(ReadInt32()); } /// /// Reads the specified number of bytes directly from the stream. /// /// The number of bytes to read /// A byte[] containing the read bytes public byte[] ReadBytesDirect(int count) { return ReadBytes(count); } /// /// Returns a DateTime value from the stream. /// /// A DateTime value. public DateTime ReadDateTime() { return DateTime.FromBinary(ReadInt64()); } /// /// Returns a Guid value from the stream. /// /// A DateTime value. public Guid ReadGuid() { return new Guid(ReadBytes(16)); } /// /// Returns an object based on the SerializedType read next from the stream. /// /// An object instance. public object ReadObject() { return ProcessObject((SerializedType) ReadByte()); } /// /// Called ReadOptimizedString(). /// This override to hide base BinaryReader.ReadString(). /// /// A string value. public override string ReadString() { return ReadOptimizedString(); } /// /// Returns a string value from the stream. /// /// A string value. public string ReadStringDirect() { return base.ReadString(); } /// /// Returns a TimeSpan value from the stream. /// /// A TimeSpan value. public TimeSpan ReadTimeSpan() { return new TimeSpan(ReadInt64()); } /// /// Returns a Type or null from the stream. /// /// Throws an exception if the Type cannot be found. /// /// A Type instance. public Type ReadType() { return ReadType(true); } /// /// Returns a Type or null from the stream. /// /// Throws an exception if the Type cannot be found and throwOnError is true. /// /// A Type instance. public Type ReadType(bool throwOnError) { if (ReadTypeCode() == SerializedType.NullType) return null; return Type.GetType(ReadOptimizedString(), throwOnError); } /// /// Returns an ArrayList from the stream that was stored optimized. /// /// An ArrayList instance. public ArrayList ReadOptimizedArrayList() { return new ArrayList(ReadOptimizedObjectArray()); } /// /// Returns a BitArray from the stream that was stored optimized. /// /// A BitArray instance. public BitArray ReadOptimizedBitArray() { var length = ReadOptimizedInt32(); if (length == 0) return FullyOptimizableTypedArray; return new BitArray(ReadBytes((length + 7) / 8)) { Length = length }; } /// /// Returns a BitVector32 value from the stream that was stored optimized. /// /// A BitVector32 value. public BitVector32 ReadOptimizedBitVector32() { return new BitVector32(Read7BitEncodedInt()); } /// /// Returns a DateTime value from the stream that was stored optimized. /// /// A DateTime value. 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; } /// /// Returns a Decimal value from the stream that was stored optimized. /// /// A Decimal value. 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); } /// /// Returns an Int32 value from the stream that was stored optimized. /// /// An Int32 value. 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; } } /// /// Returns an Int16 value from the stream that was stored optimized. /// /// An Int16 value. public short ReadOptimizedInt16() { return (short) ReadOptimizedInt32(); } /// /// Returns an Int64 value from the stream that was stored optimized. /// /// An Int64 value. 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; } } /// /// Returns an object[] from the stream that was stored optimized. /// /// An object[] instance. public object[] ReadOptimizedObjectArray() { return ReadOptimizedObjectArray(null); } /// /// 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. /// /// /// The Type of the expected array elements. null will return a plain object[]. /// An object[] instance. 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; } /// /// Returns a pair of object[] arrays from the stream that were stored optimized. /// /// A pair of object[] arrays. 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; } } } /// /// Returns a string value from the stream that was stored optimized. /// /// A string value. 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"); } } /// /// Returns a TimeSpan value from the stream that was stored optimized. /// /// A TimeSpan value. public TimeSpan ReadOptimizedTimeSpan() { return DecodeTimeSpan(ReadByte()); } /// /// Returns a Type from the stream. /// /// Throws an exception if the Type cannot be found. /// /// A Type instance. public Type ReadOptimizedType() { return ReadOptimizedType(true); } /// /// Returns a Type from the stream. /// /// Throws an exception if the Type cannot be found and throwOnError is true. /// /// A Type instance. public Type ReadOptimizedType(bool throwOnError) { return Type.GetType(ReadOptimizedString(), throwOnError); } /// /// Returns a UInt16 value from the stream that was stored optimized. /// /// A UInt16 value. public ushort ReadOptimizedUInt16() { return (ushort) ReadOptimizedUInt32(); } /// /// Returns a UInt32 value from the stream that was stored optimized. /// /// A UInt32 value. 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; } } /// /// Returns a UInt64 value from the stream that was stored optimized. /// /// A UInt64 value. 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; } } /// /// Returns a typed array from the stream. /// /// A typed array. public Array ReadTypedArray() { return (Array) ProcessArrayTypes(ReadTypeCode(), null); } /// /// Returns a new, simple generic dictionary populated with keys and values from the stream. /// /// The key Type. /// The value Type. /// A new, simple, populated generic Dictionary. public Dictionary ReadDictionary() { var result = new Dictionary(); ReadDictionary(result); return result; } /// /// 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. /// /// The key Type. /// The value Type. public void ReadDictionary(Dictionary dictionary) { var keys = (K[]) ProcessArrayTypes(ReadTypeCode(), typeof(K)); var values = (V[]) ProcessArrayTypes(ReadTypeCode(), typeof(V)); if (dictionary == null) { dictionary = new Dictionary(keys.Length); } for (var i = 0; i < keys.Length; i++) { dictionary.Add(keys[i], values[i]); } } /// /// Returns a generic List populated with values from the stream. /// /// The list Type. /// A new generic List. public List ReadList() { return new List((T[]) ProcessArrayTypes(ReadTypeCode(), typeof(T))); } /// /// Returns a Nullable struct from the stream. /// The value returned must be cast to the correct Nullable type. /// Synonym for ReadObject(); /// /// A struct value or null public ValueType ReadNullable() { return (ValueType) ReadObject(); } /// /// Returns a Nullable Boolean from the stream. /// /// A Nullable Boolean. public Boolean? ReadNullableBoolean() { return (bool?) ReadObject(); } /// /// Returns a Nullable Byte from the stream. /// /// A Nullable Byte. public Byte? ReadNullableByte() { return (byte?) ReadObject(); } /// /// Returns a Nullable Char from the stream. /// /// A Nullable Char. public Char? ReadNullableChar() { return (char?) ReadObject(); } /// /// Returns a Nullable DateTime from the stream. /// /// A Nullable DateTime. public DateTime? ReadNullableDateTime() { return (DateTime?) ReadObject(); } /// /// Returns a Nullable Decimal from the stream. /// /// A Nullable Decimal. public Decimal? ReadNullableDecimal() { return (decimal?) ReadObject(); } /// /// Returns a Nullable Double from the stream. /// /// A Nullable Double. public Double? ReadNullableDouble() { return (double?) ReadObject(); } /// /// Returns a Nullable Guid from the stream. /// /// A Nullable Guid. public Guid? ReadNullableGuid() { return (Guid?) ReadObject(); } /// /// Returns a Nullable Int16 from the stream. /// /// A Nullable Int16. public Int16? ReadNullableInt16() { return (short?) ReadObject(); } /// /// Returns a Nullable Int32 from the stream. /// /// A Nullable Int32. public Int32? ReadNullableInt32() { return (int?) ReadObject(); } /// /// Returns a Nullable Int64 from the stream. /// /// A Nullable Int64. public Int64? ReadNullableInt64() { return (long?) ReadObject(); } /// /// Returns a Nullable SByte from the stream. /// /// A Nullable SByte. public SByte? ReadNullableSByte() { return (sbyte?) ReadObject(); } /// /// Returns a Nullable Single from the stream. /// /// A Nullable Single. public Single? ReadNullableSingle() { return (float?) ReadObject(); } /// /// Returns a Nullable TimeSpan from the stream. /// /// A Nullable TimeSpan. public TimeSpan? ReadNullableTimeSpan() { return (TimeSpan?) ReadObject(); } /// /// Returns a Nullable UInt16 from the stream. /// /// A Nullable UInt16. public UInt16? ReadNullableUInt16() { return (ushort?) ReadObject(); } /// /// Returns a Nullable UInt32 from the stream. /// /// A Nullable UInt32. public UInt32? ReadNullableUInt32() { return (uint?) ReadObject(); } /// /// Returns a Nullable UInt64 from the stream. /// /// A Nullable UInt64. public UInt64? ReadNullableUInt64() { return (ulong?) ReadObject(); } /// /// Returns a Byte[] from the stream. /// /// A Byte instance; or null. public byte[] ReadByteArray() { switch (ReadTypeCode()) { case SerializedType.NullType: return null; case SerializedType.EmptyTypedArrayType: return new byte[0]; default: return ReadByteArrayInternal(); } } /// /// Returns a Char[] from the stream. /// /// A Char[] value; or null. public char[] ReadCharArray() { switch (ReadTypeCode()) { case SerializedType.NullType: return null; case SerializedType.EmptyTypedArrayType: return new char[0]; default: return ReadCharArrayInternal(); } } /// /// Returns a Double[] from the stream. /// /// A Double[] instance; or null. public double[] ReadDoubleArray() { switch (ReadTypeCode()) { case SerializedType.NullType: return null; case SerializedType.EmptyTypedArrayType: return new double[0]; default: return ReadDoubleArrayInternal(); } } /// /// Returns a Guid[] from the stream. /// /// A Guid[] instance; or null. public Guid[] ReadGuidArray() { switch (ReadTypeCode()) { case SerializedType.NullType: return null; case SerializedType.EmptyTypedArrayType: return new Guid[0]; default: return ReadGuidArrayInternal(); } } /// /// Returns an Int16[] from the stream. /// /// An Int16[] instance; or null. 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; } } /// /// Returns an object[] or null from the stream. /// /// A DateTime value. public object[] ReadObjectArray() { return ReadObjectArray(null); } /// /// 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. /// /// /// The Type of the expected array elements. null will return a plain object[]. /// An object[] instance. 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); } } /// /// Returns a Single[] from the stream. /// /// A Single[] instance; or null. public float[] ReadSingleArray() { switch (ReadTypeCode()) { case SerializedType.NullType: return null; case SerializedType.EmptyTypedArrayType: return new float[0]; default: return ReadSingleArrayInternal(); } } /// /// Returns an SByte[] from the stream. /// /// An SByte[] instance; or null. public sbyte[] ReadSByteArray() { switch (ReadTypeCode()) { case SerializedType.NullType: return null; case SerializedType.EmptyTypedArrayType: return new sbyte[0]; default: return ReadSByteArrayInternal(); } } /// /// Returns a string[] or null from the stream. /// /// An string[] instance. public string[] ReadStringArray() { return (string[]) ReadObjectArray(typeof(string)); } /// /// Returns a UInt16[] from the stream. /// /// A UInt16[] instance; or null. 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; } } /// /// Returns a Boolean[] from the stream. /// /// A Boolean[] instance; or null. public bool[] ReadBooleanArray() { switch (ReadTypeCode()) { case SerializedType.NullType: return null; case SerializedType.EmptyTypedArrayType: return new bool[0]; default: return ReadBooleanArrayInternal(); } } /// /// Returns a DateTime[] from the stream. /// /// A DateTime[] instance; or null. 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; } } /// /// Returns a Decimal[] from the stream. /// /// A Decimal[] instance; or null. public decimal[] ReadDecimalArray() { switch (ReadTypeCode()) { case SerializedType.NullType: return null; case SerializedType.EmptyTypedArrayType: return new decimal[0]; default: return ReadDecimalArrayInternal(); } } /// /// Returns an Int32[] from the stream. /// /// An Int32[] instance; or null. 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; } } /// /// Returns an Int64[] from the stream. /// /// An Int64[] instance; or null. 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; } } /// /// Returns a string[] from the stream that was stored optimized. /// /// An string[] instance. public string[] ReadOptimizedStringArray() { return (string[]) ReadOptimizedObjectArray(typeof(string)); } /// /// Returns a TimeSpan[] from the stream. /// /// A TimeSpan[] instance; or null. 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; } } /// /// Returns a UInt[] from the stream. /// /// A UInt[] instance; or null. 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; } } /// /// Returns a UInt64[] from the stream. /// /// A UInt64[] instance; or null. 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; } } /// /// Returns a Boolean[] from the stream. /// /// A Boolean[] instance; or null. public bool[] ReadOptimizedBooleanArray() { return ReadBooleanArray(); } /// /// Returns a DateTime[] from the stream. /// /// A DateTime[] instance; or null. public DateTime[] ReadOptimizedDateTimeArray() { return ReadDateTimeArray(); } /// /// Returns a Decimal[] from the stream. /// /// A Decimal[] instance; or null. public decimal[] ReadOptimizedDecimalArray() { return ReadDecimalArray(); } /// /// Returns a Int16[] from the stream. /// /// An Int16[] instance; or null. public short[] ReadOptimizedInt16Array() { return ReadInt16Array(); } /// /// Returns a Int32[] from the stream. /// /// An Int32[] instance; or null. public int[] ReadOptimizedInt32Array() { return ReadInt32Array(); } /// /// Returns a Int64[] from the stream. /// /// A Int64[] instance; or null. public long[] ReadOptimizedInt64Array() { return ReadInt64Array(); } /// /// Returns a TimeSpan[] from the stream. /// /// A TimeSpan[] instance; or null. public TimeSpan[] ReadOptimizedTimeSpanArray() { return ReadTimeSpanArray(); } /// /// Returns a UInt16[] from the stream. /// /// A UInt16[] instance; or null. public ushort[] ReadOptimizedUInt16Array() { return ReadUInt16Array(); } /// /// Returns a UInt32[] from the stream. /// /// A UInt32[] instance; or null. public uint[] ReadOptimizedUInt32Array() { return ReadUInt32Array(); } /// /// Returns a UInt64[] from the stream. /// /// A UInt64[] instance; or null. public ulong[] ReadOptimizedUInt64Array() { return ReadUInt64Array(); } /// /// Allows an existing object, implementing IOwnedDataSerializable, to /// retrieve its owned data from the stream. /// /// Any IOwnedDataSerializable object. /// An optional, arbitrary object to allow context to be provided. public void ReadOwnedData(IOwnedDataSerializable target, object context) { target.DeserializeOwnedData(this, context); } /// /// Returns the object associated with the object token read next from the stream. /// /// An object. public object ReadTokenizedObject() { var token = ReadOptimizedInt32(); if (token >= objectTokenList.Count) { var tokenizedObject = ReadObject(); objectTokenList.Add(tokenizedObject); return tokenizedObject; } return objectTokenList[token]; } /// /// Initializes the token tables. /// /// The string token table presize. /// The object token table presize. void InitializeTokenTables(int stringTokenTablePresize, int objectTokenTablePresize) { stringTokenList = new List(stringTokenTablePresize); objectTokenList = new List(objectTokenTablePresize); } /// /// Returns a TimeSpan decoded from packed data. /// This routine is called from ReadOptimizedDateTime() and ReadOptimizedTimeSpan(). /// /// 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. /// /// /// The first of two always-present bytes. /// A decoded TimeSpan 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); } /// /// Creates a BitArray representing which elements of a typed array /// are serializable. /// /// The type of typed array. /// A BitArray denoting which elements are serializable. BitArray ReadTypedArrayOptimizeFlags(SerializedType serializedType) { switch (serializedType) { case SerializedType.FullyOptimizedTypedArrayType: return FullyOptimizableTypedArray; case SerializedType.PartiallyOptimizedTypedArrayType: return ReadOptimizedBitArray(); default: return null; } } /// /// Returns an object based on supplied SerializedType. /// /// An object instance. 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); } } } /// /// 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. /// /// The SerializedType to check. /// The Type of array element; null if to be read from stream. /// 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; } /// /// Returns the string value associated with the string token read next from the stream. /// /// A DateTime value. string ReadTokenizedString(int bucket) { var stringTokenIndex = (ReadOptimizedInt32() << 7) + bucket; if (stringTokenIndex >= stringTokenList.Count) { stringTokenList.Add(base.ReadString()); } return stringTokenList[stringTokenIndex]; } /// /// Returns the SerializedType read next from the stream. /// /// A SerializedType value. SerializedType ReadTypeCode() { return (SerializedType) ReadByte(); } /// /// Internal implementation returning a Bool[]. /// /// A Bool[]. 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; } /// /// Internal implementation returning a Byte[]. /// /// A Byte[]. byte[] ReadByteArrayInternal() { return ReadBytes(ReadOptimizedInt32()); } /// /// Internal implementation returning a Char[]. /// /// A Char[]. char[] ReadCharArrayInternal() { return ReadChars(ReadOptimizedInt32()); } /// /// Internal implementation returning a Decimal[]. /// /// A Decimal[]. decimal[] ReadDecimalArrayInternal() { var result = new decimal[ReadOptimizedInt32()]; for(var i = 0; i < result.Length; i++) { result[i] = ReadOptimizedDecimal(); } return result; } /// /// Internal implementation returning a Double[]. /// /// A Double[]. double[] ReadDoubleArrayInternal() { var result = new double[ReadOptimizedInt32()]; for(var i = 0; i < result.Length; i++) { result[i] = ReadDouble(); } return result; } /// /// Internal implementation returning a Guid[]. /// /// A Guid[]. Guid[] ReadGuidArrayInternal() { var result = new Guid[ReadOptimizedInt32()]; for(var i = 0; i < result.Length; i++) { result[i] = ReadGuid(); } return result; } /// /// Internal implementation returning an SByte[]. /// /// An SByte[]. sbyte[] ReadSByteArrayInternal() { var result = new sbyte[ReadOptimizedInt32()]; for(var i = 0; i < result.Length; i++) { result[i] = ReadSByte(); } return result; } /// /// Internal implementation returning a Single[]. /// /// A Single[]. float[] ReadSingleArrayInternal() { var result = new float[ReadOptimizedInt32()]; for(var i = 0; i < result.Length; i++) { result[i] = ReadSingle(); } return result; } #region Class Property declarations /// /// 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. /// public int BytesRemaining { get { return endPosition == 0 ? -1 : endPosition - (int) BaseStream.Position; } } #endregion } }