Merge pull request #615 from nealef/master
[mono.git] / mcs / class / corlib / System.Runtime.Serialization.Formatters.Binary / ObjectWriter.cs
1 // ObjectWriter.cs
2 //
3 // Author:
4 //   Lluis Sanchez Gual (lluis@ideary.com)
5 //
6 // (C) 2003 Lluis Sanchez Gual
7
8 //
9 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System;
32 using System.IO;
33 using System.Collections;
34 using System.Runtime.Serialization;
35 using System.Runtime.Remoting.Messaging;
36 using System.Reflection;
37 using System.Globalization;
38 using System.Runtime.CompilerServices;
39
40 namespace System.Runtime.Serialization.Formatters.Binary
41 {
42 #if NET_4_0
43         public class GetForwardedAttribute
44         {
45                 public static string GetAssemblyName (Type self) 
46                 {
47                         var attrs = self.GetCustomAttributes(
48                                         typeof (TypeForwardedFromAttribute), false);
49                         if (attrs.Length == 0)
50                                 return self.Assembly.FullName;
51                         else
52                                 return ((TypeForwardedFromAttribute)attrs [0]).AssemblyFullName;
53                 }
54         }
55 #endif
56
57         abstract class TypeMetadata
58         {
59                 public string TypeAssemblyName;
60                 public string InstanceTypeName;
61                 
62                 public abstract void WriteAssemblies (ObjectWriter ow, BinaryWriter writer);
63                 public abstract void WriteTypeData (ObjectWriter ow, BinaryWriter writer, bool writeTypes);
64                 public abstract void WriteObjectData (ObjectWriter ow, BinaryWriter writer, object data);
65                 
66                 public virtual bool IsCompatible (TypeMetadata other)
67                 {
68                         return true;
69                 }
70
71 #if NET_4_0
72                 public void BindToName (string assemblyName, string typeName)
73                 {
74                         if (assemblyName != null)
75                                 TypeAssemblyName = assemblyName;
76                         if (typeName != null)
77                                 InstanceTypeName = typeName;
78                 }
79 #endif
80                 
81                 public abstract bool RequiresTypes { get; }
82         }
83         
84         abstract class ClrTypeMetadata: TypeMetadata
85         {
86                 public Type InstanceType;
87
88                 public ClrTypeMetadata (Type instanceType)
89                 {
90                         InstanceType = instanceType;
91                         InstanceTypeName = instanceType.FullName;
92 #if NET_4_0
93                         TypeAssemblyName = GetForwardedAttribute.GetAssemblyName(instanceType);
94 #else
95                         TypeAssemblyName = instanceType.Assembly.FullName;
96 #endif
97                 }
98                 
99                 public override bool RequiresTypes {
100                         get { return false; }
101                 }
102         }
103         
104         class SerializableTypeMetadata: TypeMetadata
105         {
106                 Type[] types;
107                 string[] names;
108                 
109                 public SerializableTypeMetadata (Type itype, SerializationInfo info)
110                 {
111                         types = new Type [info.MemberCount];
112                         names = new string [info.MemberCount];
113
114                         SerializationInfoEnumerator e = info.GetEnumerator ();
115
116                         int n = 0;
117                         while (e.MoveNext ())
118                         {
119                                 types[n] = e.ObjectType;
120                                 names[n] = e.Name;
121                                 n++;
122                         }
123
124                         TypeAssemblyName = info.AssemblyName;
125 #if NET_4_0
126                         InstanceTypeName = GetForwardedAttribute.GetAssemblyName(itype);
127 #else
128                         InstanceTypeName = info.FullTypeName;
129 #endif
130                 }
131                 
132                 public override bool IsCompatible (TypeMetadata other)
133                 {
134                         if (!(other is SerializableTypeMetadata)) return false;
135                         
136                         SerializableTypeMetadata tm = (SerializableTypeMetadata)other;
137                         if (types.Length != tm.types.Length) return false;
138                         if (TypeAssemblyName != tm.TypeAssemblyName) return false;
139                         if (InstanceTypeName != tm.InstanceTypeName) return false;
140                         for (int n=0; n<types.Length; n++)
141                         {
142                                 if (types[n] != tm.types[n]) return false;
143                                 if (names[n] != tm.names[n]) return false;
144                         }
145                         return true;
146                 }
147                 
148                 public override void WriteAssemblies (ObjectWriter ow, BinaryWriter writer)
149                 {
150                         foreach (Type mtype in types)
151                         {
152                                 Type type = mtype;
153                                 while (type.IsArray) 
154                                         type = type.GetElementType();
155                                         
156                                 ow.WriteTypeAssembly (writer, type);
157                         }
158                 }
159                 
160                 public override void WriteTypeData (ObjectWriter ow, BinaryWriter writer, bool writeTypes)
161                 {
162                         writer.Write (types.Length);
163
164                         // Names of fields
165                         foreach (string name in names)
166                                 writer.Write (name);
167
168                         // Types of fields
169                         foreach (Type type in types) 
170                                 ObjectWriter.WriteTypeCode (writer, type);
171
172                         // Type specs of fields
173                         foreach (Type type in types)
174                                 ow.WriteTypeSpec (writer, type);
175                 }
176                 
177                 public override void WriteObjectData (ObjectWriter ow, BinaryWriter writer, object data)
178                 {
179                         SerializationInfo info = (SerializationInfo) data;
180                         SerializationInfoEnumerator e = info.GetEnumerator ();
181
182                         while (e.MoveNext ())
183                                 ow.WriteValue (writer, e.ObjectType, e.Value);
184                 }
185                 
186                 public override bool RequiresTypes {
187                         get { return true; }
188                 }
189         }
190         
191         class MemberTypeMetadata: ClrTypeMetadata
192         {
193                 MemberInfo[] members;
194                 
195                 public MemberTypeMetadata (Type type, StreamingContext context): base (type)
196                 {
197                         members = FormatterServices.GetSerializableMembers (type, context);
198                 }
199
200                 public override void WriteAssemblies (ObjectWriter ow, BinaryWriter writer)
201                 {
202                         foreach (FieldInfo field in members)
203                         {
204                                 Type type = field.FieldType;
205                                 while (type.IsArray) 
206                                         type = type.GetElementType();
207                                         
208                                 ow.WriteTypeAssembly (writer, type);
209                         }
210                 }
211                 
212                 public override void WriteTypeData (ObjectWriter ow, BinaryWriter writer, bool writeTypes)
213                 {
214                         writer.Write (members.Length);
215
216                         // Names of fields
217                         foreach (FieldInfo field in members)
218                                 writer.Write (field.Name);
219
220                         if (writeTypes) {
221                                 // Types of fields
222                                 foreach (FieldInfo field in members)
223                                         ObjectWriter.WriteTypeCode (writer, field.FieldType);
224         
225                                 // Type specs of fields
226                                 foreach (FieldInfo field in members)
227                                         ow.WriteTypeSpec (writer, field.FieldType);
228                         }
229                 }
230                 
231                 public override void WriteObjectData (ObjectWriter ow, BinaryWriter writer, object data)
232                 {
233                         object[] values = FormatterServices.GetObjectData (data, members);
234                         for (int n=0; n<values.Length; n++)
235                                 ow.WriteValue (writer, ((FieldInfo)members[n]).FieldType, values[n]);
236                 }
237         }
238         
239         internal class ObjectWriter
240         {
241                 ObjectIDGenerator _idGenerator = new ObjectIDGenerator();
242                 Hashtable _cachedMetadata = new Hashtable();
243                 Queue _pendingObjects = new Queue();
244                 Hashtable _assemblyCache = new Hashtable();
245                 
246                 // Type metadata that can be shared with all serializers
247                 static Hashtable _cachedTypes = new Hashtable();
248
249                 internal static Assembly CorlibAssembly = typeof(string).Assembly;
250                 internal static string CorlibAssemblyName = typeof(string).Assembly.FullName;
251
252                 ISurrogateSelector _surrogateSelector;
253                 StreamingContext _context;
254                 FormatterAssemblyStyle _assemblyFormat;
255                 FormatterTypeStyle _typeFormat;
256 #if NET_4_0
257                 SerializationBinder _binder;
258 #endif
259                 byte[] arrayBuffer;
260                 int ArrayBufferLength = 4096;
261                 SerializationObjectManager _manager;
262                 
263                 class MetadataReference
264                 {
265                         public TypeMetadata Metadata;
266                         public long ObjectID;
267                         
268                         public MetadataReference (TypeMetadata metadata, long id)
269                         {
270                                 Metadata = metadata;
271                                 ObjectID = id;
272                         }
273                 }
274                 
275                 public ObjectWriter (BinaryFormatter formatter)
276                 {
277                         _surrogateSelector = formatter.SurrogateSelector;
278                         _context = formatter.Context;
279                         _assemblyFormat = formatter.AssemblyFormat;
280                         _typeFormat = formatter.TypeFormat;
281                         _manager = new SerializationObjectManager (formatter.Context);
282 #if NET_4_0
283                         _binder = formatter.Binder;
284 #endif
285                 }
286
287                 public void WriteObjectGraph (BinaryWriter writer, object obj, Header[] headers)
288                 {
289                         _pendingObjects.Clear();
290                         if (headers != null) QueueObject (headers);
291                         QueueObject (obj);
292                         WriteQueuedObjects (writer);
293                         WriteSerializationEnd (writer);
294                         _manager.RaiseOnSerializedEvent ();
295                 }
296
297                 public void QueueObject (object obj)
298                 {
299                         _pendingObjects.Enqueue (obj);
300                 }
301
302                 public void WriteQueuedObjects (BinaryWriter writer)
303                 {
304                         while (_pendingObjects.Count > 0)
305                                 WriteObjectInstance (writer, _pendingObjects.Dequeue(), false);
306                 }
307
308                 public void WriteObjectInstance (BinaryWriter writer, object obj, bool isValueObject)
309                 {
310                         bool firstTime;
311                         long id;
312
313                         // If the object is a value type (not boxed) then there is no need
314                         // to register it in the id generator, because it won't have other
315                         // references to it
316
317                         if (isValueObject) id = _idGenerator.NextId;
318                         else id = _idGenerator.GetId (obj, out firstTime);
319
320                         if (obj is string) {
321                                 WriteString (writer, id, (string)obj);
322                         }
323                         else if (obj is Array) {
324                                 WriteArray (writer, id, (Array)obj);
325                         }
326                         else
327                                 WriteObject (writer, id, obj);
328                 }
329
330                 public static void WriteSerializationEnd (BinaryWriter writer)
331                 {
332                         writer.Write ((byte) BinaryElement.End);
333                 }
334
335                 private void WriteObject (BinaryWriter writer, long id, object obj)
336                 {
337                         object data;
338                         TypeMetadata metadata;
339
340                         GetObjectData (obj, out metadata, out data);
341                         MetadataReference metadataReference = (MetadataReference)_cachedMetadata [metadata.InstanceTypeName];
342
343                         if (metadataReference != null && metadata.IsCompatible (metadataReference.Metadata))
344                         {
345                                 // An object of the same type has already been serialized
346                                 // It is not necessary to write again type metadata
347
348                                 writer.Write ((byte) BinaryElement.RefTypeObject);
349                                 writer.Write ((int)id);
350
351                                 writer.Write ((int)metadataReference.ObjectID);
352                                 metadata.WriteObjectData (this, writer, data);
353                                 return;
354                         }
355
356                         if (metadataReference == null)
357                         {
358                                 metadataReference = new MetadataReference (metadata, id);
359                                 _cachedMetadata [metadata.InstanceTypeName] = metadataReference;
360                         }
361                         
362                         bool writeTypes = metadata.RequiresTypes || _typeFormat == FormatterTypeStyle.TypesAlways;
363
364                         BinaryElement objectTag;
365
366                         int assemblyId;
367                         if (metadata.TypeAssemblyName == CorlibAssemblyName)
368                         {
369                                 // A corlib type
370                                 objectTag = writeTypes ? BinaryElement.RuntimeObject : BinaryElement.UntypedRuntimeObject;
371                                 assemblyId = -1;
372                         }
373                         else
374                         {
375                                 objectTag = writeTypes ? BinaryElement.ExternalObject : BinaryElement.UntypedExternalObject;
376                                 assemblyId = WriteAssemblyName (writer, metadata.TypeAssemblyName);
377                         }
378
379                         // Registers the assemblies needed for each field
380                         // If there are assemblies that where not registered before this object,
381                         // write them now
382
383                         metadata.WriteAssemblies (this, writer);
384
385                         // Writes the object
386
387                         writer.Write ((byte) objectTag);
388                         writer.Write ((int)id);
389                         writer.Write (metadata.InstanceTypeName);
390                         
391                         metadata.WriteTypeData (this, writer, writeTypes);
392                         if (assemblyId != -1) writer.Write (assemblyId);
393                         
394                         metadata.WriteObjectData (this, writer, data);
395                 }
396
397                 private void GetObjectData (object obj, out TypeMetadata metadata, out object data)
398                 {
399                         Type instanceType = obj.GetType();
400 #if NET_4_0
401                         string binderAssemblyName = null;
402                         string binderTypeName = null;
403                         if (_binder != null)
404                                 _binder.BindToName (instanceType, out binderAssemblyName, out binderTypeName);
405 #endif
406                         // Check if the formatter has a surrogate selector, if it does, 
407                         // check if the surrogate selector handles objects of the given type. 
408
409                         if (_surrogateSelector != null)
410                         {
411                                 ISurrogateSelector selector;
412                                 ISerializationSurrogate surrogate = _surrogateSelector.GetSurrogate (instanceType, _context, out selector);
413                                 if (surrogate != null)
414                                 {
415                                         SerializationInfo info = new SerializationInfo (instanceType, new FormatterConverter ());
416                                         surrogate.GetObjectData (obj, info, _context);
417                                         metadata = new SerializableTypeMetadata (instanceType, info);
418 #if NET_4_0
419                                         if (_binder != null)
420                                                 metadata.BindToName (binderAssemblyName, binderTypeName);
421 #endif
422
423                                         data = info;
424                                         return;
425                                 }
426                         }
427
428                         // Check if the object is marked with the Serializable attribute
429
430                         BinaryCommon.CheckSerializable (instanceType, _surrogateSelector, _context);
431
432                         _manager.RegisterObject (obj);
433
434                         ISerializable ser = obj as ISerializable;
435
436                         if (ser != null) 
437                         {
438                                 SerializationInfo info = new SerializationInfo (instanceType, new FormatterConverter ());
439                                 ser.GetObjectData (info, _context);
440                                 metadata = new SerializableTypeMetadata (instanceType, info);
441 #if NET_4_0
442                                 if (_binder != null)
443                                         metadata.BindToName (binderAssemblyName, binderTypeName);
444 #endif
445
446                                 data = info;
447                         } 
448                         else 
449                         {
450                                 data = obj;
451                                 if (_context.Context != null)
452                                 {
453                                         // Don't cache metadata info when the Context property is not null sice
454                                         // we can't control the number of possible contexts in this case
455                                         metadata = new MemberTypeMetadata (instanceType, _context);
456 #if NET_4_0
457                                         if (_binder != null)
458                                                 metadata.BindToName (binderAssemblyName, binderTypeName);
459 #endif
460
461                                         return;
462                                 }
463                                 
464                                 Hashtable typesTable;
465                                 bool isNew = false;
466                                 lock (_cachedTypes) {
467                                         typesTable = (Hashtable) _cachedTypes [_context.State];
468                                         if (typesTable == null) {
469                                                 typesTable = new Hashtable ();
470                                                 _cachedTypes [_context.State] = typesTable;
471                                                 isNew = true;
472                                         }
473                                 }
474
475                                 metadata = null;
476                                 lock (typesTable) {
477                                         if (!isNew) {
478                                                 metadata = (TypeMetadata) typesTable [instanceType];
479                                         }
480
481                                         if (metadata == null) {
482                                                 metadata = CreateMemberTypeMetadata (instanceType);
483 #if NET_4_0
484                                                 if (_binder != null)
485                                                         metadata.BindToName (binderAssemblyName, binderTypeName);
486 #endif
487                                         }
488
489                                         typesTable [instanceType] = metadata;
490                                 }
491                         }
492                 }
493                 
494                 TypeMetadata CreateMemberTypeMetadata (Type type)
495                 {
496 #if !FULL_AOT_RUNTIME
497                         if (!BinaryCommon.UseReflectionSerialization) {
498                                 Type metaType = CodeGenerator.GenerateMetadataType (type, _context);
499                                 return (TypeMetadata) Activator.CreateInstance (metaType);
500                         }
501                         else
502 #endif
503                                 return new MemberTypeMetadata (type, _context);
504                 }
505
506                 private void WriteArray (BinaryWriter writer, long id, Array array)
507                 {
508                         // There are 4 ways of serializing arrays:
509                         // The element GenericArray (7) can be used for all arrays.
510                         // The element ArrayOfPrimitiveType (15) can be used for single-dimensional
511                         // arrays of primitive types
512                         // The element ArrayOfObject (16) can be used for single-dimensional Object arrays
513                         // The element ArrayOfString (17) can be used for single-dimensional string arrays
514
515                         Type elementType = array.GetType().GetElementType();
516
517                         if (elementType == typeof (object) && array.Rank == 1) {
518                                 WriteObjectArray (writer, id, array);
519                         }
520                         else if (elementType == typeof (string) && array.Rank == 1) {
521                                 WriteStringArray (writer, id, array);
522                         }
523                         else if (BinaryCommon.IsPrimitive(elementType) && array.Rank == 1) {
524                                 WritePrimitiveTypeArray (writer, id, array);
525                         }
526                         else
527                                 WriteGenericArray (writer, id, array);
528                 }
529
530                 private void WriteGenericArray (BinaryWriter writer, long id, Array array)
531                 {
532                         Type elementType = array.GetType().GetElementType();
533
534                         // Registers and writes the assembly of the array element type if needed
535
536                         var tag = GetTypeTag (elementType);
537                         if ((tag != TypeTag.ArrayOfObject) && (tag != TypeTag.ArrayOfString) && (tag != TypeTag.ArrayOfPrimitiveType))
538                                 WriteTypeAssembly (writer, elementType);
539
540                         // Writes the array
541
542                         writer.Write ((byte) BinaryElement.GenericArray);
543                         writer.Write ((int)id);
544                         
545                         // Write the structure of the array
546
547                         if (elementType.IsArray) 
548                                 writer.Write ((byte) ArrayStructure.Jagged);
549                         else if (array.Rank == 1)
550                                 writer.Write ((byte) ArrayStructure.SingleDimensional);
551                         else
552                                 writer.Write ((byte) ArrayStructure.MultiDimensional);
553
554                         // Write the number of dimensions and the length
555                         // of each dimension
556
557                         writer.Write (array.Rank);
558                         for (int n=0; n<array.Rank; n++)
559                                 writer.Write (array.GetUpperBound (n) + 1);
560
561                         // Writes the type
562                         WriteTypeCode (writer, elementType);
563                         WriteTypeSpec (writer, elementType);
564
565                         // Writes the values. For single-dimension array, a special tag is used
566                         // to represent multiple consecutive null values. I don't know why this
567                         // optimization is not used for multidimensional arrays.
568
569                         if (array.Rank == 1 && !elementType.IsValueType)
570                         {
571                                 WriteSingleDimensionArrayElements (writer, array, elementType);
572                         }
573                         else
574                         {
575                                 foreach (object item in array)
576                                         WriteValue (writer, elementType, item);
577                         }
578                 }
579
580                 private void WriteObjectArray (BinaryWriter writer, long id, Array array)
581                 {
582                         writer.Write ((byte) BinaryElement.ArrayOfObject);
583                         writer.Write ((int)id);
584                         writer.Write (array.Length);    // Single dimension. Just write the length
585                         WriteSingleDimensionArrayElements (writer, array, typeof (object));
586                 }
587
588                 private void WriteStringArray (BinaryWriter writer, long id, Array array)
589                 {
590                         writer.Write ((byte) BinaryElement.ArrayOfString);
591                         writer.Write ((int)id);
592                         writer.Write (array.Length);    // Single dimension. Just write the length
593                         WriteSingleDimensionArrayElements (writer, array, typeof (string));
594                 }
595
596                 private void WritePrimitiveTypeArray (BinaryWriter writer, long id, Array array)
597                 {
598                         writer.Write ((byte) BinaryElement.ArrayOfPrimitiveType);
599                         writer.Write ((int)id);
600                         writer.Write (array.Length);    // Single dimension. Just write the length
601
602                         Type elementType = array.GetType().GetElementType();
603                         WriteTypeSpec (writer, elementType);
604
605                         switch (Type.GetTypeCode (elementType))
606                         {
607                                 case TypeCode.Boolean:
608                                         foreach (bool item in (bool[]) array)
609                                                 writer.Write (item);
610                                         break;
611
612                                 case TypeCode.Byte:
613                                         writer.Write ((byte[]) array);
614                                         break;
615
616                                 case TypeCode.Char:
617                                         writer.Write ((char[]) array);
618                                         break;
619
620                                 case TypeCode.DateTime: 
621                                         foreach (DateTime item in (DateTime[]) array)
622                                                 writer.Write (item.ToBinary ());
623                                         break;
624
625                                 case TypeCode.Decimal:
626                                         foreach (decimal item in (decimal[]) array)
627                                                 writer.Write (item);
628                                         break;
629
630                                 case TypeCode.Double:
631                                         if (array.Length > 2)
632                                                 BlockWrite (writer, array, 8);
633                                         else
634                                                 foreach (double item in (double[]) array)
635                                                         writer.Write (item);
636                                         break;
637
638                                 case TypeCode.Int16:
639                                         if (array.Length > 2)
640                                                 BlockWrite (writer, array, 2);
641                                         else
642                                                 foreach (short item in (short[]) array)
643                                                         writer.Write (item);
644                                         break;
645
646                                 case TypeCode.Int32:
647                                         if (array.Length > 2)
648                                                 BlockWrite (writer, array, 4);
649                                         else
650                                                 foreach (int item in (int[]) array)
651                                                         writer.Write (item);
652                                         break;
653
654                                 case TypeCode.Int64:
655                                         if (array.Length > 2)
656                                                 BlockWrite (writer, array, 8);
657                                         else
658                                                 foreach (long item in (long[]) array)
659                                                         writer.Write (item);
660                                         break;
661
662                                 case TypeCode.SByte:
663                                         if (array.Length > 2)
664                                                 BlockWrite (writer, array, 1);
665                                         else
666                                                 foreach (sbyte item in (sbyte[]) array)
667                                                         writer.Write (item);
668                                         break;
669
670                                 case TypeCode.Single:
671                                         if (array.Length > 2)
672                                                 BlockWrite (writer, array, 4);
673                                         else
674                                                 foreach (float item in (float[]) array)
675                                                         writer.Write (item);
676                                         break;
677
678                                 case TypeCode.UInt16:
679                                         if (array.Length > 2)
680                                                 BlockWrite (writer, array, 2);
681                                         else
682                                                 foreach (ushort item in (ushort[]) array)
683                                                         writer.Write (item);
684                                         break;
685
686                                 case TypeCode.UInt32:
687                                         if (array.Length > 2)
688                                                 BlockWrite (writer, array, 4);
689                                         else
690                                                 foreach (uint item in (uint[]) array)
691                                                         writer.Write (item);
692                                         break;
693
694                                 case TypeCode.UInt64:
695                                         if (array.Length > 2)
696                                                 BlockWrite (writer, array, 8);
697                                         else
698                                                 foreach (ulong item in (ulong[]) array)
699                                                         writer.Write (item);
700                                         break;
701
702                                 case TypeCode.String:
703                                         foreach (string item in (string[]) array)
704                                                 writer.Write (item);
705                                         break;
706
707                                 default:
708                                         if (elementType == typeof (TimeSpan)) {
709                                                 foreach (TimeSpan item in (TimeSpan[]) array)
710                                                         writer.Write (item.Ticks);
711                                         }
712                                         else
713                                                 throw new NotSupportedException ("Unsupported primitive type: " + elementType.FullName);
714                                         break;
715                         }                       
716                 }
717                 
718                 private void BlockWrite (BinaryWriter writer, Array array, int dataSize)
719                 {
720                         int totalSize = Buffer.ByteLength (array);
721                         
722                         if (arrayBuffer == null || (totalSize > arrayBuffer.Length && arrayBuffer.Length != ArrayBufferLength))
723                                 arrayBuffer = new byte [totalSize <= ArrayBufferLength ? totalSize : ArrayBufferLength];
724                         
725                         int pos = 0;
726                         while (totalSize > 0) {
727                                 int size = totalSize < arrayBuffer.Length ? totalSize : arrayBuffer.Length;
728                                 Buffer.BlockCopy (array, pos, arrayBuffer, 0, size);
729                                 
730                                 if (!BitConverter.IsLittleEndian && dataSize > 1)
731                                         BinaryCommon.SwapBytes (arrayBuffer, size, dataSize);
732                                 
733                                 writer.Write (arrayBuffer, 0, size);
734                                 totalSize -= size;
735                                 pos += size;
736                         }
737                 }
738
739                 private void WriteSingleDimensionArrayElements (BinaryWriter writer, Array array, Type elementType)
740                 {
741                         int numNulls = 0;
742                         foreach (object val in array)
743                         {
744                                 if (val != null && numNulls > 0)
745                                 {
746                                         WriteNullFiller (writer, numNulls);
747                                         WriteValue (writer, elementType, val);
748                                         numNulls = 0;
749                                 }
750                                 else if (val == null)
751                                         numNulls++;
752                                 else
753                                         WriteValue (writer, elementType, val);
754                         }
755                         if (numNulls > 0)
756                                 WriteNullFiller (writer, numNulls);
757                 }
758
759                 private void WriteNullFiller (BinaryWriter writer, int numNulls)
760                 {
761                         if (numNulls == 1) {
762                                 writer.Write ((byte) BinaryElement.NullValue);
763                         }
764                         else if (numNulls == 2) {
765                                 writer.Write ((byte) BinaryElement.NullValue);
766                                 writer.Write ((byte) BinaryElement.NullValue);
767                         }
768                         else if (numNulls <= byte.MaxValue) {
769                                 writer.Write ((byte) BinaryElement.ArrayFiller8b);
770                                 writer.Write ((byte) numNulls);
771                         }
772                         else {
773                                 writer.Write ((byte) BinaryElement.ArrayFiller32b);
774                                 writer.Write (numNulls);
775                         }
776                 }
777
778                 private void WriteObjectReference (BinaryWriter writer, long id)
779                 {
780
781                         writer.Write ((byte) BinaryElement.ObjectReference);
782                         writer.Write ((int)id);
783                 }
784
785                 public void WriteValue (BinaryWriter writer, Type valueType, object val)
786                 {
787                         if (val == null) 
788                         {
789                                 BinaryCommon.CheckSerializable (valueType, _surrogateSelector, _context);
790                                 writer.Write ((byte) BinaryElement.NullValue);
791                         }
792                         else if (BinaryCommon.IsPrimitive(val.GetType()))
793                         {
794                                 if (!BinaryCommon.IsPrimitive(valueType))
795                                 {
796                                         // It is a boxed primitive type value
797                                         writer.Write ((byte) BinaryElement.BoxedPrimitiveTypeValue);
798                                         WriteTypeSpec (writer, val.GetType());
799                                 }
800                                 WritePrimitiveValue (writer, val);
801                         }
802                         else if (valueType.IsValueType)
803                         {
804                                 // Value types are written embedded in the containing object
805                                 WriteObjectInstance (writer, val, true);
806                         }
807                         else if (val is string)
808                         {
809                                 // Strings are written embedded, unless already registered
810                                 bool firstTime;
811                                 long id = _idGenerator.GetId (val, out firstTime);
812
813                                 if (firstTime) WriteObjectInstance (writer, val, false);
814                                 else WriteObjectReference (writer, id);
815                         }                       
816                         else
817                         {
818                                 // It is a reference type. Write a forward reference and queue the
819                                 // object to the pending object list (unless already written).
820
821                                 bool firstTime;
822                                 long id = _idGenerator.GetId (val, out firstTime);
823
824                                 if (firstTime) _pendingObjects.Enqueue (val);
825                                 WriteObjectReference (writer, id);
826                         }
827                 }
828                 
829                 private void WriteString (BinaryWriter writer, long id, string str)
830                 {
831                         writer.Write ((byte) BinaryElement.String);
832                         writer.Write ((int)id);
833                         writer.Write (str);
834                 }
835
836                 public int WriteTypeAssembly (BinaryWriter writer, Type aType)
837                 {
838 #if NET_4_0
839                         return WriteAssemblyName (writer, GetForwardedAttribute.GetAssemblyName(aType));
840 #else
841                         return WriteAssemblyName (writer, aType.Assembly.FullName);
842 #endif
843                 }
844                 
845                 public int WriteAssembly (BinaryWriter writer, Assembly assembly)
846                 {
847                         return WriteAssemblyName (writer, assembly.FullName);
848                 }
849                 
850                 public int WriteAssemblyName (BinaryWriter writer, string assembly)
851                 {
852                         if (assembly == ObjectWriter.CorlibAssemblyName) return -1;
853                         
854                         bool firstTime;
855                         int id = RegisterAssembly (assembly, out firstTime);
856                         if (!firstTime) return id;
857                                         
858                         writer.Write ((byte) BinaryElement.Assembly);
859                         writer.Write (id);
860                         if (_assemblyFormat == FormatterAssemblyStyle.Full)
861                                 writer.Write (assembly);
862                         else {
863                                 int i = assembly.IndexOf (',');
864                                 if (i != -1) assembly = assembly.Substring (0, i);
865                                 writer.Write (assembly);
866                         }
867                                 
868                         return id;
869                 }
870
871                 public int GetAssemblyId (Assembly assembly)
872                 {
873                         return GetAssemblyNameId (assembly.FullName);
874                 }
875                 
876                 public int GetAssemblyNameId (string assembly)
877                 {
878                         return (int)_assemblyCache[assembly];
879                 }
880
881                 private int RegisterAssembly (string assembly, out bool firstTime)
882                 {
883                         if (_assemblyCache.ContainsKey (assembly))
884                         {
885                                 firstTime = false;
886                                 return (int)_assemblyCache[assembly];
887                         }
888                         else
889                         {
890                                 int id = (int)_idGenerator.GetId (0, out firstTime);
891                                 _assemblyCache.Add (assembly, id);
892                                 return id;
893                         }
894                 }
895
896                 public static void WritePrimitiveValue (BinaryWriter writer, object value)
897                 {
898                         Type type = value.GetType();
899
900                         switch (Type.GetTypeCode (type))
901                         {
902                                 case TypeCode.Boolean:
903                                         writer.Write ((bool)value);
904                                         break;
905
906                                 case TypeCode.Byte:
907                                         writer.Write ((byte) value);
908                                         break;
909
910                                 case TypeCode.Char:
911                                         writer.Write ((char) value);
912                                         break;
913
914                                 case TypeCode.DateTime: 
915                                         writer.Write ( ((DateTime)value).ToBinary ());
916                                         break;
917
918                                 case TypeCode.Decimal:
919                                         writer.Write (((decimal) value).ToString (CultureInfo.InvariantCulture));
920                                         break;
921
922                                 case TypeCode.Double:
923                                         writer.Write ((double) value);
924                                         break;
925
926                                 case TypeCode.Int16:
927                                         writer.Write ((short) value);
928                                         break;
929
930                                 case TypeCode.Int32:
931                                         writer.Write ((int) value);
932                                         break;
933
934                                 case TypeCode.Int64:
935                                         writer.Write ((long) value);
936                                         break;
937
938                                 case TypeCode.SByte:
939                                         writer.Write ((sbyte) value);
940                                         break;
941
942                                 case TypeCode.Single:
943                                         writer.Write ((float) value);
944                                         break;
945
946                                 case TypeCode.UInt16:
947                                         writer.Write ((ushort) value);
948                                         break;
949
950                                 case TypeCode.UInt32:
951                                         writer.Write ((uint) value);
952                                         break;
953
954                                 case TypeCode.UInt64:
955                                         writer.Write ((ulong) value);
956                                         break;
957
958                                 case TypeCode.String:
959                                         writer.Write ((string) value);
960                                         break;
961
962                                 default:
963                                         if (type == typeof (TimeSpan))
964                                                 writer.Write (((TimeSpan)value).Ticks);
965                                         else
966                                                 throw new NotSupportedException ("Unsupported primitive type: " + value.GetType().FullName);
967                                         break;
968                         }
969                 }
970
971                 public static void WriteTypeCode (BinaryWriter writer, Type type)
972                 {
973                         writer.Write ((byte) GetTypeTag (type));
974                 }
975
976                 public static TypeTag GetTypeTag (Type type)
977                 {
978                         if (type == typeof (string)) {
979                                 return TypeTag.String;
980                         }
981                         else if (BinaryCommon.IsPrimitive (type)) {
982                                 return TypeTag.PrimitiveType;
983                         }
984                         else if (type == typeof (object)) {
985                                 return TypeTag.ObjectType;
986                         }
987                         else if (type.IsArray && type.GetArrayRank() == 1 && type.GetElementType() == typeof (object)) {
988                                 return TypeTag.ArrayOfObject; 
989                         }
990                         else if (type.IsArray && type.GetArrayRank() == 1 && type.GetElementType() == typeof (string)){
991                                 return TypeTag.ArrayOfString;
992                         }
993                         else if (type.IsArray && type.GetArrayRank() == 1 && BinaryCommon.IsPrimitive(type.GetElementType())) {
994                                 return TypeTag.ArrayOfPrimitiveType;
995                         }
996                         else if (type.Assembly == CorlibAssembly) {
997                                 return TypeTag.RuntimeType;
998                         }
999                         else
1000                                 return TypeTag.GenericType;
1001                 }
1002
1003                 public void WriteTypeSpec (BinaryWriter writer, Type type)
1004                 {
1005                         // WARNING Keep in sync with EmitWriteTypeSpec
1006                         
1007                         switch (GetTypeTag (type))
1008                         {
1009                                 case TypeTag.PrimitiveType:
1010                                         writer.Write (BinaryCommon.GetTypeCode (type));
1011                                         break;
1012
1013                                 case TypeTag.RuntimeType:
1014                                         string fullName = type.FullName;
1015                                         // Map System.MonoType to MS.NET's System.RuntimeType,
1016                                         // when called in remoting context.
1017                                         // Note that this code does not need to be in sync with
1018                                         // EmitWriteTypeSpec because serializing a MethodCall
1019                                         // won't trigger the CodeGenerator.
1020                                         if (_context.State == StreamingContextStates.Remoting)
1021                                                 if (type == typeof (System.MonoType))
1022                                                         fullName =  "System.RuntimeType";
1023                                                 else if (type == typeof (System.MonoType[]))
1024                                                         fullName =  "System.RuntimeType[]";
1025                                         writer.Write (fullName);
1026                                         break;
1027
1028                                 case TypeTag.GenericType:
1029                                         writer.Write (type.FullName);
1030 #if NET_4_0
1031                                         string asmName = GetForwardedAttribute.GetAssemblyName(type);
1032                                         writer.Write ((int) GetAssemblyNameId (asmName));
1033 #else
1034                                         writer.Write ((int)GetAssemblyId (type.Assembly));
1035 #endif
1036                                         break;
1037
1038                                 case TypeTag.ArrayOfPrimitiveType:
1039                                         writer.Write (BinaryCommon.GetTypeCode (type.GetElementType()));
1040                                         break;
1041
1042                                 default:
1043                                         // Type spec not needed
1044                                         break;
1045                         }
1046                 }
1047         }
1048 }