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