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