From b193dc91a51238af6c029d90924f48535e7d9a16 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 27 Aug 2011 17:10:19 +0200 Subject: [PATCH] FastSerializer: Add support for 'Fixed Instances', e.g. for serializing object graphs that have references to a singleton. --- .../Utils/FastSerializer.cs | 55 +++++++++++++++---- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/ICSharpCode.NRefactory/Utils/FastSerializer.cs b/ICSharpCode.NRefactory/Utils/FastSerializer.cs index d28d07ab40..9a14c03315 100644 --- a/ICSharpCode.NRefactory/Utils/FastSerializer.cs +++ b/ICSharpCode.NRefactory/Utils/FastSerializer.cs @@ -35,10 +35,19 @@ namespace ICSharpCode.NRefactory.Utils /// full assembly and type names. /// public SerializationBinder SerializationBinder { get; set; } + + /// + /// Can be used to set several 'fixed' instances. + /// When serializing, such instances will not be included; and any references to a fixed instance + /// will be stored as the index in this array. + /// When deserializing, the same (or equivalent) instances must be specified, and the deserializer + /// will use them in place of the fixed instances. + /// + public object[] FixedInstances { get; set; } #endregion #region Constants - const int magic = 0x71D18A5D; + const int magic = 0x71D28A5D; const byte Type_ReferenceType = 1; const byte Type_ValueType = 2; @@ -79,6 +88,7 @@ namespace ICSharpCode.NRefactory.Utils readonly FastSerializer fastSerializer; public readonly BinaryWriter writer; + int fixedInstanceCount; internal SerializationContext(FastSerializer fastSerializer, BinaryWriter writer) { @@ -89,6 +99,19 @@ namespace ICSharpCode.NRefactory.Utils } #region Scanning + public void MarkFixedInstances(object[] fixedInstances) + { + if (fixedInstances == null) + return; + foreach (object obj in fixedInstances) { + if (!objectToID.ContainsKey(obj)) { + objectToID.Add(obj, instances.Count); + instances.Add(obj); + fixedInstanceCount++; + } + } + } + /// /// Marks an instance for future scanning. /// @@ -106,7 +129,8 @@ namespace ICSharpCode.NRefactory.Utils { Log("Scanning..."); // starting from 1, because index 0 is null - for (int i = 1; i < instances.Count; i++) { + // Also, do not scan any of the 'fixed instances'. + for (int i = 1 + fixedInstanceCount; i < instances.Count; i++) { object instance = instances[i]; ISerializable serializable = instance as ISerializable; Type type = instance.GetType(); @@ -224,6 +248,7 @@ namespace ICSharpCode.NRefactory.Utils writer.Write(instances.Count); writer.Write(types.Count); writer.Write(assemblyNames.Count); + writer.Write(fixedInstanceCount); foreach (string assemblyName in assemblyNames) { writer.Write(assemblyName); @@ -280,7 +305,7 @@ namespace ICSharpCode.NRefactory.Utils // Write out information necessary to create the instances // starting from 1, because index 0 is null - for (int i = 1; i < instances.Count; i++) { + for (int i = 1 + fixedInstanceCount; i < instances.Count; i++) { SerializationType sType = objectTypes[i]; if (types.Count <= ushort.MaxValue) writer.Write((ushort)sType.ID); @@ -296,7 +321,7 @@ namespace ICSharpCode.NRefactory.Utils } } // Write out information necessary to fill data into the instances - for (int i = 1; i < instances.Count; i++) { + for (int i = 1 + fixedInstanceCount; i < instances.Count; i++) { Log("0x{2:x6}, Write #{0}: {1}", i, objectTypes[i].Type.Name, writer.BaseStream.Position); objectTypes[i].Writer(this, instances[i]); } @@ -744,10 +769,12 @@ namespace ICSharpCode.NRefactory.Utils public void Serialize(BinaryWriter writer, object instance) { SerializationContext context = new SerializationContext(this, writer); + context.MarkFixedInstances(this.FixedInstances); context.Mark(instance); context.Scan(); context.ScanTypes(); context.Write(); + context.WriteObjectID(instance); } delegate void TypeSerializer(object instance, SerializationContext context); @@ -833,6 +860,15 @@ namespace ICSharpCode.NRefactory.Utils context.Objects = new object[reader.ReadInt32()]; context.Types = new Type[reader.ReadInt32()]; string[] assemblyNames = new string[reader.ReadInt32()]; + int fixedInstanceCount = reader.ReadInt32(); + + if (fixedInstanceCount != 0) { + if (this.FixedInstances == null || this.FixedInstances.Length != fixedInstanceCount) + throw new SerializationException("Number of fixed instances doesn't match"); + for (int i = 0; i < fixedInstanceCount; i++) { + context.Objects[i + 1] = this.FixedInstances[i]; + } + } for (int i = 0; i < assemblyNames.Length; i++) { assemblyNames[i] = reader.ReadString(); @@ -884,7 +920,7 @@ namespace ICSharpCode.NRefactory.Utils } context.DeserializeTypeDescriptions(this); int[] typeIDByObjectID = new int[context.Objects.Length]; - for (int i = 1; i < context.Objects.Length; i++) { + for (int i = 1 + fixedInstanceCount; i < context.Objects.Length; i++) { int typeID = context.ReadTypeID(); object instance; @@ -904,7 +940,7 @@ namespace ICSharpCode.NRefactory.Utils } List customDeserializatons = new List(); ObjectReader[] objectReaders = new ObjectReader[context.Types.Length]; // index: type ID - for (int i = 1; i < context.Objects.Length; i++) { + for (int i = 1 + fixedInstanceCount; i < context.Objects.Length; i++) { object instance = context.Objects[i]; int typeID = typeIDByObjectID[i]; Log("0x{2:x6} Read #{0}: {1}", i, context.Types[typeID].Name, reader.BaseStream.Position); @@ -933,16 +969,13 @@ namespace ICSharpCode.NRefactory.Utils foreach (CustomDeserialization customDeserializaton in customDeserializatons) { customDeserializaton.Run(streamingContext); } - for (int i = 1; i < context.Objects.Length; i++) { + for (int i = 1 + fixedInstanceCount; i < context.Objects.Length; i++) { IDeserializationCallback dc = context.Objects[i] as IDeserializationCallback; if (dc != null) dc.OnDeserialization(null); } - if (context.Objects.Length <= 1) - return null; - else - return context.Objects[1]; + return context.ReadObject(); } #region Object Reader