1
ļ»æ// Copyright (c) 2011 Daniel Grunwald
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:
9
// The above copyright notice and this permission notice shall be included in all copies or
10
// substantial portions of the Software.
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.
20
using System.Collections.Generic;
21
using System.Diagnostics;
23
using System.Reflection;
24
using System.Reflection.Emit;
25
using System.Runtime.Serialization;
27
namespace ICSharpCode.NRefactory.Utils
29
public class FastSerializer
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.
37
public SerializationBinder SerializationBinder { get; set; }
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.
46
public object[] FixedInstances { get; set; }
50
const int magic = 0x71D28A5D;
52
const byte Type_ReferenceType = 1;
53
const byte Type_ValueType = 2;
54
const byte Type_SZArray = 3;
55
const byte Type_ParameterizedType = 4;
59
sealed class SerializationType
61
public readonly int ID;
62
public readonly Type Type;
64
public SerializationType(int iD, Type type)
70
public ObjectScanner Scanner;
71
public ObjectWriter Writer;
72
public string TypeName;
73
public int AssemblyNameID;
76
sealed class SerializationContext
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;
83
readonly Dictionary<Type, SerializationType> typeMap = new Dictionary<Type, SerializationType>();
84
readonly List<SerializationType> types = new List<SerializationType>();
86
readonly Dictionary<string, int> assemblyNameToID = new Dictionary<string, int>();
87
readonly List<string> assemblyNames = new List<string>();
89
readonly FastSerializer fastSerializer;
90
public readonly BinaryWriter writer;
91
int fixedInstanceCount;
93
internal SerializationContext(FastSerializer fastSerializer, BinaryWriter writer)
95
this.fastSerializer = fastSerializer;
97
instances.Add(null); // use object ID 0 for null
98
objectTypes.Add(null);
102
public void MarkFixedInstances(object[] fixedInstances)
104
if (fixedInstances == null)
106
foreach (object obj in fixedInstances) {
107
if (!objectToID.ContainsKey(obj)) {
108
objectToID.Add(obj, instances.Count);
110
fixedInstanceCount++;
116
/// Marks an instance for future scanning.
118
public void Mark(object instance)
120
if (instance == null || objectToID.ContainsKey(instance))
122
Log(" Mark {0}", instance.GetType().Name);
124
objectToID.Add(instance, instances.Count);
125
instances.Add(instance);
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);
144
foreach (SerializationEntry entry in info) {
147
sType.Writer = serializationInfoWriter;
149
ObjectScanner objectScanner = sType.Scanner;
150
if (objectScanner == null) {
151
objectScanner = fastSerializer.GetScanner(type);
152
sType.Scanner = objectScanner;
153
sType.Writer = fastSerializer.GetWriter(type);
155
objectScanner(this, instance);
162
SerializationType MarkType(Type type)
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())
175
} else if (type.IsGenericParameter) {
176
throw new NotSupportedException();
178
var serializationBinder = fastSerializer.SerializationBinder;
179
if (serializationBinder != null) {
180
serializationBinder.BindToName(type, out assemblyName, out typeName);
182
assemblyName = type.Assembly.FullName;
183
typeName = type.FullName;
184
Debug.Assert(typeName != null);
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);
198
typeMap.Add(type, sType);
200
Log("Registered type %{0}: {1}", sType.ID, type);
201
if (type == typeof(string)) {
208
internal void ScanTypes()
210
for (int i = 0; i < types.Count; i++) {
211
Type type = types[i].Type;
212
if (type.IsGenericTypeDefinition || type.HasElementType)
214
if (typeof(ISerializable).IsAssignableFrom(type))
216
foreach (FieldInfo field in GetSerializableFields(type)) {
217
MarkType(field.FieldType);
224
public void WriteObjectID(object instance)
226
int id = (instance == null) ? 0 : objectToID[instance];
227
if (instances.Count <= ushort.MaxValue)
228
writer.Write((ushort)id);
233
void WriteTypeID(Type type)
235
Debug.Assert(typeMap.ContainsKey(type));
236
int typeID = typeMap[type].ID;
237
if (types.Count <= ushort.MaxValue)
238
writer.Write((ushort)typeID);
240
writer.Write(typeID);
243
internal void Write()
247
// Write out type information
248
writer.Write(instances.Count);
249
writer.Write(types.Count);
250
writer.Write(assemblyNames.Count);
251
writer.Write(fixedInstanceCount);
253
foreach (string assemblyName in assemblyNames) {
254
writer.Write(assemblyName);
257
foreach (SerializationType sType in types) {
258
Type type = sType.Type;
259
if (type.HasElementType) {
261
if (type.GetArrayRank() == 1)
262
writer.Write(Type_SZArray);
264
throw new NotSupportedException();
266
throw new NotSupportedException();
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);
276
if (type.IsValueType) {
277
writer.Write(Type_ValueType);
279
writer.Write(Type_ReferenceType);
281
if (assemblyNames.Count <= ushort.MaxValue)
282
writer.Write((ushort)sType.AssemblyNameID);
284
writer.Write(sType.AssemblyNameID);
285
writer.Write(sType.TypeName);
288
foreach (SerializationType sType in types) {
289
Type type = sType.Type;
290
if (type.IsGenericTypeDefinition || type.HasElementType)
292
if (type.IsPrimitive || typeof(ISerializable).IsAssignableFrom(type)) {
293
writer.Write(byte.MaxValue);
295
var fields = GetSerializableFields(type);
296
if (fields.Count >= byte.MaxValue)
297
throw new SerializationException("Too many fields.");
298
writer.Write((byte)fields.Count);
299
foreach (var field in fields) {
300
WriteTypeID(field.FieldType);
301
writer.Write(field.Name);
306
// Write out information necessary to create the instances
307
// starting from 1, because index 0 is null
308
for (int i = 1 + fixedInstanceCount; i < instances.Count; i++) {
309
SerializationType sType = objectTypes[i];
310
if (types.Count <= ushort.MaxValue)
311
writer.Write((ushort)sType.ID);
313
writer.Write(sType.ID);
314
if (sType == stringType) {
315
// Strings are written to the output immediately
316
// - we can't create an empty string and fill it later
317
writer.Write((string)instances[i]);
318
} else if (sType.Type.IsArray) {
319
// For arrays, write down the length, because we need that to create the array instance
320
writer.Write(((Array)instances[i]).Length);
323
// Write out information necessary to fill data into the instances
324
for (int i = 1 + fixedInstanceCount; i < instances.Count; i++) {
325
Log("0x{2:x6}, Write #{0}: {1}", i, objectTypes[i].Type.Name, writer.BaseStream.Position);
326
objectTypes[i].Writer(this, instances[i]);
328
Log("Serialization done.");
333
#region Object Scanners
334
delegate void ObjectScanner(SerializationContext context, object instance);
336
static readonly MethodInfo mark = typeof(SerializationContext).GetMethod("Mark", new[] { typeof(object) });
337
static readonly FieldInfo writerField = typeof(SerializationContext).GetField("writer");
339
Dictionary<Type, ObjectScanner> scanners = new Dictionary<Type, ObjectScanner>();
341
ObjectScanner GetScanner(Type type)
343
ObjectScanner scanner;
344
if (!scanners.TryGetValue(type, out scanner)) {
345
scanner = CreateScanner(type);
346
scanners.Add(type, scanner);
351
ObjectScanner CreateScanner(Type type)
353
bool isArray = type.IsArray;
355
if (type.GetArrayRank() != 1)
356
throw new NotSupportedException();
357
type = type.GetElementType();
358
if (!type.IsValueType) {
359
return delegate (SerializationContext context, object array) {
360
foreach (object val in (object[])array) {
366
for (Type baseType = type; baseType != null; baseType = baseType.BaseType) {
367
if (!baseType.IsSerializable)
368
throw new SerializationException("Type " + baseType + " is not [Serializable].");
370
List<FieldInfo> fields = GetSerializableFields(type);
371
fields.RemoveAll(f => !IsReferenceOrContainsReferences(f.FieldType));
372
if (fields.Count == 0) {
373
// The scanner has nothing to do for this object.
377
DynamicMethod dynamicMethod = new DynamicMethod(
378
(isArray ? "ScanArray_" : "Scan_") + type.Name,
379
typeof(void), new [] { typeof(SerializationContext), typeof(object) },
381
ILGenerator il = dynamicMethod.GetILGenerator();
385
var instance = il.DeclareLocal(type.MakeArrayType());
386
il.Emit(OpCodes.Ldarg_1);
387
il.Emit(OpCodes.Castclass, type.MakeArrayType());
388
il.Emit(OpCodes.Stloc, instance); // instance = (type[])arg_1;
390
// for (int i = 0; i < instance.Length; i++) scan instance[i];
391
var loopStart = il.DefineLabel();
392
var loopHead = il.DefineLabel();
393
var loopVariable = il.DeclareLocal(typeof(int));
394
il.Emit(OpCodes.Ldc_I4_0);
395
il.Emit(OpCodes.Stloc, loopVariable); // loopVariable = 0
396
il.Emit(OpCodes.Br, loopHead); // goto loopHead;
398
il.MarkLabel(loopStart);
400
il.Emit(OpCodes.Ldloc, instance); // instance
401
il.Emit(OpCodes.Ldloc, loopVariable); // instance, loopVariable
402
il.Emit(OpCodes.Ldelem, type); // &instance[loopVariable]
403
EmitScanValueType(il, type);
406
il.Emit(OpCodes.Ldloc, loopVariable); // loopVariable
407
il.Emit(OpCodes.Ldc_I4_1); // loopVariable, 1
408
il.Emit(OpCodes.Add); // loopVariable+1
409
il.Emit(OpCodes.Stloc, loopVariable); // loopVariable++;
411
il.MarkLabel(loopHead);
412
il.Emit(OpCodes.Ldloc, loopVariable); // loopVariable
413
il.Emit(OpCodes.Ldloc, instance); // loopVariable, instance
414
il.Emit(OpCodes.Ldlen); // loopVariable, instance.Length
415
il.Emit(OpCodes.Conv_I4);
416
il.Emit(OpCodes.Blt, loopStart); // if (loopVariable < instance.Length) goto loopStart;
417
} else if (type.IsValueType) {
419
il.Emit(OpCodes.Ldarg_1);
420
il.Emit(OpCodes.Unbox_Any, type);
421
EmitScanValueType(il, type);
424
var instance = il.DeclareLocal(type);
425
il.Emit(OpCodes.Ldarg_1);
426
il.Emit(OpCodes.Castclass, type);
427
il.Emit(OpCodes.Stloc, instance); // instance = (type)arg_1;
429
foreach (FieldInfo field in fields) {
430
EmitScanField(il, instance, field); // scan instance.Field
433
il.Emit(OpCodes.Ret);
434
return (ObjectScanner)dynamicMethod.CreateDelegate(typeof(ObjectScanner));
438
/// Emit 'scan instance.Field'.
439
/// Stack transition: ... => ...
441
void EmitScanField(ILGenerator il, LocalBuilder instance, FieldInfo field)
443
if (field.FieldType.IsValueType) {
444
il.Emit(OpCodes.Ldloc, instance); // instance
445
il.Emit(OpCodes.Ldfld, field); // instance.field
446
EmitScanValueType(il, field.FieldType);
448
il.Emit(OpCodes.Ldarg_0); // context
449
il.Emit(OpCodes.Ldloc, instance); // context, instance
450
il.Emit(OpCodes.Ldfld, field); // context, instance.field
451
il.Emit(OpCodes.Call, mark); // context.Mark(instance.field);
456
/// Stack transition: ..., value => ...
458
void EmitScanValueType(ILGenerator il, Type valType)
460
var fieldRef = il.DeclareLocal(valType);
461
il.Emit(OpCodes.Stloc, fieldRef);
463
foreach (FieldInfo field in GetSerializableFields(valType)) {
464
if (IsReferenceOrContainsReferences(field.FieldType)) {
465
EmitScanField(il, fieldRef, field);
470
static List<FieldInfo> GetSerializableFields(Type type)
472
List<FieldInfo> fields = new List<FieldInfo>();
473
for (Type baseType = type; baseType != null; baseType = baseType.BaseType) {
474
FieldInfo[] declFields = baseType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly);
475
Array.Sort(declFields, (a,b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal));
476
fields.AddRange(declFields);
478
fields.RemoveAll(f => f.IsNotSerialized);
482
static bool IsReferenceOrContainsReferences(Type type)
484
if (!type.IsValueType)
486
if (type.IsPrimitive)
488
foreach (FieldInfo field in GetSerializableFields(type)) {
489
if (IsReferenceOrContainsReferences(field.FieldType))
496
#region Object Writers
497
delegate void ObjectWriter(SerializationContext context, object instance);
499
static readonly MethodInfo writeObjectID = typeof(SerializationContext).GetMethod("WriteObjectID", new[] { typeof(object) });
501
static readonly MethodInfo writeByte = typeof(BinaryWriter).GetMethod("Write", new[] { typeof(byte) });
502
static readonly MethodInfo writeShort = typeof(BinaryWriter).GetMethod("Write", new[] { typeof(short) });
503
static readonly MethodInfo writeInt = typeof(BinaryWriter).GetMethod("Write", new[] { typeof(int) });
504
static readonly MethodInfo writeLong = typeof(BinaryWriter).GetMethod("Write", new[] { typeof(long) });
505
static readonly MethodInfo writeFloat = typeof(BinaryWriter).GetMethod("Write", new[] { typeof(float) });
506
static readonly MethodInfo writeDouble = typeof(BinaryWriter).GetMethod("Write", new[] { typeof(double) });
507
OpCode callVirt = OpCodes.Callvirt;
509
static readonly ObjectWriter serializationInfoWriter = delegate(SerializationContext context, object instance) {
510
BinaryWriter writer = context.writer;
511
SerializationInfo info = (SerializationInfo)instance;
512
writer.Write(info.MemberCount);
513
foreach (SerializationEntry entry in info) {
514
writer.Write(entry.Name);
515
context.WriteObjectID(entry.Value);
519
Dictionary<Type, ObjectWriter> writers = new Dictionary<Type, ObjectWriter>();
521
ObjectWriter GetWriter(Type type)
524
if (!writers.TryGetValue(type, out writer)) {
525
writer = CreateWriter(type);
526
writers.Add(type, writer);
531
ObjectWriter CreateWriter(Type type)
533
if (type == typeof(string)) {
534
// String contents are written in the object creation section,
535
// not into the field value section.
538
bool isArray = type.IsArray;
540
if (type.GetArrayRank() != 1)
541
throw new NotSupportedException();
542
type = type.GetElementType();
543
if (!type.IsValueType) {
544
return delegate (SerializationContext context, object array) {
545
foreach (object val in (object[])array) {
546
context.WriteObjectID(val);
549
} else if (type == typeof(byte)) {
550
return delegate (SerializationContext context, object array) {
551
context.writer.Write((byte[])array);
555
List<FieldInfo> fields = GetSerializableFields(type);
556
if (fields.Count == 0) {
557
// The writer has nothing to do for this object.
562
DynamicMethod dynamicMethod = new DynamicMethod(
563
(isArray ? "WriteArray_" : "Write_") + type.Name,
564
typeof(void), new [] { typeof(SerializationContext), typeof(object) },
566
ILGenerator il = dynamicMethod.GetILGenerator();
568
var writer = il.DeclareLocal(typeof(BinaryWriter));
570
il.Emit(OpCodes.Ldarg_0);
571
il.Emit(OpCodes.Ldfld, writerField);
572
il.Emit(OpCodes.Stloc, writer); // writer = context.writer;
575
var instance = il.DeclareLocal(type.MakeArrayType());
576
il.Emit(OpCodes.Ldarg_1);
577
il.Emit(OpCodes.Castclass, type.MakeArrayType());
578
il.Emit(OpCodes.Stloc, instance); // instance = (type[])arg_1;
580
// for (int i = 0; i < instance.Length; i++) write instance[i];
582
var loopStart = il.DefineLabel();
583
var loopHead = il.DefineLabel();
584
var loopVariable = il.DeclareLocal(typeof(int));
585
il.Emit(OpCodes.Ldc_I4_0);
586
il.Emit(OpCodes.Stloc, loopVariable); // loopVariable = 0
587
il.Emit(OpCodes.Br, loopHead); // goto loopHead;
589
il.MarkLabel(loopStart);
591
if (type.IsEnum || type.IsPrimitive) {
593
type = type.GetEnumUnderlyingType();
595
Debug.Assert(type.IsPrimitive);
596
il.Emit(OpCodes.Ldloc, writer); // writer
597
il.Emit(OpCodes.Ldloc, instance); // writer, instance
598
il.Emit(OpCodes.Ldloc, loopVariable); // writer, instance, loopVariable
599
switch (Type.GetTypeCode(type)) {
600
case TypeCode.Boolean:
603
il.Emit(OpCodes.Ldelem_I1); // writer, instance[loopVariable]
604
il.Emit(callVirt, writeByte); // writer.Write(instance[loopVariable]);
608
case TypeCode.UInt16:
609
il.Emit(OpCodes.Ldelem_I2); // writer, instance[loopVariable]
610
il.Emit(callVirt, writeShort); // writer.Write(instance[loopVariable]);
613
case TypeCode.UInt32:
614
il.Emit(OpCodes.Ldelem_I4); // writer, instance[loopVariable]
615
il.Emit(callVirt, writeInt); // writer.Write(instance[loopVariable]);
618
case TypeCode.UInt64:
619
il.Emit(OpCodes.Ldelem_I8); // writer, instance[loopVariable]
620
il.Emit(callVirt, writeLong); // writer.Write(instance[loopVariable]);
622
case TypeCode.Single:
623
il.Emit(OpCodes.Ldelem_R4); // writer, instance[loopVariable]
624
il.Emit(callVirt, writeFloat); // writer.Write(instance[loopVariable]);
626
case TypeCode.Double:
627
il.Emit(OpCodes.Ldelem_R8); // writer, instance[loopVariable]
628
il.Emit(callVirt, writeDouble); // writer.Write(instance[loopVariable]);
631
throw new NotSupportedException("Unknown primitive type " + type);
634
il.Emit(OpCodes.Ldloc, instance); // instance
635
il.Emit(OpCodes.Ldloc, loopVariable); // instance, loopVariable
636
il.Emit(OpCodes.Ldelem, type); // instance[loopVariable]
637
EmitWriteValueType(il, writer, type);
640
il.Emit(OpCodes.Ldloc, loopVariable); // loopVariable
641
il.Emit(OpCodes.Ldc_I4_1); // loopVariable, 1
642
il.Emit(OpCodes.Add); // loopVariable+1
643
il.Emit(OpCodes.Stloc, loopVariable); // loopVariable++;
645
il.MarkLabel(loopHead);
646
il.Emit(OpCodes.Ldloc, loopVariable); // loopVariable
647
il.Emit(OpCodes.Ldloc, instance); // loopVariable, instance
648
il.Emit(OpCodes.Ldlen); // loopVariable, instance.Length
649
il.Emit(OpCodes.Conv_I4);
650
il.Emit(OpCodes.Blt, loopStart); // if (loopVariable < instance.Length) goto loopStart;
651
} else if (type.IsValueType) {
653
if (type.IsEnum || type.IsPrimitive) {
654
il.Emit(OpCodes.Ldloc, writer);
655
il.Emit(OpCodes.Ldarg_1);
656
il.Emit(OpCodes.Unbox_Any, type);
657
WritePrimitiveValue(il, type);
659
il.Emit(OpCodes.Ldarg_1);
660
il.Emit(OpCodes.Unbox_Any, type);
661
EmitWriteValueType(il, writer, type);
665
var instance = il.DeclareLocal(type);
666
il.Emit(OpCodes.Ldarg_1);
667
il.Emit(OpCodes.Castclass, type);
668
il.Emit(OpCodes.Stloc, instance); // instance = (type)arg_1;
670
foreach (FieldInfo field in fields) {
671
EmitWriteField(il, writer, instance, field); // write instance.Field
674
il.Emit(OpCodes.Ret);
675
return (ObjectWriter)dynamicMethod.CreateDelegate(typeof(ObjectWriter));
679
/// Emit 'write instance.Field'.
680
/// Stack transition: ... => ...
682
void EmitWriteField(ILGenerator il, LocalBuilder writer, LocalBuilder instance, FieldInfo field)
684
Type fieldType = field.FieldType;
685
if (fieldType.IsValueType) {
686
if (fieldType.IsPrimitive || fieldType.IsEnum) {
687
il.Emit(OpCodes.Ldloc, writer); // writer
688
il.Emit(OpCodes.Ldloc, instance); // writer, instance
689
il.Emit(OpCodes.Ldfld, field); // writer, instance.field
690
WritePrimitiveValue(il, fieldType);
692
il.Emit(OpCodes.Ldloc, instance); // instance
693
il.Emit(OpCodes.Ldfld, field); // instance.field
694
EmitWriteValueType(il, writer, fieldType);
697
il.Emit(OpCodes.Ldarg_0); // context
698
il.Emit(OpCodes.Ldloc, instance); // context, instance
699
il.Emit(OpCodes.Ldfld, field); // context, instance.field
700
il.Emit(OpCodes.Call, writeObjectID); // context.WriteObjectID(instance.field);
705
/// Writes a primitive value of the specified type.
706
/// Stack transition: ..., writer, value => ...
708
void WritePrimitiveValue(ILGenerator il, Type fieldType)
710
if (fieldType.IsEnum) {
711
fieldType = fieldType.GetEnumUnderlyingType();
712
Debug.Assert(fieldType.IsPrimitive);
714
switch (Type.GetTypeCode(fieldType)) {
715
case TypeCode.Boolean:
718
il.Emit(callVirt, writeByte); // writer.Write(value);
722
case TypeCode.UInt16:
723
il.Emit(callVirt, writeShort); // writer.Write(value);
726
case TypeCode.UInt32:
727
il.Emit(callVirt, writeInt); // writer.Write(value);
730
case TypeCode.UInt64:
731
il.Emit(callVirt, writeLong); // writer.Write(value);
733
case TypeCode.Single:
734
il.Emit(callVirt, writeFloat); // writer.Write(value);
736
case TypeCode.Double:
737
il.Emit(callVirt, writeDouble); // writer.Write(value);
740
throw new NotSupportedException("Unknown primitive type " + fieldType);
745
/// Stack transition: ..., value => ...
747
void EmitWriteValueType(ILGenerator il, LocalBuilder writer, Type valType)
749
Debug.Assert(valType.IsValueType);
750
Debug.Assert(!(valType.IsEnum || valType.IsPrimitive));
752
var fieldVal = il.DeclareLocal(valType);
753
il.Emit(OpCodes.Stloc, fieldVal);
755
foreach (FieldInfo field in GetSerializableFields(valType)) {
756
EmitWriteField(il, writer, fieldVal, field);
761
StreamingContext streamingContext = new StreamingContext(StreamingContextStates.All);
762
FormatterConverter formatterConverter = new FormatterConverter();
764
public void Serialize(Stream stream, object instance)
766
Serialize(new BinaryWriterWith7BitEncodedInts(stream), instance);
769
public void Serialize(BinaryWriter writer, object instance)
771
SerializationContext context = new SerializationContext(this, writer);
772
context.MarkFixedInstances(this.FixedInstances);
773
context.Mark(instance);
777
context.WriteObjectID(instance);
780
delegate void TypeSerializer(object instance, SerializationContext context);
783
#region Deserialization
784
sealed class DeserializationContext
786
public Type[] Types; // index: type ID
788
public object[] Objects; // index: object ID
790
public BinaryReader Reader;
792
public object ReadObject()
794
if (this.Objects.Length <= ushort.MaxValue)
795
return this.Objects[Reader.ReadUInt16()];
797
return this.Objects[Reader.ReadInt32()];
800
#region DeserializeTypeDescriptions
801
internal int ReadTypeID()
803
if (this.Types.Length <= ushort.MaxValue)
804
return Reader.ReadUInt16();
806
return Reader.ReadInt32();
809
internal void DeserializeTypeDescriptions()
811
for (int i = 0; i < this.Types.Length; i++) {
812
Type type = this.Types[i];
813
if (type.IsGenericTypeDefinition || type.HasElementType)
815
bool isCustomSerialization = typeof(ISerializable).IsAssignableFrom(type);
816
bool typeIsSpecial = type.IsPrimitive || isCustomSerialization;
818
byte serializedFieldCount = Reader.ReadByte();
819
if (serializedFieldCount == byte.MaxValue) {
822
throw new SerializationException("Type " + type + " was serialized as special type, but isn't special now.");
825
throw new SerializationException("Type " + type.FullName + " wasn't serialized as special type, but is special now.");
827
var availableFields = GetSerializableFields(this.Types[i]);
828
if (availableFields.Count != serializedFieldCount)
829
throw new SerializationException("Number of fields on " + type.FullName + " has changed.");
830
for (int j = 0; j < serializedFieldCount; j++) {
831
int fieldTypeID = ReadTypeID();
833
string fieldName = Reader.ReadString();
834
FieldInfo fieldInfo = availableFields[j];
835
if (fieldInfo.Name != fieldName)
836
throw new SerializationException("Field mismatch on type " + type.FullName);
837
if (fieldInfo.FieldType != this.Types[fieldTypeID])
838
throw new SerializationException(type.FullName + "." + fieldName + " was serialized as " + this.Types[fieldTypeID] + ", but now is " + fieldInfo.FieldType);
846
delegate void ObjectReader(DeserializationContext context, object instance);
848
public object Deserialize(Stream stream)
850
return Deserialize(new BinaryReaderWith7BitEncodedInts(stream));
853
public object Deserialize(BinaryReader reader)
855
if (reader.ReadInt32() != magic)
856
throw new SerializationException("The data cannot be read by FastSerializer (unknown magic value)");
858
DeserializationContext context = new DeserializationContext();
859
context.Reader = reader;
860
context.Objects = new object[reader.ReadInt32()];
861
context.Types = new Type[reader.ReadInt32()];
862
string[] assemblyNames = new string[reader.ReadInt32()];
863
int fixedInstanceCount = reader.ReadInt32();
865
if (fixedInstanceCount != 0) {
866
if (this.FixedInstances == null || this.FixedInstances.Length != fixedInstanceCount)
867
throw new SerializationException("Number of fixed instances doesn't match");
868
for (int i = 0; i < fixedInstanceCount; i++) {
869
context.Objects[i + 1] = this.FixedInstances[i];
873
for (int i = 0; i < assemblyNames.Length; i++) {
874
assemblyNames[i] = reader.ReadString();
876
int stringTypeID = -1;
877
for (int i = 0; i < context.Types.Length; i++) {
878
byte typeKind = reader.ReadByte();
880
case Type_ReferenceType:
883
if (assemblyNames.Length <= ushort.MaxValue)
884
assemblyID = reader.ReadUInt16();
886
assemblyID = reader.ReadInt32();
887
string assemblyName = assemblyNames[assemblyID];
888
string typeName = reader.ReadString();
890
if (SerializationBinder != null) {
891
type = SerializationBinder.BindToType(assemblyName, typeName);
893
type = Assembly.Load(assemblyName).GetType(typeName);
896
throw new SerializationException("Could not find '" + typeName + "' in '" + assemblyName + "'");
897
if (typeKind == Type_ValueType && !type.IsValueType)
898
throw new SerializationException("Expected '" + typeName + "' to be a value type, but it is reference type");
899
if (typeKind == Type_ReferenceType && type.IsValueType)
900
throw new SerializationException("Expected '" + typeName + "' to be a reference type, but it is value type");
901
context.Types[i] = type;
902
if (type == typeof(string))
906
context.Types[i] = context.Types[context.ReadTypeID()].MakeArrayType();
908
case Type_ParameterizedType:
909
Type genericType = context.Types[context.ReadTypeID()];
910
int typeParameterCount = genericType.GetGenericArguments().Length;
911
Type[] typeArguments = new Type[typeParameterCount];
912
for (int j = 0; j < typeArguments.Length; j++) {
913
typeArguments[j] = context.Types[context.ReadTypeID()];
915
context.Types[i] = genericType.MakeGenericType(typeArguments);
918
throw new SerializationException("Unknown type kind");
921
context.DeserializeTypeDescriptions();
922
int[] typeIDByObjectID = new int[context.Objects.Length];
923
for (int i = 1 + fixedInstanceCount; i < context.Objects.Length; i++) {
924
int typeID = context.ReadTypeID();
927
if (typeID == stringTypeID) {
928
instance = reader.ReadString();
930
Type type = context.Types[typeID];
932
int length = reader.ReadInt32();
933
instance = Array.CreateInstance(type.GetElementType(), length);
935
instance = FormatterServices.GetUninitializedObject(type);
938
context.Objects[i] = instance;
939
typeIDByObjectID[i] = typeID;
941
List<CustomDeserialization> customDeserializatons = new List<CustomDeserialization>();
942
ObjectReader[] objectReaders = new ObjectReader[context.Types.Length]; // index: type ID
943
for (int i = 1 + fixedInstanceCount; i < context.Objects.Length; i++) {
944
object instance = context.Objects[i];
945
int typeID = typeIDByObjectID[i];
946
Log("0x{2:x6} Read #{0}: {1}", i, context.Types[typeID].Name, reader.BaseStream.Position);
947
ISerializable serializable = instance as ISerializable;
948
if (serializable != null) {
949
Type type = context.Types[typeID];
950
SerializationInfo info = new SerializationInfo(type, formatterConverter);
951
int count = reader.ReadInt32();
952
for (int j = 0; j < count; j++) {
953
string name = reader.ReadString();
954
object val = context.ReadObject();
955
info.AddValue(name, val);
957
CustomDeserializationAction action = GetCustomDeserializationAction(type);
958
customDeserializatons.Add(new CustomDeserialization(instance, info, action));
960
ObjectReader objectReader = objectReaders[typeID];
961
if (objectReader == null) {
962
objectReader = GetReader(context.Types[typeID]);
963
objectReaders[typeID] = objectReader;
965
objectReader(context, instance);
968
Log("File was read successfully, now running {0} custom deserializations...", customDeserializatons.Count);
969
foreach (CustomDeserialization customDeserializaton in customDeserializatons) {
970
customDeserializaton.Run(streamingContext);
972
for (int i = 1 + fixedInstanceCount; i < context.Objects.Length; i++) {
973
IDeserializationCallback dc = context.Objects[i] as IDeserializationCallback;
975
dc.OnDeserialization(null);
978
return context.ReadObject();
981
#region Object Reader
982
static readonly FieldInfo readerField = typeof(DeserializationContext).GetField("Reader");
983
static readonly MethodInfo readObject = typeof(DeserializationContext).GetMethod("ReadObject");
985
static readonly MethodInfo readByte = typeof(BinaryReader).GetMethod("ReadByte");
986
static readonly MethodInfo readShort = typeof(BinaryReader).GetMethod("ReadInt16");
987
static readonly MethodInfo readInt = typeof(BinaryReader).GetMethod("ReadInt32");
988
static readonly MethodInfo readLong = typeof(BinaryReader).GetMethod("ReadInt64");
989
static readonly MethodInfo readFloat = typeof(BinaryReader).GetMethod("ReadSingle");
990
static readonly MethodInfo readDouble = typeof(BinaryReader).GetMethod("ReadDouble");
992
Dictionary<Type, ObjectReader> readers = new Dictionary<Type, ObjectReader>();
994
ObjectReader GetReader(Type type)
997
if (!readers.TryGetValue(type, out reader)) {
998
reader = CreateReader(type);
999
readers.Add(type, reader);
1004
ObjectReader CreateReader(Type type)
1006
if (type == typeof(string)) {
1007
// String contents are written in the object creation section,
1008
// not into the field value section; so there's nothing to read here.
1011
bool isArray = type.IsArray;
1013
if (type.GetArrayRank() != 1)
1014
throw new NotSupportedException();
1015
type = type.GetElementType();
1016
if (!type.IsValueType) {
1017
return delegate (DeserializationContext context, object arrayInstance) {
1018
object[] array = (object[])arrayInstance;
1019
for (int i = 0; i < array.Length; i++) {
1020
array[i] = context.ReadObject();
1023
} else if (type == typeof(byte)) {
1024
return delegate (DeserializationContext context, object arrayInstance) {
1025
byte[] array = (byte[])arrayInstance;
1026
BinaryReader binaryReader = context.Reader;
1030
bytesRead = binaryReader.Read(array, pos, array.Length - pos);
1032
} while (bytesRead > 0);
1033
if (pos != array.Length)
1034
throw new EndOfStreamException();
1038
var fields = GetSerializableFields(type);
1039
if (fields.Count == 0) {
1040
// The reader has nothing to do for this object.
1041
return delegate { };
1044
DynamicMethod dynamicMethod = new DynamicMethod(
1045
(isArray ? "ReadArray_" : "Read_") + type.Name,
1046
MethodAttributes.Public | MethodAttributes.Static,
1047
CallingConventions.Standard,
1048
typeof(void), new [] { typeof(DeserializationContext), typeof(object) },
1051
ILGenerator il = dynamicMethod.GetILGenerator();
1053
var reader = il.DeclareLocal(typeof(BinaryReader));
1055
il.Emit(OpCodes.Ldarg_0);
1056
il.Emit(OpCodes.Ldfld, readerField);
1057
il.Emit(OpCodes.Stloc, reader); // reader = context.reader;
1060
var instance = il.DeclareLocal(type.MakeArrayType());
1061
il.Emit(OpCodes.Ldarg_1);
1062
il.Emit(OpCodes.Castclass, type.MakeArrayType());
1063
il.Emit(OpCodes.Stloc, instance); // instance = (type[])arg_1;
1065
// for (int i = 0; i < instance.Length; i++) read &instance[i];
1067
var loopStart = il.DefineLabel();
1068
var loopHead = il.DefineLabel();
1069
var loopVariable = il.DeclareLocal(typeof(int));
1070
il.Emit(OpCodes.Ldc_I4_0);
1071
il.Emit(OpCodes.Stloc, loopVariable); // loopVariable = 0
1072
il.Emit(OpCodes.Br, loopHead); // goto loopHead;
1074
il.MarkLabel(loopStart);
1076
if (type.IsEnum || type.IsPrimitive) {
1078
type = type.GetEnumUnderlyingType();
1080
Debug.Assert(type.IsPrimitive);
1081
il.Emit(OpCodes.Ldloc, instance); // instance
1082
il.Emit(OpCodes.Ldloc, loopVariable); // instance, loopVariable
1083
ReadPrimitiveValue(il, reader, type); // instance, loopVariable, value
1084
switch (Type.GetTypeCode(type)) {
1085
case TypeCode.Boolean:
1086
case TypeCode.SByte:
1088
il.Emit(OpCodes.Stelem_I1); // instance[loopVariable] = value;
1091
case TypeCode.Int16:
1092
case TypeCode.UInt16:
1093
il.Emit(OpCodes.Stelem_I2); // instance[loopVariable] = value;
1095
case TypeCode.Int32:
1096
case TypeCode.UInt32:
1097
il.Emit(OpCodes.Stelem_I4); // instance[loopVariable] = value;
1099
case TypeCode.Int64:
1100
case TypeCode.UInt64:
1101
il.Emit(OpCodes.Stelem_I8); // instance[loopVariable] = value;
1103
case TypeCode.Single:
1104
il.Emit(OpCodes.Stelem_R4); // instance[loopVariable] = value;
1106
case TypeCode.Double:
1107
il.Emit(OpCodes.Stelem_R8); // instance[loopVariable] = value;
1110
throw new NotSupportedException("Unknown primitive type " + type);
1113
il.Emit(OpCodes.Ldloc, instance); // instance
1114
il.Emit(OpCodes.Ldloc, loopVariable); // instance, loopVariable
1115
il.Emit(OpCodes.Ldelema, type); // instance[loopVariable]
1116
EmitReadValueType(il, reader, type);
1119
il.Emit(OpCodes.Ldloc, loopVariable); // loopVariable
1120
il.Emit(OpCodes.Ldc_I4_1); // loopVariable, 1
1121
il.Emit(OpCodes.Add); // loopVariable+1
1122
il.Emit(OpCodes.Stloc, loopVariable); // loopVariable++;
1124
il.MarkLabel(loopHead);
1125
il.Emit(OpCodes.Ldloc, loopVariable); // loopVariable
1126
il.Emit(OpCodes.Ldloc, instance); // loopVariable, instance
1127
il.Emit(OpCodes.Ldlen); // loopVariable, instance.Length
1128
il.Emit(OpCodes.Conv_I4);
1129
il.Emit(OpCodes.Blt, loopStart); // if (loopVariable < instance.Length) goto loopStart;
1130
} else if (type.IsValueType) {
1132
il.Emit(OpCodes.Ldarg_1); // instance
1133
il.Emit(OpCodes.Unbox, type); // &(Type)instance
1134
if (type.IsEnum || type.IsPrimitive) {
1136
type = type.GetEnumUnderlyingType();
1138
Debug.Assert(type.IsPrimitive);
1139
ReadPrimitiveValue(il, reader, type); // &(Type)instance, value
1140
switch (Type.GetTypeCode(type)) {
1141
case TypeCode.Boolean:
1142
case TypeCode.SByte:
1144
il.Emit(OpCodes.Stind_I1);
1147
case TypeCode.Int16:
1148
case TypeCode.UInt16:
1149
il.Emit(OpCodes.Stind_I2);
1151
case TypeCode.Int32:
1152
case TypeCode.UInt32:
1153
il.Emit(OpCodes.Stind_I4);
1155
case TypeCode.Int64:
1156
case TypeCode.UInt64:
1157
il.Emit(OpCodes.Stind_I8);
1159
case TypeCode.Single:
1160
il.Emit(OpCodes.Stind_R4);
1162
case TypeCode.Double:
1163
il.Emit(OpCodes.Stind_R8);
1166
throw new NotSupportedException("Unknown primitive type " + type);
1169
EmitReadValueType(il, reader, type);
1173
var instance = il.DeclareLocal(type);
1174
il.Emit(OpCodes.Ldarg_1);
1175
il.Emit(OpCodes.Castclass, type);
1176
il.Emit(OpCodes.Stloc, instance); // instance = (type)arg_1;
1178
foreach (FieldInfo field in fields) {
1179
EmitReadField(il, reader, instance, field); // read instance.Field
1182
il.Emit(OpCodes.Ret);
1183
return (ObjectReader)dynamicMethod.CreateDelegate(typeof(ObjectReader));
1186
void EmitReadField(ILGenerator il, LocalBuilder reader, LocalBuilder instance, FieldInfo field)
1188
Type fieldType = field.FieldType;
1189
if (fieldType.IsValueType) {
1190
if (fieldType.IsPrimitive || fieldType.IsEnum) {
1191
il.Emit(OpCodes.Ldloc, instance); // instance
1192
ReadPrimitiveValue(il, reader, fieldType); // instance, value
1193
il.Emit(OpCodes.Stfld, field); // instance.field = value;
1195
il.Emit(OpCodes.Ldloc, instance); // instance
1196
il.Emit(OpCodes.Ldflda, field); // &instance.field
1197
EmitReadValueType(il, reader, fieldType);
1200
il.Emit(OpCodes.Ldloc, instance); // instance
1201
il.Emit(OpCodes.Ldarg_0); // instance, context
1202
il.Emit(OpCodes.Call, readObject); // instance, context.ReadObject()
1203
il.Emit(OpCodes.Castclass, fieldType);
1204
il.Emit(OpCodes.Stfld, field); // instance.field = (fieldType) context.ReadObject();
1209
/// Reads a primitive value of the specified type.
1210
/// Stack transition: ... => ..., value
1212
void ReadPrimitiveValue(ILGenerator il, LocalBuilder reader, Type fieldType)
1214
if (fieldType.IsEnum) {
1215
fieldType = fieldType.GetEnumUnderlyingType();
1216
Debug.Assert(fieldType.IsPrimitive);
1218
il.Emit(OpCodes.Ldloc, reader);
1219
switch (Type.GetTypeCode(fieldType)) {
1220
case TypeCode.Boolean:
1221
case TypeCode.SByte:
1223
il.Emit(callVirt, readByte);
1226
case TypeCode.Int16:
1227
case TypeCode.UInt16:
1228
il.Emit(callVirt, readShort);
1230
case TypeCode.Int32:
1231
case TypeCode.UInt32:
1232
il.Emit(callVirt, readInt);
1234
case TypeCode.Int64:
1235
case TypeCode.UInt64:
1236
il.Emit(callVirt, readLong);
1238
case TypeCode.Single:
1239
il.Emit(callVirt, readFloat);
1241
case TypeCode.Double:
1242
il.Emit(callVirt, readDouble);
1245
throw new NotSupportedException("Unknown primitive type " + fieldType);
1250
/// Stack transition: ..., field-ref => ...
1252
void EmitReadValueType(ILGenerator il, LocalBuilder reader, Type valType)
1254
Debug.Assert(valType.IsValueType);
1255
Debug.Assert(!(valType.IsEnum || valType.IsPrimitive));
1257
var fieldRef = il.DeclareLocal(valType.MakeByRefType());
1258
il.Emit(OpCodes.Stloc, fieldRef);
1260
foreach (FieldInfo field in GetSerializableFields(valType)) {
1261
EmitReadField(il, reader, fieldRef, field);
1266
#region Custom Deserialization
1267
struct CustomDeserialization
1269
readonly object instance;
1270
readonly SerializationInfo serializationInfo;
1271
readonly CustomDeserializationAction action;
1273
public CustomDeserialization(object instance, SerializationInfo serializationInfo, CustomDeserializationAction action)
1275
this.instance = instance;
1276
this.serializationInfo = serializationInfo;
1277
this.action = action;
1280
public void Run(StreamingContext context)
1282
action(instance, serializationInfo, context);
1286
delegate void CustomDeserializationAction(object instance, SerializationInfo info, StreamingContext context);
1288
Dictionary<Type, CustomDeserializationAction> customDeserializationActions = new Dictionary<Type, CustomDeserializationAction>();
1290
CustomDeserializationAction GetCustomDeserializationAction(Type type)
1292
CustomDeserializationAction action;
1293
if (!customDeserializationActions.TryGetValue(type, out action)) {
1294
action = CreateCustomDeserializationAction(type);
1295
customDeserializationActions.Add(type, action);
1300
static CustomDeserializationAction CreateCustomDeserializationAction(Type type)
1302
ConstructorInfo ctor = type.GetConstructor(
1303
BindingFlags.DeclaredOnly | BindingFlags.ExactBinding | BindingFlags.Instance
1304
| BindingFlags.NonPublic | BindingFlags.Public,
1306
new Type [] { typeof(SerializationInfo), typeof(StreamingContext) },
1309
throw new SerializationException("Could not find deserialization constructor for " + type.FullName);
1311
DynamicMethod dynamicMethod = new DynamicMethod(
1312
"CallCtor_" + type.Name,
1313
MethodAttributes.Public | MethodAttributes.Static,
1314
CallingConventions.Standard,
1315
typeof(void), new [] { typeof(object), typeof(SerializationInfo), typeof(StreamingContext) },
1318
ILGenerator il = dynamicMethod.GetILGenerator();
1319
il.Emit(OpCodes.Ldarg_0);
1320
il.Emit(OpCodes.Ldarg_1);
1321
il.Emit(OpCodes.Ldarg_2);
1322
il.Emit(OpCodes.Call, ctor);
1323
il.Emit(OpCodes.Ret);
1324
return (CustomDeserializationAction)dynamicMethod.CreateDelegate(typeof(CustomDeserializationAction));
1329
[Conditional("DEBUG_SERIALIZER")]
1330
static void Log(string format, params object[] args)
1332
Debug.WriteLine(format, args);