Free cookie consent management tool by TermsFeed Policy Generator

source: branches/WebJobManager/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory-5.5.0/Utils/FastSerializer.cs @ 18242

Last change on this file since 18242 was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

File size: 46.9 KB
Line 
1// Copyright (c) 2011 Daniel Grunwald
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4// software and associated documentation files (the "Software"), to deal in the Software
5// without restriction, including without limitation the rights to use, copy, modify, merge,
6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7// to whom the Software is furnished to do so, subject to the following conditions:
8//
9// The above copyright notice and this permission notice shall be included in all copies or
10// substantial portions of the Software.
11//
12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17// DEALINGS IN THE SOFTWARE.
18
19using System;
20using System.Collections.Generic;
21using System.Diagnostics;
22using System.IO;
23using System.Reflection;
24using System.Reflection.Emit;
25using System.Runtime.Serialization;
26
27namespace ICSharpCode.NRefactory.Utils
28{
29  public class FastSerializer
30  {
31    #region Properties
32    /// <summary>
33    /// Gets/Sets the serialization binder that is being used.
34    /// The default value is null, which will cause the FastSerializer to use the
35    /// full assembly and type names.
36    /// </summary>
37    public SerializationBinder SerializationBinder { get; set; }
38   
39    /// <summary>
40    /// Can be used to set several 'fixed' instances.
41    /// When serializing, such instances will not be included; and any references to a fixed instance
42    /// will be stored as the index in this array.
43    /// When deserializing, the same (or equivalent) instances must be specified, and the deserializer
44    /// will use them in place of the fixed instances.
45    /// </summary>
46    public object[] FixedInstances { get; set; }
47    #endregion
48   
49    #region Constants
50    const int magic = 0x71D28A5E;
51   
52    const byte Type_ReferenceType = 1;
53    const byte Type_ValueType = 2;
54    const byte Type_SZArray = 3;
55    const byte Type_ParameterizedType = 4;
56    #endregion
57   
58    #region Serialization
59    sealed class SerializationType
60    {
61      public readonly int ID;
62      public readonly Type Type;
63     
64      public SerializationType(int iD, Type type)
65      {
66        this.ID = iD;
67        this.Type = type;
68      }
69     
70      public ObjectScanner Scanner;
71      public ObjectWriter Writer;
72      public string TypeName;
73      public int AssemblyNameID;
74    }
75   
76    sealed class SerializationContext
77    {
78      readonly Dictionary<object, int> objectToID = new Dictionary<object, int>(ReferenceComparer.Instance);
79      readonly List<object> instances = new List<object>(); // index: object ID
80      readonly List<SerializationType> objectTypes = new List<SerializationType>(); // index: object ID
81      SerializationType stringType;
82     
83      readonly Dictionary<Type, SerializationType> typeMap = new Dictionary<Type, SerializationType>();
84      readonly List<SerializationType> types = new List<SerializationType>();
85     
86      readonly Dictionary<string, int> assemblyNameToID = new Dictionary<string, int>();
87      readonly List<string> assemblyNames = new List<string>();
88     
89      readonly FastSerializer fastSerializer;
90      public readonly BinaryWriter writer;
91      int fixedInstanceCount;
92     
93      internal SerializationContext(FastSerializer fastSerializer, BinaryWriter writer)
94      {
95        this.fastSerializer = fastSerializer;
96        this.writer = writer;
97        instances.Add(null); // use object ID 0 for null
98        objectTypes.Add(null);
99      }
100     
101      #region Scanning
102      public void MarkFixedInstances(object[] fixedInstances)
103      {
104        if (fixedInstances == null)
105          return;
106        foreach (object obj in fixedInstances) {
107          if (!objectToID.ContainsKey(obj)) {
108            objectToID.Add(obj, instances.Count);
109            instances.Add(obj);
110            fixedInstanceCount++;
111          }
112        }
113      }
114     
115      /// <summary>
116      /// Marks an instance for future scanning.
117      /// </summary>
118      public void Mark(object instance)
119      {
120        if (instance == null || objectToID.ContainsKey(instance))
121          return;
122        Log(" Mark {0}", instance.GetType().Name);
123       
124        objectToID.Add(instance, instances.Count);
125        instances.Add(instance);
126      }
127     
128      internal void Scan()
129      {
130        Log("Scanning...");
131        // starting from 1, because index 0 is null
132        // Also, do not scan any of the 'fixed instances'.
133        for (int i = 1 + fixedInstanceCount; i < instances.Count; i++) {
134          object instance = instances[i];
135          ISerializable serializable = instance as ISerializable;
136          Type type = instance.GetType();
137          Log("Scan #{0}: {1}", i, type.Name);
138          SerializationType sType = MarkType(type);
139          objectTypes.Add(sType);
140          if (serializable != null) {
141            SerializationInfo info = new SerializationInfo(type, fastSerializer.formatterConverter);
142            serializable.GetObjectData(info, fastSerializer.streamingContext);
143            instances[i] = info;
144            foreach (SerializationEntry entry in info) {
145              Mark(entry.Value);
146            }
147            sType.Writer = serializationInfoWriter;
148          } else {
149            ObjectScanner objectScanner = sType.Scanner;
150            if (objectScanner == null) {
151              objectScanner = fastSerializer.GetScanner(type);
152              sType.Scanner = objectScanner;
153              sType.Writer = fastSerializer.GetWriter(type);
154            }
155            objectScanner(this, instance);
156          }
157        }
158      }
159      #endregion
160     
161      #region Scan Types
162      SerializationType MarkType(Type type)
163      {
164        SerializationType sType;
165        if (!typeMap.TryGetValue(type, out sType)) {
166          string assemblyName = null;
167          string typeName = null;
168          if (type.HasElementType) {
169            Debug.Assert(type.IsArray);
170            MarkType(type.GetElementType());
171          } else if (type.IsGenericType && !type.IsGenericTypeDefinition) {
172            MarkType(type.GetGenericTypeDefinition());
173            foreach (Type typeArg in type.GetGenericArguments())
174              MarkType(typeArg);
175          } else if (type.IsGenericParameter) {
176            throw new NotSupportedException();
177          } else {
178            var serializationBinder = fastSerializer.SerializationBinder;
179            if (serializationBinder != null) {
180              serializationBinder.BindToName(type, out assemblyName, out typeName);
181            } else {
182              assemblyName = type.Assembly.FullName;
183              typeName = type.FullName;
184              Debug.Assert(typeName != null);
185            }
186          }
187         
188          sType = new SerializationType(typeMap.Count, type);
189          sType.TypeName = typeName;
190          if (assemblyName != null) {
191            if (!assemblyNameToID.TryGetValue(assemblyName, out sType.AssemblyNameID)) {
192              sType.AssemblyNameID = assemblyNames.Count;
193              assemblyNameToID.Add(assemblyName, sType.AssemblyNameID);
194              assemblyNames.Add(assemblyName);
195              Log("Registered assembly #{0}: {1}", sType.AssemblyNameID, assemblyName);
196            }
197          }
198          typeMap.Add(type, sType);
199          types.Add(sType);
200          Log("Registered type %{0}: {1}", sType.ID, type);
201          if (type == typeof(string)) {
202            stringType = sType;
203          }
204        }
205        return sType;
206      }
207     
208      internal void ScanTypes()
209      {
210        for (int i = 0; i < types.Count; i++) {
211          Type type = types[i].Type;
212          if (type.IsGenericTypeDefinition || type.HasElementType)
213            continue;
214          if (typeof(ISerializable).IsAssignableFrom(type))
215            continue;
216          foreach (FieldInfo field in GetSerializableFields(type)) {
217            MarkType(field.FieldType);
218          }
219        }
220      }
221      #endregion
222     
223      #region Writing
224      public void WriteObjectID(object instance)
225      {
226        int id = (instance == null) ? 0 : objectToID[instance];
227        if (instances.Count <= ushort.MaxValue)
228          writer.Write((ushort)id);
229        else
230          writer.Write(id);
231      }
232     
233      void WriteTypeID(Type type)
234      {
235        Debug.Assert(typeMap.ContainsKey(type));
236        int typeID = typeMap[type].ID;
237        if (types.Count <= ushort.MaxValue)
238          writer.Write((ushort)typeID);
239        else
240          writer.Write(typeID);
241      }
242     
243      internal void Write()
244      {
245        Log("Writing...");
246        writer.Write(magic);
247        // Write out type information
248        writer.Write(instances.Count);
249        writer.Write(types.Count);
250        writer.Write(assemblyNames.Count);
251        writer.Write(fixedInstanceCount);
252       
253        foreach (string assemblyName in assemblyNames) {
254          writer.Write(assemblyName);
255        }
256       
257        foreach (SerializationType sType in types) {
258          Type type = sType.Type;
259          if (type.HasElementType) {
260            if (type.IsArray) {
261              if (type.GetArrayRank() == 1)
262                writer.Write(Type_SZArray);
263              else
264                throw new NotSupportedException();
265            } else {
266              throw new NotSupportedException();
267            }
268            WriteTypeID(type.GetElementType());
269          } else if (type.IsGenericType && !type.IsGenericTypeDefinition) {
270            writer.Write(Type_ParameterizedType);
271            WriteTypeID(type.GetGenericTypeDefinition());
272            foreach (Type typeArg in type.GetGenericArguments()) {
273              WriteTypeID(typeArg);
274            }
275          } else {
276            if (type.IsValueType) {
277              writer.Write(Type_ValueType);
278            } else {
279              writer.Write(Type_ReferenceType);
280            }
281            if (assemblyNames.Count <= ushort.MaxValue)
282              writer.Write((ushort)sType.AssemblyNameID);
283            else
284              writer.Write(sType.AssemblyNameID);
285            writer.Write(sType.TypeName);
286          }
287        }
288        foreach (SerializationType sType in types) {
289          Type type = sType.Type;
290          if (type.IsGenericTypeDefinition || type.HasElementType)
291            continue;
292          writer.Write(FastSerializerVersionAttribute.GetVersionNumber(type));
293          if (type.IsPrimitive || typeof(ISerializable).IsAssignableFrom(type)) {
294            writer.Write(byte.MaxValue);
295          } else {
296            var fields = GetSerializableFields(type);
297            if (fields.Count >= byte.MaxValue)
298              throw new SerializationException("Too many fields.");
299            writer.Write((byte)fields.Count);
300            foreach (var field in fields) {
301              WriteTypeID(field.FieldType);
302              writer.Write(field.Name);
303            }
304          }
305        }
306       
307        // Write out information necessary to create the instances
308        // starting from 1, because index 0 is null
309        for (int i = 1 + fixedInstanceCount; i < instances.Count; i++) {
310          SerializationType sType = objectTypes[i];
311          if (types.Count <= ushort.MaxValue)
312            writer.Write((ushort)sType.ID);
313          else
314            writer.Write(sType.ID);
315          if (sType == stringType) {
316            // Strings are written to the output immediately
317            // - we can't create an empty string and fill it later
318            writer.Write((string)instances[i]);
319          } else if (sType.Type.IsArray) {
320            // For arrays, write down the length, because we need that to create the array instance
321            writer.Write(((Array)instances[i]).Length);
322          }
323        }
324        // Write out information necessary to fill data into the instances
325        for (int i = 1 + fixedInstanceCount; i < instances.Count; i++) {
326          Log("0x{2:x6}, Write #{0}: {1}", i, objectTypes[i].Type.Name, writer.BaseStream.Position);
327          objectTypes[i].Writer(this, instances[i]);
328        }
329        Log("Serialization done.");
330      }
331      #endregion
332    }
333   
334    #region Object Scanners
335    delegate void ObjectScanner(SerializationContext context, object instance);
336   
337    static readonly MethodInfo mark = typeof(SerializationContext).GetMethod("Mark", new[] { typeof(object) });
338    static readonly FieldInfo writerField = typeof(SerializationContext).GetField("writer");
339   
340    Dictionary<Type, ObjectScanner> scanners = new Dictionary<Type, ObjectScanner>();
341   
342    ObjectScanner GetScanner(Type type)
343    {
344      ObjectScanner scanner;
345      if (!scanners.TryGetValue(type, out scanner)) {
346        scanner = CreateScanner(type);
347        scanners.Add(type, scanner);
348      }
349      return scanner;
350    }
351   
352    ObjectScanner CreateScanner(Type type)
353    {
354      bool isArray = type.IsArray;
355      if (isArray) {
356        if (type.GetArrayRank() != 1)
357          throw new NotSupportedException();
358        type = type.GetElementType();
359        if (!type.IsValueType) {
360          return delegate (SerializationContext context, object array) {
361            foreach (object val in (object[])array) {
362              context.Mark(val);
363            }
364          };
365        }
366      }
367      for (Type baseType = type; baseType != null; baseType = baseType.BaseType) {
368        if (!baseType.IsSerializable)
369          throw new SerializationException("Type " + baseType + " is not [Serializable].");
370      }
371      List<FieldInfo> fields = GetSerializableFields(type);
372      fields.RemoveAll(f => !IsReferenceOrContainsReferences(f.FieldType));
373      if (fields.Count == 0) {
374        // The scanner has nothing to do for this object.
375        return delegate { };
376      }
377     
378      DynamicMethod dynamicMethod = new DynamicMethod(
379        (isArray ? "ScanArray_" : "Scan_") + type.Name,
380        typeof(void), new [] { typeof(SerializationContext), typeof(object) },
381        true);
382      ILGenerator il = dynamicMethod.GetILGenerator();
383     
384     
385      if (isArray) {
386        var instance = il.DeclareLocal(type.MakeArrayType());
387        il.Emit(OpCodes.Ldarg_1);
388        il.Emit(OpCodes.Castclass, type.MakeArrayType());
389        il.Emit(OpCodes.Stloc, instance); // instance = (type[])arg_1;
390       
391        // for (int i = 0; i < instance.Length; i++) scan instance[i];
392        var loopStart = il.DefineLabel();
393        var loopHead = il.DefineLabel();
394        var loopVariable = il.DeclareLocal(typeof(int));
395        il.Emit(OpCodes.Ldc_I4_0);
396        il.Emit(OpCodes.Stloc, loopVariable); // loopVariable = 0
397        il.Emit(OpCodes.Br, loopHead); // goto loopHead;
398       
399        il.MarkLabel(loopStart);
400       
401        il.Emit(OpCodes.Ldloc, instance); // instance
402        il.Emit(OpCodes.Ldloc, loopVariable); // instance, loopVariable
403        il.Emit(OpCodes.Ldelem, type); // &instance[loopVariable]
404        EmitScanValueType(il, type);
405       
406       
407        il.Emit(OpCodes.Ldloc, loopVariable); // loopVariable
408        il.Emit(OpCodes.Ldc_I4_1); // loopVariable, 1
409        il.Emit(OpCodes.Add); // loopVariable+1
410        il.Emit(OpCodes.Stloc, loopVariable); // loopVariable++;
411       
412        il.MarkLabel(loopHead);
413        il.Emit(OpCodes.Ldloc, loopVariable); // loopVariable
414        il.Emit(OpCodes.Ldloc, instance); // loopVariable, instance
415        il.Emit(OpCodes.Ldlen); // loopVariable, instance.Length
416        il.Emit(OpCodes.Conv_I4);
417        il.Emit(OpCodes.Blt, loopStart); // if (loopVariable < instance.Length) goto loopStart;
418      } else if (type.IsValueType) {
419        // boxed value type
420        il.Emit(OpCodes.Ldarg_1);
421        il.Emit(OpCodes.Unbox_Any, type);
422        EmitScanValueType(il, type);
423      } else {
424        // reference type
425        var instance = il.DeclareLocal(type);
426        il.Emit(OpCodes.Ldarg_1);
427        il.Emit(OpCodes.Castclass, type);
428        il.Emit(OpCodes.Stloc, instance); // instance = (type)arg_1;
429       
430        foreach (FieldInfo field in fields) {
431          EmitScanField(il, instance, field); // scan instance.Field
432        }
433      }
434      il.Emit(OpCodes.Ret);
435      return (ObjectScanner)dynamicMethod.CreateDelegate(typeof(ObjectScanner));
436    }
437
438    /// <summary>
439    /// Emit 'scan instance.Field'.
440    /// Stack transition: ... => ...
441    /// </summary>
442    void EmitScanField(ILGenerator il, LocalBuilder instance, FieldInfo field)
443    {
444      if (field.FieldType.IsValueType) {
445        il.Emit(OpCodes.Ldloc, instance); // instance
446        il.Emit(OpCodes.Ldfld, field); // instance.field
447        EmitScanValueType(il, field.FieldType);
448      } else {
449        il.Emit(OpCodes.Ldarg_0); // context
450        il.Emit(OpCodes.Ldloc, instance); // context, instance
451        il.Emit(OpCodes.Ldfld, field); // context, instance.field
452        il.Emit(OpCodes.Call, mark); // context.Mark(instance.field);
453      }
454    }
455
456    /// <summary>
457    /// Stack transition: ..., value => ...
458    /// </summary>
459    void EmitScanValueType(ILGenerator il, Type valType)
460    {
461      var fieldRef = il.DeclareLocal(valType);
462      il.Emit(OpCodes.Stloc, fieldRef);
463     
464      foreach (FieldInfo field in GetSerializableFields(valType)) {
465        if (IsReferenceOrContainsReferences(field.FieldType)) {
466          EmitScanField(il, fieldRef, field);
467        }
468      }
469    }
470
471    static List<FieldInfo> GetSerializableFields(Type type)
472    {
473      List<FieldInfo> fields = new List<FieldInfo>();
474      for (Type baseType = type; baseType != null; baseType = baseType.BaseType) {
475        FieldInfo[] declFields = baseType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly);
476        Array.Sort(declFields, (a,b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal));
477        fields.AddRange(declFields);
478      }
479      fields.RemoveAll(f => f.IsNotSerialized);
480      return fields;
481    }
482
483    static bool IsReferenceOrContainsReferences(Type type)
484    {
485      if (!type.IsValueType)
486        return true;
487      if (type.IsPrimitive)
488        return false;
489      foreach (FieldInfo field in GetSerializableFields(type)) {
490        if (IsReferenceOrContainsReferences(field.FieldType))
491          return true;
492      }
493      return false;
494    }
495    #endregion
496
497    #region Object Writers
498    delegate void ObjectWriter(SerializationContext context, object instance);
499
500    static readonly MethodInfo writeObjectID = typeof(SerializationContext).GetMethod("WriteObjectID", new[] { typeof(object) });
501
502    static readonly MethodInfo writeByte = typeof(BinaryWriter).GetMethod("Write", new[] { typeof(byte) });
503    static readonly MethodInfo writeShort = typeof(BinaryWriter).GetMethod("Write", new[] { typeof(short) });
504    static readonly MethodInfo writeInt = typeof(BinaryWriter).GetMethod("Write", new[] { typeof(int) });
505    static readonly MethodInfo writeLong = typeof(BinaryWriter).GetMethod("Write", new[] { typeof(long) });
506    static readonly MethodInfo writeFloat = typeof(BinaryWriter).GetMethod("Write", new[] { typeof(float) });
507    static readonly MethodInfo writeDouble = typeof(BinaryWriter).GetMethod("Write", new[] { typeof(double) });
508    OpCode callVirt = OpCodes.Callvirt;
509
510    static readonly ObjectWriter serializationInfoWriter = delegate(SerializationContext context, object instance) {
511      BinaryWriter writer = context.writer;
512      SerializationInfo info = (SerializationInfo)instance;
513      writer.Write(info.MemberCount);
514      foreach (SerializationEntry entry in info) {
515        writer.Write(entry.Name);
516        context.WriteObjectID(entry.Value);
517      }
518    };
519
520    Dictionary<Type, ObjectWriter> writers = new Dictionary<Type, ObjectWriter>();
521
522    ObjectWriter GetWriter(Type type)
523    {
524      ObjectWriter writer;
525      if (!writers.TryGetValue(type, out writer)) {
526        writer = CreateWriter(type);
527        writers.Add(type, writer);
528      }
529      return writer;
530    }
531
532    ObjectWriter CreateWriter(Type type)
533    {
534      if (type == typeof(string)) {
535        // String contents are written in the object creation section,
536        // not into the field value section.
537        return delegate {};
538      }
539      bool isArray = type.IsArray;
540      if (isArray) {
541        if (type.GetArrayRank() != 1)
542          throw new NotSupportedException();
543        type = type.GetElementType();
544        if (!type.IsValueType) {
545          return delegate (SerializationContext context, object array) {
546            foreach (object val in (object[])array) {
547              context.WriteObjectID(val);
548            }
549          };
550        } else if (type == typeof(byte)) {
551          return delegate (SerializationContext context, object array) {
552            context.writer.Write((byte[])array);
553          };
554        }
555      }
556      List<FieldInfo> fields = GetSerializableFields(type);
557      if (fields.Count == 0) {
558        // The writer has nothing to do for this object.
559        return delegate { };
560      }
561     
562     
563      DynamicMethod dynamicMethod = new DynamicMethod(
564        (isArray ? "WriteArray_" : "Write_") + type.Name,
565        typeof(void), new [] { typeof(SerializationContext), typeof(object) },
566        true);
567      ILGenerator il = dynamicMethod.GetILGenerator();
568     
569      var writer = il.DeclareLocal(typeof(BinaryWriter));
570     
571      il.Emit(OpCodes.Ldarg_0);
572      il.Emit(OpCodes.Ldfld, writerField);
573      il.Emit(OpCodes.Stloc, writer); // writer = context.writer;
574     
575      if (isArray) {
576        var instance = il.DeclareLocal(type.MakeArrayType());
577        il.Emit(OpCodes.Ldarg_1);
578        il.Emit(OpCodes.Castclass, type.MakeArrayType());
579        il.Emit(OpCodes.Stloc, instance); // instance = (type[])arg_1;
580       
581        // for (int i = 0; i < instance.Length; i++) write instance[i];
582       
583        var loopStart = il.DefineLabel();
584        var loopHead = il.DefineLabel();
585        var loopVariable = il.DeclareLocal(typeof(int));
586        il.Emit(OpCodes.Ldc_I4_0);
587        il.Emit(OpCodes.Stloc, loopVariable); // loopVariable = 0
588        il.Emit(OpCodes.Br, loopHead); // goto loopHead;
589       
590        il.MarkLabel(loopStart);
591       
592        if (type.IsEnum || type.IsPrimitive) {
593          if (type.IsEnum) {
594            type = type.GetEnumUnderlyingType();
595          }
596          Debug.Assert(type.IsPrimitive);
597          il.Emit(OpCodes.Ldloc, writer); // writer
598          il.Emit(OpCodes.Ldloc, instance); // writer, instance
599          il.Emit(OpCodes.Ldloc, loopVariable); // writer, instance, loopVariable
600          switch (Type.GetTypeCode(type)) {
601            case TypeCode.Boolean:
602            case TypeCode.SByte:
603            case TypeCode.Byte:
604              il.Emit(OpCodes.Ldelem_I1); // writer, instance[loopVariable]
605              il.Emit(callVirt, writeByte); // writer.Write(instance[loopVariable]);
606              break;
607            case TypeCode.Char:
608            case TypeCode.Int16:
609            case TypeCode.UInt16:
610              il.Emit(OpCodes.Ldelem_I2); // writer, instance[loopVariable]
611              il.Emit(callVirt, writeShort); // writer.Write(instance[loopVariable]);
612              break;
613            case TypeCode.Int32:
614            case TypeCode.UInt32:
615              il.Emit(OpCodes.Ldelem_I4);  // writer, instance[loopVariable]
616              il.Emit(callVirt, writeInt); // writer.Write(instance[loopVariable]);
617              break;
618            case TypeCode.Int64:
619            case TypeCode.UInt64:
620              il.Emit(OpCodes.Ldelem_I8);  // writer, instance[loopVariable]
621              il.Emit(callVirt, writeLong); // writer.Write(instance[loopVariable]);
622              break;
623            case TypeCode.Single:
624              il.Emit(OpCodes.Ldelem_R4);  // writer, instance[loopVariable]
625              il.Emit(callVirt, writeFloat); // writer.Write(instance[loopVariable]);
626              break;
627            case TypeCode.Double:
628              il.Emit(OpCodes.Ldelem_R8);  // writer, instance[loopVariable]
629              il.Emit(callVirt, writeDouble); // writer.Write(instance[loopVariable]);
630              break;
631            default:
632              throw new NotSupportedException("Unknown primitive type " + type);
633          }
634        } else {
635          il.Emit(OpCodes.Ldloc, instance); // instance
636          il.Emit(OpCodes.Ldloc, loopVariable); // instance, loopVariable
637          il.Emit(OpCodes.Ldelem, type); // instance[loopVariable]
638          EmitWriteValueType(il, writer, type);
639        }
640       
641        il.Emit(OpCodes.Ldloc, loopVariable); // loopVariable
642        il.Emit(OpCodes.Ldc_I4_1); // loopVariable, 1
643        il.Emit(OpCodes.Add); // loopVariable+1
644        il.Emit(OpCodes.Stloc, loopVariable); // loopVariable++;
645       
646        il.MarkLabel(loopHead);
647        il.Emit(OpCodes.Ldloc, loopVariable); // loopVariable
648        il.Emit(OpCodes.Ldloc, instance); // loopVariable, instance
649        il.Emit(OpCodes.Ldlen); // loopVariable, instance.Length
650        il.Emit(OpCodes.Conv_I4);
651        il.Emit(OpCodes.Blt, loopStart); // if (loopVariable < instance.Length) goto loopStart;
652      } else if (type.IsValueType) {
653        // boxed value type
654        if (type.IsEnum || type.IsPrimitive) {
655          il.Emit(OpCodes.Ldloc, writer);
656          il.Emit(OpCodes.Ldarg_1);
657          il.Emit(OpCodes.Unbox_Any, type);
658          WritePrimitiveValue(il, type);
659        } else {
660          il.Emit(OpCodes.Ldarg_1);
661          il.Emit(OpCodes.Unbox_Any, type);
662          EmitWriteValueType(il, writer, type);
663        }
664      } else {
665        // reference type
666        var instance = il.DeclareLocal(type);
667        il.Emit(OpCodes.Ldarg_1);
668        il.Emit(OpCodes.Castclass, type);
669        il.Emit(OpCodes.Stloc, instance); // instance = (type)arg_1;
670       
671        foreach (FieldInfo field in fields) {
672          EmitWriteField(il, writer, instance, field); // write instance.Field
673        }
674      }
675      il.Emit(OpCodes.Ret);
676      return (ObjectWriter)dynamicMethod.CreateDelegate(typeof(ObjectWriter));
677    }
678
679    /// <summary>
680    /// Emit 'write instance.Field'.
681    /// Stack transition: ... => ...
682    /// </summary>
683    void EmitWriteField(ILGenerator il, LocalBuilder writer, LocalBuilder instance, FieldInfo field)
684    {
685      Type fieldType = field.FieldType;
686      if (fieldType.IsValueType) {
687        if (fieldType.IsPrimitive || fieldType.IsEnum) {
688          il.Emit(OpCodes.Ldloc, writer); // writer
689          il.Emit(OpCodes.Ldloc, instance); // writer, instance
690          il.Emit(OpCodes.Ldfld, field); // writer, instance.field
691          WritePrimitiveValue(il, fieldType);
692        } else {
693          il.Emit(OpCodes.Ldloc, instance); // instance
694          il.Emit(OpCodes.Ldfld, field); // instance.field
695          EmitWriteValueType(il, writer, fieldType);
696        }
697      } else {
698        il.Emit(OpCodes.Ldarg_0); // context
699        il.Emit(OpCodes.Ldloc, instance); // context, instance
700        il.Emit(OpCodes.Ldfld, field); // context, instance.field
701        il.Emit(OpCodes.Call, writeObjectID); // context.WriteObjectID(instance.field);
702      }
703    }
704   
705    /// <summary>
706    /// Writes a primitive value of the specified type.
707    /// Stack transition: ..., writer, value => ...
708    /// </summary>
709    void WritePrimitiveValue(ILGenerator il, Type fieldType)
710    {
711      if (fieldType.IsEnum) {
712        fieldType = fieldType.GetEnumUnderlyingType();
713        Debug.Assert(fieldType.IsPrimitive);
714      }
715      switch (Type.GetTypeCode(fieldType)) {
716        case TypeCode.Boolean:
717        case TypeCode.SByte:
718        case TypeCode.Byte:
719          il.Emit(callVirt, writeByte); // writer.Write(value);
720          break;
721        case TypeCode.Char:
722        case TypeCode.Int16:
723        case TypeCode.UInt16:
724          il.Emit(callVirt, writeShort); // writer.Write(value);
725          break;
726        case TypeCode.Int32:
727        case TypeCode.UInt32:
728          il.Emit(callVirt, writeInt); // writer.Write(value);
729          break;
730        case TypeCode.Int64:
731        case TypeCode.UInt64:
732          il.Emit(callVirt, writeLong); // writer.Write(value);
733          break;
734        case TypeCode.Single:
735          il.Emit(callVirt, writeFloat); // writer.Write(value);
736          break;
737        case TypeCode.Double:
738          il.Emit(callVirt, writeDouble); // writer.Write(value);
739          break;
740        default:
741          throw new NotSupportedException("Unknown primitive type " + fieldType);
742      }
743    }
744
745    /// <summary>
746    /// Stack transition: ..., value => ...
747    /// </summary>
748    void EmitWriteValueType(ILGenerator il, LocalBuilder writer, Type valType)
749    {
750      Debug.Assert(valType.IsValueType);
751      Debug.Assert(!(valType.IsEnum || valType.IsPrimitive));
752     
753      var fieldVal = il.DeclareLocal(valType);
754      il.Emit(OpCodes.Stloc, fieldVal);
755     
756      foreach (FieldInfo field in GetSerializableFields(valType)) {
757        EmitWriteField(il, writer, fieldVal, field);
758      }
759    }
760    #endregion
761
762    StreamingContext streamingContext = new StreamingContext(StreamingContextStates.All);
763    FormatterConverter formatterConverter = new FormatterConverter();
764
765    public void Serialize(Stream stream, object instance)
766    {
767      Serialize(new BinaryWriterWith7BitEncodedInts(stream), instance);
768    }
769
770    public void Serialize(BinaryWriter writer, object instance)
771    {
772      SerializationContext context = new SerializationContext(this, writer);
773      context.MarkFixedInstances(this.FixedInstances);
774      context.Mark(instance);
775      context.Scan();
776      context.ScanTypes();
777      context.Write();
778      context.WriteObjectID(instance);
779    }
780
781    delegate void TypeSerializer(object instance, SerializationContext context);
782    #endregion
783
784    #region Deserialization
785    sealed class DeserializationContext
786    {
787      public Type[] Types; // index: type ID
788     
789      public object[] Objects; // index: object ID
790     
791      public BinaryReader Reader;
792     
793      public object ReadObject()
794      {
795        if (this.Objects.Length <= ushort.MaxValue)
796          return this.Objects[Reader.ReadUInt16()];
797        else
798          return this.Objects[Reader.ReadInt32()];
799      }
800     
801      #region DeserializeTypeDescriptions
802      internal int ReadTypeID()
803      {
804        if (this.Types.Length <= ushort.MaxValue)
805          return Reader.ReadUInt16();
806        else
807          return Reader.ReadInt32();
808      }
809     
810      internal void DeserializeTypeDescriptions()
811      {
812        for (int i = 0; i < this.Types.Length; i++) {
813          Type type = this.Types[i];
814          if (type.IsGenericTypeDefinition || type.HasElementType)
815            continue;
816          int versionNumber = Reader.ReadInt32();
817          if (versionNumber != FastSerializerVersionAttribute.GetVersionNumber(type))
818            throw new SerializationException("Type '" + type.FullName + "' was serialized with version " + versionNumber + ", but is version " + FastSerializerVersionAttribute.GetVersionNumber(type));
819         
820          bool isCustomSerialization = typeof(ISerializable).IsAssignableFrom(type);
821          bool typeIsSpecial = type.IsPrimitive || isCustomSerialization;
822         
823          byte serializedFieldCount = Reader.ReadByte();
824          if (serializedFieldCount == byte.MaxValue) {
825            // special type
826            if (!typeIsSpecial)
827              throw new SerializationException("Type '" + type.FullName + "' was serialized as special type, but isn't special now.");
828          } else {
829            if (typeIsSpecial)
830              throw new SerializationException("Type '" + type.FullName + "' wasn't serialized as special type, but is special now.");
831           
832            var availableFields = GetSerializableFields(this.Types[i]);
833            if (availableFields.Count != serializedFieldCount)
834              throw new SerializationException("Number of fields on " + type.FullName + " has changed.");
835            for (int j = 0; j < serializedFieldCount; j++) {
836              int fieldTypeID = ReadTypeID();
837             
838              string fieldName = Reader.ReadString();
839              FieldInfo fieldInfo = availableFields[j];
840              if (fieldInfo.Name != fieldName)
841                throw new SerializationException("Field mismatch on type " + type.FullName);
842              if (fieldInfo.FieldType != this.Types[fieldTypeID])
843                throw new SerializationException(type.FullName + "." + fieldName + " was serialized as " + this.Types[fieldTypeID] + ", but now is " + fieldInfo.FieldType);
844            }
845          }
846        }
847      }
848      #endregion
849    }
850   
851    delegate void ObjectReader(DeserializationContext context, object instance);
852   
853    public object Deserialize(Stream stream)
854    {
855      return Deserialize(new BinaryReaderWith7BitEncodedInts(stream));
856    }
857   
858    public object Deserialize(BinaryReader reader)
859    {
860      if (reader.ReadInt32() != magic)
861        throw new SerializationException("The data cannot be read by FastSerializer (unknown magic value)");
862     
863      DeserializationContext context = new DeserializationContext();
864      context.Reader = reader;
865      context.Objects = new object[reader.ReadInt32()];
866      context.Types = new Type[reader.ReadInt32()];
867      string[] assemblyNames = new string[reader.ReadInt32()];
868      int fixedInstanceCount = reader.ReadInt32();
869     
870      if (fixedInstanceCount != 0) {
871        if (this.FixedInstances == null || this.FixedInstances.Length != fixedInstanceCount)
872          throw new SerializationException("Number of fixed instances doesn't match");
873        for (int i = 0; i < fixedInstanceCount; i++) {
874          context.Objects[i + 1] = this.FixedInstances[i];
875        }
876      }
877     
878      for (int i = 0; i < assemblyNames.Length; i++) {
879        assemblyNames[i] = reader.ReadString();
880      }
881      int stringTypeID = -1;
882      for (int i = 0; i < context.Types.Length; i++) {
883        byte typeKind = reader.ReadByte();
884        switch (typeKind) {
885          case Type_ReferenceType:
886          case Type_ValueType:
887            int assemblyID;
888            if (assemblyNames.Length <= ushort.MaxValue)
889              assemblyID = reader.ReadUInt16();
890            else
891              assemblyID = reader.ReadInt32();
892            string assemblyName = assemblyNames[assemblyID];
893            string typeName = reader.ReadString();
894            Type type;
895            if (SerializationBinder != null) {
896              type = SerializationBinder.BindToType(assemblyName, typeName);
897            } else {
898              type = Assembly.Load(assemblyName).GetType(typeName);
899            }
900            if (type == null)
901              throw new SerializationException("Could not find '" + typeName + "' in '" + assemblyName + "'");
902            if (typeKind == Type_ValueType && !type.IsValueType)
903              throw new SerializationException("Expected '" + typeName + "' to be a value type, but it is reference type");
904            if (typeKind == Type_ReferenceType && type.IsValueType)
905              throw new SerializationException("Expected '" + typeName + "' to be a reference type, but it is value type");
906            context.Types[i] = type;
907            if (type == typeof(string))
908              stringTypeID = i;
909            break;
910          case Type_SZArray:
911            context.Types[i] = context.Types[context.ReadTypeID()].MakeArrayType();
912            break;
913          case Type_ParameterizedType:
914            Type genericType = context.Types[context.ReadTypeID()];
915            int typeParameterCount = genericType.GetGenericArguments().Length;
916            Type[] typeArguments = new Type[typeParameterCount];
917            for (int j = 0; j < typeArguments.Length; j++) {
918              typeArguments[j] = context.Types[context.ReadTypeID()];
919            }
920            context.Types[i] = genericType.MakeGenericType(typeArguments);
921            break;
922          default:
923            throw new SerializationException("Unknown type kind");
924        }
925      }
926      context.DeserializeTypeDescriptions();
927      int[] typeIDByObjectID = new int[context.Objects.Length];
928      for (int i = 1 + fixedInstanceCount; i < context.Objects.Length; i++) {
929        int typeID = context.ReadTypeID();
930       
931        object instance;
932        if (typeID == stringTypeID) {
933          instance = reader.ReadString();
934        } else {
935          Type type = context.Types[typeID];
936          if (type.IsArray) {
937            int length = reader.ReadInt32();
938            instance = Array.CreateInstance(type.GetElementType(), length);
939          } else {
940            instance = FormatterServices.GetUninitializedObject(type);
941          }
942        }
943        context.Objects[i] = instance;
944        typeIDByObjectID[i] = typeID;
945      }
946      List<CustomDeserialization> customDeserializatons = new List<CustomDeserialization>();
947      ObjectReader[] objectReaders = new ObjectReader[context.Types.Length]; // index: type ID
948      for (int i = 1 + fixedInstanceCount; i < context.Objects.Length; i++) {
949        object instance = context.Objects[i];
950        int typeID = typeIDByObjectID[i];
951        Log("0x{2:x6} Read #{0}: {1}", i, context.Types[typeID].Name, reader.BaseStream.Position);
952        ISerializable serializable = instance as ISerializable;
953        if (serializable != null) {
954          Type type = context.Types[typeID];
955          SerializationInfo info = new SerializationInfo(type, formatterConverter);
956          int count = reader.ReadInt32();
957          for (int j = 0; j < count; j++) {
958            string name = reader.ReadString();
959            object val = context.ReadObject();
960            info.AddValue(name, val);
961          }
962          CustomDeserializationAction action = GetCustomDeserializationAction(type);
963          customDeserializatons.Add(new CustomDeserialization(instance, info, action));
964        } else {
965          ObjectReader objectReader = objectReaders[typeID];
966          if (objectReader == null) {
967            objectReader = GetReader(context.Types[typeID]);
968            objectReaders[typeID] = objectReader;
969          }
970          objectReader(context, instance);
971        }
972      }
973      Log("File was read successfully, now running {0} custom deserializations...", customDeserializatons.Count);
974      foreach (CustomDeserialization customDeserializaton in customDeserializatons) {
975        customDeserializaton.Run(streamingContext);
976      }
977      for (int i = 1 + fixedInstanceCount; i < context.Objects.Length; i++) {
978        IDeserializationCallback dc = context.Objects[i] as IDeserializationCallback;
979        if (dc != null)
980          dc.OnDeserialization(null);
981      }
982     
983      return context.ReadObject();
984    }
985   
986    #region Object Reader
987    static readonly FieldInfo readerField = typeof(DeserializationContext).GetField("Reader");
988    static readonly MethodInfo readObject = typeof(DeserializationContext).GetMethod("ReadObject");
989   
990    static readonly MethodInfo readByte = typeof(BinaryReader).GetMethod("ReadByte");
991    static readonly MethodInfo readShort = typeof(BinaryReader).GetMethod("ReadInt16");
992    static readonly MethodInfo readInt = typeof(BinaryReader).GetMethod("ReadInt32");
993    static readonly MethodInfo readLong = typeof(BinaryReader).GetMethod("ReadInt64");
994    static readonly MethodInfo readFloat = typeof(BinaryReader).GetMethod("ReadSingle");
995    static readonly MethodInfo readDouble = typeof(BinaryReader).GetMethod("ReadDouble");
996   
997    Dictionary<Type, ObjectReader> readers = new Dictionary<Type, ObjectReader>();
998
999    ObjectReader GetReader(Type type)
1000    {
1001      ObjectReader reader;
1002      if (!readers.TryGetValue(type, out reader)) {
1003        reader = CreateReader(type);
1004        readers.Add(type, reader);
1005      }
1006      return reader;
1007    }
1008   
1009    ObjectReader CreateReader(Type type)
1010    {
1011      if (type == typeof(string)) {
1012        // String contents are written in the object creation section,
1013        // not into the field value section; so there's nothing to read here.
1014        return delegate {};
1015      }
1016      bool isArray = type.IsArray;
1017      if (isArray) {
1018        if (type.GetArrayRank() != 1)
1019          throw new NotSupportedException();
1020        type = type.GetElementType();
1021        if (!type.IsValueType) {
1022          return delegate (DeserializationContext context, object arrayInstance) {
1023            object[] array = (object[])arrayInstance;
1024            for (int i = 0; i < array.Length; i++) {
1025              array[i] = context.ReadObject();
1026            }
1027          };
1028        } else if (type == typeof(byte)) {
1029          return delegate (DeserializationContext context, object arrayInstance) {
1030            byte[] array = (byte[])arrayInstance;
1031            BinaryReader binaryReader = context.Reader;
1032            int pos = 0;
1033            int bytesRead;
1034            do {
1035              bytesRead = binaryReader.Read(array, pos, array.Length - pos);
1036              pos += bytesRead;
1037            } while (bytesRead > 0);
1038            if (pos != array.Length)
1039              throw new EndOfStreamException();
1040          };
1041        }
1042      }
1043      var fields = GetSerializableFields(type);
1044      if (fields.Count == 0) {
1045        // The reader has nothing to do for this object.
1046        return delegate { };
1047      }
1048     
1049      DynamicMethod dynamicMethod = new DynamicMethod(
1050        (isArray ? "ReadArray_" : "Read_") + type.Name,
1051        MethodAttributes.Public | MethodAttributes.Static,
1052        CallingConventions.Standard,
1053        typeof(void), new [] { typeof(DeserializationContext), typeof(object) },
1054        type,
1055        true);
1056      ILGenerator il = dynamicMethod.GetILGenerator();
1057     
1058      var reader = il.DeclareLocal(typeof(BinaryReader));
1059     
1060      il.Emit(OpCodes.Ldarg_0);
1061      il.Emit(OpCodes.Ldfld, readerField);
1062      il.Emit(OpCodes.Stloc, reader); // reader = context.reader;
1063     
1064      if (isArray) {
1065        var instance = il.DeclareLocal(type.MakeArrayType());
1066        il.Emit(OpCodes.Ldarg_1);
1067        il.Emit(OpCodes.Castclass, type.MakeArrayType());
1068        il.Emit(OpCodes.Stloc, instance); // instance = (type[])arg_1;
1069       
1070        // for (int i = 0; i < instance.Length; i++) read &instance[i];
1071       
1072        var loopStart = il.DefineLabel();
1073        var loopHead = il.DefineLabel();
1074        var loopVariable = il.DeclareLocal(typeof(int));
1075        il.Emit(OpCodes.Ldc_I4_0);
1076        il.Emit(OpCodes.Stloc, loopVariable); // loopVariable = 0
1077        il.Emit(OpCodes.Br, loopHead); // goto loopHead;
1078       
1079        il.MarkLabel(loopStart);
1080       
1081        if (type.IsEnum || type.IsPrimitive) {
1082          if (type.IsEnum) {
1083            type = type.GetEnumUnderlyingType();
1084          }
1085          Debug.Assert(type.IsPrimitive);
1086          il.Emit(OpCodes.Ldloc, instance); // instance
1087          il.Emit(OpCodes.Ldloc, loopVariable); // instance, loopVariable
1088          ReadPrimitiveValue(il, reader, type); // instance, loopVariable, value
1089          switch (Type.GetTypeCode(type)) {
1090            case TypeCode.Boolean:
1091            case TypeCode.SByte:
1092            case TypeCode.Byte:
1093              il.Emit(OpCodes.Stelem_I1); // instance[loopVariable] = value;
1094              break;
1095            case TypeCode.Char:
1096            case TypeCode.Int16:
1097            case TypeCode.UInt16:
1098              il.Emit(OpCodes.Stelem_I2); // instance[loopVariable] = value;
1099              break;
1100            case TypeCode.Int32:
1101            case TypeCode.UInt32:
1102              il.Emit(OpCodes.Stelem_I4); // instance[loopVariable] = value;
1103              break;
1104            case TypeCode.Int64:
1105            case TypeCode.UInt64:
1106              il.Emit(OpCodes.Stelem_I8); // instance[loopVariable] = value;
1107              break;
1108            case TypeCode.Single:
1109              il.Emit(OpCodes.Stelem_R4); // instance[loopVariable] = value;
1110              break;
1111            case TypeCode.Double:
1112              il.Emit(OpCodes.Stelem_R8); // instance[loopVariable] = value;
1113              break;
1114            default:
1115              throw new NotSupportedException("Unknown primitive type " + type);
1116          }
1117        } else {
1118          il.Emit(OpCodes.Ldloc, instance); // instance
1119          il.Emit(OpCodes.Ldloc, loopVariable); // instance, loopVariable
1120          il.Emit(OpCodes.Ldelema, type); // instance[loopVariable]
1121          EmitReadValueType(il, reader, type);
1122        }
1123       
1124        il.Emit(OpCodes.Ldloc, loopVariable); // loopVariable
1125        il.Emit(OpCodes.Ldc_I4_1); // loopVariable, 1
1126        il.Emit(OpCodes.Add); // loopVariable+1
1127        il.Emit(OpCodes.Stloc, loopVariable); // loopVariable++;
1128       
1129        il.MarkLabel(loopHead);
1130        il.Emit(OpCodes.Ldloc, loopVariable); // loopVariable
1131        il.Emit(OpCodes.Ldloc, instance); // loopVariable, instance
1132        il.Emit(OpCodes.Ldlen); // loopVariable, instance.Length
1133        il.Emit(OpCodes.Conv_I4);
1134        il.Emit(OpCodes.Blt, loopStart); // if (loopVariable < instance.Length) goto loopStart;
1135      } else if (type.IsValueType) {
1136        // boxed value type
1137        il.Emit(OpCodes.Ldarg_1); // instance
1138        il.Emit(OpCodes.Unbox, type); // &(Type)instance
1139        if (type.IsEnum || type.IsPrimitive) {
1140          if (type.IsEnum) {
1141            type = type.GetEnumUnderlyingType();
1142          }
1143          Debug.Assert(type.IsPrimitive);
1144          ReadPrimitiveValue(il, reader, type); // &(Type)instance, value
1145          switch (Type.GetTypeCode(type)) {
1146            case TypeCode.Boolean:
1147            case TypeCode.SByte:
1148            case TypeCode.Byte:
1149              il.Emit(OpCodes.Stind_I1);
1150              break;
1151            case TypeCode.Char:
1152            case TypeCode.Int16:
1153            case TypeCode.UInt16:
1154              il.Emit(OpCodes.Stind_I2);
1155              break;
1156            case TypeCode.Int32:
1157            case TypeCode.UInt32:
1158              il.Emit(OpCodes.Stind_I4);
1159              break;
1160            case TypeCode.Int64:
1161            case TypeCode.UInt64:
1162              il.Emit(OpCodes.Stind_I8);
1163              break;
1164            case TypeCode.Single:
1165              il.Emit(OpCodes.Stind_R4);
1166              break;
1167            case TypeCode.Double:
1168              il.Emit(OpCodes.Stind_R8);
1169              break;
1170            default:
1171              throw new NotSupportedException("Unknown primitive type " + type);
1172          }
1173        } else {
1174          EmitReadValueType(il, reader, type);
1175        }
1176      } else {
1177        // reference type
1178        var instance = il.DeclareLocal(type);
1179        il.Emit(OpCodes.Ldarg_1);
1180        il.Emit(OpCodes.Castclass, type);
1181        il.Emit(OpCodes.Stloc, instance); // instance = (type)arg_1;
1182       
1183        foreach (FieldInfo field in fields) {
1184          EmitReadField(il, reader, instance, field); // read instance.Field
1185        }
1186      }
1187      il.Emit(OpCodes.Ret);
1188      return (ObjectReader)dynamicMethod.CreateDelegate(typeof(ObjectReader));
1189    }
1190
1191    void EmitReadField(ILGenerator il, LocalBuilder reader, LocalBuilder instance, FieldInfo field)
1192    {
1193      Type fieldType = field.FieldType;
1194      if (fieldType.IsValueType) {
1195        if (fieldType.IsPrimitive || fieldType.IsEnum) {
1196          il.Emit(OpCodes.Ldloc, instance); // instance
1197          ReadPrimitiveValue(il, reader, fieldType); // instance, value
1198          il.Emit(OpCodes.Stfld, field); // instance.field = value;
1199        } else {
1200          il.Emit(OpCodes.Ldloc, instance); // instance
1201          il.Emit(OpCodes.Ldflda, field); // &instance.field
1202          EmitReadValueType(il, reader, fieldType);
1203        }
1204      } else {
1205        il.Emit(OpCodes.Ldloc, instance); // instance
1206        il.Emit(OpCodes.Ldarg_0); // instance, context
1207        il.Emit(OpCodes.Call, readObject); // instance, context.ReadObject()
1208        il.Emit(OpCodes.Castclass, fieldType);
1209        il.Emit(OpCodes.Stfld, field); // instance.field = (fieldType) context.ReadObject();
1210      }
1211    }
1212
1213    /// <summary>
1214    /// Reads a primitive value of the specified type.
1215    /// Stack transition: ... => ..., value
1216    /// </summary>
1217    void ReadPrimitiveValue(ILGenerator il, LocalBuilder reader, Type fieldType)
1218    {
1219      if (fieldType.IsEnum) {
1220        fieldType = fieldType.GetEnumUnderlyingType();
1221        Debug.Assert(fieldType.IsPrimitive);
1222      }
1223      il.Emit(OpCodes.Ldloc, reader);
1224      switch (Type.GetTypeCode(fieldType)) {
1225        case TypeCode.Boolean:
1226        case TypeCode.SByte:
1227        case TypeCode.Byte:
1228          il.Emit(callVirt, readByte);
1229          break;
1230        case TypeCode.Char:
1231        case TypeCode.Int16:
1232        case TypeCode.UInt16:
1233          il.Emit(callVirt, readShort);
1234          break;
1235        case TypeCode.Int32:
1236        case TypeCode.UInt32:
1237          il.Emit(callVirt, readInt);
1238          break;
1239        case TypeCode.Int64:
1240        case TypeCode.UInt64:
1241          il.Emit(callVirt, readLong);
1242          break;
1243        case TypeCode.Single:
1244          il.Emit(callVirt, readFloat);
1245          break;
1246        case TypeCode.Double:
1247          il.Emit(callVirt, readDouble);
1248          break;
1249        default:
1250          throw new NotSupportedException("Unknown primitive type " + fieldType);
1251      }
1252    }
1253
1254    /// <summary>
1255    /// Stack transition: ..., field-ref => ...
1256    /// </summary>
1257    void EmitReadValueType(ILGenerator il, LocalBuilder reader, Type valType)
1258    {
1259      Debug.Assert(valType.IsValueType);
1260      Debug.Assert(!(valType.IsEnum || valType.IsPrimitive));
1261     
1262      var fieldRef = il.DeclareLocal(valType.MakeByRefType());
1263      il.Emit(OpCodes.Stloc, fieldRef);
1264     
1265      foreach (FieldInfo field in GetSerializableFields(valType)) {
1266        EmitReadField(il, reader, fieldRef, field);
1267      }
1268    }
1269    #endregion
1270   
1271    #region Custom Deserialization
1272    struct CustomDeserialization
1273    {
1274      readonly object instance;
1275      readonly SerializationInfo serializationInfo;
1276      readonly CustomDeserializationAction action;
1277     
1278      public CustomDeserialization(object instance, SerializationInfo serializationInfo, CustomDeserializationAction action)
1279      {
1280        this.instance = instance;
1281        this.serializationInfo = serializationInfo;
1282        this.action = action;
1283      }
1284     
1285      public void Run(StreamingContext context)
1286      {
1287        action(instance, serializationInfo, context);
1288      }
1289    }
1290   
1291    delegate void CustomDeserializationAction(object instance, SerializationInfo info, StreamingContext context);
1292   
1293    Dictionary<Type, CustomDeserializationAction> customDeserializationActions = new Dictionary<Type, CustomDeserializationAction>();
1294   
1295    CustomDeserializationAction GetCustomDeserializationAction(Type type)
1296    {
1297      CustomDeserializationAction action;
1298      if (!customDeserializationActions.TryGetValue(type, out action)) {
1299        action = CreateCustomDeserializationAction(type);
1300        customDeserializationActions.Add(type, action);
1301      }
1302      return action;
1303    }
1304   
1305    static CustomDeserializationAction CreateCustomDeserializationAction(Type type)
1306    {
1307      ConstructorInfo ctor = type.GetConstructor(
1308        BindingFlags.DeclaredOnly | BindingFlags.ExactBinding | BindingFlags.Instance
1309        | BindingFlags.NonPublic | BindingFlags.Public,
1310        null,
1311        new Type [] { typeof(SerializationInfo), typeof(StreamingContext) },
1312        null);
1313      if (ctor == null)
1314        throw new SerializationException("Could not find deserialization constructor for " + type.FullName);
1315     
1316      DynamicMethod dynamicMethod = new DynamicMethod(
1317        "CallCtor_" + type.Name,
1318        MethodAttributes.Public | MethodAttributes.Static,
1319        CallingConventions.Standard,
1320        typeof(void), new [] { typeof(object), typeof(SerializationInfo), typeof(StreamingContext) },
1321        type,
1322        true);
1323      ILGenerator il = dynamicMethod.GetILGenerator();
1324      il.Emit(OpCodes.Ldarg_0);
1325      il.Emit(OpCodes.Ldarg_1);
1326      il.Emit(OpCodes.Ldarg_2);
1327      il.Emit(OpCodes.Call, ctor);
1328      il.Emit(OpCodes.Ret);
1329      return (CustomDeserializationAction)dynamicMethod.CreateDelegate(typeof(CustomDeserializationAction));
1330    }
1331    #endregion
1332    #endregion
1333   
1334    [Conditional("DEBUG_SERIALIZER")]
1335    static void Log(string format, params object[] args)
1336    {
1337      Debug.WriteLine(format, args);
1338    }
1339  }
1340 
1341  /// <summary>
1342  /// Specifies the version of the class.
1343  /// The <see cref="FastSerializer"/> will refuse to deserialize an instance that was stored by
1344  /// a different version of the class than the current one.
1345  /// </summary>
1346  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum)]
1347  public class FastSerializerVersionAttribute : Attribute
1348  {
1349    readonly int versionNumber;
1350   
1351    public FastSerializerVersionAttribute(int versionNumber)
1352    {
1353      this.versionNumber = versionNumber;
1354    }
1355   
1356    public int VersionNumber {
1357      get {
1358        return versionNumber;
1359      }
1360    }
1361   
1362    internal static int GetVersionNumber(Type type)
1363    {
1364      var arr = type.GetCustomAttributes(typeof(FastSerializerVersionAttribute), false);
1365      if (arr.Length == 0)
1366        return 0;
1367      else
1368        return ((FastSerializerVersionAttribute)arr[0]).VersionNumber;
1369    }
1370  }
1371}
Note: See TracBrowser for help on using the repository browser.