// // Copyright 2011 Kazuki Oikawa // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // using System; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Reflection.Emit; using System.Threading; using MsgPack.Compiler; namespace MsgPack { public class CompiledPacker { static PackerBase _publicFieldPacker, _allFieldPacker; PackerBase _packer; static CompiledPacker () { _publicFieldPacker = new MethodBuilderPacker (); _allFieldPacker = new DynamicMethodPacker (); } public CompiledPacker () : this (false) {} public CompiledPacker (bool packPrivateField) { _packer = (packPrivateField ? _allFieldPacker : _publicFieldPacker); } public void Prepare () { _packer.CreatePacker (); _packer.CreateUnpacker (); } #region Generics Pack/Unpack Methods public byte[] Pack (T o) { using (MemoryStream ms = new MemoryStream ()) { Pack (ms, o); return ms.ToArray (); } } public void Pack (Stream strm, T o) { _packer.CreatePacker () (new MsgPackWriter (strm), o); } public T Unpack (byte[] buf) { return Unpack (buf, 0, buf.Length); } public T Unpack (byte[] buf, int offset, int size) { using (MemoryStream ms = new MemoryStream (buf, offset, size)) { return Unpack (ms); } } public T Unpack (Stream strm) { return _packer.CreateUnpacker () (new MsgPackReader (strm)); } #endregion #region Non-generics Pack/Unpack Methods public byte[] Pack (object o) { using (MemoryStream ms = new MemoryStream ()) { Pack (ms, o); return ms.ToArray (); } } public void Pack (Stream strm, object o) { throw new NotImplementedException (); } public object Unpack (Type t, byte[] buf) { return Unpack (t, buf, 0, buf.Length); } public object Unpack (Type t, byte[] buf, int offset, int size) { using (MemoryStream ms = new MemoryStream (buf, offset, size)) { return Unpack (t, ms); } } public object Unpack (Type t, Stream strm) { throw new NotImplementedException (); } #endregion #region Compiled Packer Implementations public abstract class PackerBase { Dictionary _packers = new Dictionary (); Dictionary _unpackers = new Dictionary (); protected Dictionary _packMethods = new Dictionary (); protected Dictionary _unpackMethods = new Dictionary (); protected PackerBase () { DefaultPackMethods.Register (_packMethods, _unpackMethods); } public Action CreatePacker () { Delegate d; lock (_packers) { if (!_packers.TryGetValue (typeof (T), out d)) { d = CreatePacker_Internal (); _packers.Add (typeof (T), d); } } return (Action)d; } public Func CreateUnpacker () { Delegate d; lock (_unpackers) { if (!_unpackers.TryGetValue (typeof (T), out d)) { d = CreateUnpacker_Internal (); _unpackers.Add (typeof (T), d); } } return (Func)d; } protected abstract Action CreatePacker_Internal (); protected abstract Func CreateUnpacker_Internal (); } public sealed class DynamicMethodPacker : PackerBase { private static MethodInfo LookupMemberMappingMethod; static Dictionary> UnpackMemberMappings; static DynamicMethodPacker () { UnpackMemberMappings = new Dictionary> (); LookupMemberMappingMethod = typeof (DynamicMethodPacker).GetMethod ("LookupMemberMapping", BindingFlags.Static | BindingFlags.NonPublic); } public DynamicMethodPacker () : base () { } protected override Action CreatePacker_Internal () { DynamicMethod dm = CreatePacker (typeof (T), CreatePackDynamicMethod (typeof (T))); return (Action)dm.CreateDelegate (typeof (Action)); } protected override Func CreateUnpacker_Internal () { DynamicMethod dm = CreateUnpacker (typeof (T), CreateUnpackDynamicMethod (typeof (T))); return (Func)dm.CreateDelegate (typeof (Func)); } DynamicMethod CreatePacker (Type t, DynamicMethod dm) { ILGenerator il = dm.GetILGenerator (); _packMethods.Add (t, dm); PackILGenerator.EmitPackCode (t, dm, il, LookupMembers, FormatMemberName, LookupPackMethod); return dm; } DynamicMethod CreateUnpacker (Type t, DynamicMethod dm) { ILGenerator il = dm.GetILGenerator (); _unpackMethods.Add (t, dm); PackILGenerator.EmitUnpackCode (t, dm, il, LookupMembers, FormatMemberName, LookupUnpackMethod, LookupMemberMapping, LookupMemberMappingMethod); return dm; } static DynamicMethod CreatePackDynamicMethod (Type t) { return CreateDynamicMethod (typeof (void), new Type[] {typeof (MsgPackWriter), t}); } static DynamicMethod CreateUnpackDynamicMethod (Type t) { return CreateDynamicMethod (t, new Type[] {typeof (MsgPackReader)}); } static MemberInfo[] LookupMembers (Type t) { BindingFlags baseFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; System.Collections.Generic.List list = new System.Collections.Generic.List (); list.AddRange (t.GetFields (baseFlags)); // TODO: Add NonSerialized Attribute Filter ? return list.ToArray (); } MethodInfo LookupPackMethod (Type t) { MethodInfo mi; DynamicMethod dm; if (_packMethods.TryGetValue (t, out mi)) return mi; dm = CreatePackDynamicMethod (t); return CreatePacker (t, dm); } MethodInfo LookupUnpackMethod (Type t) { MethodInfo mi; if (_unpackMethods.TryGetValue (t, out mi)) return mi; DynamicMethod dm = CreateUnpackDynamicMethod (t); return CreateUnpacker (t, dm); } static string FormatMemberName (MemberInfo m) { if (m.MemberType != MemberTypes.Field) return m.Name; int pos; string name = m.Name; if (name[0] == '<' && (pos = name.IndexOf ('>')) > 1) name = name.Substring (1, pos - 1); // Auto-Property (\<.+\>) return name; } static int _dynamicMethodIdx = 0; static DynamicMethod CreateDynamicMethod (Type returnType, Type[] parameterTypes) { string name = "_" + Interlocked.Increment (ref _dynamicMethodIdx).ToString (); return new DynamicMethod (name, returnType, parameterTypes, true); } internal static IDictionary LookupMemberMapping (Type t) { IDictionary mapping; lock (UnpackMemberMappings) { if (!UnpackMemberMappings.TryGetValue (t, out mapping)) { mapping = new Dictionary (); UnpackMemberMappings.Add (t, mapping); } } return mapping; } } public sealed class MethodBuilderPacker : PackerBase { public const string AssemblyName = "MessagePackInternalAssembly"; static AssemblyName DynamicAsmName; static AssemblyBuilder DynamicAsmBuilder; static ModuleBuilder DynamicModuleBuilder; private static MethodInfo LookupMemberMappingMethod; static Dictionary> UnpackMemberMappings; static MethodBuilderPacker () { UnpackMemberMappings = new Dictionary> (); LookupMemberMappingMethod = typeof (MethodBuilderPacker).GetMethod ("LookupMemberMapping", BindingFlags.Static | BindingFlags.NonPublic); DynamicAsmName = new AssemblyName (AssemblyName); DynamicAsmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly (DynamicAsmName, AssemblyBuilderAccess.Run); DynamicModuleBuilder = DynamicAsmBuilder.DefineDynamicModule (DynamicAsmName.Name); } public MethodBuilderPacker () : base () { } protected override Action CreatePacker_Internal () { TypeBuilder tb; MethodBuilder mb; CreatePackMethodBuilder (typeof (T), out tb, out mb); _packMethods.Add (typeof (T), mb); CreatePacker (typeof (T), mb); MethodInfo mi = ToCallableMethodInfo (typeof (T), tb, true); return (Action)Delegate.CreateDelegate (typeof (Action), mi); } protected override Func CreateUnpacker_Internal () { TypeBuilder tb; MethodBuilder mb; CreateUnpackMethodBuilder (typeof (T), out tb, out mb); _unpackMethods.Add (typeof (T), mb); CreateUnpacker (typeof (T), mb); MethodInfo mi = ToCallableMethodInfo (typeof (T), tb, false); return (Func)Delegate.CreateDelegate (typeof (Func), mi); } void CreatePacker (Type t, MethodBuilder mb) { ILGenerator il = mb.GetILGenerator (); PackILGenerator.EmitPackCode (t, mb, il, LookupMembers, FormatMemberName, LookupPackMethod); } void CreateUnpacker (Type t, MethodBuilder mb) { ILGenerator il = mb.GetILGenerator (); PackILGenerator.EmitUnpackCode (t, mb, il, LookupMembers, FormatMemberName, LookupUnpackMethod, LookupMemberMapping, LookupMemberMappingMethod); } MethodInfo ToCallableMethodInfo (Type t, TypeBuilder tb, bool isPacker) { Type type = tb.CreateType (); MethodInfo mi = type.GetMethod (isPacker ? "Pack" : "Unpack", BindingFlags.Static | BindingFlags.Public); if (isPacker) { _packMethods[t] = mi; } else { _unpackMethods[t] = mi; } return mi; } MethodInfo LookupPackMethod (Type t) { MethodInfo mi; TypeBuilder tb; MethodBuilder mb; if (_packMethods.TryGetValue (t, out mi)) return mi; CreatePackMethodBuilder (t, out tb, out mb); _packMethods.Add (t, mb); CreatePacker (t, mb); return ToCallableMethodInfo (t, tb, true); } MethodInfo LookupUnpackMethod (Type t) { MethodInfo mi; TypeBuilder tb; MethodBuilder mb; if (_unpackMethods.TryGetValue (t, out mi)) return mi; CreateUnpackMethodBuilder (t, out tb, out mb); _unpackMethods.Add (t, mb); CreateUnpacker (t, mb); return ToCallableMethodInfo (t, tb, false); } static string FormatMemberName (MemberInfo m) { return m.Name; } static MemberInfo[] LookupMembers (Type t) { BindingFlags baseFlags = BindingFlags.Instance | BindingFlags.Public; System.Collections.Generic.List list = new System.Collections.Generic.List (); list.AddRange (t.GetFields (baseFlags)); // TODO: Add NonSerialized Attribute Filter ? return list.ToArray (); } static void CreatePackMethodBuilder (Type t, out TypeBuilder tb, out MethodBuilder mb) { tb = DynamicModuleBuilder.DefineType (t.Name + "PackerType", TypeAttributes.Public); mb = tb.DefineMethod ("Pack", MethodAttributes.Static | MethodAttributes.Public, typeof (void), new Type[] {typeof (MsgPackWriter), t}); } static void CreateUnpackMethodBuilder (Type t, out TypeBuilder tb, out MethodBuilder mb) { tb = DynamicModuleBuilder.DefineType (t.Name + "UnpackerType", TypeAttributes.Public); mb = tb.DefineMethod ("Unpack", MethodAttributes.Static | MethodAttributes.Public, t, new Type[] {typeof (MsgPackReader)}); } internal static IDictionary LookupMemberMapping (Type t) { IDictionary mapping; lock (UnpackMemberMappings) { if (!UnpackMemberMappings.TryGetValue (t, out mapping)) { mapping = new Dictionary (); UnpackMemberMappings.Add (t, mapping); } } return mapping; } } #endregion #region default pack/unpack methods internal static class DefaultPackMethods { public static void Register (Dictionary packMethods, Dictionary unpackMethods) { RegisterPackMethods (packMethods); RegisterUnpackMethods (unpackMethods); } #region Pack static void RegisterPackMethods (Dictionary packMethods) { Type type = typeof (DefaultPackMethods); MethodInfo[] methods = type.GetMethods (BindingFlags.Static | BindingFlags.NonPublic); string methodName = "Pack"; for (int i = 0; i < methods.Length; i ++) { if (!methodName.Equals (methods[i].Name)) continue; ParameterInfo[] parameters = methods[i].GetParameters (); if (parameters.Length != 2 || parameters[0].ParameterType != typeof (MsgPackWriter)) continue; packMethods.Add (parameters[1].ParameterType, methods[i]); } } internal static void Pack (MsgPackWriter writer, string x) { if (x == null) { writer.WriteNil (); } else { writer.Write (x, false); } } #endregion #region Unpack static void RegisterUnpackMethods (Dictionary unpackMethods) { BindingFlags flags = BindingFlags.Static | BindingFlags.NonPublic; Type type = typeof (DefaultPackMethods); MethodInfo mi = type.GetMethod ("Unpack_Signed", flags); unpackMethods.Add (typeof (sbyte), mi); unpackMethods.Add (typeof (short), mi); unpackMethods.Add (typeof (int), mi); mi = type.GetMethod ("Unpack_Signed64", flags); unpackMethods.Add (typeof (long), mi); mi = type.GetMethod ("Unpack_Unsigned", flags); unpackMethods.Add (typeof (byte), mi); unpackMethods.Add (typeof (ushort), mi); unpackMethods.Add (typeof (char), mi); unpackMethods.Add (typeof (uint), mi); mi = type.GetMethod ("Unpack_Unsigned64", flags); unpackMethods.Add (typeof (ulong), mi); mi = type.GetMethod ("Unpack_Boolean", flags); unpackMethods.Add (typeof (bool), mi); mi = type.GetMethod ("Unpack_Float", flags); unpackMethods.Add (typeof (float), mi); mi = type.GetMethod ("Unpack_Double", flags); unpackMethods.Add (typeof (double), mi); mi = type.GetMethod ("Unpack_String", flags); unpackMethods.Add (typeof (string), mi); } internal static int Unpack_Signed (MsgPackReader reader) { if (!reader.Read () || !reader.IsSigned ()) UnpackFailed (); return reader.ValueSigned; } internal static long Unpack_Signed64 (MsgPackReader reader) { if (!reader.Read ()) UnpackFailed (); if (reader.IsSigned ()) return reader.ValueSigned; if (reader.IsSigned64 ()) return reader.ValueSigned64; UnpackFailed (); return 0; // unused } internal static uint Unpack_Unsigned (MsgPackReader reader) { if (!reader.Read () || !reader.IsUnsigned ()) UnpackFailed (); return reader.ValueUnsigned; } internal static ulong Unpack_Unsigned64 (MsgPackReader reader) { if (!reader.Read ()) UnpackFailed (); if (reader.IsUnsigned ()) return reader.ValueUnsigned; if (reader.IsUnsigned64 ()) return reader.ValueUnsigned64; UnpackFailed (); return 0; // unused } internal static bool Unpack_Boolean (MsgPackReader reader) { if (!reader.Read () || !reader.IsBoolean ()) UnpackFailed (); return reader.ValueBoolean; } internal static float Unpack_Float (MsgPackReader reader) { if (!reader.Read () || reader.Type != TypePrefixes.Float) UnpackFailed (); return reader.ValueFloat; } internal static double Unpack_Double (MsgPackReader reader) { if (!reader.Read () || reader.Type != TypePrefixes.Double) UnpackFailed (); return reader.ValueDouble; } internal static string Unpack_String (MsgPackReader reader) { if (!reader.Read () || !reader.IsRaw ()) UnpackFailed (); return reader.ReadRawString (); } internal static void UnpackFailed () { throw new FormatException (); } #endregion } #endregion } }