[C#] Сериализатор, более быстрый чем BinaryFormatter
От: Chardex Россия  
Дата: 03.08.06 15:52
Оценка: 21 (4)
Но не знаю как сделать поддержку циклов в графе объекта.
Для .NET 2.0.
Используется Dynamic Method.
Прирост в скорости при десериализации приблизительно в 5 раз, при сериализации в 2 раза.
Вот результаты теста:

Для 10000 объектов
BinaryFormatter serialize - 00:00:02.5504643, stream length = 25620000
BinaryFormatter desirialize - 00:00:02.7247557
CustomFormatter serialize - 00:00:00.9539868, stream length = 11730000
CustomFormatter desirialize - 00:00:00.5176641, 5,2635593485598
или для 50000 объектов
BinaryFormatter serialize - 00:00:13.6380023, stream length = 130750000
BinaryFormatter desirialize - 00:00:14.5414160
CustomFormatter serialize - 00:00:05.7686300, stream length = 63050000
CustomFormatter desirialize - 00:00:02.7774915, 5,23544923254071

Код теста

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace Chardex
{

    class Program
    {
        static void Main(string[] args)
        {
            for (int k = 0; k < 10; k++)
            {
                CustomFormatter f = new CustomFormatter();
                MemoryStream ms = new MemoryStream();
                BinaryFormatter f2 = new BinaryFormatter();
                MemoryStream ms2 = new MemoryStream();

                SuperClass sc = new SuperClass();

                int count = 10000;

                Stopwatch sw = new Stopwatch();
                sw.Start();
                for (int i = 0; i < count; i++)
                {
                    f2.Serialize(ms2, sc);
                }
                sw.Stop();

                Console.WriteLine("BinaryFormatter serialize - " + sw.Elapsed + ", stream length = " + ms2.Length);

                ms2.Position = 0;

                sw.Reset();
                sw.Start();
                for (int i = 0; i < count; i++)
                {
                    SuperClass sc2 = (SuperClass)f2.Deserialize(ms2);
                }
                sw.Stop();

                Console.WriteLine("BinaryFormatter desirialize - " + sw.Elapsed);
                double ticks = sw.ElapsedTicks;

                sw.Reset();
                sw.Start();
                for (int i = 0; i < count; i++)
                {
                    f.Serialize(ms, sc);
                }
                sw.Stop();

                Console.WriteLine("CustomFormatter serialize - " + sw.Elapsed + ", stream length = " + ms.Length);

                ms.Position = 0;

                sw.Reset();
                sw.Start();
                for (int i = 0; i < count; i++)
                {
                    SuperClass sc2 = (SuperClass)f.Deserialize(ms);
                }
                sw.Stop();

                Console.WriteLine("CustomFormatter desirialize - " + sw.Elapsed + ", " + ticks / (double)sw.ElapsedTicks);

                Console.WriteLine();
            }
        }
    }

    [Serializable]
    public class SuperClass
    {
        int intFld = 0;
        Test testObj = new Test();
        Dictionary<DateTime, Test> testDic = new Dictionary<DateTime, Test>();
        int[] testArr = new int[] { 0, 1, 2, 3, 4 };
        List<Guid> testList = new List<Guid>();

        public SuperClass()
        {
            testObj.t = new Test2();
            intFld = 100;
            testDic[DateTime.Now] = new Test();
            testDic[DateTime.Now] = new Test();
            testDic[DateTime.Now] = new Test();
            testDic[DateTime.Now] = new Test();
            testDic[DateTime.Now] = new Test();
            testList.Add(Guid.NewGuid());
            testList.Add(Guid.NewGuid());
            testList.Add(Guid.NewGuid());
        }
    }
    [Serializable]
    public class Test
    {
        private int val = 500;
        private int test = 25;
        private int test2 = 25;
        private int tes42 = 25;
        private string testStre = "lalalal";
        public Test2 t;
    }
    [Serializable]
    public class Test2
    {
        private int val = 500;
        private int test = 25;
        private int test2 = 25;
        private int tes42 = 25;
        private string testStre = "lalalal";
    }

}

Исходники в след. сообщении
Re: Исходники
От: Chardex Россия  
Дата: 03.08.06 15:54
Оценка:
CustomFormatter.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;

namespace Chardex
{
    public class CustomFormatter
    {
        private static Dictionary<string, Type> _typeCache = new Dictionary<string, Type>();
        private static Dictionary<int, TypeHelper> _processedTypes = new Dictionary<int, TypeHelper>();
        private static Dictionary<Type, Tuple<string, long>> _typeInfos = new Dictionary<Type, Tuple<string, long>>();

        public void Serialize(Stream ms, object graph)
        {
            Serialize(ms, graph, new List<object>(), new Dictionary<Type, int>());
        }

        private void Serialize(Stream ms, object obj, List<object> addedObjs, Dictionary<Type, int> processedTypes)
        {
            if (obj == null)
            {
                Write(ms, Codes.Null);
                return;
            }
            
            Type objType = obj.GetType();

            if (objType == typeof (Int32))
            {
                Write(ms, Codes.Int32);
                Write(ms, (Int32) obj);
                return;
            }
            if (objType == typeof (Int16))
            {
                Write(ms, Codes.Int16);
                Write(ms, (Int16) obj);
                return;
            }
            if (objType == typeof (Int64))
            {
                Write(ms, Codes.Int64);
                Write(ms, (Int64) obj);
                return;
            }
            if (objType == typeof (UInt32))
            {
                Write(ms, Codes.UInt32);
                Write(ms, (UInt32) obj);
                return;
            }
            if (objType == typeof (UInt16))
            {
                Write(ms, Codes.UInt16);
                Write(ms, (UInt16) obj);
                return;
            }
            if (objType == typeof (UInt64))
            {
                Write(ms, Codes.UInt64);
                Write(ms, (UInt64) obj);
                return;
            }
            if (objType == typeof (Boolean))
            {
                Write(ms, Codes.Boolean);
                Write(ms, (Boolean) obj);
                return;
            }
            if (objType == typeof (String))
            {
                Write(ms, Codes.String);
                Write(ms, (String) obj);
                return;
            }
            if (objType == typeof (Byte))
            {
                Write(ms, Codes.Byte);
                Write(ms, (Byte) obj);
                return;
            }
            if (objType == typeof(Double))
            {
                Write(ms, Codes.Double);
                Write(ms, (Double)obj);
                return;
            }
            if (objType == typeof(Single))
            {
                Write(ms, Codes.Single);
                Write(ms, (Single)obj);
                return;
            }
            if (objType == typeof(Decimal))
            {
                Write(ms, Codes.Decimal);
                Write(ms, (Decimal)obj);
                return;
            }
            if (!objType.IsValueType)
            {
                if (addedObjs.Contains(obj))
                    throw new ArgumentException("Loop detected", "obj");
                else
                    addedObjs.Add(obj);
            }

            if (objType.IsArray)
            {
                WriteArray(addedObjs, ms, obj, objType, processedTypes);
                return;
            }
            if (typeof (IDictionary).IsAssignableFrom(objType))
            {
                WriteDictionary(addedObjs, ms, obj, objType, processedTypes);
                return;
            }
            if (typeof (IList).IsAssignableFrom(objType))
            {
                WriteList(addedObjs, ms, obj, objType, processedTypes);
                return;
            }
            if (objType.IsPrimitive)
                throw new ArgumentException("Primitiv type which is not exepted", "obj");

            WriteCustomObject(addedObjs, ms, obj, objType, processedTypes);
        }


        public object Deserialize(Stream ms)
        {
            byte code = ReadByte(ms);

            if (code == Codes.Null)
                return null;
            if (code == Codes.Int32)
                return ReadInt32(ms);
            if (code == Codes.Int16)
                return ReadInt16(ms);
            if (code == Codes.Int64)
                return ReadInt64(ms);
            if (code == Codes.String)
                return ReadString(ms);
            if (code == Codes.Byte)
                return ReadByte(ms);
            if (code == Codes.Boolean)
                return ReadBoolean(ms);
            if (code == Codes.Array)
                return ReadArray(ms);
            if (code == Codes.List)
                return ReadList(ms);
            if (code == Codes.Dictionaty)
                return ReadDictionary(ms);
            if (code == Codes.CustomObject)
                return ReadCustomObject(ms);
            if (code == Codes.UInt32)
                return ReadUInt32(ms);
            if (code == Codes.UInt16)
                return ReadUInt16(ms);
            if (code == Codes.UInt64)
                return ReadUInt64(ms);
            if (code == Codes.Decimal)
                return ReadDecimal(ms);
            if (code == Codes.Double)
                return ReadDouble(ms);
            if (code == Codes.Single)
                return ReadSingle(ms);

            throw new ArgumentException("Unknown  code " + code);
        }

        #region Read methods

        private object ReadCustomObject(Stream ms)
        {
            TypeHelper typeHelper = ReadType(ms);
            long pos = ms.Position;
            int length = ReadInt32(ms);
            if (typeHelper == null)
            {
                ms.Position = pos + length;
                return null;
            }

            long endPos = ms.Position + length - sizeof (int);
            object obj = typeHelper.CreateInstance();

            if (typeHelper.HashChanged)
            {
                try
                {
                    List<Tuple<int, SetterDelegate>> setters = typeHelper.Setters;
                    int index = 0;
                    while (ms.Position != endPos)
                    {
                        int fieldKey = ReadInt32(ms);
                        object field = Deserialize(ms);
                        if (setters[index].ItemA == fieldKey)
                            setters[index].ItemB(obj, field);
                        else
                        {
                            for (int i = 0; i < setters.Count; i++)
                            {
                                if (setters[i].ItemA == fieldKey)
                                {
                                    setters[i].ItemB(obj, field);
                                    break;
                                }
                            }
                        }

                        index++;
                    }
                }
                catch
                {
                    ms.Position = pos + length;
                }
            }
            else
            {
                List<Tuple<int, SetterDelegate>> setters = typeHelper.Setters;
                int index = 0;

                while (ms.Position != endPos)
                {
                    ReadInt32(ms);
                    object field = Deserialize(ms);
                    setters[index].ItemB(obj, field);
                    index++;
                }
            }
            return obj;
        }

        private object ReadArray(Stream ms)
        {
            TypeHelper typeHelper = ReadType(ms);
            long pos = ms.Position;
            int msLength = ReadInt32(ms);
            if (typeHelper == null || typeHelper.Type == null)
            {
                ms.Position = pos + msLength;
                return null;
            }
            int length = ReadInt32(ms);
            Array arr = Array.CreateInstance(typeHelper.Type, length);
            for (int i = 0; i < length; i++)
            {
                arr.SetValue(Deserialize(ms), i);
            }
            return arr;
        }

        private object ReadDictionary(Stream ms)
        {
            TypeHelper typeHelper = ReadType(ms);
            long pos = ms.Position;
            int msLength = ReadInt32(ms);
            if (typeHelper == null)
            {
                ms.Position = pos + msLength;
                return null;
            }
            int length = ReadInt32(ms);
            IDictionary dic = (IDictionary) typeHelper.CreateInstance();
            for (int i = 0; i < length; i++)
            {
                object key = Deserialize(ms);
                object val = Deserialize(ms);
                dic.Add(key, val);
            }
            return dic;
        }

        private object ReadList(Stream ms)
        {
            TypeHelper typeHelper = ReadType(ms);
            long pos = ms.Position;
            int msLength = ReadInt32(ms);
            if (typeHelper == null)
            {
                ms.Position = pos + msLength;
                return null;
            }
            int length = ReadInt32(ms);
            IList collection = (IList) typeHelper.CreateInstance();
            for (int i = 0; i < length; i++)
            {
                collection.Add(Deserialize(ms));
            }
            return collection;
        }

        private TypeHelper ReadType(Stream ms)
        {
            int typeCode = ReadInt32(ms);
            TypeHelper typeHelper;

            if (typeCode == 0)
            {
                string typeStr = ReadString(ms);
                long key = ReadInt64(ms);
                if (!_processedTypes.TryGetValue(typeCode, out typeHelper))
                {
                    Type type;
                    if (!_typeCache.TryGetValue(typeStr, out type))
                    {
                        type = Type.GetType(typeStr);
                        _typeCache[typeStr] = type;
                    }

                    if (type != null)
                    {
                        typeHelper = TypeHelper.Create(type);
                        typeCode = type.FullName.GetHashCode();
                    }
                    _processedTypes[typeCode] = typeHelper;
                }
                typeHelper.LoadedKey = key;
            }
            else
            {
                _processedTypes.TryGetValue(typeCode, out typeHelper);
            }

            return typeHelper;
        }


        private byte ReadByte(Stream ms)
        {
            byte[] b = new byte[sizeof (byte)];
            ms.Read(b, 0, sizeof (byte));
            return b[0];
        }

        private Boolean ReadBoolean(Stream ms)
        {
            byte[] b = new byte[sizeof (Boolean)];
            ms.Read(b, 0, sizeof (Boolean));
            return BitConverter.ToBoolean(b, 0);
        }

        private UInt32 ReadUInt32(Stream ms)
        {
            byte[] b = new byte[sizeof (UInt32)];
            ms.Read(b, 0, sizeof (UInt32));
            return BitConverter.ToUInt32(b, 0);
        }

        private UInt16 ReadUInt16(Stream ms)
        {
            byte[] b = new byte[sizeof (UInt16)];
            ms.Read(b, 0, sizeof (UInt16));
            return BitConverter.ToUInt16(b, 0);
        }

        private UInt64 ReadUInt64(Stream ms)
        {
            byte[] b = new byte[sizeof (UInt64)];
            ms.Read(b, 0, sizeof (UInt64));
            return BitConverter.ToUInt64(b, 0);
        }

        private Int32 ReadInt32(Stream ms)
        {
            byte[] b = new byte[sizeof (Int32)];
            ms.Read(b, 0, sizeof (Int32));
            return BitConverter.ToInt32(b, 0);
        }

        private Int16 ReadInt16(Stream ms)
        {
            byte[] b = new byte[sizeof (Int16)];
            ms.Read(b, 0, sizeof (Int16));
            return BitConverter.ToInt16(b, 0);
        }

        private Int64 ReadInt64(Stream ms)
        {
            byte[] b = new byte[sizeof (Int64)];
            ms.Read(b, 0, sizeof (Int64));
            return BitConverter.ToInt64(b, 0);
        }
        private Double ReadDouble(Stream ms)
        {
            byte[] b = new byte[sizeof(Double)];
            ms.Read(b, 0, sizeof(Double));
            return BitConverter.ToDouble(b, 0);
        }

        private Single ReadSingle(Stream ms)
        {
            byte[] b = new byte[sizeof(Single)];
            ms.Read(b, 0, sizeof(Single));
            return BitConverter.ToSingle(b, 0);
        }

        private Decimal ReadDecimal(Stream ms)
        {
            int l = ReadInt32(ms);
            int [] bits = new int[l];
            for (int i = 0; i < l; i++)
            {
                bits[i] = ReadInt32(ms);
            }
            return new decimal(bits);
        }

        private string ReadString(Stream ms)
        {
            int l = ReadInt32(ms);
            if (l == -1)
                return null;
            byte[] b = new byte[l];
            ms.Read(b, 0, b.Length);
            return Encoding.Default.GetString(b);
        }

        #endregion

        #region Write methods

        private void WriteCustomObject(List<object> addedObjs, Stream ms, object obj, Type objType,
                                       Dictionary<Type, int> processedTypes)
        {
            Write(ms, Codes.CustomObject);
            WriteType(ms, objType, processedTypes);
            long pos = ms.Position;
            Write(ms, 0);
            TypeHelper helper = TypeHelper.Create(objType);
            foreach (
                FieldInfo field in
                    objType.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic))
            {
                Write(ms, field.Name.GetHashCode());
                Serialize(ms, helper.GetField(obj, field.Name), addedObjs, processedTypes);
            }
            long end = ms.Position;
            ms.Position = pos;
            Write(ms, (int) (end - pos));
            ms.Position = end;
        }

        private void WriteArray(List<object> addedObjs, Stream ms, object obj, Type objType,
                                Dictionary<Type, int> processedTypes)
        {
            Write(ms, Codes.Array);
            WriteType(ms, objType.GetElementType(), processedTypes);
            long msPos = ms.Position;
            Write(ms, 0);
            Array arr = (Array) obj;
            Write(ms, arr.Length);
            for (int i = 0; i < arr.Length; i++)
            {
                Serialize(ms, arr.GetValue(i), addedObjs, processedTypes);
            }
            long msEnd = ms.Position;
            ms.Position = msPos;
            Write(ms, (int) (msEnd - msPos));
            ms.Position = msEnd;
        }

        private void WriteDictionary(List<object> addedObjs, Stream ms, object obj, Type objType,
                                     Dictionary<Type, int> processedTypes)
        {
            Write(ms, Codes.Dictionaty);
            WriteType(ms, objType, processedTypes);
            long msPos = ms.Position;
            Write(ms, 0);
            Write(ms, ((IDictionary) obj).Count);
            foreach (DictionaryEntry entry in (IDictionary) obj)
            {
                Serialize(ms, entry.Key, addedObjs, processedTypes);
                Serialize(ms, entry.Value, addedObjs, processedTypes);
            }
            long msEnd = ms.Position;
            ms.Position = msPos;
            Write(ms, (int) (msEnd - msPos));
            ms.Position = msEnd;
        }

        private void WriteList(List<object> addedObjs, Stream ms, object obj, Type objType,
                               Dictionary<Type, int> processedTypes)
        {
            Write(ms, Codes.List);
            WriteType(ms, objType, processedTypes);
            long msPos = ms.Position;
            Write(ms, 0);
            Write(ms, ((IList) obj).Count);
            IList list = (IList) obj;
            for (int i = list.Count - 1; i >= 0; i--)
            {
                Serialize(ms, list[i], addedObjs, processedTypes);
            }

            long msEnd = ms.Position;
            ms.Position = msPos;
            Write(ms, (int) (msEnd - msPos));
            ms.Position = msEnd;
        }

        private void WriteType(Stream ms, Type type, Dictionary<Type, int> processedTypes)
        {
            int typeKey;
            if (!processedTypes.TryGetValue(type, out typeKey))
            {
                typeKey = type.FullName.GetHashCode();
                processedTypes[type] = typeKey;
                Write(ms, 0);
                Tuple<string, long> info;
                if (!_typeInfos.TryGetValue(type, out info))
                {
                    string typeName = type.FullName + ", " + type.Assembly.GetName().Name;
                    DateTime time = File.GetCreationTime(type.Assembly.Location);
                    info = new Tuple<string, long>(typeName, time.Ticks);
                    _typeInfos[type] = info;
                }

                Write(ms, info.ItemA);
                Write(ms, info.ItemB);
            }
            else
            {
                Write(ms, typeKey);
            }
        }

        private void Write(Stream ms, byte b)
        {
            ms.WriteByte(b);
        }

        private void Write(Stream ms, Int32 i)
        {
            ms.Write(BitConverter.GetBytes(i), 0, sizeof (Int32));
        }

        private void Write(Stream ms, Int16 i)
        {
            ms.Write(BitConverter.GetBytes(i), 0, sizeof (Int16));
        }

        private void Write(Stream ms, Int64 i)
        {
            ms.Write(BitConverter.GetBytes(i), 0, sizeof (Int64));
        }

        private void Write(Stream ms, Boolean b)
        {
            ms.Write(BitConverter.GetBytes(b), 0, sizeof (Boolean));
        }

        private void Write(Stream ms, UInt32 i)
        {
            ms.Write(BitConverter.GetBytes(i), 0, sizeof (UInt32));
        }

        private void Write(Stream ms, UInt16 i)
        {
            ms.Write(BitConverter.GetBytes(i), 0, sizeof (UInt16));
        }

        private void Write(Stream ms, UInt64 i)
        {
            ms.Write(BitConverter.GetBytes(i), 0, sizeof (UInt64));
        }
        private void Write(Stream ms, Double d)
        {
            ms.Write(BitConverter.GetBytes(d), 0, sizeof(Double));
        }

        private void Write(Stream ms, Single s)
        {
            ms.Write(BitConverter.GetBytes(s), 0, sizeof(Single));
        }

        private void Write(Stream ms, Decimal d)
        {
            int[] bits = Decimal.GetBits(d);
            Write(ms, bits.Length);
            for (int i = 0; i < bits.Length; i++)
                Write(ms, bits[i]);
        }

        private void Write(Stream ms, string s)
        {
            if (s == null)
            {
                ms.Write(BitConverter.GetBytes(-1), 0, sizeof (Int32));
                return;
            }
            byte[] bytes = Encoding.Default.GetBytes(s);
            ms.Write(BitConverter.GetBytes(bytes.Length), 0, sizeof (Int32));
            ms.Write(bytes, 0, bytes.Length);
        }

        #endregion

        #region Codes

        public static class Codes
        {
            public const byte Null = 0;
            public const byte Int32 = 1;
            public const byte Int16 = 2;
            public const byte Int64 = 3;
            public const byte String = 4;
            public const byte CustomObject = 5;
            public const byte Array = 6;
            public const byte Dictionaty = 7;
            public const byte List = 8;
            public const byte UInt32 = 9;
            public const byte UInt16 = 10;
            public const byte UInt64 = 11;
            public const byte Byte = 12;
            public const byte Boolean = 13;
            public const byte Double = 13;
            public const byte Single = 13;
            public const byte Decimal = 13;
        }

        #endregion
    }
}


TypeHelper.cs

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

namespace Chardex
{
    public delegate object ConstructorDelegate();
    public delegate void SetterDelegate(object obj, object val);
    public delegate object GetterDelegate(object obj);
    
    public class TypeHelper
    {
        static Dictionary<Type, TypeHelper> _helpers = new Dictionary<Type, TypeHelper>();
        private Type _type;
        private ConstructorDelegate _constructor;
        private Dictionary<string, Tuple<GetterDelegate, SetterDelegate>> _fieldAccessors = new Dictionary<string, Tuple<GetterDelegate, SetterDelegate>>();
        private List<Tuple<int, SetterDelegate>> _setters = new List<Tuple<int, SetterDelegate>>();
        private long _key;
        private long _loadedKey;
        private TypeHelper()
        {
            
        }

        public Type Type
        {
            get { return _type; }
        }
        public bool HashChanged
        {
            get { return _loadedKey != _key; }
        }


        public static TypeHelper Create(Type type)
        {
            TypeHelper helper;
            if (_helpers.TryGetValue(type, out helper))
                return helper;
            helper = new TypeHelper();
            helper._type = type;
            ConstructorInfo ci = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, new CallingConventions(), Type.EmptyTypes, null);

            DynamicMethod dmethod = new DynamicMethod("", typeof(object), new Type[] { }, type);
            ILGenerator igen = dmethod.GetILGenerator();
            if (!type.IsValueType)
            {
                igen.Emit(OpCodes.Newobj, ci);
                igen.Emit(OpCodes.Castclass, type);
                igen.Emit(OpCodes.Ret);
            }
            else
            {
                LocalBuilder lb = igen.DeclareLocal(type);
                igen.Emit(OpCodes.Ldloca_S, lb);
                igen.Emit(OpCodes.Initobj, type);
                igen.Emit(OpCodes.Ldloc_0);
                igen.Emit(OpCodes.Box, type);
                igen.Emit(OpCodes.Ret);
            }
            
            helper._constructor = (ConstructorDelegate)dmethod.CreateDelegate(typeof(ConstructorDelegate));

            _helpers[type] = helper;
            helper._key = System.IO.File.GetCreationTime(type.Assembly.Location).Ticks;
            return helper;
        }
        public Tuple<GetterDelegate, SetterDelegate> GetFieldAccessors(string field)
        {
            Tuple<GetterDelegate, SetterDelegate> accessors;
            if (_fieldAccessors.TryGetValue(field, out accessors))
                return accessors;
            
            FieldInfo fi = _type.GetField(field, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            if (fi == null) 
                throw new ArgumentException("Field " + field + " not founded", "field");

            DynamicMethod setterMethod = new DynamicMethod("", typeof(void), new Type[] { typeof(object), typeof(object) }, _type);
            ILGenerator setterGen = setterMethod.GetILGenerator();

            setterGen.Emit(OpCodes.Ldarg_0);
            setterGen.Emit(OpCodes.Castclass, _type);
            setterGen.Emit(OpCodes.Ldarg_1);
            setterGen.Emit(OpCodes.Unbox_Any, fi.FieldType);
            setterGen.Emit(OpCodes.Stfld, fi);
            setterGen.Emit(OpCodes.Ret);

            SetterDelegate setter = (SetterDelegate)setterMethod.CreateDelegate(typeof(SetterDelegate));

            DynamicMethod getterMethod = new DynamicMethod("", typeof(object), new Type[] { typeof(object) }, _type);
            ILGenerator getterGen = getterMethod.GetILGenerator();

            getterGen.Emit(OpCodes.Ldarg_0);
            getterGen.Emit(OpCodes.Castclass, _type);
            getterGen.Emit(OpCodes.Ldfld, fi);
            if (fi.FieldType.IsValueType)
                getterGen.Emit(OpCodes.Box, fi.FieldType);
            getterGen.Emit(OpCodes.Ret);

            GetterDelegate getter = (GetterDelegate)getterMethod.CreateDelegate(typeof(GetterDelegate));
            
            
            accessors = new Tuple<GetterDelegate, SetterDelegate>(getter, setter);
            _setters.Add(new Tuple<int, SetterDelegate>(field.GetHashCode(), setter));
            _fieldAccessors[field] = accessors;
            return accessors;
        }
        public object GetField(object obj, string name)
        {
            return GetFieldAccessors(name).ItemA(obj);
        }
        public void SetField(string name, object obj, object val)
        {
            GetFieldAccessors(name).ItemB(obj, val);
        }
        public object CreateInstance()
        {
            return _constructor();
        }

        public List<Tuple<int, SetterDelegate>> Setters
        {
            get { return _setters; }
        }

        public long Key
        {
            get { return _key; }
        }

        public long LoadedKey
        {
            set { _loadedKey = value; }
        }
    }
}
Re: [C#] Сериализатор, более быстрый чем BinaryFormatter
От: Oyster Украина https://github.com/devoyster
Дата: 04.08.06 06:37
Оценка:
Здравствуйте, Chardex, Вы писали:

C>Но не знаю как сделать поддержку циклов в графе объекта.


Так же, как это сделано в стандартном сериализаторе — при сериализации завести словарь, где ключ — объект, а значение — некий идентификатор. Если при сериализации встречается объект, который уже есть в словаре, то вместо его состояния записать вот этот вот идентификатор. При чтении то же самое, но наоборот.
Re[2]: [C#] Сериализатор, более быстрый чем BinaryFormatter
От: Chardex Россия  
Дата: 04.08.06 09:05
Оценка:
Здравствуйте, Oyster, Вы писали:

O>Так же, как это сделано в стандартном сериализаторе — при сериализации завести словарь, где ключ — объект, а значение — некий идентификатор. Если при сериализации встречается объект, который уже есть в словаре, то вместо его состояния записать вот этот вот идентификатор. При чтении то же самое, но наоборот.

Я так думал, но мне кажется что это скажется на производительность, хотя попробую.
Re[3]: [C#] Сериализатор, более быстрый чем BinaryFormatter
От: Oyster Украина https://github.com/devoyster
Дата: 04.08.06 11:32
Оценка: +1
Здравствуйте, Chardex, Вы писали:

C>Я так думал, но мне кажется что это скажется на производительность,


Имхо возможность сериализовать граф с циклическими ссылками настолько важна, что без неё твоя библиотека не особо полезна.

C>хотя попробую.
Re[2]: Исходники, с поддержкой циклов
От: Chardex Россия  
Дата: 05.08.06 16:12
Оценка:
Если кому интересно — могу добавить комментариев
CustomFormatter.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

namespace Chardex
{
    public class CustomFormatter
    {
        private static Dictionary<string, Type> _typeCache = new Dictionary<string, Type>();
        private static Dictionary<int, TypeHelper> _processedTypes = new Dictionary<int, TypeHelper>();
        private static Dictionary<Type, Tuple<string, long>> _typeInfos = new Dictionary<Type, Tuple<string, long>>();

        public void Serialize(Stream stream, object graph)
        {
            Serialize(graph, new SerializationContext(stream));
        }

        private void Serialize(object obj, SerializationContext context)
        {
            if (obj == null)
            {
                context.Write(Codes.Null);
                return;
            }

            Type objType = obj.GetType();

            if (objType == typeof(Int32))
            {
                context.Write(Codes.Int32);
                context.Write((Int32)obj);
                return;
            }
            if (objType == typeof(Int16))
            {
                context.Write(Codes.Int16);
                context.Write((Int16)obj);
                return;
            }
            if (objType == typeof(Int64))
            {
                context.Write(Codes.Int64);
                context.Write((Int64)obj);
                return;
            }
            if (objType == typeof(UInt32))
            {
                context.Write(Codes.UInt32);
                context.Write((UInt32)obj);
                return;
            }
            if (objType == typeof(UInt16))
            {
                context.Write(Codes.UInt16);
                context.Write((UInt16)obj);
                return;
            }
            if (objType == typeof(UInt64))
            {
                context.Write(Codes.UInt64);
                context.Write((UInt64)obj);
                return;
            }
            if (objType == typeof(Boolean))
            {
                context.Write(Codes.Boolean);
                context.Write((Boolean)obj);
                return;
            }
            if (objType == typeof(String))
            {
                context.Write(Codes.String);
                context.Write((String)obj);
                return;
            }
            if (objType == typeof(Byte))
            {
                context.Write(Codes.Byte);
                context.Write((Byte)obj);
                return;
            }
            if (objType == typeof(Double))
            {
                context.Write(Codes.Double);
                context.Write((Double)obj);
                return;
            }
            if (objType == typeof(Single))
            {
                context.Write(Codes.Single);
                context.Write((Single)obj);
                return;
            }
            if (objType == typeof(Decimal))
            {
                context.Write(Codes.Decimal);
                context.Write((Decimal)obj);
                return;
            }
            if (objType.IsPrimitive)
                throw new ArgumentException("Primitiv type which is not exepted", "obj");

            if (context.AddedObjects.Contains(obj))
            {
                context.Write(Codes.Reference);
                context.Write(context.AddedObjects.IndexOf(obj));
                return;
            }
            context.AddedObjects.Add(obj);

            if (objType.IsArray)
            {
                WriteArray(obj, objType, context);
                return;
            }
            if (typeof(IDictionary).IsAssignableFrom(objType))
            {
                WriteDictionary(obj, objType, context);
                return;
            }
            if (typeof(IList).IsAssignableFrom(objType))
            {
                WriteList(obj, objType, context);
                return;
            }

            WriteCustomObject(obj, objType, context);
        }

        private object Deserialize(DeserializationContext context)
        {
            byte code = context.ReadByte();

            if (code == Codes.Null)
                return null;
            if (code == Codes.Int32)
                return context.ReadInt32();
            if (code == Codes.Int16)
                return context.ReadInt16();
            if (code == Codes.Int64)
                return context.ReadInt64();
            if (code == Codes.String)
                return context.ReadString();
            if (code == Codes.Byte)
                return context.ReadByte();
            if (code == Codes.Boolean)
                return context.ReadBoolean();
            if (code == Codes.Array)
                return ReadArray(context);
            if (code == Codes.List)
                return ReadList(context);
            if (code == Codes.Dictionaty)
                return ReadDictionary(context);
            if (code == Codes.CustomObject)
                return ReadCustomObject(context);
            if (code == Codes.UInt32)
                return context.ReadUInt32();
            if (code == Codes.UInt16)
                return context.ReadUInt16();
            if (code == Codes.UInt64)
                return context.ReadUInt64();
            if (code == Codes.Decimal)
                return context.ReadDecimal();
            if (code == Codes.Double)
                return context.ReadDouble();
            if (code == Codes.Single)
                return context.ReadSingle();
            if (code == Codes.Reference)
                return context.AddedObjects[context.ReadInt32()];

            throw new ArgumentException("Unknown  code " + code);
        }
        public object Deserialize(Stream stream)
        {
            return Deserialize(new DeserializationContext(stream));
        }

        #region Read methods

        private object ReadCustomObject(DeserializationContext context)
        {
            TypeHelper typeHelper = ReadType(context);
            long pos = context.Stream.Position;
            int length = context.ReadInt32();
            if (typeHelper == null)
            {
                context.Stream.Position = pos + length;
                return null;
            }

            long endPos = context.Stream.Position + length - sizeof(int);
            object obj = typeHelper.CreateInstance();
            context.AddedObjects.Add(obj);
            
            if (typeHelper.HashChanged)
            {
                try
                {
                    if (typeHelper.IsValueType)
                    {
                        List<Tuple<int, ValueTypeSetterDelegate>> valueTypeSetters = typeHelper.ValueTypeSetters;
                        int index = 0;
                        while (context.Stream.Position != endPos)
                        {
                            int fieldKey = context.ReadInt32();
                            object field = Deserialize(context);
                            if (valueTypeSetters[index].ItemA == fieldKey)
                                obj = valueTypeSetters[index].ItemB(obj, field);
                            else
                            {
                                for (int i = 0; i < valueTypeSetters.Count; i++)
                                {
                                    if (valueTypeSetters[i].ItemA == fieldKey)
                                    {
                                        obj = valueTypeSetters[i].ItemB(obj, field);
                                        break;
                                    }
                                }
                            }
                            index++;
                        }
                    }
                    else
                    {
                        List<Tuple<int, ReferenceTypeSetterDelegate>> referenceTypeSetters = typeHelper.ReferenceTypeSetters;
                        int index = 0;
                        while (context.Stream.Position != endPos)
                        {
                            int fieldKey = context.ReadInt32();
                            object field = Deserialize(context);
                            if (referenceTypeSetters[index].ItemA == fieldKey)
                                referenceTypeSetters[index].ItemB(obj, field);
                            else
                            {
                                for (int i = 0; i < referenceTypeSetters.Count; i++)
                                {
                                    if (referenceTypeSetters[i].ItemA == fieldKey)
                                    {
                                        referenceTypeSetters[i].ItemB(obj, field);
                                        break;
                                    }
                                }
                            }

                            index++;
                        }
                    }
                }
                catch
                {
                    context.Stream.Position = pos + length;
                }
            }
            else
            {
                if (typeHelper.IsValueType)
                {
                    List<Tuple<int, ValueTypeSetterDelegate>> setters = typeHelper.ValueTypeSetters;
                    int index = 0;

                    while (context.Stream.Position != endPos)
                    {
                        context.ReadInt32();
                        object field = Deserialize(context);
                        obj = setters[index].ItemB(obj, field);
                        index++;
                    }
                }
                else
                {
                    List<Tuple<int, ReferenceTypeSetterDelegate>> setters = typeHelper.ReferenceTypeSetters;
                    int index = 0;

                    while (context.Stream.Position != endPos)
                    {
                        context.ReadInt32();
                        object field = Deserialize(context);
                        setters[index].ItemB(obj, field);
                        index++;
                    }
                }
            }
            return obj;
        }

        private object ReadArray(DeserializationContext context)
        {
            TypeHelper typeHelper = ReadType(context);
            long pos = context.Stream.Position;
            int msLength = context.ReadInt32();
            if (typeHelper == null || typeHelper.Type == null)
            {
                context.Stream.Position = pos + msLength;
                return null;
            }
            int length = context.ReadInt32();
            Array arr = Array.CreateInstance(typeHelper.Type, length);
            context.AddedObjects.Add(arr);

            for (int i = 0; i < length; i++)
            {
                arr.SetValue(Deserialize(context), i);
            }
            return arr;
        }

        private object ReadDictionary(DeserializationContext context)
        {
            TypeHelper typeHelper = ReadType(context);
            long pos = context.Stream.Position;
            int msLength = context.ReadInt32();
            if (typeHelper == null)
            {
                context.Stream.Position = pos + msLength;
                return null;
            }
            int length = context.ReadInt32();
            IDictionary dic = (IDictionary)typeHelper.CreateInstance();
            context.AddedObjects.Add(dic);
            
            for (int i = 0; i < length; i++)
            {
                object key = Deserialize(context);
                object val = Deserialize(context);
                dic.Add(key, val);
            }
            return dic;
        }

        private object ReadList(DeserializationContext context)
        {
            TypeHelper typeHelper = ReadType(context);
            long pos = context.Stream.Position;
            int msLength = context.ReadInt32();
            if (typeHelper == null)
            {
                context.Stream.Position = pos + msLength;
                return null;
            }
            int length = context.ReadInt32();
            IList collection = (IList)typeHelper.CreateInstance();
            context.AddedObjects.Add(collection);
            
            for (int i = 0; i < length; i++)
            {
                collection.Add(Deserialize(context));
            }
            return collection;
        }

        private TypeHelper ReadType(DeserializationContext context)
        {
            int typeCode = context.ReadInt32();
            TypeHelper typeHelper;

            if (typeCode == 0)
            {
                string typeStr = context.ReadString();
                long key = context.ReadInt64();
                if (!_processedTypes.TryGetValue(typeCode, out typeHelper))
                {
                    Type type;
                    if (!_typeCache.TryGetValue(typeStr, out type))
                    {
                        type = Type.GetType(typeStr);
                        _typeCache[typeStr] = type;
                    }

                    if (type != null)
                    {
                        typeHelper = TypeHelper.Create(type);
                        typeCode = type.FullName.GetHashCode();
                    }
                    _processedTypes[typeCode] = typeHelper;
                }
                typeHelper.LoadedKey = key;
            }
            else
            {
                _processedTypes.TryGetValue(typeCode, out typeHelper);
            }

            return typeHelper;
        }


    

        #endregion

        #region Write methods

        private void WriteCustomObject(object obj, Type objType, SerializationContext context)
        {
            context.Write(Codes.CustomObject);
            WriteType(objType, context);
            long pos = context.Stream.Position;
            context.Write(0);
            TypeHelper helper = TypeHelper.Create(objType);

            foreach (FieldInfo field in
                    objType.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic))
            {
                context.Write(field.Name.GetHashCode());
                Serialize(helper.GetField(obj, field.Name), context);
            }

            long end = context.Stream.Position;
            context.Stream.Position = pos;
            context.Write((int)(end - pos));
            context.Stream.Position = end;
        }

        private void WriteArray(object obj, Type objType, SerializationContext context)
        {
            context.Write(Codes.Array);
            WriteType(objType.GetElementType(), context);

            long msPos = context.Stream.Position;
            context.Write(0);
            Array arr = (Array)obj;
            context.Write(arr.Length);

            for (int i = 0; i < arr.Length; i++)
            {
                Serialize(arr.GetValue(i), context);
            }

            long msEnd = context.Stream.Position;
            context.Stream.Position = msPos;
            context.Write((int)(msEnd - msPos));
            context.Stream.Position = msEnd;
        }

        private void WriteDictionary(object obj, Type objType, SerializationContext context)
        {
            context.Write(Codes.Dictionaty);
            WriteType(objType, context);
            long msPos = context.Stream.Position;
            context.Write(0);
            context.Write(((IDictionary)obj).Count);

            foreach (DictionaryEntry entry in (IDictionary)obj)
            {
                Serialize(entry.Key, context);
                Serialize(entry.Value, context);
            }

            long msEnd = context.Stream.Position;
            context.Stream.Position = msPos;
            context.Write((int)(msEnd - msPos));
            context.Stream.Position = msEnd;
        }

        private void WriteList(object obj, Type objType, SerializationContext context)
        {
            context.Write(Codes.List);
            WriteType(objType, context);
            long msPos = context.Stream.Position;
            context.Write(0);
            context.Write(((IList)obj).Count);
            IList list = (IList)obj;
            for (int i = list.Count - 1; i >= 0; i--)
            {
                Serialize(list[i], context);
            }

            long msEnd = context.Stream.Position;
            context.Stream.Position = msPos;
            context.Write((int)(msEnd - msPos));
            context.Stream.Position = msEnd;
        }

        private void WriteType(Type type, SerializationContext context)
        {
            int typeKey;
            if (!context.ProcessedTypes.TryGetValue(type, out typeKey))
            {
                typeKey = type.FullName.GetHashCode();
                context.ProcessedTypes[type] = typeKey;
                context.Write(0);
                Tuple<string, long> info;
                if (!_typeInfos.TryGetValue(type, out info))
                {
                    string typeName = type.FullName + ", " + type.Assembly.GetName().Name;
                    DateTime time = File.GetCreationTime(type.Assembly.Location);
                    info = new Tuple<string, long>(typeName, time.Ticks);
                    _typeInfos[type] = info;
                }

                context.Write(info.ItemA);
                context.Write(info.ItemB);
            }
            else
            {
                context.Write(typeKey);
            }
        }



        #endregion

        #region Codes

        public static class Codes
        {
            public const byte Null = 0;
            public const byte Int32 = 1;
            public const byte Int16 = 2;
            public const byte Int64 = 3;
            public const byte String = 4;
            public const byte CustomObject = 5;
            public const byte Array = 6;
            public const byte Dictionaty = 7;
            public const byte List = 8;
            public const byte UInt32 = 9;
            public const byte UInt16 = 10;
            public const byte UInt64 = 11;
            public const byte Byte = 12;
            public const byte Boolean = 13;
            public const byte Double = 14;
            public const byte Single = 15;
            public const byte Decimal = 16;
            public const byte Reference = 17;
        }

        #endregion
    }
}

DeserializationContext.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace Chardex
{
    class DeserializationContext
    {
        private Stream _stream;
        private List<object> _addedObjects = new List<object>();
        public DeserializationContext(Stream stream)
        {
            _stream = stream;
        }

        public List<object> AddedObjects
        {
            get { return _addedObjects; }
        }

        public Stream Stream
        {
            get { return _stream; }
        }

        public byte ReadByte()
        {
            byte[] b = new byte[sizeof(byte)];
            _stream.Read(b, 0, sizeof(byte));
            return b[0];
        }

        public Boolean ReadBoolean()
        {
            byte[] b = new byte[sizeof(Boolean)];
            _stream.Read(b, 0, sizeof(Boolean));
            return BitConverter.ToBoolean(b, 0);
        }

        public UInt32 ReadUInt32()
        {
            byte[] b = new byte[sizeof(UInt32)];
            _stream.Read(b, 0, sizeof(UInt32));
            return BitConverter.ToUInt32(b, 0);
        }

        public UInt16 ReadUInt16()
        {
            byte[] b = new byte[sizeof(UInt16)];
            _stream.Read(b, 0, sizeof(UInt16));
            return BitConverter.ToUInt16(b, 0);
        }

        public UInt64 ReadUInt64()
        {
            byte[] b = new byte[sizeof(UInt64)];
            _stream.Read(b, 0, sizeof(UInt64));
            return BitConverter.ToUInt64(b, 0);
        }

        public Int32 ReadInt32()
        {
            byte[] b = new byte[sizeof(Int32)];
            _stream.Read(b, 0, sizeof(Int32));
            return BitConverter.ToInt32(b, 0);
        }

        public Int16 ReadInt16()
        {
            byte[] b = new byte[sizeof(Int16)];
            _stream.Read(b, 0, sizeof(Int16));
            return BitConverter.ToInt16(b, 0);
        }

        public Int64 ReadInt64()
        {
            byte[] b = new byte[sizeof(Int64)];
            _stream.Read(b, 0, sizeof(Int64));
            return BitConverter.ToInt64(b, 0);
        }
        public Double ReadDouble()
        {
            byte[] b = new byte[sizeof(Double)];
            _stream.Read(b, 0, sizeof(Double));
            return BitConverter.ToDouble(b, 0);
        }

        public Single ReadSingle()
        {
            byte[] b = new byte[sizeof(Single)];
            _stream.Read(b, 0, sizeof(Single));
            return BitConverter.ToSingle(b, 0);
        }

        public Decimal ReadDecimal()
        {
            int l = ReadInt32();
            int[] bits = new int[l];
            for (int i = 0; i < l; i++)
            {
                bits[i] = ReadInt32();
            }
            return new decimal(bits);
        }

        public string ReadString()
        {
            int l = ReadInt32();
            if (l == -1)
                return null;
            byte[] b = new byte[l];
            _stream.Read(b, 0, b.Length);
            return Encoding.Default.GetString(b);
        }
    }

}


SerializationContext.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace Chardex
{
    class SerializationContext
    {
        private Stream _stream;
        private List<object> _addedObjects = new List<object>();
        private Dictionary<Type, int> _processedTypes = new Dictionary<Type, int>();
        public SerializationContext(Stream stream)
        {
            _stream = stream;
        }

        public Stream Stream
        {
            get { return _stream; }
        }

        public List<object> AddedObjects
        {
            get { return _addedObjects; }
        }

        public Dictionary<Type, int> ProcessedTypes
        {
            get { return _processedTypes; }
        }

        public void Write(byte b)
        {
            _stream.WriteByte(b);
        }

        public void Write(Int32 i)
        {
            _stream.Write(BitConverter.GetBytes(i), 0, sizeof(Int32));
        }

        public void Write(Int16 i)
        {
            _stream.Write(BitConverter.GetBytes(i), 0, sizeof(Int16));
        }

        public void Write(Int64 i)
        {
            _stream.Write(BitConverter.GetBytes(i), 0, sizeof(Int64));
        }

        public void Write(Boolean b)
        {
            _stream.Write(BitConverter.GetBytes(b), 0, sizeof(Boolean));
        }

        public void Write(UInt32 i)
        {
            _stream.Write(BitConverter.GetBytes(i), 0, sizeof(UInt32));
        }

        public void Write(UInt16 i)
        {
            _stream.Write(BitConverter.GetBytes(i), 0, sizeof(UInt16));
        }

        public void Write(UInt64 i)
        {
            _stream.Write(BitConverter.GetBytes(i), 0, sizeof(UInt64));
        }
        public void Write(Double d)
        {
            _stream.Write(BitConverter.GetBytes(d), 0, sizeof(Double));
        }

        public void Write(Single s)
        {
            _stream.Write(BitConverter.GetBytes(s), 0, sizeof(Single));
        }

        public void Write(Decimal d)
        {
            int[] bits = Decimal.GetBits(d);
            Write(bits.Length);
            for (int i = 0; i < bits.Length; i++)
                Write(bits[i]);
        }

        public void Write(string s)
        {
            if (s == null)
            {
                _stream.Write(BitConverter.GetBytes(-1), 0, sizeof(Int32));
                return;
            }
            byte[] bytes = Encoding.Default.GetBytes(s);
            _stream.Write(BitConverter.GetBytes(bytes.Length), 0, sizeof(Int32));
            _stream.Write(bytes, 0, bytes.Length);
        }
    }

}
TypeHelper.cs

[c#]
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

namespace Chardex
{
    public delegate object ConstructorDelegate();
    public delegate void ReferenceTypeSetterDelegate(object obj, object val);
    public delegate object ValueTypeSetterDelegate(object obj, object val);
    public delegate object GetterDelegate(object obj);
    
    public class TypeHelper
    {
        static Dictionary<Type, TypeHelper> _helpers = new Dictionary<Type, TypeHelper>();
        private Type _type;
        private ConstructorDelegate _constructor;
        private Dictionary<string, GetterDelegate> _getters = new Dictionary<string, GetterDelegate>();
        private List<Tuple<int, ReferenceTypeSetterDelegate>> _referenceTypeSetters = new List<Tuple<int, ReferenceTypeSetterDelegate>>();
        private List<Tuple<int, ValueTypeSetterDelegate>> _valueTypeSetters = new List<Tuple<int, ValueTypeSetterDelegate>>();
        
        private long _key;
        private long _loadedKey;
        private bool _isValueType;
        private TypeHelper()
        {
            
        }

        public Type Type
        {
            get { return _type; }
        }
        public bool HashChanged
        {
            get { return _loadedKey != _key; }
        }


        public static TypeHelper Create(Type type)
        {
            TypeHelper helper;
            if (_helpers.TryGetValue(type, out helper))
                return helper;
            helper = new TypeHelper();
            helper._type = type;

            DynamicMethod dmethod = new DynamicMethod("", typeof(object), new Type[] { }, type);
            ILGenerator igen = dmethod.GetILGenerator();
            if (!type.IsValueType)
            {
                ConstructorInfo ci = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, new CallingConventions(), Type.EmptyTypes, null);
                igen.Emit(OpCodes.Newobj, ci);
                igen.Emit(OpCodes.Castclass, type);
                igen.Emit(OpCodes.Ret);
            }
            else
            {
                LocalBuilder lb = igen.DeclareLocal(type);
                igen.Emit(OpCodes.Ldloca_S, lb);
                igen.Emit(OpCodes.Initobj, type);
                igen.Emit(OpCodes.Ldloc_0);
                igen.Emit(OpCodes.Box, type);
                igen.Emit(OpCodes.Ret);
            }
            foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic))
            {
                if (type.IsValueType)
                    helper.CreateValueTypeSetter(field);
                else 
                    helper.CreateReferenceSetter(field);
            }
            
            helper._constructor = (ConstructorDelegate)dmethod.CreateDelegate(typeof(ConstructorDelegate));
            helper._isValueType = type.IsValueType;
            
            _helpers[type] = helper;
            helper._key = System.IO.File.GetCreationTime(type.Assembly.Location).Ticks;
            return helper;
        }
        
        public void CreateReferenceSetter(FieldInfo fi)
        {
            DynamicMethod setterMethod = new DynamicMethod("", typeof(void), new Type[] { typeof(object), typeof(object) }, _type);
            ILGenerator setterGen = setterMethod.GetILGenerator();
            setterGen.Emit(OpCodes.Ldarg_0);
            setterGen.Emit(OpCodes.Castclass, _type);
            setterGen.Emit(OpCodes.Ldarg_1);
            setterGen.Emit(OpCodes.Unbox_Any, fi.FieldType);
            setterGen.Emit(OpCodes.Stfld, fi);
            setterGen.Emit(OpCodes.Ret);
            ReferenceTypeSetterDelegate referenceTypeSetter = (ReferenceTypeSetterDelegate)setterMethod.CreateDelegate(typeof(ReferenceTypeSetterDelegate));
            _referenceTypeSetters.Add(new Tuple<int, ReferenceTypeSetterDelegate>(fi.Name.GetHashCode(), referenceTypeSetter));
        }

        public void CreateValueTypeSetter(FieldInfo fi)
        {
            DynamicMethod setterMethod = new DynamicMethod("", typeof(object), new Type[] { typeof(object), typeof(object) }, _type);
            ILGenerator setterGen = setterMethod.GetILGenerator();
            LocalBuilder lb = setterGen.DeclareLocal(_type);
            setterGen.Emit(OpCodes.Ldarg_0);
            setterGen.Emit(OpCodes.Unbox_Any, _type);
            setterGen.Emit(OpCodes.Stloc_0);
            setterGen.Emit(OpCodes.Ldloca_S, lb);
            setterGen.Emit(OpCodes.Ldarg_1);
            setterGen.Emit(OpCodes.Unbox_Any, fi.FieldType);
            setterGen.Emit(OpCodes.Stfld, fi);
            setterGen.Emit(OpCodes.Ldloc_0);
            setterGen.Emit(OpCodes.Box, _type);
            setterGen.Emit(OpCodes.Ret);
            ValueTypeSetterDelegate valueSetter = (ValueTypeSetterDelegate)setterMethod.CreateDelegate(typeof(ValueTypeSetterDelegate));
            _valueTypeSetters.Add(new Tuple<int, ValueTypeSetterDelegate>(fi.Name.GetHashCode(), valueSetter));
        }
        public object GetField(object obj, string name)
        {
            GetterDelegate getter;
            if (!_getters.TryGetValue(name, out getter))
                getter = CreateGetter(name);
            return getter(obj);
        }

        private GetterDelegate CreateGetter(string name)
        {
            GetterDelegate getter;
            FieldInfo fi = _type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            if (fi == null)
                throw new ArgumentException("Field " + name + " not founded", "field");

                
            DynamicMethod getterMethod = new DynamicMethod("", typeof(object), new Type[] { typeof(object) }, _type);
            ILGenerator getterGen = getterMethod.GetILGenerator();

            if (!IsValueType)
            {
                getterGen.Emit(OpCodes.Ldarg_0);
                getterGen.Emit(OpCodes.Castclass, _type);
                getterGen.Emit(OpCodes.Ldfld, fi);
                if (fi.FieldType.IsValueType)
                    getterGen.Emit(OpCodes.Box, fi.FieldType);
                getterGen.Emit(OpCodes.Ret);
            }
            else
            {
                LocalBuilder lb = getterGen.DeclareLocal(_type);
                getterGen.Emit(OpCodes.Ldarg_0);
                getterGen.Emit(OpCodes.Unbox_Any, _type);
                getterGen.Emit(OpCodes.Stloc_0);
                getterGen.Emit(OpCodes.Ldloca_S, lb);
                getterGen.Emit(OpCodes.Ldfld, fi);
                getterGen.Emit(OpCodes.Box, fi.FieldType);
                getterGen.Emit(OpCodes.Ret);
            }

            getter = (GetterDelegate)getterMethod.CreateDelegate(typeof(GetterDelegate));
            _getters[name] = getter;
            return getter;
        }

        public object CreateInstance()
        {
            return _constructor();
        }

        public List<Tuple<int, ReferenceTypeSetterDelegate>> ReferenceTypeSetters
        {
            get { return _referenceTypeSetters; }
        }

        public long Key
        {
            get { return _key; }
        }

        public long LoadedKey
        {
            set { _loadedKey = value; }
        }

        public bool IsValueType
        {
            get { return _isValueType; }
        }

        public List<Tuple<int, ValueTypeSetterDelegate>> ValueTypeSetters
        {
            get { return _valueTypeSetters; }
        }
    }
}


[/c#]
Re[4]: [C#] Сериализатор, более быстрый чем BinaryFormatter
От: Chardex Россия  
Дата: 06.08.06 11:50
Оценка:
Что-то никому не интересно
А у меня уже выигрыш в 7,5 раз по сравнению со стандартным...
Re[3]: Исходники, с поддержкой циклов
От: Chardex Россия  
Дата: 06.08.06 14:34
Оценка:
Вот проект, вместе с тестом и исходниками. Поддерживает цыклы, в 7-8 раз выигрывает у BinaryFormatter'а по скорости.
Если найдете ошибки, или то, что десериализуется некорректно, сообщите пожалуйста
Re[5]: [C#] Сериализатор, более быстрый чем BinaryFormatter
От: Oyster Украина https://github.com/devoyster
Дата: 07.08.06 06:11
Оценка: +2
Здравствуйте, Chardex, Вы писали:

C>Что-то никому не интересно

C>А у меня уже выигрыш в 7,5 раз по сравнению со стандартным...

Вообще, конечно, интересно, но мне, например, хотелось бы увидеть полноценную реализацию IFormatter с поддержкой как минимум интерфейсов ISerializable и IDeserializationCallback на сериализуемых объектах, т.е. полную замену стандартному механизму сериализации. Да и про атрибут NonSerializedAttribute на полях забывать нельзя.

Иначе использовать такую библиотеку именно как замену стандартного BinaryFormatter в реальном приложении будет тяжело.
Re[6]: [C#] Сериализатор, более быстрый чем BinaryFormatter
От: Chardex Россия  
Дата: 07.08.06 07:26
Оценка:
Здравствуйте, Oyster, Вы писали:

O>Вообще, конечно, интересно, но мне, например, хотелось бы увидеть полноценную реализацию IFormatter с поддержкой как минимум интерфейсов ISerializable и IDeserializationCallback на сериализуемых объектах, т.е. полную замену стандартному механизму сериализации. Да и про атрибут NonSerializedAttribute на полях забывать нельзя.


Или я не понял роли класса System.Runtime.Serialization.SerializationInfo, или для работы моего форматтера мне надо его переопределить! А он sealed
Подскажите пожалуйста с чего начать. Что-то примеров найти не могу...
Re[7]: [C#] Сериализатор, более быстрый чем BinaryFormatter
От: Chardex Россия  
Дата: 07.08.06 07:41
Оценка:
Все, ссори, разобрался
Re[8]: [C#] Сериализатор, более быстрый чем BinaryFormatter
От: Oyster Украина https://github.com/devoyster
Дата: 07.08.06 08:04
Оценка:
Здравствуйте, Chardex, Вы писали:

C>Все, ссори, разобрался


Да — из него потом можно всё извлечь

Кстати, ещё нельзя забывать про такую полезную штуку, как version check, чтобы сериализация выдала вразумительный exception, если версия объекта поменялась (поля добавились/удалились/изменились), а не "проглотила" это изменение и неправильно заполнила поля.

У меня была идейка на эту тему — взять имена всех полей вместе с типами и сгенерить по этому делу CRC32, который для типа помещать в поток (можно один раз). Если CRC поменялся, то version check failed. Конечно, CRC32 не самый надёжный способ, зато весит мало (на то оно и CRC).
Re[9]: [C#] Сериализатор, более быстрый чем BinaryFormatter
От: Chardex Россия  
Дата: 07.08.06 09:03
Оценка: 10 (2)
Здравствуйте, Oyster, Вы писали:

O>Кстати, ещё нельзя забывать про такую полезную штуку, как version check, чтобы сериализация выдала вразумительный exception, если версия объекта поменялась (поля добавились/удалились/изменились), а не "проглотила" это изменение и неправильно заполнила поля.

У меня сейчас работает так: при сохранении объекта вместе с ним сохраняется некий ключ, по которому при восстановлении можно проверить был ли тип изменен (На данный момент сделал в лоб — этот ключ — дата создания сборки, где тип... ). Если тип не был измененен, сериализация идет без проверок и лишних затрат. Если тип был изменен — то поля которые были удалены/добавлены/изменен_тип просто не будут затронуты и не будет exception'ов. Хотя можно кидать их без проблем, хоть опционально Только у меня вместо имен полей, для повышения производительности, сохраняются их хеши, так что имя поля в исключении дать не смогу.
Вообщем при изменении версии типа объекта, вся неизменнная часть графа восстанавливается.
O>У меня была идейка на эту тему — взять имена всех полей вместе с типами и сгенерить по этому делу CRC32, который для типа помещать в поток (можно один раз). Если CRC поменялся, то version check failed. Конечно, CRC32 не самый надёжный способ, зато весит мало (на то оно и CRC).
Я конечно понимаю, что моя версия с датой сборки — неправильное решение, но кому и зачем надо будет ее менять Чем она плоха?)
Re[10]: [C#] Сериализатор, более быстрый чем BinaryFormatter
От: Oyster Украина https://github.com/devoyster
Дата: 07.08.06 10:22
Оценка:
Здравствуйте, Chardex, Вы писали:

C>Я конечно понимаю, что моя версия с датой сборки — неправильное решение, но кому и зачем надо будет ее менять Чем она плоха?)


Решение с датой сборки в общем-то неплохое (я просто не додумался до него), но оно не будет работать с динамическими сборками (с генерацией типов в рантайме через Reflection.Emit), т.к. для динамической сборки дата создания будет новая при каждом запуске приложения. Т.е., например, прокси-типы, создаваемые на лету в BLToolkit
Автор(ы): Игорь Ткачёв
Дата: 01.07.2003
В статье подробно рассматривается состав и способы применения пространства имён Rsdn.Framework.Data, представляющего собой высокоуровневую обёртку над ADO.NET.
(и в моём приложении, например), десериализовать не удастся при следующем запуске приложения...
Re[11]: [C#] Сериализатор, более быстрый чем BinaryFormatter
От: Chardex Россия  
Дата: 07.08.06 11:03
Оценка:
Здравствуйте, Oyster, Вы писали:

O>Решение с датой сборки в общем-то неплохое (я просто не додумался до него), но оно не будет работать с динамическими сборками (с генерацией типов в рантайме через Reflection.Emit), т.к. для динамической сборки дата создания будет новая при каждом запуске приложения. Т.е., например, прокси-типы, создаваемые на лету в BLToolkit
Автор(ы): Игорь Ткачёв
Дата: 01.07.2003
В статье подробно рассматривается состав и способы применения пространства имён Rsdn.Framework.Data, представляющего собой высокоуровневую обёртку над ADO.NET.
(и в моём приложении, например), десериализовать не удастся при следующем запуске приложения...

Еще как удастся, только будет лишние наклодные расходы на проверку неизменности данных — медленнее будет десериализовываться, но все равно гораздо быстрее стандартного.
Попробую с CRC32 в ближайшее время.
Re[12]: [C#] Сериализатор, более быстрый чем BinaryFormatter
От: Oyster Украина https://github.com/devoyster
Дата: 07.08.06 11:37
Оценка:
Здравствуйте, Chardex, Вы писали:

C>Еще как удастся, только будет лишние наклодные расходы на проверку неизменности данных — медленнее будет десериализовываться, но все равно гораздо быстрее стандартного.


А... всё, понял. Тогда всё нормально
Re[4]: Исходники, с поддержкой циклов
От: Chardex Россия  
Дата: 08.08.06 10:22
Оценка:
Здравствуйте, Chardex, Вы писали:

Добавил поддержку ISerializable, IDeserializationCallback, IDeserializationCallback. Пронаследовался от IFormatter, но не пойму пока что мне делать с SurrogateSelector и Binder'ом. Как я понял, Binder это для случая если тип изменился, конвертации между типами и т.п., Context просто для установления где (для чего) у нас проиходит сериализация, а вот SurrogateSelector вообще И еще я исполизую стандартный FormatterConverter для передачи в констурктор SerializationInfo, это нормально?
Можно ли мой форматтер прикрутить к ремоутингу?
Re[5]: Исходники, с поддержкой циклов
От: VladiCh  
Дата: 13.08.06 10:08
Оценка: +1
Здравствуйте, Chardex, Вы писали:

C>Здравствуйте, Chardex, Вы писали:


C>Добавил поддержку ISerializable, IDeserializationCallback, IDeserializationCallback. Пронаследовался от IFormatter, но не пойму пока что мне делать с SurrogateSelector и Binder'ом. Как я понял, Binder это для случая если тип изменился, конвертации между типами и т.п., Context просто для установления где (для чего) у нас проиходит сериализация, а вот SurrogateSelector вообще И еще я исполизую стандартный FormatterConverter для передачи в констурктор SerializationInfo, это нормально?

C>Можно ли мой форматтер прикрутить к ремоутингу?

Вещь довольно интересная, хотя есть несколько замечаний:
1. Быстродействие сериалайзера можно увеличить, храня индексы уже сериализованных объектов в Dictionary, а не в List. А то делать постоянно по List'у Contains как-то некузяво.
2. Сериализовывать классы, реализующие IList и IDictionary в самом сериалайзере тоже не есть правильно, т.к. неизвестно заранее, какие именно данные нужно сериализовывать этим классам. А вот сериализовывать более компактно DataSet/DataTable было бы неплохо.
3. С сериализацией generic-типов как-то не очень.
4. В выложенных исходниках я не заметил поддержки ISerializable, NonSerialized, OptionalAttribute, Binder'а и т.п.
5. Ну и вообще там много чего соптимизировать можно как по скорости, так и по размеру хранимых данных и концептуально тоже.

Если есть желание, мог бы с некоторыми моментами помочь, т.к. у меня как раз стоит сейчас задача написания подобной штуки.
P.S. Если все требуемые интерфейсы поддерживаются, то прикрутить эту штуку к ремоутингу вполне реально.
Re[6]: Исходники, с поддержкой циклов
От: Oyster Украина https://github.com/devoyster
Дата: 14.08.06 11:34
Оценка:
Здравствуйте, VladiCh, Вы писали:

VC>4. В выложенных исходниках я не заметил поддержки ISerializable, NonSerialized, OptionalAttribute, Binder'а и т.п.


Вот здесь — Re[3]: Исходники, с поддержкой циклов
Автор: Chardex
Дата: 06.08.06
— ссылка на архив, где уже есть поддержка ISerializable, NonSerializedAttribute и IDeserializationCallback вроде.

А какое отношение OptionalAttribute имеет к сериализации, кстати?
Re[5]: Исходники, с поддержкой циклов
От: Oyster Украина https://github.com/devoyster
Дата: 14.08.06 11:34
Оценка:
Здравствуйте, Chardex, Вы писали:

C>... а вот SurrogateSelector вообще


SurrogateSelector нужен для того, чтобы по типу выбрать ISerializationSurrogate, который позволяет нужный тип сериализовать "вручную", как если бы тот реализовывал ISerializable. Очень полезная штука, на самом деле.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.