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 | |
---|
19 | using System; |
---|
20 | using System.Collections.Generic; |
---|
21 | using System.Diagnostics; |
---|
22 | using System.IO; |
---|
23 | using System.Reflection; |
---|
24 | using System.Reflection.Emit; |
---|
25 | using System.Runtime.Serialization; |
---|
26 | |
---|
27 | namespace 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 | } |
---|