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