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