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