-// ObjectWriter.cs\r
-//\r
-// Author:\r
-// Lluis Sanchez Gual (lluis@ideary.com)\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.IO;\r
-using System.Collections;\r
-using System.Runtime.Serialization;\r
-using System.Runtime.Remoting.Messaging;\r
-using System.Reflection;\r
-using System.Globalization;\r
-\r
-namespace System.Runtime.Serialization.Formatters.Binary\r
-{\r
- abstract class TypeMetadata\r
- {\r
- public string TypeAssemblyName;\r
- public string InstanceTypeName;\r
- \r
- public abstract void WriteAssemblies (ObjectWriter ow, BinaryWriter writer);\r
- public abstract void WriteTypeData (ObjectWriter ow, BinaryWriter writer, bool writeTypes);\r
- public abstract void WriteObjectData (ObjectWriter ow, BinaryWriter writer, object data);\r
- \r
- public virtual bool IsCompatible (TypeMetadata other)\r
- {\r
- return true;\r
- }\r
- \r
- public abstract bool RequiresTypes { get; }\r
- }\r
- \r
- abstract class ClrTypeMetadata: TypeMetadata\r
- {\r
- public Type InstanceType;\r
-\r
- public ClrTypeMetadata (Type instanceType)\r
- {\r
- InstanceType = instanceType;\r
- InstanceTypeName = instanceType.FullName;\r
- TypeAssemblyName = instanceType.Assembly.FullName;\r
- }\r
- \r
- public override bool RequiresTypes {\r
- get { return false; }\r
- }\r
- }\r
- \r
- class SerializableTypeMetadata: TypeMetadata\r
- {\r
- Type[] types;\r
- string[] names;\r
- \r
- public SerializableTypeMetadata (Type itype, SerializationInfo info)\r
- {\r
- types = new Type [info.MemberCount];\r
- names = new string [info.MemberCount];\r
-\r
- SerializationInfoEnumerator e = info.GetEnumerator ();\r
-\r
- int n = 0;\r
- while (e.MoveNext ())\r
- {\r
- types[n] = e.ObjectType;\r
- names[n] = e.Name;\r
- n++;\r
- }\r
-\r
- TypeAssemblyName = info.AssemblyName;\r
- InstanceTypeName = info.FullTypeName;\r
- }\r
- \r
- public override bool IsCompatible (TypeMetadata other)\r
- {\r
- if (!(other is SerializableTypeMetadata)) return false;\r
- \r
- SerializableTypeMetadata tm = (SerializableTypeMetadata)other;\r
- if (types.Length != tm.types.Length) return false;\r
- if (TypeAssemblyName != tm.TypeAssemblyName) return false;\r
- if (InstanceTypeName != tm.InstanceTypeName) return false;\r
- for (int n=0; n<types.Length; n++)\r
- {\r
- if (types[n] != tm.types[n]) return false;\r
- if (names[n] != tm.names[n]) return false;\r
- }\r
- return true;\r
- }\r
- \r
- public override void WriteAssemblies (ObjectWriter ow, BinaryWriter writer)\r
- {\r
- foreach (Type mtype in types)\r
- {\r
- Type type = mtype;\r
- while (type.IsArray) \r
- type = type.GetElementType();\r
- \r
- ow.WriteAssembly (writer, type.Assembly);\r
- }\r
- }\r
- \r
- public override void WriteTypeData (ObjectWriter ow, BinaryWriter writer, bool writeTypes)\r
- {\r
- writer.Write (types.Length);\r
-\r
- // Names of fields\r
- foreach (string name in names)\r
- writer.Write (name);\r
-\r
- // Types of fields\r
- foreach (Type type in types)\r
- ObjectWriter.WriteTypeCode (writer, type);\r
-\r
- // Type specs of fields\r
- foreach (Type type in types)\r
- ow.WriteTypeSpec (writer, type);\r
- }\r
- \r
- public override void WriteObjectData (ObjectWriter ow, BinaryWriter writer, object data)\r
- {\r
- SerializationInfo info = (SerializationInfo) data;\r
- SerializationInfoEnumerator e = info.GetEnumerator ();\r
-\r
- while (e.MoveNext ())\r
- ow.WriteValue (writer, e.ObjectType, e.Value);\r
- }\r
- \r
- public override bool RequiresTypes {\r
- get { return true; }\r
- }\r
- }\r
- \r
- class MemberTypeMetadata: ClrTypeMetadata\r
- {\r
- MemberInfo[] members;\r
- \r
- public MemberTypeMetadata (Type type, StreamingContext context): base (type)\r
- {\r
- members = FormatterServices.GetSerializableMembers (type, context);\r
- }\r
-\r
- public override void WriteAssemblies (ObjectWriter ow, BinaryWriter writer)\r
- {\r
- foreach (FieldInfo field in members)\r
- {\r
- Type type = field.FieldType;\r
- while (type.IsArray) \r
- type = type.GetElementType();\r
- \r
- ow.WriteAssembly (writer, type.Assembly);\r
- }\r
- }\r
- \r
- public override void WriteTypeData (ObjectWriter ow, BinaryWriter writer, bool writeTypes)\r
- {\r
- writer.Write (members.Length);\r
-\r
- // Names of fields\r
- foreach (FieldInfo field in members)\r
- writer.Write (field.Name);\r
-\r
- if (writeTypes) {\r
- // Types of fields\r
- foreach (FieldInfo field in members)\r
- ObjectWriter.WriteTypeCode (writer, field.FieldType);\r
- \r
- // Type specs of fields\r
- foreach (FieldInfo field in members)\r
- ow.WriteTypeSpec (writer, field.FieldType);\r
- }\r
- }\r
- \r
- public override void WriteObjectData (ObjectWriter ow, BinaryWriter writer, object data)\r
- {\r
- object[] values = FormatterServices.GetObjectData (data, members);\r
- for (int n=0; n<values.Length; n++)\r
- ow.WriteValue (writer, ((FieldInfo)members[n]).FieldType, values[n]);\r
- }\r
- }\r
- \r
- internal class ObjectWriter\r
- {\r
- ObjectIDGenerator _idGenerator = new ObjectIDGenerator();\r
- Hashtable _cachedMetadata = new Hashtable();\r
- Queue _pendingObjects = new Queue();\r
- Hashtable _assemblyCache = new Hashtable();\r
- \r
- // Type metadata that can be shared with all serializers\r
- static Hashtable _cachedTypes = new Hashtable();\r
-\r
- internal static Assembly CorlibAssembly = typeof(string).Assembly;\r
- internal static string CorlibAssemblyName = typeof(string).Assembly.FullName;\r
-\r
- ISurrogateSelector _surrogateSelector;\r
- StreamingContext _context;\r
- FormatterAssemblyStyle _assemblyFormat;\r
- FormatterTypeStyle _typeFormat;\r
- byte[] arrayBuffer;\r
- int ArrayBufferLength = 4096;\r
- \r
- class MetadataReference\r
- {\r
- public TypeMetadata Metadata;\r
- public long ObjectID;\r
- \r
- public MetadataReference (TypeMetadata metadata, long id)\r
- {\r
- Metadata = metadata;\r
- ObjectID = id;\r
- }\r
- }\r
- \r
- public ObjectWriter (ISurrogateSelector surrogateSelector, StreamingContext context, FormatterAssemblyStyle assemblyFormat, FormatterTypeStyle typeFormat)\r
- {\r
- _surrogateSelector = surrogateSelector;\r
- _context = context;\r
- _assemblyFormat = assemblyFormat;\r
- _typeFormat = typeFormat;\r
- }\r
-\r
- public void WriteObjectGraph (BinaryWriter writer, object obj, Header[] headers)\r
- {\r
- _pendingObjects.Clear();\r
- if (headers != null) QueueObject (headers);\r
- QueueObject (obj);\r
- WriteQueuedObjects (writer);\r
- WriteSerializationEnd (writer);\r
- }\r
-\r
- public void QueueObject (object obj)\r
- {\r
- _pendingObjects.Enqueue (obj);\r
- }\r
-\r
- public void WriteQueuedObjects (BinaryWriter writer)\r
- {\r
- while (_pendingObjects.Count > 0)\r
- WriteObjectInstance (writer, _pendingObjects.Dequeue(), false);\r
- }\r
-\r
- public void WriteObjectInstance (BinaryWriter writer, object obj, bool isValueObject)\r
- {\r
- bool firstTime;\r
- long id;\r
-\r
- // If the object is a value type (not boxed) then there is no need\r
- // to register it in the id generator, because it won't have other\r
- // references to it\r
-\r
- if (isValueObject) id = _idGenerator.NextId;\r
- else id = _idGenerator.GetId (obj, out firstTime);\r
-\r
- if (obj is string) {\r
- WriteString (writer, id, (string)obj);\r
- }\r
- else if (obj is Array) {\r
- WriteArray (writer, id, (Array)obj);\r
- }\r
- else\r
- WriteObject (writer, id, obj);\r
- }\r
-\r
- public static void WriteSerializationEnd (BinaryWriter writer)\r
- {\r
- writer.Write ((byte) BinaryElement.End);\r
- }\r
-\r
- private void WriteObject (BinaryWriter writer, long id, object obj)\r
- {\r
- object data;\r
- TypeMetadata metadata;\r
-\r
- GetObjectData (obj, out metadata, out data);\r
- MetadataReference metadataReference = (MetadataReference)_cachedMetadata [metadata.InstanceTypeName];\r
-\r
- if (metadataReference != null && metadata.IsCompatible (metadataReference.Metadata))\r
- {\r
- // An object of the same type has already been serialized\r
- // It is not necessary to write again type metadata\r
-\r
- writer.Write ((byte) BinaryElement.RefTypeObject);\r
- writer.Write ((int)id);\r
-\r
- writer.Write ((int)metadataReference.ObjectID);\r
- metadata.WriteObjectData (this, writer, data);\r
- return;\r
- }\r
-\r
- if (metadataReference == null)\r
- {\r
- metadataReference = new MetadataReference (metadata, id);\r
- _cachedMetadata [metadata.InstanceTypeName] = metadataReference;\r
- }\r
- \r
- bool writeTypes = metadata.RequiresTypes || _typeFormat == FormatterTypeStyle.TypesAlways;\r
-\r
- BinaryElement objectTag;\r
-\r
- int assemblyId;\r
- if (metadata.TypeAssemblyName == CorlibAssemblyName)\r
- {\r
- // A corlib type\r
- objectTag = writeTypes ? BinaryElement.RuntimeObject : BinaryElement.UntypedRuntimeObject;\r
- assemblyId = -1;\r
- }\r
- else\r
- {\r
- objectTag = writeTypes ? BinaryElement.ExternalObject : BinaryElement.UntypedExternalObject;\r
- assemblyId = WriteAssemblyName (writer, metadata.TypeAssemblyName);\r
- }\r
-\r
- // Registers the assemblies needed for each field\r
- // If there are assemblies that where not registered before this object,\r
- // write them now\r
-\r
- metadata.WriteAssemblies (this, writer);\r
-\r
- // Writes the object\r
-\r
- writer.Write ((byte) objectTag);\r
- writer.Write ((int)id);\r
- writer.Write (metadata.InstanceTypeName);\r
- \r
- metadata.WriteTypeData (this, writer, writeTypes);\r
- if (assemblyId != -1) writer.Write (assemblyId);\r
- \r
- metadata.WriteObjectData (this, writer, data);\r
- }\r
-\r
- private void GetObjectData (object obj, out TypeMetadata metadata, out object data)\r
- {\r
- Type instanceType = obj.GetType();\r
-\r
- // Check if the formatter has a surrogate selector, if it does, \r
- // check if the surrogate selector handles objects of the given type. \r
-\r
- if (_surrogateSelector != null)\r
- {\r
- ISurrogateSelector selector;\r
- ISerializationSurrogate surrogate = _surrogateSelector.GetSurrogate (instanceType, _context, out selector);\r
- if (surrogate != null)\r
- {\r
- SerializationInfo info = new SerializationInfo (instanceType, new FormatterConverter ());\r
- surrogate.GetObjectData (obj, info, _context);\r
- metadata = new SerializableTypeMetadata (instanceType, info);\r
- data = info;\r
- return;\r
- }\r
- }\r
-\r
- // Check if the object is marked with the Serializable attribute\r
-\r
- BinaryCommon.CheckSerializable (instanceType, _surrogateSelector, _context);\r
-\r
- ISerializable ser = obj as ISerializable;\r
-\r
- if (ser != null) \r
- {\r
- SerializationInfo info = new SerializationInfo (instanceType, new FormatterConverter ());\r
- ser.GetObjectData (info, _context);\r
- metadata = new SerializableTypeMetadata (instanceType, info);\r
- data = info;\r
- } \r
- else \r
- {\r
- data = obj;\r
- if (_context.Context != null)\r
- {\r
- // Don't cache metadata info when the Context property is not null sice\r
- // we can't control the number of possible contexts in this case\r
- metadata = new MemberTypeMetadata (instanceType, _context);\r
- return;\r
- }\r
- \r
- Hashtable typesTable;\r
- bool isNew = false;\r
- lock (_cachedTypes) {\r
- typesTable = (Hashtable) _cachedTypes [_context.State];\r
- if (typesTable == null) {\r
- typesTable = new Hashtable ();\r
- _cachedTypes [_context.State] = typesTable;\r
- isNew = true;\r
- }\r
- }\r
-\r
- metadata = null;\r
- lock (typesTable) {\r
- if (!isNew) {\r
- metadata = (TypeMetadata) typesTable [instanceType];\r
- }\r
-\r
- if (metadata == null) {\r
- metadata = CreateMemberTypeMetadata (instanceType);\r
- }\r
-\r
- typesTable [instanceType] = metadata;\r
- }\r
- }\r
- }\r
- \r
- TypeMetadata CreateMemberTypeMetadata (Type type)\r
- {\r
- if (!BinaryCommon.UseReflectionSerialization) {\r
- Type metaType = CodeGenerator.GenerateMetadataType (type, _context);\r
- return (TypeMetadata) Activator.CreateInstance (metaType);\r
- }\r
- else\r
- return new MemberTypeMetadata (type, _context);\r
- }\r
-\r
- private void WriteArray (BinaryWriter writer, long id, Array array)\r
- {\r
- // There are 4 ways of serializing arrays:\r
- // The element GenericArray (7) can be used for all arrays.\r
- // The element ArrayOfPrimitiveType (15) can be used for single-dimensional\r
- // arrays of primitive types\r
- // The element ArrayOfObject (16) can be used for single-dimensional Object arrays\r
- // The element ArrayOfString (17) can be used for single-dimensional string arrays\r
-\r
- Type elementType = array.GetType().GetElementType();\r
-\r
- if (elementType == typeof (object) && array.Rank == 1) {\r
- WriteObjectArray (writer, id, array);\r
- }\r
- else if (elementType == typeof (string) && array.Rank == 1) {\r
- WriteStringArray (writer, id, array);\r
- }\r
- else if (BinaryCommon.IsPrimitive(elementType) && array.Rank == 1) {\r
- WritePrimitiveTypeArray (writer, id, array);\r
- }\r
- else\r
- WriteGenericArray (writer, id, array);\r
- }\r
-\r
- private void WriteGenericArray (BinaryWriter writer, long id, Array array)\r
- {\r
- Type elementType = array.GetType().GetElementType();\r
-\r
- // Registers and writes the assembly of the array element type if needed\r
-\r
- if (!elementType.IsArray)\r
- WriteAssembly (writer, elementType.Assembly);\r
-\r
- // Writes the array\r
-\r
- writer.Write ((byte) BinaryElement.GenericArray);\r
- writer.Write ((int)id);\r
- \r
- // Write the structure of the array\r
-\r
- if (elementType.IsArray) \r
- writer.Write ((byte) ArrayStructure.Jagged);\r
- else if (array.Rank == 1)\r
- writer.Write ((byte) ArrayStructure.SingleDimensional);\r
- else\r
- writer.Write ((byte) ArrayStructure.MultiDimensional);\r
-\r
- // Write the number of dimensions and the length\r
- // of each dimension\r
-\r
- writer.Write (array.Rank);\r
- for (int n=0; n<array.Rank; n++)\r
- writer.Write (array.GetUpperBound (n) + 1);\r
-\r
- // Writes the type\r
- WriteTypeCode (writer, elementType);\r
- WriteTypeSpec (writer, elementType);\r
-\r
- // Writes the values. For single-dimension array, a special tag is used\r
- // to represent multiple consecutive null values. I don't know why this\r
- // optimization is not used for multidimensional arrays.\r
-\r
- if (array.Rank == 1 && !elementType.IsValueType)\r
- {\r
- WriteSingleDimensionArrayElements (writer, array, elementType);\r
- }\r
- else\r
- {\r
- foreach (object item in array)\r
- WriteValue (writer, elementType, item);\r
- }\r
- }\r
-\r
- private void WriteObjectArray (BinaryWriter writer, long id, Array array)\r
- {\r
- writer.Write ((byte) BinaryElement.ArrayOfObject);\r
- writer.Write ((int)id);\r
- writer.Write (array.Length); // Single dimension. Just write the length\r
- WriteSingleDimensionArrayElements (writer, array, typeof (object));\r
- }\r
-\r
- private void WriteStringArray (BinaryWriter writer, long id, Array array)\r
- {\r
- writer.Write ((byte) BinaryElement.ArrayOfString);\r
- writer.Write ((int)id);\r
- writer.Write (array.Length); // Single dimension. Just write the length\r
- WriteSingleDimensionArrayElements (writer, array, typeof (string));\r
- }\r
-\r
- private void WritePrimitiveTypeArray (BinaryWriter writer, long id, Array array)\r
- {\r
- writer.Write ((byte) BinaryElement.ArrayOfPrimitiveType);\r
- writer.Write ((int)id);\r
- writer.Write (array.Length); // Single dimension. Just write the length\r
-\r
- Type elementType = array.GetType().GetElementType();\r
- WriteTypeSpec (writer, elementType);\r
-\r
- switch (Type.GetTypeCode (elementType))\r
- {\r
- case TypeCode.Boolean:\r
- foreach (bool item in (bool[]) array)\r
- writer.Write (item);\r
- break;\r
-\r
- case TypeCode.Byte:\r
- writer.Write ((byte[]) array);\r
- break;\r
-\r
- case TypeCode.Char:\r
- writer.Write ((char[]) array);\r
- break;\r
-\r
- case TypeCode.DateTime: \r
- foreach (DateTime item in (DateTime[]) array) {\r
- ulong val = (ulong) item.Ticks;\r
-#if NET_2_0\r
- val |= ((ulong) item.Kind) << 62;\r
-#endif\r
- writer.Write (val);\r
- }\r
- break;\r
-\r
- case TypeCode.Decimal:\r
- foreach (decimal item in (decimal[]) array)\r
- writer.Write (item);\r
- break;\r
-\r
- case TypeCode.Double:\r
- if (array.Length > 2)\r
- BlockWrite (writer, array, 8);\r
- else\r
- foreach (double item in (double[]) array)\r
- writer.Write (item);\r
- break;\r
-\r
- case TypeCode.Int16:\r
- if (array.Length > 2)\r
- BlockWrite (writer, array, 2);\r
- else\r
- foreach (short item in (short[]) array)\r
- writer.Write (item);\r
- break;\r
-\r
- case TypeCode.Int32:\r
- if (array.Length > 2)\r
- BlockWrite (writer, array, 4);\r
- else\r
- foreach (int item in (int[]) array)\r
- writer.Write (item);\r
- break;\r
-\r
- case TypeCode.Int64:\r
- if (array.Length > 2)\r
- BlockWrite (writer, array, 8);\r
- else\r
- foreach (long item in (long[]) array)\r
- writer.Write (item);\r
- break;\r
-\r
- case TypeCode.SByte:\r
- if (array.Length > 2)\r
- BlockWrite (writer, array, 1);\r
- else\r
- foreach (sbyte item in (sbyte[]) array)\r
- writer.Write (item);\r
- break;\r
-\r
- case TypeCode.Single:\r
- if (array.Length > 2)\r
- BlockWrite (writer, array, 4);\r
- else\r
- foreach (float item in (float[]) array)\r
- writer.Write (item);\r
- break;\r
-\r
- case TypeCode.UInt16:\r
- if (array.Length > 2)\r
- BlockWrite (writer, array, 2);\r
- else\r
- foreach (ushort item in (ushort[]) array)\r
- writer.Write (item);\r
- break;\r
-\r
- case TypeCode.UInt32:\r
- if (array.Length > 2)\r
- BlockWrite (writer, array, 4);\r
- else\r
- foreach (uint item in (uint[]) array)\r
- writer.Write (item);\r
- break;\r
-\r
- case TypeCode.UInt64:\r
- if (array.Length > 2)\r
- BlockWrite (writer, array, 8);\r
- else\r
- foreach (ulong item in (ulong[]) array)\r
- writer.Write (item);\r
- break;\r
-\r
- case TypeCode.String:\r
- foreach (string item in (string[]) array)\r
- writer.Write (item);\r
- break;\r
-\r
- default:\r
- if (elementType == typeof (TimeSpan)) {\r
- foreach (TimeSpan item in (TimeSpan[]) array)\r
- writer.Write (item.Ticks);\r
- }\r
- else\r
- throw new NotSupportedException ("Unsupported primitive type: " + elementType.FullName);\r
- break;\r
- } \r
- }\r
- \r
- private void BlockWrite (BinaryWriter writer, 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
- Buffer.BlockCopy (array, pos, arrayBuffer, 0, size);\r
- \r
- if (!BitConverter.IsLittleEndian && dataSize > 1)\r
- BinaryCommon.SwapBytes (arrayBuffer, size, dataSize);\r
- \r
- writer.Write (arrayBuffer, 0, size);\r
- totalSize -= size;\r
- pos += size;\r
- }\r
- }\r
-\r
- private void WriteSingleDimensionArrayElements (BinaryWriter writer, Array array, Type elementType)\r
- {\r
- int numNulls = 0;\r
- foreach (object val in array)\r
- {\r
- if (val != null && numNulls > 0)\r
- {\r
- WriteNullFiller (writer, numNulls);\r
- WriteValue (writer, elementType, val);\r
- numNulls = 0;\r
- }\r
- else if (val == null)\r
- numNulls++;\r
- else\r
- WriteValue (writer, elementType, val);\r
- }\r
- if (numNulls > 0)\r
- WriteNullFiller (writer, numNulls);\r
- }\r
-\r
- private void WriteNullFiller (BinaryWriter writer, int numNulls)\r
- {\r
- if (numNulls == 1) {\r
- writer.Write ((byte) BinaryElement.NullValue);\r
- }\r
- else if (numNulls == 2) {\r
- writer.Write ((byte) BinaryElement.NullValue);\r
- writer.Write ((byte) BinaryElement.NullValue);\r
- }\r
- else if (numNulls <= byte.MaxValue) {\r
- writer.Write ((byte) BinaryElement.ArrayFiller8b);\r
- writer.Write ((byte) numNulls);\r
- }\r
- else {\r
- writer.Write ((byte) BinaryElement.ArrayFiller32b);\r
- writer.Write (numNulls);\r
- }\r
- }\r
-\r
- private void WriteObjectReference (BinaryWriter writer, long id)\r
- {\r
-\r
- writer.Write ((byte) BinaryElement.ObjectReference);\r
- writer.Write ((int)id);\r
- }\r
-\r
- public void WriteValue (BinaryWriter writer, Type valueType, object val)\r
- {\r
- if (val == null) \r
- {\r
- BinaryCommon.CheckSerializable (valueType, _surrogateSelector, _context);\r
- writer.Write ((byte) BinaryElement.NullValue);\r
- }\r
- else if (BinaryCommon.IsPrimitive(val.GetType()))\r
- {\r
- if (!BinaryCommon.IsPrimitive(valueType))\r
- {\r
- // It is a boxed primitive type value\r
- writer.Write ((byte) BinaryElement.BoxedPrimitiveTypeValue);\r
- WriteTypeSpec (writer, val.GetType());\r
- }\r
- WritePrimitiveValue (writer, val);\r
- }\r
- else if (valueType.IsValueType)\r
- {\r
- // Value types are written embedded in the containing object\r
- WriteObjectInstance (writer, val, true);\r
- }\r
- else if (val is string)\r
- {\r
- // Strings are written embedded, unless already registered\r
- bool firstTime;\r
- long id = _idGenerator.GetId (val, out firstTime);\r
-\r
- if (firstTime) WriteObjectInstance (writer, val, false);\r
- else WriteObjectReference (writer, id);\r
- } \r
- else\r
- {\r
- // It is a reference type. Write a forward reference and queue the\r
- // object to the pending object list (unless already written).\r
-\r
- bool firstTime;\r
- long id = _idGenerator.GetId (val, out firstTime);\r
-\r
- if (firstTime) _pendingObjects.Enqueue (val);\r
- WriteObjectReference (writer, id);\r
- }\r
- }\r
- \r
- private void WriteString (BinaryWriter writer, long id, string str)\r
- {\r
- writer.Write ((byte) BinaryElement.String);\r
- writer.Write ((int)id);\r
- writer.Write (str);\r
- }\r
-\r
- public int WriteAssembly (BinaryWriter writer, Assembly assembly)\r
- {\r
- return WriteAssemblyName (writer, assembly.FullName);\r
- }\r
- \r
- public int WriteAssemblyName (BinaryWriter writer, string assembly)\r
- {\r
- if (assembly == ObjectWriter.CorlibAssemblyName) return -1;\r
- \r
- bool firstTime;\r
- int id = RegisterAssembly (assembly, out firstTime);\r
- if (!firstTime) return id;\r
- \r
- writer.Write ((byte) BinaryElement.Assembly);\r
- writer.Write (id);\r
- if (_assemblyFormat == FormatterAssemblyStyle.Full)\r
- writer.Write (assembly);\r
- else {\r
- int i = assembly.IndexOf (',');\r
- if (i != -1) assembly = assembly.Substring (0, i);\r
- writer.Write (assembly);\r
- }\r
- \r
- return id;\r
- }\r
-\r
- public int GetAssemblyId (Assembly assembly)\r
- {\r
- return GetAssemblyNameId (assembly.FullName);\r
- }\r
- \r
- public int GetAssemblyNameId (string assembly)\r
- {\r
- return (int)_assemblyCache[assembly];\r
- }\r
-\r
- private int RegisterAssembly (string assembly, out bool firstTime)\r
- {\r
- if (_assemblyCache.ContainsKey (assembly))\r
- {\r
- firstTime = false;\r
- return (int)_assemblyCache[assembly];\r
- }\r
- else\r
- {\r
- int id = (int)_idGenerator.GetId (0, out firstTime);\r
- _assemblyCache.Add (assembly, id);\r
- return id;\r
- }\r
- }\r
-\r
- public static void WritePrimitiveValue (BinaryWriter writer, object value)\r
- {\r
- Type type = value.GetType();\r
-\r
- switch (Type.GetTypeCode (type))\r
- {\r
- case TypeCode.Boolean:\r
- writer.Write ((bool)value);\r
- break;\r
-\r
- case TypeCode.Byte:\r
- writer.Write ((byte) value);\r
- break;\r
-\r
- case TypeCode.Char:\r
- writer.Write ((char) value);\r
- break;\r
-\r
- case TypeCode.DateTime: \r
- writer.Write ( ((DateTime)value).Ticks);\r
- break;\r
-\r
- case TypeCode.Decimal:\r
- writer.Write (((decimal) value).ToString (CultureInfo.InvariantCulture));\r
- break;\r
-\r
- case TypeCode.Double:\r
- writer.Write ((double) value);\r
- break;\r
-\r
- case TypeCode.Int16:\r
- writer.Write ((short) value);\r
- break;\r
-\r
- case TypeCode.Int32:\r
- writer.Write ((int) value);\r
- break;\r
-\r
- case TypeCode.Int64:\r
- writer.Write ((long) value);\r
- break;\r
-\r
- case TypeCode.SByte:\r
- writer.Write ((sbyte) value);\r
- break;\r
-\r
- case TypeCode.Single:\r
- writer.Write ((float) value);\r
- break;\r
-\r
- case TypeCode.UInt16:\r
- writer.Write ((ushort) value);\r
- break;\r
-\r
- case TypeCode.UInt32:\r
- writer.Write ((uint) value);\r
- break;\r
-\r
- case TypeCode.UInt64:\r
- writer.Write ((ulong) value);\r
- break;\r
-\r
- case TypeCode.String:\r
- writer.Write ((string) value);\r
- break;\r
-\r
- default:\r
- if (type == typeof (TimeSpan))\r
- writer.Write (((TimeSpan)value).Ticks);\r
- else\r
- throw new NotSupportedException ("Unsupported primitive type: " + value.GetType().FullName);\r
- break;\r
- }\r
- }\r
-\r
- public static void WriteTypeCode (BinaryWriter writer, Type type)\r
- {\r
- writer.Write ((byte) GetTypeTag (type));\r
- }\r
-\r
- public static TypeTag GetTypeTag (Type type)\r
- {\r
- if (type == typeof (string)) {\r
- return TypeTag.String;\r
- }\r
- else if (BinaryCommon.IsPrimitive (type)) {\r
- return TypeTag.PrimitiveType;\r
- }\r
- else if (type == typeof (object)) {\r
- return TypeTag.ObjectType;\r
- }\r
- else if (type.IsArray && type.GetArrayRank() == 1 && type.GetElementType() == typeof (object)) {\r
- return TypeTag.ArrayOfObject; \r
- }\r
- else if (type.IsArray && type.GetArrayRank() == 1 && type.GetElementType() == typeof (string)){\r
- return TypeTag.ArrayOfString;\r
- }\r
- else if (type.IsArray && type.GetArrayRank() == 1 && BinaryCommon.IsPrimitive(type.GetElementType())) {\r
- return TypeTag.ArrayOfPrimitiveType;\r
- }\r
- else if (type.Assembly == CorlibAssembly) {\r
- return TypeTag.RuntimeType;\r
- }\r
- else\r
- return TypeTag.GenericType;\r
- }\r
-\r
- public void WriteTypeSpec (BinaryWriter writer, Type type)\r
- {\r
- // WARNING Keep in sync with EmitWriteTypeSpec\r
- \r
- switch (GetTypeTag (type))\r
- {\r
- case TypeTag.PrimitiveType:\r
- writer.Write (BinaryCommon.GetTypeCode (type));\r
- break;\r
-\r
- case TypeTag.RuntimeType:\r
- writer.Write (type.FullName);\r
- break;\r
-\r
- case TypeTag.GenericType:\r
- writer.Write (type.FullName);\r
- writer.Write ((int)GetAssemblyId (type.Assembly));\r
- break;\r
-\r
- case TypeTag.ArrayOfPrimitiveType:\r
- writer.Write (BinaryCommon.GetTypeCode (type.GetElementType()));\r
- break;\r
-\r
- default:\r
- // Type spec not needed\r
- break;\r
- }\r
- }\r
- }\r
-}\r
+// ObjectWriter.cs
+//
+// Author:
+// Lluis Sanchez Gual (lluis@ideary.com)
+//
+// (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.IO;
+using System.Collections;
+using System.Runtime.Serialization;
+using System.Runtime.Remoting.Messaging;
+using System.Reflection;
+using System.Globalization;
+
+namespace System.Runtime.Serialization.Formatters.Binary
+{
+ abstract class TypeMetadata
+ {
+ public string TypeAssemblyName;
+ public string InstanceTypeName;
+
+ public abstract void WriteAssemblies (ObjectWriter ow, BinaryWriter writer);
+ public abstract void WriteTypeData (ObjectWriter ow, BinaryWriter writer, bool writeTypes);
+ public abstract void WriteObjectData (ObjectWriter ow, BinaryWriter writer, object data);
+
+ public virtual bool IsCompatible (TypeMetadata other)
+ {
+ return true;
+ }
+
+#if NET_4_0
+ public void BindToName (string assemblyName, string typeName)
+ {
+ if (assemblyName != null)
+ TypeAssemblyName = assemblyName;
+ if (typeName != null)
+ InstanceTypeName = typeName;
+ }
+#endif
+
+ public abstract bool RequiresTypes { get; }
+ }
+
+ abstract class ClrTypeMetadata: TypeMetadata
+ {
+ public Type InstanceType;
+
+ public ClrTypeMetadata (Type instanceType)
+ {
+ InstanceType = instanceType;
+ InstanceTypeName = instanceType.FullName;
+ TypeAssemblyName = instanceType.Assembly.FullName;
+ }
+
+ public override bool RequiresTypes {
+ get { return false; }
+ }
+ }
+
+ class SerializableTypeMetadata: TypeMetadata
+ {
+ Type[] types;
+ string[] names;
+
+ public SerializableTypeMetadata (Type itype, SerializationInfo info)
+ {
+ types = new Type [info.MemberCount];
+ names = new string [info.MemberCount];
+
+ SerializationInfoEnumerator e = info.GetEnumerator ();
+
+ int n = 0;
+ while (e.MoveNext ())
+ {
+ types[n] = e.ObjectType;
+ names[n] = e.Name;
+ n++;
+ }
+
+ TypeAssemblyName = info.AssemblyName;
+ InstanceTypeName = info.FullTypeName;
+ }
+
+ public override bool IsCompatible (TypeMetadata other)
+ {
+ if (!(other is SerializableTypeMetadata)) return false;
+
+ SerializableTypeMetadata tm = (SerializableTypeMetadata)other;
+ if (types.Length != tm.types.Length) return false;
+ if (TypeAssemblyName != tm.TypeAssemblyName) return false;
+ if (InstanceTypeName != tm.InstanceTypeName) return false;
+ for (int n=0; n<types.Length; n++)
+ {
+ if (types[n] != tm.types[n]) return false;
+ if (names[n] != tm.names[n]) return false;
+ }
+ return true;
+ }
+
+ public override void WriteAssemblies (ObjectWriter ow, BinaryWriter writer)
+ {
+ foreach (Type mtype in types)
+ {
+ Type type = mtype;
+ while (type.IsArray)
+ type = type.GetElementType();
+
+ ow.WriteAssembly (writer, type.Assembly);
+ }
+ }
+
+ public override void WriteTypeData (ObjectWriter ow, BinaryWriter writer, bool writeTypes)
+ {
+ writer.Write (types.Length);
+
+ // Names of fields
+ foreach (string name in names)
+ writer.Write (name);
+
+ // Types of fields
+ foreach (Type type in types)
+ ObjectWriter.WriteTypeCode (writer, type);
+
+ // Type specs of fields
+ foreach (Type type in types)
+ ow.WriteTypeSpec (writer, type);
+ }
+
+ public override void WriteObjectData (ObjectWriter ow, BinaryWriter writer, object data)
+ {
+ SerializationInfo info = (SerializationInfo) data;
+ SerializationInfoEnumerator e = info.GetEnumerator ();
+
+ while (e.MoveNext ())
+ ow.WriteValue (writer, e.ObjectType, e.Value);
+ }
+
+ public override bool RequiresTypes {
+ get { return true; }
+ }
+ }
+
+ class MemberTypeMetadata: ClrTypeMetadata
+ {
+ MemberInfo[] members;
+
+ public MemberTypeMetadata (Type type, StreamingContext context): base (type)
+ {
+ members = FormatterServices.GetSerializableMembers (type, context);
+ }
+
+ public override void WriteAssemblies (ObjectWriter ow, BinaryWriter writer)
+ {
+ foreach (FieldInfo field in members)
+ {
+ Type type = field.FieldType;
+ while (type.IsArray)
+ type = type.GetElementType();
+
+ ow.WriteAssembly (writer, type.Assembly);
+ }
+ }
+
+ public override void WriteTypeData (ObjectWriter ow, BinaryWriter writer, bool writeTypes)
+ {
+ writer.Write (members.Length);
+
+ // Names of fields
+ foreach (FieldInfo field in members)
+ writer.Write (field.Name);
+
+ if (writeTypes) {
+ // Types of fields
+ foreach (FieldInfo field in members)
+ ObjectWriter.WriteTypeCode (writer, field.FieldType);
+
+ // Type specs of fields
+ foreach (FieldInfo field in members)
+ ow.WriteTypeSpec (writer, field.FieldType);
+ }
+ }
+
+ public override void WriteObjectData (ObjectWriter ow, BinaryWriter writer, object data)
+ {
+ object[] values = FormatterServices.GetObjectData (data, members);
+ for (int n=0; n<values.Length; n++)
+ ow.WriteValue (writer, ((FieldInfo)members[n]).FieldType, values[n]);
+ }
+ }
+
+ internal class ObjectWriter
+ {
+ ObjectIDGenerator _idGenerator = new ObjectIDGenerator();
+ Hashtable _cachedMetadata = new Hashtable();
+ Queue _pendingObjects = new Queue();
+ Hashtable _assemblyCache = new Hashtable();
+
+ // Type metadata that can be shared with all serializers
+ static Hashtable _cachedTypes = new Hashtable();
+
+ internal static Assembly CorlibAssembly = typeof(string).Assembly;
+ internal static string CorlibAssemblyName = typeof(string).Assembly.FullName;
+
+ ISurrogateSelector _surrogateSelector;
+ StreamingContext _context;
+ FormatterAssemblyStyle _assemblyFormat;
+ FormatterTypeStyle _typeFormat;
+#if NET_4_0
+ SerializationBinder _binder;
+#endif
+ byte[] arrayBuffer;
+ int ArrayBufferLength = 4096;
+ SerializationObjectManager _manager;
+
+ class MetadataReference
+ {
+ public TypeMetadata Metadata;
+ public long ObjectID;
+
+ public MetadataReference (TypeMetadata metadata, long id)
+ {
+ Metadata = metadata;
+ ObjectID = id;
+ }
+ }
+
+ public ObjectWriter (BinaryFormatter formatter)
+ {
+ _surrogateSelector = formatter.SurrogateSelector;
+ _context = formatter.Context;
+ _assemblyFormat = formatter.AssemblyFormat;
+ _typeFormat = formatter.TypeFormat;
+ _manager = new SerializationObjectManager (formatter.Context);
+#if NET_4_0
+ _binder = formatter.Binder;
+#endif
+ }
+
+ public void WriteObjectGraph (BinaryWriter writer, object obj, Header[] headers)
+ {
+ _pendingObjects.Clear();
+ if (headers != null) QueueObject (headers);
+ QueueObject (obj);
+ WriteQueuedObjects (writer);
+ WriteSerializationEnd (writer);
+ _manager.RaiseOnSerializedEvent ();
+ }
+
+ public void QueueObject (object obj)
+ {
+ _pendingObjects.Enqueue (obj);
+ }
+
+ public void WriteQueuedObjects (BinaryWriter writer)
+ {
+ while (_pendingObjects.Count > 0)
+ WriteObjectInstance (writer, _pendingObjects.Dequeue(), false);
+ }
+
+ public void WriteObjectInstance (BinaryWriter writer, object obj, bool isValueObject)
+ {
+ bool firstTime;
+ long id;
+
+ // If the object is a value type (not boxed) then there is no need
+ // to register it in the id generator, because it won't have other
+ // references to it
+
+ if (isValueObject) id = _idGenerator.NextId;
+ else id = _idGenerator.GetId (obj, out firstTime);
+
+ if (obj is string) {
+ WriteString (writer, id, (string)obj);
+ }
+ else if (obj is Array) {
+ WriteArray (writer, id, (Array)obj);
+ }
+ else
+ WriteObject (writer, id, obj);
+ }
+
+ public static void WriteSerializationEnd (BinaryWriter writer)
+ {
+ writer.Write ((byte) BinaryElement.End);
+ }
+
+ private void WriteObject (BinaryWriter writer, long id, object obj)
+ {
+ object data;
+ TypeMetadata metadata;
+
+ GetObjectData (obj, out metadata, out data);
+ MetadataReference metadataReference = (MetadataReference)_cachedMetadata [metadata.InstanceTypeName];
+
+ if (metadataReference != null && metadata.IsCompatible (metadataReference.Metadata))
+ {
+ // An object of the same type has already been serialized
+ // It is not necessary to write again type metadata
+
+ writer.Write ((byte) BinaryElement.RefTypeObject);
+ writer.Write ((int)id);
+
+ writer.Write ((int)metadataReference.ObjectID);
+ metadata.WriteObjectData (this, writer, data);
+ return;
+ }
+
+ if (metadataReference == null)
+ {
+ metadataReference = new MetadataReference (metadata, id);
+ _cachedMetadata [metadata.InstanceTypeName] = metadataReference;
+ }
+
+ bool writeTypes = metadata.RequiresTypes || _typeFormat == FormatterTypeStyle.TypesAlways;
+
+ BinaryElement objectTag;
+
+ int assemblyId;
+ if (metadata.TypeAssemblyName == CorlibAssemblyName)
+ {
+ // A corlib type
+ objectTag = writeTypes ? BinaryElement.RuntimeObject : BinaryElement.UntypedRuntimeObject;
+ assemblyId = -1;
+ }
+ else
+ {
+ objectTag = writeTypes ? BinaryElement.ExternalObject : BinaryElement.UntypedExternalObject;
+ assemblyId = WriteAssemblyName (writer, metadata.TypeAssemblyName);
+ }
+
+ // Registers the assemblies needed for each field
+ // If there are assemblies that where not registered before this object,
+ // write them now
+
+ metadata.WriteAssemblies (this, writer);
+
+ // Writes the object
+
+ writer.Write ((byte) objectTag);
+ writer.Write ((int)id);
+ writer.Write (metadata.InstanceTypeName);
+
+ metadata.WriteTypeData (this, writer, writeTypes);
+ if (assemblyId != -1) writer.Write (assemblyId);
+
+ metadata.WriteObjectData (this, writer, data);
+ }
+
+ private void GetObjectData (object obj, out TypeMetadata metadata, out object data)
+ {
+ Type instanceType = obj.GetType();
+#if NET_4_0
+ string binderAssemblyName = null;
+ string binderTypeName = null;
+ if (_binder != null)
+ _binder.BindToName (instanceType, out binderAssemblyName, out binderTypeName);
+#endif
+ // Check if the formatter has a surrogate selector, if it does,
+ // check if the surrogate selector handles objects of the given type.
+
+ if (_surrogateSelector != null)
+ {
+ ISurrogateSelector selector;
+ ISerializationSurrogate surrogate = _surrogateSelector.GetSurrogate (instanceType, _context, out selector);
+ if (surrogate != null)
+ {
+ SerializationInfo info = new SerializationInfo (instanceType, new FormatterConverter ());
+ surrogate.GetObjectData (obj, info, _context);
+ metadata = new SerializableTypeMetadata (instanceType, info);
+#if NET_4_0
+ if (_binder != null)
+ metadata.BindToName (binderAssemblyName, binderTypeName);
+#endif
+
+ data = info;
+ return;
+ }
+ }
+
+ // Check if the object is marked with the Serializable attribute
+
+ BinaryCommon.CheckSerializable (instanceType, _surrogateSelector, _context);
+
+ _manager.RegisterObject (obj);
+
+ ISerializable ser = obj as ISerializable;
+
+ if (ser != null)
+ {
+ SerializationInfo info = new SerializationInfo (instanceType, new FormatterConverter ());
+ ser.GetObjectData (info, _context);
+ metadata = new SerializableTypeMetadata (instanceType, info);
+#if NET_4_0
+ if (_binder != null)
+ metadata.BindToName (binderAssemblyName, binderTypeName);
+#endif
+
+ data = info;
+ }
+ else
+ {
+ data = obj;
+ if (_context.Context != null)
+ {
+ // Don't cache metadata info when the Context property is not null sice
+ // we can't control the number of possible contexts in this case
+ metadata = new MemberTypeMetadata (instanceType, _context);
+#if NET_4_0
+ if (_binder != null)
+ metadata.BindToName (binderAssemblyName, binderTypeName);
+#endif
+
+ return;
+ }
+
+ Hashtable typesTable;
+ bool isNew = false;
+ lock (_cachedTypes) {
+ typesTable = (Hashtable) _cachedTypes [_context.State];
+ if (typesTable == null) {
+ typesTable = new Hashtable ();
+ _cachedTypes [_context.State] = typesTable;
+ isNew = true;
+ }
+ }
+
+ metadata = null;
+ lock (typesTable) {
+ if (!isNew) {
+ metadata = (TypeMetadata) typesTable [instanceType];
+ }
+
+ if (metadata == null) {
+ metadata = CreateMemberTypeMetadata (instanceType);
+#if NET_4_0
+ if (_binder != null)
+ metadata.BindToName (binderAssemblyName, binderTypeName);
+#endif
+ }
+
+ typesTable [instanceType] = metadata;
+ }
+ }
+ }
+
+ TypeMetadata CreateMemberTypeMetadata (Type type)
+ {
+ if (!BinaryCommon.UseReflectionSerialization) {
+ Type metaType = CodeGenerator.GenerateMetadataType (type, _context);
+ return (TypeMetadata) Activator.CreateInstance (metaType);
+ }
+ else
+ return new MemberTypeMetadata (type, _context);
+ }
+
+ private void WriteArray (BinaryWriter writer, long id, Array array)
+ {
+ // There are 4 ways of serializing arrays:
+ // The element GenericArray (7) can be used for all arrays.
+ // The element ArrayOfPrimitiveType (15) can be used for single-dimensional
+ // arrays of primitive types
+ // The element ArrayOfObject (16) can be used for single-dimensional Object arrays
+ // The element ArrayOfString (17) can be used for single-dimensional string arrays
+
+ Type elementType = array.GetType().GetElementType();
+
+ if (elementType == typeof (object) && array.Rank == 1) {
+ WriteObjectArray (writer, id, array);
+ }
+ else if (elementType == typeof (string) && array.Rank == 1) {
+ WriteStringArray (writer, id, array);
+ }
+ else if (BinaryCommon.IsPrimitive(elementType) && array.Rank == 1) {
+ WritePrimitiveTypeArray (writer, id, array);
+ }
+ else
+ WriteGenericArray (writer, id, array);
+ }
+
+ private void WriteGenericArray (BinaryWriter writer, long id, Array array)
+ {
+ Type elementType = array.GetType().GetElementType();
+
+ // Registers and writes the assembly of the array element type if needed
+
+ if (!elementType.IsArray)
+ WriteAssembly (writer, elementType.Assembly);
+
+ // Writes the array
+
+ writer.Write ((byte) BinaryElement.GenericArray);
+ writer.Write ((int)id);
+
+ // Write the structure of the array
+
+ if (elementType.IsArray)
+ writer.Write ((byte) ArrayStructure.Jagged);
+ else if (array.Rank == 1)
+ writer.Write ((byte) ArrayStructure.SingleDimensional);
+ else
+ writer.Write ((byte) ArrayStructure.MultiDimensional);
+
+ // Write the number of dimensions and the length
+ // of each dimension
+
+ writer.Write (array.Rank);
+ for (int n=0; n<array.Rank; n++)
+ writer.Write (array.GetUpperBound (n) + 1);
+
+ // Writes the type
+ WriteTypeCode (writer, elementType);
+ WriteTypeSpec (writer, elementType);
+
+ // Writes the values. For single-dimension array, a special tag is used
+ // to represent multiple consecutive null values. I don't know why this
+ // optimization is not used for multidimensional arrays.
+
+ if (array.Rank == 1 && !elementType.IsValueType)
+ {
+ WriteSingleDimensionArrayElements (writer, array, elementType);
+ }
+ else
+ {
+ foreach (object item in array)
+ WriteValue (writer, elementType, item);
+ }
+ }
+
+ private void WriteObjectArray (BinaryWriter writer, long id, Array array)
+ {
+ writer.Write ((byte) BinaryElement.ArrayOfObject);
+ writer.Write ((int)id);
+ writer.Write (array.Length); // Single dimension. Just write the length
+ WriteSingleDimensionArrayElements (writer, array, typeof (object));
+ }
+
+ private void WriteStringArray (BinaryWriter writer, long id, Array array)
+ {
+ writer.Write ((byte) BinaryElement.ArrayOfString);
+ writer.Write ((int)id);
+ writer.Write (array.Length); // Single dimension. Just write the length
+ WriteSingleDimensionArrayElements (writer, array, typeof (string));
+ }
+
+ private void WritePrimitiveTypeArray (BinaryWriter writer, long id, Array array)
+ {
+ writer.Write ((byte) BinaryElement.ArrayOfPrimitiveType);
+ writer.Write ((int)id);
+ writer.Write (array.Length); // Single dimension. Just write the length
+
+ Type elementType = array.GetType().GetElementType();
+ WriteTypeSpec (writer, elementType);
+
+ switch (Type.GetTypeCode (elementType))
+ {
+ case TypeCode.Boolean:
+ foreach (bool item in (bool[]) array)
+ writer.Write (item);
+ break;
+
+ case TypeCode.Byte:
+ writer.Write ((byte[]) array);
+ break;
+
+ case TypeCode.Char:
+ writer.Write ((char[]) array);
+ break;
+
+ case TypeCode.DateTime:
+ foreach (DateTime item in (DateTime[]) array)
+ writer.Write (item.ToBinary ());
+ break;
+
+ case TypeCode.Decimal:
+ foreach (decimal item in (decimal[]) array)
+ writer.Write (item);
+ break;
+
+ case TypeCode.Double:
+ if (array.Length > 2)
+ BlockWrite (writer, array, 8);
+ else
+ foreach (double item in (double[]) array)
+ writer.Write (item);
+ break;
+
+ case TypeCode.Int16:
+ if (array.Length > 2)
+ BlockWrite (writer, array, 2);
+ else
+ foreach (short item in (short[]) array)
+ writer.Write (item);
+ break;
+
+ case TypeCode.Int32:
+ if (array.Length > 2)
+ BlockWrite (writer, array, 4);
+ else
+ foreach (int item in (int[]) array)
+ writer.Write (item);
+ break;
+
+ case TypeCode.Int64:
+ if (array.Length > 2)
+ BlockWrite (writer, array, 8);
+ else
+ foreach (long item in (long[]) array)
+ writer.Write (item);
+ break;
+
+ case TypeCode.SByte:
+ if (array.Length > 2)
+ BlockWrite (writer, array, 1);
+ else
+ foreach (sbyte item in (sbyte[]) array)
+ writer.Write (item);
+ break;
+
+ case TypeCode.Single:
+ if (array.Length > 2)
+ BlockWrite (writer, array, 4);
+ else
+ foreach (float item in (float[]) array)
+ writer.Write (item);
+ break;
+
+ case TypeCode.UInt16:
+ if (array.Length > 2)
+ BlockWrite (writer, array, 2);
+ else
+ foreach (ushort item in (ushort[]) array)
+ writer.Write (item);
+ break;
+
+ case TypeCode.UInt32:
+ if (array.Length > 2)
+ BlockWrite (writer, array, 4);
+ else
+ foreach (uint item in (uint[]) array)
+ writer.Write (item);
+ break;
+
+ case TypeCode.UInt64:
+ if (array.Length > 2)
+ BlockWrite (writer, array, 8);
+ else
+ foreach (ulong item in (ulong[]) array)
+ writer.Write (item);
+ break;
+
+ case TypeCode.String:
+ foreach (string item in (string[]) array)
+ writer.Write (item);
+ break;
+
+ default:
+ if (elementType == typeof (TimeSpan)) {
+ foreach (TimeSpan item in (TimeSpan[]) array)
+ writer.Write (item.Ticks);
+ }
+ else
+ throw new NotSupportedException ("Unsupported primitive type: " + elementType.FullName);
+ break;
+ }
+ }
+
+ private void BlockWrite (BinaryWriter writer, 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;
+ Buffer.BlockCopy (array, pos, arrayBuffer, 0, size);
+
+ if (!BitConverter.IsLittleEndian && dataSize > 1)
+ BinaryCommon.SwapBytes (arrayBuffer, size, dataSize);
+
+ writer.Write (arrayBuffer, 0, size);
+ totalSize -= size;
+ pos += size;
+ }
+ }
+
+ private void WriteSingleDimensionArrayElements (BinaryWriter writer, Array array, Type elementType)
+ {
+ int numNulls = 0;
+ foreach (object val in array)
+ {
+ if (val != null && numNulls > 0)
+ {
+ WriteNullFiller (writer, numNulls);
+ WriteValue (writer, elementType, val);
+ numNulls = 0;
+ }
+ else if (val == null)
+ numNulls++;
+ else
+ WriteValue (writer, elementType, val);
+ }
+ if (numNulls > 0)
+ WriteNullFiller (writer, numNulls);
+ }
+
+ private void WriteNullFiller (BinaryWriter writer, int numNulls)
+ {
+ if (numNulls == 1) {
+ writer.Write ((byte) BinaryElement.NullValue);
+ }
+ else if (numNulls == 2) {
+ writer.Write ((byte) BinaryElement.NullValue);
+ writer.Write ((byte) BinaryElement.NullValue);
+ }
+ else if (numNulls <= byte.MaxValue) {
+ writer.Write ((byte) BinaryElement.ArrayFiller8b);
+ writer.Write ((byte) numNulls);
+ }
+ else {
+ writer.Write ((byte) BinaryElement.ArrayFiller32b);
+ writer.Write (numNulls);
+ }
+ }
+
+ private void WriteObjectReference (BinaryWriter writer, long id)
+ {
+
+ writer.Write ((byte) BinaryElement.ObjectReference);
+ writer.Write ((int)id);
+ }
+
+ public void WriteValue (BinaryWriter writer, Type valueType, object val)
+ {
+ if (val == null)
+ {
+ BinaryCommon.CheckSerializable (valueType, _surrogateSelector, _context);
+ writer.Write ((byte) BinaryElement.NullValue);
+ }
+ else if (BinaryCommon.IsPrimitive(val.GetType()))
+ {
+ if (!BinaryCommon.IsPrimitive(valueType))
+ {
+ // It is a boxed primitive type value
+ writer.Write ((byte) BinaryElement.BoxedPrimitiveTypeValue);
+ WriteTypeSpec (writer, val.GetType());
+ }
+ WritePrimitiveValue (writer, val);
+ }
+ else if (valueType.IsValueType)
+ {
+ // Value types are written embedded in the containing object
+ WriteObjectInstance (writer, val, true);
+ }
+ else if (val is string)
+ {
+ // Strings are written embedded, unless already registered
+ bool firstTime;
+ long id = _idGenerator.GetId (val, out firstTime);
+
+ if (firstTime) WriteObjectInstance (writer, val, false);
+ else WriteObjectReference (writer, id);
+ }
+ else
+ {
+ // It is a reference type. Write a forward reference and queue the
+ // object to the pending object list (unless already written).
+
+ bool firstTime;
+ long id = _idGenerator.GetId (val, out firstTime);
+
+ if (firstTime) _pendingObjects.Enqueue (val);
+ WriteObjectReference (writer, id);
+ }
+ }
+
+ private void WriteString (BinaryWriter writer, long id, string str)
+ {
+ writer.Write ((byte) BinaryElement.String);
+ writer.Write ((int)id);
+ writer.Write (str);
+ }
+
+ public int WriteAssembly (BinaryWriter writer, Assembly assembly)
+ {
+ return WriteAssemblyName (writer, assembly.FullName);
+ }
+
+ public int WriteAssemblyName (BinaryWriter writer, string assembly)
+ {
+ if (assembly == ObjectWriter.CorlibAssemblyName) return -1;
+
+ bool firstTime;
+ int id = RegisterAssembly (assembly, out firstTime);
+ if (!firstTime) return id;
+
+ writer.Write ((byte) BinaryElement.Assembly);
+ writer.Write (id);
+ if (_assemblyFormat == FormatterAssemblyStyle.Full)
+ writer.Write (assembly);
+ else {
+ int i = assembly.IndexOf (',');
+ if (i != -1) assembly = assembly.Substring (0, i);
+ writer.Write (assembly);
+ }
+
+ return id;
+ }
+
+ public int GetAssemblyId (Assembly assembly)
+ {
+ return GetAssemblyNameId (assembly.FullName);
+ }
+
+ public int GetAssemblyNameId (string assembly)
+ {
+ return (int)_assemblyCache[assembly];
+ }
+
+ private int RegisterAssembly (string assembly, out bool firstTime)
+ {
+ if (_assemblyCache.ContainsKey (assembly))
+ {
+ firstTime = false;
+ return (int)_assemblyCache[assembly];
+ }
+ else
+ {
+ int id = (int)_idGenerator.GetId (0, out firstTime);
+ _assemblyCache.Add (assembly, id);
+ return id;
+ }
+ }
+
+ public static void WritePrimitiveValue (BinaryWriter writer, object value)
+ {
+ Type type = value.GetType();
+
+ switch (Type.GetTypeCode (type))
+ {
+ case TypeCode.Boolean:
+ writer.Write ((bool)value);
+ break;
+
+ case TypeCode.Byte:
+ writer.Write ((byte) value);
+ break;
+
+ case TypeCode.Char:
+ writer.Write ((char) value);
+ break;
+
+ case TypeCode.DateTime:
+ writer.Write ( ((DateTime)value).ToBinary ());
+ break;
+
+ case TypeCode.Decimal:
+ writer.Write (((decimal) value).ToString (CultureInfo.InvariantCulture));
+ break;
+
+ case TypeCode.Double:
+ writer.Write ((double) value);
+ break;
+
+ case TypeCode.Int16:
+ writer.Write ((short) value);
+ break;
+
+ case TypeCode.Int32:
+ writer.Write ((int) value);
+ break;
+
+ case TypeCode.Int64:
+ writer.Write ((long) value);
+ break;
+
+ case TypeCode.SByte:
+ writer.Write ((sbyte) value);
+ break;
+
+ case TypeCode.Single:
+ writer.Write ((float) value);
+ break;
+
+ case TypeCode.UInt16:
+ writer.Write ((ushort) value);
+ break;
+
+ case TypeCode.UInt32:
+ writer.Write ((uint) value);
+ break;
+
+ case TypeCode.UInt64:
+ writer.Write ((ulong) value);
+ break;
+
+ case TypeCode.String:
+ writer.Write ((string) value);
+ break;
+
+ default:
+ if (type == typeof (TimeSpan))
+ writer.Write (((TimeSpan)value).Ticks);
+ else
+ throw new NotSupportedException ("Unsupported primitive type: " + value.GetType().FullName);
+ break;
+ }
+ }
+
+ public static void WriteTypeCode (BinaryWriter writer, Type type)
+ {
+ writer.Write ((byte) GetTypeTag (type));
+ }
+
+ public static TypeTag GetTypeTag (Type type)
+ {
+ if (type == typeof (string)) {
+ return TypeTag.String;
+ }
+ else if (BinaryCommon.IsPrimitive (type)) {
+ return TypeTag.PrimitiveType;
+ }
+ else if (type == typeof (object)) {
+ return TypeTag.ObjectType;
+ }
+ else if (type.IsArray && type.GetArrayRank() == 1 && type.GetElementType() == typeof (object)) {
+ return TypeTag.ArrayOfObject;
+ }
+ else if (type.IsArray && type.GetArrayRank() == 1 && type.GetElementType() == typeof (string)){
+ return TypeTag.ArrayOfString;
+ }
+ else if (type.IsArray && type.GetArrayRank() == 1 && BinaryCommon.IsPrimitive(type.GetElementType())) {
+ return TypeTag.ArrayOfPrimitiveType;
+ }
+ else if (type.Assembly == CorlibAssembly) {
+ return TypeTag.RuntimeType;
+ }
+ else
+ return TypeTag.GenericType;
+ }
+
+ public void WriteTypeSpec (BinaryWriter writer, Type type)
+ {
+ // WARNING Keep in sync with EmitWriteTypeSpec
+
+ switch (GetTypeTag (type))
+ {
+ case TypeTag.PrimitiveType:
+ writer.Write (BinaryCommon.GetTypeCode (type));
+ break;
+
+ case TypeTag.RuntimeType:
+ string fullName = type.FullName;
+ // Map System.MonoType to MS.NET's System.RuntimeType,
+ // when called in remoting context.
+ // Note that this code does not need to be in sync with
+ // EmitWriteTypeSpec because serializing a MethodCall
+ // won't trigger the CodeGenerator.
+ if (_context.State == StreamingContextStates.Remoting)
+ if (type == typeof (System.MonoType))
+ fullName = "System.RuntimeType";
+ else if (type == typeof (System.MonoType[]))
+ fullName = "System.RuntimeType[]";
+ writer.Write (fullName);
+ break;
+
+ case TypeTag.GenericType:
+ writer.Write (type.FullName);
+ writer.Write ((int)GetAssemblyId (type.Assembly));
+ break;
+
+ case TypeTag.ArrayOfPrimitiveType:
+ writer.Write (BinaryCommon.GetTypeCode (type.GetElementType()));
+ break;
+
+ default:
+ // Type spec not needed
+ break;
+ }
+ }
+ }
+}