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