hellbound/dll_src/msgpack/CompiledPacker.cs

550 lines
16 KiB
C#
Raw Permalink Normal View History

2021-11-26 11:16:25 +03:00
//
// 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<T> ()
{
_packer.CreatePacker<T> ();
_packer.CreateUnpacker<T> ();
}
#region Generics Pack/Unpack Methods
public byte[] Pack<T> (T o)
{
using (MemoryStream ms = new MemoryStream ()) {
Pack<T> (ms, o);
return ms.ToArray ();
}
}
public void Pack<T> (Stream strm, T o)
{
_packer.CreatePacker<T> () (new MsgPackWriter (strm), o);
}
public T Unpack<T> (byte[] buf)
{
return Unpack<T> (buf, 0, buf.Length);
}
public T Unpack<T> (byte[] buf, int offset, int size)
{
using (MemoryStream ms = new MemoryStream (buf, offset, size)) {
return Unpack<T> (ms);
}
}
public T Unpack<T> (Stream strm)
{
return _packer.CreateUnpacker<T> () (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<Type, Delegate> _packers = new Dictionary<Type, Delegate> ();
Dictionary<Type, Delegate> _unpackers = new Dictionary<Type, Delegate> ();
protected Dictionary<Type, MethodInfo> _packMethods = new Dictionary<Type, MethodInfo> ();
protected Dictionary<Type, MethodInfo> _unpackMethods = new Dictionary<Type, MethodInfo> ();
protected PackerBase ()
{
DefaultPackMethods.Register (_packMethods, _unpackMethods);
}
public Action<MsgPackWriter, T> CreatePacker<T> ()
{
Delegate d;
lock (_packers) {
if (!_packers.TryGetValue (typeof (T), out d)) {
d = CreatePacker_Internal<T> ();
_packers.Add (typeof (T), d);
}
}
return (Action<MsgPackWriter, T>)d;
}
public Func<MsgPackReader, T> CreateUnpacker<T> ()
{
Delegate d;
lock (_unpackers) {
if (!_unpackers.TryGetValue (typeof (T), out d)) {
d = CreateUnpacker_Internal<T> ();
_unpackers.Add (typeof (T), d);
}
}
return (Func<MsgPackReader, T>)d;
}
protected abstract Action<MsgPackWriter, T> CreatePacker_Internal<T> ();
protected abstract Func<MsgPackReader, T> CreateUnpacker_Internal<T> ();
}
public sealed class DynamicMethodPacker : PackerBase
{
private static MethodInfo LookupMemberMappingMethod;
static Dictionary<Type, IDictionary<string, int>> UnpackMemberMappings;
static DynamicMethodPacker ()
{
UnpackMemberMappings = new Dictionary<Type, IDictionary<string, int>> ();
LookupMemberMappingMethod = typeof (DynamicMethodPacker).GetMethod ("LookupMemberMapping", BindingFlags.Static | BindingFlags.NonPublic);
}
public DynamicMethodPacker () : base ()
{
}
protected override Action<MsgPackWriter, T> CreatePacker_Internal<T> ()
{
DynamicMethod dm = CreatePacker (typeof (T), CreatePackDynamicMethod (typeof (T)));
return (Action<MsgPackWriter, T>)dm.CreateDelegate (typeof (Action<MsgPackWriter, T>));
}
protected override Func<MsgPackReader, T> CreateUnpacker_Internal<T> ()
{
DynamicMethod dm = CreateUnpacker (typeof (T), CreateUnpackDynamicMethod (typeof (T)));
return (Func<MsgPackReader, T>)dm.CreateDelegate (typeof (Func<MsgPackReader, T>));
}
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;
List<MemberInfo> list = new List<MemberInfo> ();
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 (\<.+\>) <ab>
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<string,int> LookupMemberMapping (Type t)
{
IDictionary<string, int> mapping;
lock (UnpackMemberMappings) {
if (!UnpackMemberMappings.TryGetValue (t, out mapping)) {
mapping = new Dictionary<string, int> ();
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<Type, IDictionary<string, int>> UnpackMemberMappings;
static MethodBuilderPacker ()
{
UnpackMemberMappings = new Dictionary<Type, IDictionary<string, int>> ();
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<MsgPackWriter, T> CreatePacker_Internal<T> ()
{
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<MsgPackWriter, T>)Delegate.CreateDelegate (typeof (Action<MsgPackWriter, T>), mi);
}
protected override Func<MsgPackReader, T> CreateUnpacker_Internal<T> ()
{
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<MsgPackReader, T>)Delegate.CreateDelegate (typeof (Func<MsgPackReader, T>), 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;
List<MemberInfo> list = new List<MemberInfo> ();
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<string,int> LookupMemberMapping (Type t)
{
IDictionary<string, int> mapping;
lock (UnpackMemberMappings) {
if (!UnpackMemberMappings.TryGetValue (t, out mapping)) {
mapping = new Dictionary<string, int> ();
UnpackMemberMappings.Add (t, mapping);
}
}
return mapping;
}
}
#endregion
#region default pack/unpack methods
internal static class DefaultPackMethods
{
public static void Register (Dictionary<Type, MethodInfo> packMethods, Dictionary<Type, MethodInfo> unpackMethods)
{
RegisterPackMethods (packMethods);
RegisterUnpackMethods (unpackMethods);
}
#region Pack
static void RegisterPackMethods (Dictionary<Type, MethodInfo> 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<Type, MethodInfo> 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
}
}