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