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