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