// ObjectReader.cs // // Author: // Lluis Sanchez Gual (lluis@ideary.com) // Patrik Torstensson // // (C) 2003 Lluis Sanchez Gual // // Copyright (C) 2004 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Runtime.Serialization; using System.IO; using System.Collections; using System.Reflection; using System.Runtime.Remoting.Messaging; using System.Globalization; namespace System.Runtime.Serialization.Formatters.Binary { internal class ObjectReader { BinaryFormatter _formatter; ISurrogateSelector _surrogateSelector; StreamingContext _context; SerializationBinder _binder; #if NET_1_1 TypeFilterLevel _filterLevel; #endif ObjectManager _manager; Hashtable _registeredAssemblies = new Hashtable(); Hashtable _typeMetadataCache = new Hashtable(); object _lastObject = null; long _lastObjectID = 0; long _rootObjectID = 0; byte[] arrayBuffer; int ArrayBufferLength = 4096; class TypeMetadata { public Type Type; public Type[] MemberTypes; public string[] MemberNames; public MemberInfo[] MemberInfos; public int FieldCount; public bool NeedsSerializationInfo; } class ArrayNullFiller { public ArrayNullFiller(int count) { NullCount = count; } public int NullCount; } public ObjectReader (BinaryFormatter formatter) { _formatter = formatter; _surrogateSelector = formatter.SurrogateSelector; _context = formatter.Context; _binder = formatter.Binder; _manager = new ObjectManager (_surrogateSelector, _context); #if NET_1_1 _filterLevel = formatter.FilterLevel; #endif } public void ReadObjectGraph (BinaryReader reader, bool readHeaders, out object result, out Header[] headers) { headers = null; // Reads the objects. The first object in the stream is the // root object. while (ReadNextObject (reader)) { if (readHeaders && (headers == null)) headers = (Header[])CurrentObject; else if (_rootObjectID == 0) _rootObjectID = _lastObjectID; } result = _manager.GetObject (_rootObjectID); } public bool ReadNextObject (BinaryReader reader) { BinaryElement element = (BinaryElement)reader.ReadByte (); if (element == BinaryElement.End) { _manager.DoFixups(); _manager.RaiseDeserializationEvent(); return false; } SerializationInfo info; long objectId; ReadObject (element, reader, out objectId, out _lastObject, out info); if (objectId != 0) { RegisterObject (objectId, _lastObject, info, 0, null, null); _lastObjectID = objectId; } return true; } public object CurrentObject { get { return _lastObject; } } // Reads an object from the stream. The object is registered in the ObjectManager. // The result can be either the object instance // or the id of the object (when what is found in the stream is an object reference). // If an object instance is read, the objectId is set to 0. private void ReadObject (BinaryElement element, BinaryReader reader, out long objectId, out object value, out SerializationInfo info) { switch (element) { case BinaryElement.RefTypeObject: ReadRefTypeObjectInstance (reader, out objectId, out value, out info); break; case BinaryElement.UntypedRuntimeObject: ReadObjectInstance (reader, true, false, out objectId, out value, out info); break; case BinaryElement.UntypedExternalObject: ReadObjectInstance (reader, false, false, out objectId, out value, out info); break; case BinaryElement.RuntimeObject: ReadObjectInstance (reader, true, true, out objectId, out value, out info); break; case BinaryElement.ExternalObject: ReadObjectInstance (reader, false, true, out objectId, out value, out info); break; case BinaryElement.String: info = null; ReadStringIntance (reader, out objectId, out value); break; case BinaryElement.GenericArray: info = null; ReadGenericArray (reader, out objectId, out value); break; case BinaryElement.BoxedPrimitiveTypeValue: value = ReadBoxedPrimitiveTypeValue (reader); objectId = 0; info = null; break; case BinaryElement.NullValue: value = null; objectId = 0; info = null; break; case BinaryElement.Assembly: ReadAssembly (reader); ReadObject ((BinaryElement)reader.ReadByte (), reader, out objectId, out value, out info); break; case BinaryElement.ArrayFiller8b: value = new ArrayNullFiller(reader.ReadByte()); objectId = 0; info = null; break; case BinaryElement.ArrayFiller32b: value = new ArrayNullFiller(reader.ReadInt32()); objectId = 0; info = null; break; case BinaryElement.ArrayOfPrimitiveType: ReadArrayOfPrimitiveType (reader, out objectId, out value); info = null; break; case BinaryElement.ArrayOfObject: ReadArrayOfObject (reader, out objectId, out value); info = null; break; case BinaryElement.ArrayOfString: ReadArrayOfString (reader, out objectId, out value); info = null; break; default: throw new SerializationException ("Unexpected binary element: " + (int)element); } } private void ReadAssembly (BinaryReader reader) { long id = (long) reader.ReadUInt32 (); string assemblyName = reader.ReadString (); _registeredAssemblies [id] = assemblyName; } private void ReadObjectInstance (BinaryReader reader, bool isRuntimeObject, bool hasTypeInfo, out long objectId, out object value, out SerializationInfo info) { objectId = (long) reader.ReadUInt32 (); TypeMetadata metadata = ReadTypeMetadata (reader, isRuntimeObject, hasTypeInfo); ReadObjectContent (reader, metadata, objectId, out value, out info); } private void ReadRefTypeObjectInstance (BinaryReader reader, out long objectId, out object value, out SerializationInfo info) { objectId = (long) reader.ReadUInt32 (); long refTypeObjectId = (long) reader.ReadUInt32 (); // Gets the type of the referred object and its metadata object refObj = _manager.GetObject (refTypeObjectId); if (refObj == null) throw new SerializationException ("Invalid binary format"); TypeMetadata metadata = (TypeMetadata)_typeMetadataCache [refObj.GetType()]; ReadObjectContent (reader, metadata, objectId, out value, out info); } private void ReadObjectContent (BinaryReader reader, TypeMetadata metadata, long objectId, out object objectInstance, out SerializationInfo info) { #if NET_1_1 if (_filterLevel == TypeFilterLevel.Low) objectInstance = FormatterServices.GetSafeUninitializedObject (metadata.Type); else #endif objectInstance = FormatterServices.GetUninitializedObject (metadata.Type); #if NET_2_0 _manager.RaiseOnDeserializingEvent (objectInstance); #endif info = metadata.NeedsSerializationInfo ? new SerializationInfo(metadata.Type, new FormatterConverter()) : null; if (metadata.MemberNames != null) for (int n=0; n= 0; dim--) indices[dim] = array.GetLowerBound (dim); bool end = false; while (!end) { ReadValue (reader, array, objectId, null, elementType, null, null, indices); for (int dim = array.Rank-1; dim >= 0; dim--) { indices[dim]++; if (indices[dim] > array.GetUpperBound (dim)) { if (dim > 0) { indices[dim] = array.GetLowerBound (dim); continue; // Increment the next dimension's index } end = true; // That was the last dimension. Finished. } break; } } val = array; } private object ReadBoxedPrimitiveTypeValue (BinaryReader reader) { Type type = ReadType (reader, TypeTag.PrimitiveType); return ReadPrimitiveTypeValue (reader, type); } private void ReadArrayOfPrimitiveType (BinaryReader reader, out long objectId, out object val) { objectId = (long) reader.ReadUInt32 (); int length = reader.ReadInt32 (); Type elementType = ReadType (reader, TypeTag.PrimitiveType); switch (Type.GetTypeCode (elementType)) { case TypeCode.Boolean: { bool[] arr = new bool [length]; for (int n = 0; n < length; n++) arr [n] = reader.ReadBoolean(); val = arr; break; } case TypeCode.Byte: { byte[] arr = new byte [length]; int pos = 0; while (pos < length) { int nr = reader.Read (arr, pos, length - pos); if (nr == 0) break; pos += nr; } val = arr; break; } case TypeCode.Char: { char[] arr = new char [length]; int pos = 0; while (pos < length) { int nr = reader.Read (arr, pos, length - pos); if (nr == 0) break; pos += nr; } val = arr; break; } case TypeCode.DateTime: { DateTime[] arr = new DateTime [length]; for (int n = 0; n < length; n++) { ulong nr = reader.ReadUInt64 (); const ulong mask = (1ul << 62) - 1; long ticks = (long) (nr & mask); #if NET_2_0 DateTimeKind kind = (DateTimeKind) (nr >> 62); arr [n] = new DateTime (ticks, kind); #else arr [n] = new DateTime (ticks); #endif } val = arr; break; } case TypeCode.Decimal: { Decimal[] arr = new Decimal [length]; for (int n = 0; n < length; n++) arr [n] = reader.ReadDecimal(); val = arr; break; } case TypeCode.Double: { Double[] arr = new Double [length]; if (length > 2) BlockRead (reader, arr, 8); else for (int n = 0; n < length; n++) arr [n] = reader.ReadDouble(); val = arr; break; } case TypeCode.Int16: { short[] arr = new short [length]; if (length > 2) BlockRead (reader, arr, 2); else for (int n = 0; n < length; n++) arr [n] = reader.ReadInt16(); val = arr; break; } case TypeCode.Int32: { int[] arr = new int [length]; if (length > 2) BlockRead (reader, arr, 4); else for (int n = 0; n < length; n++) arr [n] = reader.ReadInt32(); val = arr; break; } case TypeCode.Int64: { long[] arr = new long [length]; if (length > 2) BlockRead (reader, arr, 8); else for (int n = 0; n < length; n++) arr [n] = reader.ReadInt64(); val = arr; break; } case TypeCode.SByte: { sbyte[] arr = new sbyte [length]; if (length > 2) BlockRead (reader, arr, 1); else for (int n = 0; n < length; n++) arr [n] = reader.ReadSByte(); val = arr; break; } case TypeCode.Single: { float[] arr = new float [length]; if (length > 2) BlockRead (reader, arr, 4); else for (int n = 0; n < length; n++) arr [n] = reader.ReadSingle(); val = arr; break; } case TypeCode.UInt16: { ushort[] arr = new ushort [length]; if (length > 2) BlockRead (reader, arr, 2); else for (int n = 0; n < length; n++) arr [n] = reader.ReadUInt16(); val = arr; break; } case TypeCode.UInt32: { uint[] arr = new uint [length]; if (length > 2) BlockRead (reader, arr, 4); else for (int n = 0; n < length; n++) arr [n] = reader.ReadUInt32(); val = arr; break; } case TypeCode.UInt64: { ulong[] arr = new ulong [length]; if (length > 2) BlockRead (reader, arr, 8); else for (int n = 0; n < length; n++) arr [n] = reader.ReadUInt64(); val = arr; break; } case TypeCode.String: { string[] arr = new string [length]; for (int n = 0; n < length; n++) arr [n] = reader.ReadString(); val = arr; break; } default: { if (elementType == typeof(TimeSpan)) { TimeSpan[] arr = new TimeSpan [length]; for (int n = 0; n < length; n++) arr [n] = new TimeSpan (reader.ReadInt64 ()); val = arr; } else throw new NotSupportedException ("Unsupported primitive type: " + elementType.FullName); break; } } } private void BlockRead (BinaryReader reader, Array array, int dataSize) { int totalSize = Buffer.ByteLength (array); if (arrayBuffer == null || (totalSize > arrayBuffer.Length && arrayBuffer.Length != ArrayBufferLength)) arrayBuffer = new byte [totalSize <= ArrayBufferLength ? totalSize : ArrayBufferLength]; int pos = 0; while (totalSize > 0) { int size = totalSize < arrayBuffer.Length ? totalSize : arrayBuffer.Length; int ap = 0; do { int nr = reader.Read (arrayBuffer, ap, size - ap); if (nr == 0) break; ap += nr; } while (ap < size); if (!BitConverter.IsLittleEndian && dataSize > 1) BinaryCommon.SwapBytes (arrayBuffer, size, dataSize); Buffer.BlockCopy (arrayBuffer, 0, array, pos, size); totalSize -= size; pos += size; } } private void ReadArrayOfObject (BinaryReader reader, out long objectId, out object array) { ReadSimpleArray (reader, typeof (object), out objectId, out array); } private void ReadArrayOfString (BinaryReader reader, out long objectId, out object array) { ReadSimpleArray (reader, typeof (string), out objectId, out array); } private void ReadSimpleArray (BinaryReader reader, Type elementType, out long objectId, out object val) { objectId = (long) reader.ReadUInt32 (); int length = reader.ReadInt32 (); int[] indices = new int[1]; Array array = Array.CreateInstance (elementType, length); for (int n = 0; n < length; n++) { indices[0] = n; ReadValue (reader, array, objectId, null, elementType, null, null, indices); n = indices[0]; } val = array; } private TypeMetadata ReadTypeMetadata (BinaryReader reader, bool isRuntimeObject, bool hasTypeInfo) { TypeMetadata metadata = new TypeMetadata(); string className = reader.ReadString (); int fieldCount = reader.ReadInt32 (); Type[] types = new Type[fieldCount]; string[] names = new string[fieldCount]; for (int n=0; n