-// ObjectReader.cs\r
-//\r
-// Author:\r
-// Lluis Sanchez Gual (lluis@ideary.com)\r
-// Patrik Torstensson\r
-//\r
-// (C) 2003 Lluis Sanchez Gual\r
-\r
-//\r
-// Copyright (C) 2004 Novell, Inc (http://www.novell.com)\r
-//\r
-// Permission is hereby granted, free of charge, to any person obtaining\r
-// a copy of this software and associated documentation files (the\r
-// "Software"), to deal in the Software without restriction, including\r
-// without limitation the rights to use, copy, modify, merge, publish,\r
-// distribute, sublicense, and/or sell copies of the Software, and to\r
-// permit persons to whom the Software is furnished to do so, subject to\r
-// the following conditions:\r
-// \r
-// The above copyright notice and this permission notice shall be\r
-// included in all copies or substantial portions of the Software.\r
-// \r
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
-//\r
-\r
-using System;\r
-using System.Runtime.Serialization;\r
-using System.IO;\r
-using System.Collections;\r
-using System.Reflection;\r
-using System.Runtime.Remoting.Messaging;\r
-using System.Globalization;\r
-\r
-namespace System.Runtime.Serialization.Formatters.Binary\r
-{\r
- internal class ObjectReader\r
- {\r
- BinaryFormatter _formatter;\r
- ISurrogateSelector _surrogateSelector;\r
- StreamingContext _context;\r
- SerializationBinder _binder;\r
- \r
-#if NET_1_1\r
- TypeFilterLevel _filterLevel;\r
-#endif\r
-\r
- ObjectManager _manager;\r
- Hashtable _registeredAssemblies = new Hashtable();\r
- Hashtable _typeMetadataCache = new Hashtable();\r
-\r
- object _lastObject = null;\r
- long _lastObjectID = 0;\r
- long _rootObjectID = 0;\r
- byte[] arrayBuffer;\r
- int ArrayBufferLength = 4096;\r
-\r
- class TypeMetadata\r
- {\r
- public Type Type;\r
- public Type[] MemberTypes;\r
- public string[] MemberNames;\r
- public MemberInfo[] MemberInfos;\r
- public int FieldCount;\r
- public bool NeedsSerializationInfo;\r
- }\r
-\r
- class ArrayNullFiller\r
- {\r
- public ArrayNullFiller(int count) { NullCount = count; }\r
- public int NullCount;\r
- }\r
-\r
- public ObjectReader (BinaryFormatter formatter)\r
- {\r
- _formatter = formatter;\r
- _surrogateSelector = formatter.SurrogateSelector;\r
- _context = formatter.Context;\r
- _binder = formatter.Binder;\r
- _manager = new ObjectManager (_surrogateSelector, _context);\r
- \r
-#if NET_1_1\r
- _filterLevel = formatter.FilterLevel;\r
-#endif\r
- }\r
-\r
- public void ReadObjectGraph (BinaryReader reader, bool readHeaders, out object result, out Header[] headers)\r
- {\r
- headers = null;\r
-\r
- // Reads the objects. The first object in the stream is the\r
- // root object.\r
-\r
- while (ReadNextObject (reader))\r
- {\r
- if (readHeaders && (headers == null))\r
- headers = (Header[])CurrentObject;\r
- else\r
- if (_rootObjectID == 0) _rootObjectID = _lastObjectID;\r
- }\r
-\r
- result = _manager.GetObject (_rootObjectID);\r
- }\r
-\r
- public bool ReadNextObject (BinaryReader reader)\r
- {\r
- BinaryElement element = (BinaryElement)reader.ReadByte ();\r
- if (element == BinaryElement.End)\r
- {\r
- _manager.DoFixups();\r
-\r
- _manager.RaiseDeserializationEvent();\r
- return false;\r
- }\r
-\r
- SerializationInfo info;\r
- long objectId;\r
-\r
- ReadObject (element, reader, out objectId, out _lastObject, out info);\r
-\r
- if (objectId != 0) {\r
- RegisterObject (objectId, _lastObject, info, 0, null, null);\r
- _lastObjectID = objectId; \r
- }\r
- \r
- return true;\r
- }\r
-\r
- public object CurrentObject\r
- {\r
- get { return _lastObject; }\r
- }\r
-\r
- // Reads an object from the stream. The object is registered in the ObjectManager.\r
- // The result can be either the object instance\r
- // or the id of the object (when what is found in the stream is an object reference).\r
- // If an object instance is read, the objectId is set to 0.\r
- \r
- private void ReadObject (BinaryElement element, BinaryReader reader, out long objectId, out object value, out SerializationInfo info)\r
- {\r
- switch (element)\r
- {\r
- case BinaryElement.RefTypeObject:\r
- ReadRefTypeObjectInstance (reader, out objectId, out value, out info);\r
- break;\r
-\r
- case BinaryElement.UntypedRuntimeObject:\r
- ReadObjectInstance (reader, true, false, out objectId, out value, out info);\r
- break;\r
-\r
- case BinaryElement.UntypedExternalObject:\r
- ReadObjectInstance (reader, false, false, out objectId, out value, out info);\r
- break;\r
-\r
- case BinaryElement.RuntimeObject:\r
- ReadObjectInstance (reader, true, true, out objectId, out value, out info);\r
- break;\r
-\r
- case BinaryElement.ExternalObject:\r
- ReadObjectInstance (reader, false, true, out objectId, out value, out info);\r
- break;\r
-\r
- case BinaryElement.String:\r
- info = null;\r
- ReadStringIntance (reader, out objectId, out value);\r
- break;\r
-\r
- case BinaryElement.GenericArray:\r
- info = null;\r
- ReadGenericArray (reader, out objectId, out value);\r
- break;\r
-\r
-\r
- case BinaryElement.BoxedPrimitiveTypeValue:\r
- value = ReadBoxedPrimitiveTypeValue (reader);\r
- objectId = 0;\r
- info = null;\r
- break;\r
-\r
- case BinaryElement.NullValue:\r
- value = null;\r
- objectId = 0;\r
- info = null;\r
- break;\r
-\r
- case BinaryElement.Assembly:\r
- ReadAssembly (reader);\r
- ReadObject ((BinaryElement)reader.ReadByte (), reader, out objectId, out value, out info);\r
- break;\r
-\r
- case BinaryElement.ArrayFiller8b:\r
- value = new ArrayNullFiller(reader.ReadByte());\r
- objectId = 0;\r
- info = null;\r
- break;\r
-\r
- case BinaryElement.ArrayFiller32b:\r
- value = new ArrayNullFiller(reader.ReadInt32());\r
- objectId = 0;\r
- info = null;\r
- break;\r
-\r
- case BinaryElement.ArrayOfPrimitiveType:\r
- ReadArrayOfPrimitiveType (reader, out objectId, out value);\r
- info = null;\r
- break;\r
-\r
- case BinaryElement.ArrayOfObject:\r
- ReadArrayOfObject (reader, out objectId, out value);\r
- info = null;\r
- break;\r
-\r
- case BinaryElement.ArrayOfString:\r
- ReadArrayOfString (reader, out objectId, out value);\r
- info = null;\r
- break;\r
-\r
- default:\r
- throw new SerializationException ("Unexpected binary element: " + (int)element);\r
- }\r
- }\r
-\r
- private void ReadAssembly (BinaryReader reader)\r
- {\r
- long id = (long) reader.ReadUInt32 ();\r
- string assemblyName = reader.ReadString ();\r
- _registeredAssemblies [id] = assemblyName;\r
- }\r
-\r
- private void ReadObjectInstance (BinaryReader reader, bool isRuntimeObject, bool hasTypeInfo, out long objectId, out object value, out SerializationInfo info)\r
- {\r
- objectId = (long) reader.ReadUInt32 ();\r
-\r
- TypeMetadata metadata = ReadTypeMetadata (reader, isRuntimeObject, hasTypeInfo);\r
- ReadObjectContent (reader, metadata, objectId, out value, out info);\r
- }\r
-\r
- private void ReadRefTypeObjectInstance (BinaryReader reader, out long objectId, out object value, out SerializationInfo info)\r
- {\r
- objectId = (long) reader.ReadUInt32 ();\r
- long refTypeObjectId = (long) reader.ReadUInt32 ();\r
-\r
- // Gets the type of the referred object and its metadata\r
-\r
- object refObj = _manager.GetObject (refTypeObjectId);\r
- if (refObj == null) throw new SerializationException ("Invalid binary format");\r
- TypeMetadata metadata = (TypeMetadata)_typeMetadataCache [refObj.GetType()];\r
-\r
- ReadObjectContent (reader, metadata, objectId, out value, out info);\r
- }\r
-\r
- private void ReadObjectContent (BinaryReader reader, TypeMetadata metadata, long objectId, out object objectInstance, out SerializationInfo info)\r
- {\r
-#if NET_1_1\r
- if (_filterLevel == TypeFilterLevel.Low)\r
- objectInstance = FormatterServices.GetSafeUninitializedObject (metadata.Type);\r
- else\r
-#endif\r
- objectInstance = FormatterServices.GetUninitializedObject (metadata.Type);\r
- \r
- info = metadata.NeedsSerializationInfo ? new SerializationInfo(metadata.Type, new FormatterConverter()) : null;\r
-\r
- if (metadata.MemberNames != null)\r
- for (int n=0; n<metadata.FieldCount; n++)\r
- ReadValue (reader, objectInstance, objectId, info, metadata.MemberTypes[n], metadata.MemberNames[n], null, null);\r
- else\r
- for (int n=0; n<metadata.FieldCount; n++)\r
- ReadValue (reader, objectInstance, objectId, info, metadata.MemberTypes[n], metadata.MemberInfos[n].Name, metadata.MemberInfos[n], null);\r
- }\r
-\r
- private void RegisterObject (long objectId, object objectInstance, SerializationInfo info, long parentObjectId, MemberInfo parentObjectMemeber, int[] indices)\r
- {\r
- if (parentObjectId == 0) indices = null;\r
-\r
- if (!objectInstance.GetType().IsValueType || parentObjectId == 0)\r
- _manager.RegisterObject (objectInstance, objectId, info, 0, null, null);\r
- else\r
- {\r
- if (indices != null) indices = (int[])indices.Clone();\r
- _manager.RegisterObject (objectInstance, objectId, info, parentObjectId, parentObjectMemeber, indices);\r
- }\r
- }\r
-\r
- private void ReadStringIntance (BinaryReader reader, out long objectId, out object value)\r
- {\r
- objectId = (long) reader.ReadUInt32 ();\r
- value = reader.ReadString ();\r
- }\r
-\r
- private void ReadGenericArray (BinaryReader reader, out long objectId, out object val)\r
- {\r
- objectId = (long) reader.ReadUInt32 ();\r
- // Array structure\r
- reader.ReadByte();\r
-\r
- int rank = reader.ReadInt32();\r
-\r
- bool emptyDim = false;\r
- int[] lengths = new int[rank];\r
- for (int n=0; n<rank; n++)\r
- {\r
- lengths[n] = reader.ReadInt32();\r
- if (lengths[n] == 0) emptyDim = true;\r
- }\r
-\r
- TypeTag code = (TypeTag) reader.ReadByte ();\r
- Type elementType = ReadType (reader, code);\r
-\r
- Array array = Array.CreateInstance (elementType, lengths);\r
-\r
- if (emptyDim) \r
- { \r
- val = array;\r
- return;\r
- }\r
-\r
- int[] indices = new int[rank];\r
-\r
- // Initialize indexes\r
- for (int dim = rank-1; dim >= 0; dim--)\r
- indices[dim] = array.GetLowerBound (dim);\r
-\r
- bool end = false;\r
- while (!end)\r
- {\r
- ReadValue (reader, array, objectId, null, elementType, null, null, indices);\r
-\r
- for (int dim = array.Rank-1; dim >= 0; dim--)\r
- {\r
- indices[dim]++;\r
- if (indices[dim] > array.GetUpperBound (dim))\r
- {\r
- if (dim > 0) \r
- {\r
- indices[dim] = array.GetLowerBound (dim);\r
- continue; // Increment the next dimension's index\r
- }\r
- end = true; // That was the last dimension. Finished.\r
- }\r
- break;\r
- }\r
- }\r
- val = array;\r
- }\r
-\r
- private object ReadBoxedPrimitiveTypeValue (BinaryReader reader)\r
- {\r
- Type type = ReadType (reader, TypeTag.PrimitiveType);\r
- return ReadPrimitiveTypeValue (reader, type);\r
- }\r
-\r
- private void ReadArrayOfPrimitiveType (BinaryReader reader, out long objectId, out object val)\r
- {\r
- objectId = (long) reader.ReadUInt32 ();\r
- int length = reader.ReadInt32 ();\r
- Type elementType = ReadType (reader, TypeTag.PrimitiveType);\r
-\r
- switch (Type.GetTypeCode (elementType))\r
- {\r
- case TypeCode.Boolean: {\r
- bool[] arr = new bool [length];\r
- for (int n = 0; n < length; n++) arr [n] = reader.ReadBoolean();\r
- val = arr;\r
- break;\r
- }\r
-\r
- case TypeCode.Byte: {\r
- byte[] arr = new byte [length];\r
- int pos = 0;\r
- while (pos < length) {\r
- int nr = reader.Read (arr, pos, length - pos);\r
- if (nr == 0) break;\r
- pos += nr;\r
- }\r
- val = arr;\r
- break;\r
- }\r
-\r
- case TypeCode.Char: {\r
- char[] arr = new char [length];\r
- int pos = 0;\r
- while (pos < length) {\r
- int nr = reader.Read (arr, pos, length - pos);\r
- if (nr == 0) break;\r
- pos += nr;\r
- }\r
- val = arr;\r
- break;\r
- }\r
-\r
- case TypeCode.DateTime: {\r
- DateTime[] arr = new DateTime [length];\r
- for (int n = 0; n < length; n++) {\r
- ulong nr = reader.ReadUInt64 ();\r
- const ulong mask = (1ul << 62) - 1;\r
- long ticks = (long) (nr & mask);\r
-#if NET_2_0\r
- DateTimeKind kind = (DateTimeKind) (nr >> 62);\r
- arr [n] = new DateTime (ticks, kind);\r
-#else\r
- arr [n] = new DateTime (ticks);\r
-#endif\r
- }\r
- val = arr;\r
- break;\r
- }\r
-\r
- case TypeCode.Decimal: {\r
- Decimal[] arr = new Decimal [length];\r
- for (int n = 0; n < length; n++) arr [n] = reader.ReadDecimal();\r
- val = arr;\r
- break;\r
- }\r
-\r
- case TypeCode.Double: {\r
- Double[] arr = new Double [length];\r
- if (length > 2)\r
- BlockRead (reader, arr, 8);\r
- else\r
- for (int n = 0; n < length; n++) arr [n] = reader.ReadDouble();\r
- val = arr;\r
- break;\r
- }\r
-\r
- case TypeCode.Int16: {\r
- short[] arr = new short [length];\r
- if (length > 2)\r
- BlockRead (reader, arr, 2);\r
- else\r
- for (int n = 0; n < length; n++) arr [n] = reader.ReadInt16();\r
- val = arr;\r
- break;\r
- }\r
-\r
- case TypeCode.Int32: {\r
- int[] arr = new int [length];\r
- if (length > 2)\r
- BlockRead (reader, arr, 4);\r
- else\r
- for (int n = 0; n < length; n++) arr [n] = reader.ReadInt32();\r
- val = arr;\r
- break;\r
- }\r
-\r
- case TypeCode.Int64: {\r
- long[] arr = new long [length];\r
- if (length > 2)\r
- BlockRead (reader, arr, 8);\r
- else\r
- for (int n = 0; n < length; n++) arr [n] = reader.ReadInt64();\r
- val = arr;\r
- break;\r
- }\r
-\r
- case TypeCode.SByte: {\r
- sbyte[] arr = new sbyte [length];\r
- if (length > 2)\r
- BlockRead (reader, arr, 1);\r
- else\r
- for (int n = 0; n < length; n++) arr [n] = reader.ReadSByte();\r
- val = arr;\r
- break;\r
- }\r
-\r
- case TypeCode.Single: {\r
- float[] arr = new float [length];\r
- if (length > 2)\r
- BlockRead (reader, arr, 4);\r
- else\r
- for (int n = 0; n < length; n++) arr [n] = reader.ReadSingle();\r
- val = arr;\r
- break;\r
- }\r
-\r
- case TypeCode.UInt16: {\r
- ushort[] arr = new ushort [length];\r
- if (length > 2)\r
- BlockRead (reader, arr, 2);\r
- else\r
- for (int n = 0; n < length; n++) arr [n] = reader.ReadUInt16();\r
- val = arr;\r
- break;\r
- }\r
-\r
- case TypeCode.UInt32: {\r
- uint[] arr = new uint [length];\r
- if (length > 2)\r
- BlockRead (reader, arr, 4);\r
- else\r
- for (int n = 0; n < length; n++) arr [n] = reader.ReadUInt32();\r
- val = arr;\r
- break;\r
- }\r
-\r
- case TypeCode.UInt64: {\r
- ulong[] arr = new ulong [length];\r
- if (length > 2)\r
- BlockRead (reader, arr, 8);\r
- else\r
- for (int n = 0; n < length; n++) arr [n] = reader.ReadUInt64();\r
- val = arr;\r
- break;\r
- }\r
-\r
- case TypeCode.String: {\r
- string[] arr = new string [length];\r
- for (int n = 0; n < length; n++) arr [n] = reader.ReadString();\r
- val = arr;\r
- break;\r
- }\r
-\r
- default: {\r
- if (elementType == typeof(TimeSpan)) {\r
- TimeSpan[] arr = new TimeSpan [length];\r
- for (int n = 0; n < length; n++) arr [n] = new TimeSpan (reader.ReadInt64 ());\r
- val = arr;\r
- }\r
- else\r
- throw new NotSupportedException ("Unsupported primitive type: " + elementType.FullName);\r
- break;\r
- }\r
- } \r
- }\r
-\r
- private void BlockRead (BinaryReader reader, Array array, int dataSize)\r
- {\r
- int totalSize = Buffer.ByteLength (array);\r
- \r
- if (arrayBuffer == null || (totalSize > arrayBuffer.Length && arrayBuffer.Length != ArrayBufferLength))\r
- arrayBuffer = new byte [totalSize <= ArrayBufferLength ? totalSize : ArrayBufferLength];\r
- \r
- int pos = 0;\r
- while (totalSize > 0) {\r
- int size = totalSize < arrayBuffer.Length ? totalSize : arrayBuffer.Length;\r
- int ap = 0;\r
- do {\r
- int nr = reader.Read (arrayBuffer, ap, size - ap);\r
- if (nr == 0) break;\r
- ap += nr;\r
- } while (ap < size);\r
- \r
- if (!BitConverter.IsLittleEndian && dataSize > 1)\r
- BinaryCommon.SwapBytes (arrayBuffer, size, dataSize);\r
-\r
- Buffer.BlockCopy (arrayBuffer, 0, array, pos, size);\r
- totalSize -= size;\r
- pos += size;\r
- }\r
- }\r
- \r
-\r
- private void ReadArrayOfObject (BinaryReader reader, out long objectId, out object array)\r
- {\r
- ReadSimpleArray (reader, typeof (object), out objectId, out array);\r
- }\r
- \r
- private void ReadArrayOfString (BinaryReader reader, out long objectId, out object array)\r
- {\r
- ReadSimpleArray (reader, typeof (string), out objectId, out array);\r
- }\r
-\r
- private void ReadSimpleArray (BinaryReader reader, Type elementType, out long objectId, out object val)\r
- {\r
- objectId = (long) reader.ReadUInt32 ();\r
- int length = reader.ReadInt32 ();\r
- int[] indices = new int[1];\r
-\r
- Array array = Array.CreateInstance (elementType, length);\r
- for (int n = 0; n < length; n++)\r
- {\r
- indices[0] = n;\r
- ReadValue (reader, array, objectId, null, elementType, null, null, indices);\r
- n = indices[0];\r
- }\r
- val = array;\r
- }\r
-\r
- private TypeMetadata ReadTypeMetadata (BinaryReader reader, bool isRuntimeObject, bool hasTypeInfo)\r
- {\r
- TypeMetadata metadata = new TypeMetadata();\r
-\r
- string className = reader.ReadString ();\r
- int fieldCount = reader.ReadInt32 ();\r
-\r
- Type[] types = new Type[fieldCount];\r
- string[] names = new string[fieldCount];\r
-\r
- for (int n=0; n<fieldCount; n++)\r
- names [n] = reader.ReadString ();\r
-\r
- if (hasTypeInfo)\r
- {\r
- TypeTag[] codes = new TypeTag[fieldCount];\r
-\r
- for (int n=0; n<fieldCount; n++)\r
- codes [n] = (TypeTag) reader.ReadByte ();\r
- \r
- for (int n=0; n<fieldCount; n++)\r
- types [n] = ReadType (reader, codes[n]);\r
- }\r
- \r
- // Gets the type\r
-\r
- if (!isRuntimeObject) \r
- {\r
- long assemblyId = (long)reader.ReadUInt32();\r
- metadata.Type = GetDeserializationType (assemblyId, className);\r
- }\r
- else\r
- metadata.Type = Type.GetType (className, true);\r
-\r
- metadata.MemberTypes = types;\r
- metadata.MemberNames = names;\r
- metadata.FieldCount = names.Length;\r
-\r
- // Now check if this objects needs a SerializationInfo struct for deserialziation.\r
- // SerializationInfo is needed if the object has to be deserialized using\r
- // a serialization surrogate, or if it implements ISerializable.\r
-\r
- if (_surrogateSelector != null)\r
- {\r
- // check if the surrogate selector handles objects of the given type. \r
- ISurrogateSelector selector;\r
- ISerializationSurrogate surrogate = _surrogateSelector.GetSurrogate (metadata.Type, _context, out selector);\r
- metadata.NeedsSerializationInfo = (surrogate != null);\r
- }\r
-\r
- if (!metadata.NeedsSerializationInfo)\r
- {\r
- // Check if the object is marked with the Serializable attribute\r
-\r
- if (!metadata.Type.IsSerializable)\r
- throw new SerializationException("Serializable objects must be marked with the Serializable attribute");\r
-\r
- metadata.NeedsSerializationInfo = (metadata.Type.GetInterface ("ISerializable") != null);\r
- if (!metadata.NeedsSerializationInfo)\r
- {\r
- metadata.MemberInfos = new MemberInfo [fieldCount];\r
- for (int n=0; n<fieldCount; n++)\r
- {\r
- FieldInfo field = null;\r
- string memberName = names[n];\r
- \r
- int i = memberName.IndexOf ('+');\r
- if (i != -1) {\r
- string baseTypeName = names[n].Substring (0,i);\r
- memberName = names[n].Substring (i+1);\r
- Type t = metadata.Type.BaseType;\r
- while (t != null) {\r
- if (t.Name == baseTypeName) {\r
- field = t.GetField (memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);\r
- break;\r
- }\r
- else\r
- t = t.BaseType;\r
- }\r
- }\r
- else\r
- field = metadata.Type.GetField (memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);\r
- \r
- if (field == null) throw new SerializationException ("Field \"" + names[n] + "\" not found in class " + metadata.Type.FullName);\r
- metadata.MemberInfos [n] = field;\r
- \r
- if (!hasTypeInfo) {\r
- types [n] = field.FieldType;\r
- }\r
- }\r
- metadata.MemberNames = null; // Info now in MemberInfos\r
- }\r
- }\r
-\r
- // Registers the type's metadata so it can be reused later if\r
- // a RefTypeObject element is found\r
-\r
- if (!_typeMetadataCache.ContainsKey (metadata.Type))\r
- _typeMetadataCache [metadata.Type] = metadata;\r
-\r
- return metadata;\r
- }\r
-\r
-\r
- private void ReadValue (BinaryReader reader, object parentObject, long parentObjectId, SerializationInfo info, Type valueType, string fieldName, MemberInfo memberInfo, int[] indices)\r
- {\r
- // Reads a value from the stream and assigns it to the member of an object\r
-\r
- object val;\r
-\r
- if (BinaryCommon.IsPrimitive (valueType))\r
- {\r
- val = ReadPrimitiveTypeValue (reader, valueType);\r
- SetObjectValue (parentObject, fieldName, memberInfo, info, val, valueType, indices);\r
- return;\r
- }\r
-\r
- // Gets the object\r
-\r
- BinaryElement element = (BinaryElement)reader.ReadByte ();\r
-\r
- if (element == BinaryElement.ObjectReference)\r
- {\r
- // Just read the id of the referred object and record a fixup\r
- long childObjectId = (long) reader.ReadUInt32();\r
- RecordFixup (parentObjectId, childObjectId, parentObject, info, fieldName, memberInfo, indices);\r
- return;\r
- }\r
-\r
- long objectId;\r
- SerializationInfo objectInfo;\r
-\r
- ReadObject (element, reader, out objectId, out val, out objectInfo);\r
-\r
- // There are two cases where the object cannot be assigned to the parent\r
- // and a fixup must be used:\r
- // 1) When what has been read is not an object, but an id of an object that\r
- // has not been read yet (an object reference). This is managed in the\r
- // previous block of code.\r
- // 2) When the read object is a value type object. Value type fields hold\r
- // copies of objects, not references. Thus, if the value object that\r
- // has been read has pending fixups, those fixups would be made to the\r
- // boxed copy in the ObjectManager, and not in the required object instance\r
-\r
- // First of all register the fixup, and then the object. ObjectManager is more\r
- // efficient if done in this order\r
-\r
- bool hasFixup = false;\r
- if (objectId != 0)\r
- {\r
- if (val.GetType().IsValueType)\r
- {\r
- RecordFixup (parentObjectId, objectId, parentObject, info, fieldName, memberInfo, indices);\r
- hasFixup = true;\r
- }\r
-\r
- // Register the value\r
-\r
- if (info == null && !(parentObject is Array))\r
- RegisterObject (objectId, val, objectInfo, parentObjectId, memberInfo, null);\r
- else\r
- RegisterObject (objectId, val, objectInfo, parentObjectId, null, indices);\r
- }\r
- // Assign the value to the parent object, unless there is a fixup\r
- \r
- if (!hasFixup) \r
- SetObjectValue (parentObject, fieldName, memberInfo, info, val, valueType, indices);\r
- }\r
-\r
- private void SetObjectValue (object parentObject, string fieldName, MemberInfo memberInfo, SerializationInfo info, object value, Type valueType, int[] indices)\r
- {\r
- if (value is IObjectReference)\r
- value = ((IObjectReference)value).GetRealObject (_context);\r
-\r
- if (parentObject is Array) \r
- {\r
- if (value is ArrayNullFiller) \r
- {\r
- // It must be a single dimension array of objects.\r
- // Just increase the index. Elements are null by default.\r
- int count = ((ArrayNullFiller)value).NullCount;\r
- indices[0] += count - 1;\r
- }\r
- else\r
- ((Array)parentObject).SetValue (value, indices);\r
- }\r
- else if (info != null) {\r
- info.AddValue (fieldName, value, valueType);\r
- }\r
- else {\r
- if (memberInfo is FieldInfo)\r
- ((FieldInfo)memberInfo).SetValue (parentObject, value);\r
- else\r
- ((PropertyInfo)memberInfo).SetValue (parentObject, value, null);\r
- }\r
- }\r
-\r
- private void RecordFixup (long parentObjectId, long childObjectId, object parentObject, SerializationInfo info, string fieldName, MemberInfo memberInfo, int[] indices)\r
- {\r
- if (info != null) {\r
- _manager.RecordDelayedFixup (parentObjectId, fieldName, childObjectId);\r
- }\r
- else if (parentObject is Array) {\r
- if (indices.Length == 1)\r
- _manager.RecordArrayElementFixup (parentObjectId, indices[0], childObjectId);\r
- else\r
- _manager.RecordArrayElementFixup (parentObjectId, (int[])indices.Clone(), childObjectId);\r
- }\r
- else {\r
- _manager.RecordFixup (parentObjectId, memberInfo, childObjectId);\r
- }\r
- }\r
-\r
- private Type GetDeserializationType (long assemblyId, string className)\r
- {\r
- Type t;\r
- string assemblyName = (string)_registeredAssemblies[assemblyId];\r
-\r
- if (_binder != null) {\r
- t = _binder.BindToType (assemblyName, className);\r
- if (t != null)\r
- return t;\r
- }\r
- \r
- Assembly assembly = Assembly.Load (assemblyName);\r
- t = assembly.GetType (className, true);\r
- if (t != null)\r
- return t;\r
- throw new SerializationException ("Couldn't find type '" + className + "'.");\r
- }\r
-\r
- public Type ReadType (BinaryReader reader, TypeTag code)\r
- {\r
- switch (code)\r
- {\r
- case TypeTag.PrimitiveType:\r
- return BinaryCommon.GetTypeFromCode (reader.ReadByte());\r
-\r
- case TypeTag.String:\r
- return typeof(string);\r
-\r
- case TypeTag.ObjectType:\r
- return typeof(object);\r
-\r
- case TypeTag.RuntimeType:\r
- {\r
- string name = reader.ReadString ();\r
- return Type.GetType (name, true);\r
- }\r
-\r
- case TypeTag.GenericType:\r
- {\r
- string name = reader.ReadString ();\r
- long asmid = (long) reader.ReadUInt32();\r
- return GetDeserializationType (asmid, name);\r
- }\r
-\r
- case TypeTag.ArrayOfObject:\r
- return typeof(object[]);\r
-\r
- case TypeTag.ArrayOfString:\r
- return typeof(string[]);\r
-\r
- case TypeTag.ArrayOfPrimitiveType:\r
- Type elementType = BinaryCommon.GetTypeFromCode (reader.ReadByte());\r
- return Type.GetType(elementType.FullName + "[]");\r
-\r
- default:\r
- throw new NotSupportedException ("Unknow type tag");\r
- }\r
- }\r
- \r
- public static object ReadPrimitiveTypeValue (BinaryReader reader, Type type)\r
- {\r
- if (type == null) return null;\r
-\r
- switch (Type.GetTypeCode (type))\r
- {\r
- case TypeCode.Boolean:\r
- return reader.ReadBoolean();\r
-\r
- case TypeCode.Byte:\r
- return reader.ReadByte();\r
-\r
- case TypeCode.Char:\r
- return reader.ReadChar();\r
-\r
- case TypeCode.DateTime: \r
- return new DateTime (reader.ReadInt64());\r
-\r
- case TypeCode.Decimal:\r
- return Decimal.Parse (reader.ReadString(), CultureInfo.InvariantCulture);\r
-\r
- case TypeCode.Double:\r
- return reader.ReadDouble();\r
-\r
- case TypeCode.Int16:\r
- return reader.ReadInt16();\r
-\r
- case TypeCode.Int32:\r
- return reader.ReadInt32();\r
-\r
- case TypeCode.Int64:\r
- return reader.ReadInt64();\r
-\r
- case TypeCode.SByte:\r
- return reader.ReadSByte();\r
-\r
- case TypeCode.Single:\r
- return reader.ReadSingle();\r
-\r
- case TypeCode.UInt16:\r
- return reader.ReadUInt16();\r
-\r
- case TypeCode.UInt32:\r
- return reader.ReadUInt32();\r
-\r
- case TypeCode.UInt64:\r
- return reader.ReadUInt64();\r
-\r
- case TypeCode.String:\r
- return reader.ReadString();\r
-\r
- default:\r
- if (type == typeof(TimeSpan))\r
- return new TimeSpan (reader.ReadInt64 ());\r
- else\r
- throw new NotSupportedException ("Unsupported primitive type: " + type.FullName);\r
- }\r
- }\r
- }\r
-}\r
+// 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;
+
+ TypeFilterLevel _filterLevel;
+
+ 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);
+
+ _filterLevel = formatter.FilterLevel;
+ }
+
+ public void ReadObjectGraph (BinaryReader reader, bool readHeaders, out object result, out Header[] headers)
+ {
+ BinaryElement elem = (BinaryElement)reader.ReadByte ();
+ ReadObjectGraph (elem, reader, readHeaders, out result, out headers);
+ }
+
+ public void ReadObjectGraph (BinaryElement elem, 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.
+ bool next = ReadNextObject (elem, reader);
+ if (next) {
+ do {
+ if (readHeaders && (headers == null))
+ headers = (Header[])CurrentObject;
+ else
+ if (_rootObjectID == 0) _rootObjectID = _lastObjectID;
+ } while (ReadNextObject (reader));
+ }
+
+ result = _manager.GetObject (_rootObjectID);
+ }
+
+ bool ReadNextObject (BinaryElement element, BinaryReader reader)
+ {
+ 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 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 (_filterLevel == TypeFilterLevel.Low)
+ objectInstance = FormatterServices.GetSafeUninitializedObject (metadata.Type);
+ else
+ objectInstance = FormatterServices.GetUninitializedObject (metadata.Type);
+ _manager.RaiseOnDeserializingEvent (objectInstance);
+
+ info = metadata.NeedsSerializationInfo ? new SerializationInfo(metadata.Type, new FormatterConverter()) : null;
+
+ if (metadata.MemberNames != null) {
+ for (int n=0; n<metadata.FieldCount; n++)
+ ReadValue (reader, objectInstance, objectId, info, metadata.MemberTypes[n], metadata.MemberNames[n], null, null);
+ } else
+ for (int n=0; n<metadata.FieldCount; n++) {
+ if (metadata.MemberInfos [n] != null)
+ ReadValue (reader, objectInstance, objectId, info, metadata.MemberTypes[n], metadata.MemberInfos[n].Name, metadata.MemberInfos[n], null);
+ else if (BinaryCommon.IsPrimitive(metadata.MemberTypes[n])) {
+ // Since the member info is null, the type in this
+ // domain does not have this type. Even though we
+ // are not going to store the value, we will read
+ // it from the stream so that we can advance to the
+ // next block.
+ ReadPrimitiveTypeValue (reader, metadata.MemberTypes[n]);
+ }
+ }
+ }
+
+ private void RegisterObject (long objectId, object objectInstance, SerializationInfo info, long parentObjectId, MemberInfo parentObjectMemeber, int[] indices)
+ {
+ if (parentObjectId == 0) indices = null;
+
+ if (!objectInstance.GetType().IsValueType || parentObjectId == 0)
+ _manager.RegisterObject (objectInstance, objectId, info, 0, null, null);
+ else
+ {
+ if (indices != null) indices = (int[])indices.Clone();
+ _manager.RegisterObject (objectInstance, objectId, info, parentObjectId, parentObjectMemeber, indices);
+ }
+ }
+
+ private void ReadStringIntance (BinaryReader reader, out long objectId, out object value)
+ {
+ objectId = (long) reader.ReadUInt32 ();
+ value = reader.ReadString ();
+ }
+
+ private void ReadGenericArray (BinaryReader reader, out long objectId, out object val)
+ {
+ objectId = (long) reader.ReadUInt32 ();
+ // Array structure
+ reader.ReadByte();
+
+ int rank = reader.ReadInt32();
+
+ bool emptyDim = false;
+ int[] lengths = new int[rank];
+ for (int n=0; n<rank; n++)
+ {
+ lengths[n] = reader.ReadInt32();
+ if (lengths[n] == 0) emptyDim = true;
+ }
+
+ TypeTag code = (TypeTag) reader.ReadByte ();
+ Type elementType = ReadType (reader, code);
+
+ Array array = Array.CreateInstance (elementType, lengths);
+
+ if (emptyDim)
+ {
+ val = array;
+ return;
+ }
+
+ int[] indices = new int[rank];
+
+ // Initialize indexes
+ for (int dim = rank-1; dim >= 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++) {
+ arr [n] = DateTime.FromBinary (reader.ReadInt64 ());
+ }
+ 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<fieldCount; n++)
+ names [n] = reader.ReadString ();
+
+ if (hasTypeInfo)
+ {
+ TypeTag[] codes = new TypeTag[fieldCount];
+
+ for (int n=0; n<fieldCount; n++)
+ codes [n] = (TypeTag) reader.ReadByte ();
+
+ for (int n=0; n<fieldCount; n++) {
+ Type t = ReadType (reader, codes[n], false);
+ // The field's type could not be resolved: assume it is an object.
+ if (t == null)
+ t = typeof (object);
+ types [n] = t;
+ }
+ }
+
+ // Gets the type
+
+ if (!isRuntimeObject)
+ {
+ long assemblyId = (long)reader.ReadUInt32();
+ metadata.Type = GetDeserializationType (assemblyId, className);
+ }
+ else
+ metadata.Type = Type.GetType (className, true);
+
+ metadata.MemberTypes = types;
+ metadata.MemberNames = names;
+ metadata.FieldCount = names.Length;
+
+ // Now check if this objects needs a SerializationInfo struct for deserialziation.
+ // SerializationInfo is needed if the object has to be deserialized using
+ // a serialization surrogate, or if it implements ISerializable.
+
+ if (_surrogateSelector != null)
+ {
+ // check if the surrogate selector handles objects of the given type.
+ ISurrogateSelector selector;
+ ISerializationSurrogate surrogate = _surrogateSelector.GetSurrogate (metadata.Type, _context, out selector);
+ metadata.NeedsSerializationInfo = (surrogate != null);
+ }
+
+ if (!metadata.NeedsSerializationInfo)
+ {
+ // Check if the object is marked with the Serializable attribute
+
+ if (!metadata.Type.IsSerializable)
+ throw new SerializationException("Serializable objects must be marked with the Serializable attribute");
+
+ metadata.NeedsSerializationInfo = typeof (ISerializable).IsAssignableFrom (metadata.Type);
+ if (!metadata.NeedsSerializationInfo)
+ {
+ metadata.MemberInfos = new MemberInfo [fieldCount];
+ for (int n=0; n<fieldCount; n++)
+ {
+ FieldInfo field = null;
+ string memberName = names[n];
+
+ int i = memberName.IndexOf ('+');
+ if (i != -1) {
+ string baseTypeName = names[n].Substring (0,i);
+ memberName = names[n].Substring (i+1);
+ Type t = metadata.Type.BaseType;
+ while (t != null) {
+ if (t.Name == baseTypeName) {
+ field = t.GetField (memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
+ break;
+ }
+ else
+ t = t.BaseType;
+ }
+ }
+ else
+ field = metadata.Type.GetField (memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
+
+ if (field != null)
+ metadata.MemberInfos [n] = field;
+
+ if (!hasTypeInfo) {
+ types [n] = field.FieldType;
+ }
+ }
+ metadata.MemberNames = null; // Info now in MemberInfos
+ }
+ }
+
+ // Registers the type's metadata so it can be reused later if
+ // a RefTypeObject element is found
+
+ if (!_typeMetadataCache.ContainsKey (metadata.Type))
+ _typeMetadataCache [metadata.Type] = metadata;
+
+ return metadata;
+ }
+
+ // Called for primitive types
+ static bool IsGeneric (MemberInfo minfo)
+ {
+ if (minfo == null)
+ return false;
+
+ Type mtype = null;
+ switch (minfo.MemberType) {
+ case MemberTypes.Field:
+ mtype = ((FieldInfo) minfo).FieldType;
+ break;
+ default:
+ throw new NotSupportedException ("Not supported: " + minfo.MemberType);
+ }
+ return (mtype != null && mtype.IsGenericType);
+ }
+
+ private void ReadValue (BinaryReader reader, object parentObject, long parentObjectId, SerializationInfo info, Type valueType, string fieldName, MemberInfo memberInfo, int[] indices)
+ {
+ // Reads a value from the stream and assigns it to the member of an object
+
+ object val;
+
+ if (BinaryCommon.IsPrimitive (valueType) && !IsGeneric (memberInfo))
+ {
+ val = ReadPrimitiveTypeValue (reader, valueType);
+ SetObjectValue (parentObject, fieldName, memberInfo, info, val, valueType, indices);
+ return;
+ }
+
+ // Gets the object
+
+ BinaryElement element = (BinaryElement)reader.ReadByte ();
+
+ if (element == BinaryElement.ObjectReference)
+ {
+ // Just read the id of the referred object and record a fixup
+ long childObjectId = (long) reader.ReadUInt32();
+ RecordFixup (parentObjectId, childObjectId, parentObject, info, fieldName, memberInfo, indices);
+ return;
+ }
+
+ long objectId;
+ SerializationInfo objectInfo;
+
+ ReadObject (element, reader, out objectId, out val, out objectInfo);
+
+ // There are two cases where the object cannot be assigned to the parent
+ // and a fixup must be used:
+ // 1) When what has been read is not an object, but an id of an object that
+ // has not been read yet (an object reference). This is managed in the
+ // previous block of code.
+ // 2) When the read object is a value type object. Value type fields hold
+ // copies of objects, not references. Thus, if the value object that
+ // has been read has pending fixups, those fixups would be made to the
+ // boxed copy in the ObjectManager, and not in the required object instance
+
+ // First of all register the fixup, and then the object. ObjectManager is more
+ // efficient if done in this order
+
+ bool hasFixup = false;
+ if (objectId != 0)
+ {
+ if (val.GetType().IsValueType)
+ {
+ RecordFixup (parentObjectId, objectId, parentObject, info, fieldName, memberInfo, indices);
+ hasFixup = true;
+ }
+
+ // Register the value
+
+ if (info == null && !(parentObject is Array))
+ RegisterObject (objectId, val, objectInfo, parentObjectId, memberInfo, null);
+ else
+ RegisterObject (objectId, val, objectInfo, parentObjectId, null, indices);
+ }
+ // Assign the value to the parent object, unless there is a fixup
+
+ if (!hasFixup)
+ SetObjectValue (parentObject, fieldName, memberInfo, info, val, valueType, indices);
+ }
+
+ private void SetObjectValue (object parentObject, string fieldName, MemberInfo memberInfo, SerializationInfo info, object value, Type valueType, int[] indices)
+ {
+ if (value is IObjectReference)
+ value = ((IObjectReference)value).GetRealObject (_context);
+
+ if (parentObject is Array)
+ {
+ if (value is ArrayNullFiller)
+ {
+ // It must be a single dimension array of objects.
+ // Just increase the index. Elements are null by default.
+ int count = ((ArrayNullFiller)value).NullCount;
+ indices[0] += count - 1;
+ }
+ else
+ ((Array)parentObject).SetValue (value, indices);
+ }
+ else if (info != null) {
+ info.AddValue (fieldName, value, valueType);
+ }
+ else {
+ if (memberInfo is FieldInfo)
+ ((FieldInfo)memberInfo).SetValue (parentObject, value);
+ else
+ ((PropertyInfo)memberInfo).SetValue (parentObject, value, null);
+ }
+ }
+
+ private void RecordFixup (long parentObjectId, long childObjectId, object parentObject, SerializationInfo info, string fieldName, MemberInfo memberInfo, int[] indices)
+ {
+ if (info != null) {
+ _manager.RecordDelayedFixup (parentObjectId, fieldName, childObjectId);
+ }
+ else if (parentObject is Array) {
+ if (indices.Length == 1)
+ _manager.RecordArrayElementFixup (parentObjectId, indices[0], childObjectId);
+ else
+ _manager.RecordArrayElementFixup (parentObjectId, (int[])indices.Clone(), childObjectId);
+ }
+ else {
+ _manager.RecordFixup (parentObjectId, memberInfo, childObjectId);
+ }
+ }
+
+ private Type GetDeserializationType (long assemblyId, string className)
+ {
+ return GetDeserializationType (assemblyId, className, true);
+ }
+
+ private Type GetDeserializationType (long assemblyId, string className, bool throwOnError)
+ {
+ Type t;
+ string assemblyName = (string)_registeredAssemblies[assemblyId];
+
+ if (_binder != null) {
+ t = _binder.BindToType (assemblyName, className);
+ if (t != null)
+ return t;
+ }
+
+ Assembly assembly;
+ try {
+ assembly = Assembly.Load (assemblyName);
+ } catch (Exception ex) {
+ if (!throwOnError)
+ return null;
+ throw new SerializationException (String.Format ("Couldn't find assembly '{0}'", assemblyName), ex);
+ }
+
+ t = assembly.GetType (className);
+ if (t != null)
+ return t;
+
+ if (!throwOnError)
+ return null;
+
+ throw new SerializationException (String.Format ("Couldn't find type '{0}' in assembly '{1}'", className, assemblyName));
+ }
+
+ public Type ReadType (BinaryReader reader, TypeTag code)
+ {
+ return ReadType (reader, code, true);
+ }
+
+ public Type ReadType (BinaryReader reader, TypeTag code, bool throwOnError)
+ {
+ switch (code)
+ {
+ case TypeTag.PrimitiveType:
+ return BinaryCommon.GetTypeFromCode (reader.ReadByte());
+
+ case TypeTag.String:
+ return typeof(string);
+
+ case TypeTag.ObjectType:
+ return typeof(object);
+
+ case TypeTag.RuntimeType:
+ {
+ string name = reader.ReadString ();
+ // map MS.NET's System.RuntimeType to System.MonoType
+ if (_context.State == StreamingContextStates.Remoting)
+ if (name == "System.RuntimeType")
+ return typeof (MonoType);
+ else if (name == "System.RuntimeType[]")
+ return typeof (MonoType[]);
+ Type t = Type.GetType (name);
+ if (t != null)
+ return t;
+
+ throw new SerializationException (String.Format ("Could not find type '{0}'.", name));
+ }
+
+ case TypeTag.GenericType:
+ {
+ string name = reader.ReadString ();
+ long asmid = (long) reader.ReadUInt32();
+ return GetDeserializationType (asmid, name, throwOnError);
+ }
+
+ case TypeTag.ArrayOfObject:
+ return typeof(object[]);
+
+ case TypeTag.ArrayOfString:
+ return typeof(string[]);
+
+ case TypeTag.ArrayOfPrimitiveType:
+ Type elementType = BinaryCommon.GetTypeFromCode (reader.ReadByte());
+ return Type.GetType(elementType.FullName + "[]");
+
+ default:
+ throw new NotSupportedException ("Unknow type tag");
+ }
+ }
+
+ public static object ReadPrimitiveTypeValue (BinaryReader reader, Type type)
+ {
+ if (type == null) return null;
+
+ switch (Type.GetTypeCode (type))
+ {
+ case TypeCode.Boolean:
+ return reader.ReadBoolean();
+
+ case TypeCode.Byte:
+ return reader.ReadByte();
+
+ case TypeCode.Char:
+ return reader.ReadChar();
+
+ case TypeCode.DateTime:
+ return DateTime.FromBinary (reader.ReadInt64());
+
+ case TypeCode.Decimal:
+ return Decimal.Parse (reader.ReadString(), CultureInfo.InvariantCulture);
+
+ case TypeCode.Double:
+ return reader.ReadDouble();
+
+ case TypeCode.Int16:
+ return reader.ReadInt16();
+
+ case TypeCode.Int32:
+ return reader.ReadInt32();
+
+ case TypeCode.Int64:
+ return reader.ReadInt64();
+
+ case TypeCode.SByte:
+ return reader.ReadSByte();
+
+ case TypeCode.Single:
+ return reader.ReadSingle();
+
+ case TypeCode.UInt16:
+ return reader.ReadUInt16();
+
+ case TypeCode.UInt32:
+ return reader.ReadUInt32();
+
+ case TypeCode.UInt64:
+ return reader.ReadUInt64();
+
+ case TypeCode.String:
+ return reader.ReadString();
+
+ default:
+ if (type == typeof(TimeSpan))
+ return new TimeSpan (reader.ReadInt64 ());
+ else
+ throw new NotSupportedException ("Unsupported primitive type: " + type.FullName);
+ }
+ }
+ }
+}