private static DeserializationFunc <T> CreateObjectDeserializationFunc <T>(ObjectInfo objectInfo)
        {
            var deserialzationFunc = new DynamicMethod(string.Empty, typeof(T), new[] { typeof(byte[]), typeof(int), typeof(int), typeof(int).MakeByRefType(), typeof(int), typeof(int) }, true);
            var il = deserialzationFunc.GetILGenerator();

            var size   = il.DeclareLocal(typeof(int));
            var start  = il.DeclareLocal(typeof(int));
            var result = il.DeclareLocal(typeof(T));

            {
                var recursionLimitNotReached = il.DefineLabel();

                il.EmitLoadArgument(4);
                il.EmitLoadDeclaredConstant(0);
                il.Emit(OpCodes.Bgt_S, recursionLimitNotReached);
                il.Emit(OpCodes.Ldstr, "Failed to deserialize the object, because the recursion limit was reached.");
                il.Emit(OpCodes.Newobj, typeof(SerializationException).GetConstructor(new[] { typeof(string) }));
                il.Emit(OpCodes.Throw);

                il.MarkLabel(recursionLimitNotReached);

                il.EmitLoadArgument(4);
                il.EmitLoadDeclaredConstant(1);
                il.Emit(OpCodes.Sub);
                il.EmitStoreArgument(4);
            }

            if (typeof(T).IsClass)
            {
                var valueNotNull = il.DefineLabel();

                il.EmitLoadArgument(0);
                il.EmitLoadArgument(1);
                il.EmitLoadArgument(2);
                il.EmitLoadLocalAddress(size.LocalIndex);
                il.Emit(OpCodes.Call, typeof(Binary).GetMethod(nameof(Binary.InternalReadBoolean), BindingFlagsEx.Static));
                il.Emit(OpCodes.Brtrue_S, valueNotNull);

                il.EmitLoadArgument(3);
                il.EmitLoadLocal(size.LocalIndex);
                il.Emit(OpCodes.Stind_I4);

                il.Emit(OpCodes.Ldnull);
                il.Emit(OpCodes.Ret);

                il.MarkLabel(valueNotNull);

                IncrementOffset(il, 1, size);
                DecrementCount(il, 2, size);
            }

            il.EmitLoadArgument(1);
            il.EmitStoreLocal(start.LocalIndex);

            if (objectInfo.Constructor == null)
            {
                Debug.Assert(typeof(T).IsValueType);

                il.EmitLoadLocalAddress(result.LocalIndex);
                il.Emit(OpCodes.Initobj, typeof(T));
            }
            else
            {
                if (typeof(T).IsValueType)
                {
                    il.EmitLoadLocalAddress(result.LocalIndex);
                }

                foreach (var field in objectInfo.ConstructorFields)
                {
                    DeserializeField(il, field, size);
                    IncrementOffset(il, 1, size);
                    DecrementCount(il, 2, size);
                }

                if (typeof(T).IsClass)
                {
                    il.Emit(OpCodes.Newobj, objectInfo.Constructor);
                    il.EmitStoreLocal(result.LocalIndex);
                }
                else
                {
                    Debug.Assert(typeof(T).IsValueType);

                    il.Emit(OpCodes.Call, objectInfo.Constructor);
                }
            }

            foreach (var field in objectInfo.Fields)
            {
                il.EmitLoadLocal(result.LocalIndex);
                DeserializeField(il, field, size);
                il.Emit(OpCodes.Stfld, field);

                IncrementOffset(il, 1, size);
                DecrementCount(il, 2, size);
            }

            il.EmitLoadArgument(3);
            il.EmitLoadArgument(1);
            il.EmitLoadLocal(start.LocalIndex);
            il.Emit(OpCodes.Sub);
            il.Emit(OpCodes.Stind_I4);

            il.EmitLoadLocal(result.LocalIndex);
            il.Emit(OpCodes.Ret);

            return((DeserializationFunc <T>)deserialzationFunc.CreateDelegate(typeof(DeserializationFunc <T>)));
        }
        private static GetSizeFunc <T> CreateObjectGetSizeFunc <T>(ObjectInfo objectInfo)
        {
            var getSizeFunc = new DynamicMethod(string.Empty, typeof(int), new[] { typeof(T), typeof(int), typeof(int) }, true);
            var il          = getSizeFunc.GetILGenerator();

            var totalSize = il.DeclareLocal(typeof(int));

            {
                var recursionLimitNotReached = il.DefineLabel();

                il.EmitLoadArgument(2);
                il.EmitLoadDeclaredConstant(0);
                il.Emit(OpCodes.Bgt_S, recursionLimitNotReached);
                il.Emit(OpCodes.Ldstr, "Failed to get the size of the object, because the recursion limit was reached.");
                il.Emit(OpCodes.Ldstr, "value");
                il.Emit(OpCodes.Newobj, typeof(ArgumentException).GetConstructor(new[] { typeof(string), typeof(string) }));
                il.Emit(OpCodes.Throw);

                il.MarkLabel(recursionLimitNotReached);

                il.EmitLoadArgument(2);
                il.EmitLoadDeclaredConstant(1);
                il.Emit(OpCodes.Sub);
                il.EmitStoreArgument(2);
            }

            if (typeof(T).IsClass)
            {
                var valueNotNull = il.DefineLabel();

                il.EmitLoadArgument(0);
                il.Emit(OpCodes.Ldnull);
                il.Emit(OpCodes.Cgt_Un);
                il.Emit(OpCodes.Call, typeof(Binary).GetMethod(nameof(Binary.InternalGetBooleanSize), BindingFlagsEx.Static));
                il.EmitStoreLocal(totalSize.LocalIndex);

                il.EmitLoadArgument(0);
                il.Emit(OpCodes.Brtrue_S, valueNotNull);
                il.EmitLoadLocal(totalSize.LocalIndex);
                il.Emit(OpCodes.Ret);

                il.MarkLabel(valueNotNull);
            }
            else
            {
                il.EmitLoadDeclaredConstant(0);
                il.EmitStoreLocal(totalSize.LocalIndex);
            }

            foreach (var field in objectInfo.ConstructorFields.Concat(objectInfo.Fields))
            {
                il.EmitLoadLocal(totalSize.LocalIndex);
                GetFieldSize <T>(il, field);
                il.Emit(OpCodes.Add);
                il.EmitStoreLocal(totalSize.LocalIndex);
            }

            il.EmitLoadLocal(totalSize.LocalIndex);
            il.Emit(OpCodes.Ret);

            return((GetSizeFunc <T>)getSizeFunc.CreateDelegate(typeof(GetSizeFunc <T>)));
        }
        private static SerializationFunc <T> CreateObjectSerializationFunc <T>(ObjectInfo objectInfo)
        {
            var serialzationFunc = new DynamicMethod(string.Empty, typeof(int), new[] { typeof(T), typeof(byte[]), typeof(int), typeof(int), typeof(int), typeof(int) }, true);
            var il = serialzationFunc.GetILGenerator();

            var size  = il.DeclareLocal(typeof(int));
            var start = il.DeclareLocal(typeof(int));

            {
                var recursionLimitNotReached = il.DefineLabel();

                il.EmitLoadArgument(5);
                il.EmitLoadDeclaredConstant(0);
                il.Emit(OpCodes.Bgt_S, recursionLimitNotReached);
                il.Emit(OpCodes.Ldstr, "Failed to serialize the object, because the recursion limit was reached.");
                il.Emit(OpCodes.Ldstr, "value");
                il.Emit(OpCodes.Newobj, typeof(ArgumentException).GetConstructor(new[] { typeof(string), typeof(string) }));
                il.Emit(OpCodes.Throw);

                il.MarkLabel(recursionLimitNotReached);

                il.EmitLoadArgument(5);
                il.EmitLoadDeclaredConstant(1);
                il.Emit(OpCodes.Sub);
                il.EmitStoreArgument(5);
            }

            if (typeof(T).IsClass)
            {
                var valueNotNull = il.DefineLabel();

                il.EmitLoadArgument(0);
                il.Emit(OpCodes.Ldnull);
                il.Emit(OpCodes.Cgt_Un);
                il.EmitLoadArgument(1);
                il.EmitLoadArgument(2);
                il.EmitLoadArgument(3);
                il.Emit(OpCodes.Call, typeof(Binary).GetMethod(nameof(Binary.InternalWriteBoolean), BindingFlagsEx.Static));
                il.EmitStoreLocal(size.LocalIndex);

                il.EmitLoadArgument(0);
                il.Emit(OpCodes.Brtrue_S, valueNotNull);

                il.EmitLoadLocal(size.LocalIndex);
                il.Emit(OpCodes.Ret);

                il.MarkLabel(valueNotNull);
            }

            il.EmitLoadArgument(2);
            il.EmitStoreLocal(start.LocalIndex);

            if (typeof(T).IsClass)
            {
                IncrementOffset(il, 2, size);
                DecrementCount(il, 3, size);
            }

            foreach (var field in objectInfo.ConstructorFields.Concat(objectInfo.Fields))
            {
                SerializeField <T>(il, field);
                il.EmitStoreLocal(size.LocalIndex);

                IncrementOffset(il, 2, size);
                DecrementCount(il, 3, size);
            }

            il.EmitLoadArgument(2);
            il.EmitLoadLocal(start.LocalIndex);
            il.Emit(OpCodes.Sub);
            il.Emit(OpCodes.Ret);

            return((SerializationFunc <T>)serialzationFunc.CreateDelegate(typeof(SerializationFunc <T>)));
        }