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