using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo(MsgPack.CompiledPacker.MethodBuilderPacker.AssemblyName)]
//========================================================================================================
//
// 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 NUnit.Framework;
namespace Test
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Test.Share;
    using MsgPack;
    using Microshaoft;
    //using System.Diagnostics;
    //using Microsoft.VisualStudio.TestTools.UnitTesting;
    //[TestFixture]
    public class Class1
    {
        static void Main(string[] args)
        {
            Class1 c = new Class1();
            int iterations = 1000;
            CodeTimer.ParallelTime
                        (
                            "MsgPack ObjectPacker 并行测试",
                            iterations,
                            () =>
                            {
                                c.Test_ObjectPacker();
                            }
                        );
            CodeTimer.Time
                        (
                            "MsgPack ObjectPacker 串行测试",
                            iterations,
                            () =>
                            {
                                c.Test_ObjectPacker();
                            }
                        );
            CodeTimer.ParallelTime
                        (
                            "MsgPack CompiledPacker 并行测试",
                            iterations,
                            () =>
                            {
                                c.Test_CompiledPacker();
                            }
                        );
            CodeTimer.Time
                        (
                            "MsgPack CompiledPacker 串行测试",
                            iterations,
                            () =>
                            {
                                c.Test_CompiledPacker();
                            }
                        );
            //CodeTimer.ParallelTime
            //        (
            //            "MsgPack BoxingPacker 并行测试",
            //            iterations,
            //            () =>
            //            {
            //                c.Test_BoxingPacker();
            //            }
            //        );
            //CodeTimer.Time
            //            (
            //                "MsgPack BoxingPacker 串行测试",
            //                iterations,
            //                () =>
            //                {
            //                    c.Test_BoxingPacker();
            //                }
            //            );
            CodeTimer.ParallelTime
                        (
                            "MsgPack ObjectPacker/CompiledPacker 并行测试",
                            iterations,
                            () =>
                            {
                                c.Test_HybridPacker();
                            }
                        );
            CodeTimer.Time
                        (
                            "MsgPack ObjectPacker/CompiledPacker 串行测试",
                            iterations,
                            () =>
                            {
                                c.Test_HybridPacker();
                            }
                        );
            Console.WriteLine("Hello World");
            Console.WriteLine(Environment.Version.ToString());
            Console.ReadLine();
        }
        public void Test_HybridPacker()
        {
            ObjectMsgPackHeader header = new ObjectMsgPackHeader();
            header.From = "Microshaoft";
            header.ToList = new string[] { "m1", "m2", "m3" };
            //header.Instance = header;
            ObjectMsgPackBody body = new ObjectMsgPackBody();
            body.Parameter1 = "asdasdas";
            body.Parameter2 = 1000;
            body.ParameterX = new List<string>();
            body.ParameterX.Add("aaaaa");
            body.ParameterX.Add("bbbbb");
            //body.Instance = body;
            byte[] buffer;
            ObjectMsgPack message = new ObjectMsgPack();
            message.Header = new CompiledPacker().Pack(header);
            message.Body = new ObjectPacker().Pack(body);
            message.SenderID = "asdasd";
            message.Signature = new byte[10];
            message.TimeStamp = "2012-03-22";
            buffer = new ObjectPacker().Pack(message);
            //Console.WriteLine("HybridPacker Buffer.Length {0}", buffer.Length);
            ObjectMsgPack x = new ObjectPacker().Unpack<ObjectMsgPack>(buffer);
            ObjectMsgPackHeader y = new CompiledPacker().Unpack<ObjectMsgPackHeader>(x.Header);
            ObjectMsgPackBody z = new ObjectPacker().Unpack<ObjectMsgPackBody>(x.Body);
            Print(x, y, z);
        }
        public void Test_BoxingPacker()
        {
            ObjectMsgPackHeader header = new ObjectMsgPackHeader();
            header.From = "Microshaoft";
            header.ToList = new string[] { "m1", "m2", "m3" };
            //header.Instance = header;
            ObjectMsgPackBody body = new ObjectMsgPackBody();
            body.Parameter1 = "asdasdas";
            body.Parameter2 = 1000;
            body.ParameterX = new List<string>();
            body.ParameterX.Add("aaaaa");
            body.ParameterX.Add("bbbbb");
            //body.Instance = body;
            byte[] buffer;
            ObjectMsgPack message = new ObjectMsgPack();
            message.Header = new BoxingPacker().Pack(header);
            message.Body = new BoxingPacker().Pack(body);
            message.SenderID = "asdasd";
            message.Signature = new byte[10];
            message.TimeStamp = "2012-03-22";
            buffer = new BoxingPacker().Pack(message);
            //Console.WriteLine("BoxingPacker Buffer.Length {0}", buffer.Length);
            ObjectMsgPack x = (ObjectMsgPack) new BoxingPacker().Unpack(buffer);
            ObjectMsgPackHeader y = (ObjectMsgPackHeader) new BoxingPacker().Unpack(x.Header);
            ObjectMsgPackBody z = (ObjectMsgPackBody) new BoxingPacker().Unpack(x.Body);
            Print(x, y, z);
        }
        public void Test_CompiledPacker()
        {
            ObjectMsgPackHeader header = new ObjectMsgPackHeader();
            header.From = "Microshaoft";
            header.ToList = new string[] { "m1", "m2", "m3" };
            //header.Instance = header;
            ObjectMsgPackBody body = new ObjectMsgPackBody();
            body.Parameter1 = "asdasdas";
            body.Parameter2 = 1000;
            body.ParameterX = new List<string>();
            body.ParameterX.Add("aaaaa");
            body.ParameterX.Add("bbbbb");
            //body.Instance = body;
            byte[] buffer;
            ObjectMsgPack message = new ObjectMsgPack();
            message.Header = new CompiledPacker().Pack(header);
            message.Body = new CompiledPacker().Pack(body);
            message.SenderID = "asdasd";
            message.Signature = new byte[10];
            message.TimeStamp = "2012-03-22";
            buffer = new CompiledPacker().Pack(message);
            //Console.WriteLine("CompiledPacker Buffer.Length {0}", buffer.Length);
            ObjectMsgPack x = new CompiledPacker().Unpack<ObjectMsgPack>(buffer);
            ObjectMsgPackHeader y = new CompiledPacker().Unpack<ObjectMsgPackHeader>(x.Header);
            ObjectMsgPackBody z = new CompiledPacker().Unpack<ObjectMsgPackBody>(x.Body);
            Print(x, y, z);
        }
        public void Test_ObjectPacker()
        {
            ObjectMsgPackHeader header = new ObjectMsgPackHeader();
            header.From = "Microshaoft";
            header.ToList = new string[] { "m1", "m2", "m3" };
            //header.Instance = header;
            ObjectMsgPackBody body = new ObjectMsgPackBody();
            body.Parameter1 = "asdasdas";
            body.Parameter2 = 1000;
            body.ParameterX = new List<string>();
            body.ParameterX.Add("aaaaa");
            body.ParameterX.Add("bbbbb");
            //body.Instance = body;
            byte[] buffer;
            ObjectMsgPack message = new ObjectMsgPack();
            message.Header = new ObjectPacker().Pack(header);
            message.Body = new ObjectPacker().Pack(body);
            message.SenderID = "asdasd";
            message.Signature = new byte[10];
            message.TimeStamp = "2012-03-22";
            buffer = new ObjectPacker().Pack(message);
            //Console.WriteLine("ObjectPacker Buffer.Length {0}", buffer.Length);
            ObjectMsgPack x = new ObjectPacker().Unpack<ObjectMsgPack>(buffer);
            ObjectMsgPackHeader y = new ObjectPacker().Unpack<ObjectMsgPackHeader>(x.Header);
            ObjectMsgPackBody z = new ObjectPacker().Unpack<ObjectMsgPackBody>(x.Body);
            Print(x, y, z);
        }
        public void Print(ObjectMsgPack x, ObjectMsgPackHeader y, ObjectMsgPackBody z)
        {
            return;
            Console.WriteLine(x.SenderID);
            Console.WriteLine(x.Signature.Length);
            Console.WriteLine(x.TimeStamp);
            Console.WriteLine(y.From);
            y.ToList.ToList().ForEach
                                (
                                    s =>
                                    {
                                        Console.WriteLine(s);
                                    }
                                );
            Console.WriteLine(z.Parameter1);
            Console.WriteLine(z.Parameter12);
            Console.WriteLine(z.FF.F1);
            Console.WriteLine(z.FF.F2);
            z.Parameter3.ToList().ForEach
                        (
                            s =>
                            {
                                Console.WriteLine(s);
                            }
                        );
        }
    }
}
namespace Microshaoft
{
    using System;
    using System.Diagnostics;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Runtime.InteropServices;
    public static class CodeTimer
    {
        public static void Initialize()
        {
            Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
            Thread.CurrentThread.Priority = ThreadPriority.Highest;
            Time("", 1, () => { });
        }
        public static void ParallelTime(string name, int iteration, Action action)
        {
            if (string.IsNullOrEmpty(name))
            {
                return;
            }
            // 1.
            ConsoleColor currentForeColor = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine(name);
            // 2.
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            int[] gcCounts = new int[GC.MaxGeneration + 1];
            for (int i = 0; i <= GC.MaxGeneration; i++)
            {
                gcCounts[i] = GC.CollectionCount(i);
            }
            // 3.
            Stopwatch watch = new Stopwatch();
            watch.Start();
            ulong cycleCount = GetCycleCount();
            Parallel.For
                (
                    0
                    , iteration
                    , i =>
                    {
                        action();
                    }
                );
            ulong cpuCycles = GetCycleCount() - cycleCount;
            watch.Stop();
            // 4.
            Console.ForegroundColor = currentForeColor;
            Console.WriteLine("\tTime Elapsed:\t" + watch.ElapsedMilliseconds.ToString("N0") + "ms");
            Console.WriteLine("\tCPU Cycles:\t" + cpuCycles.ToString("N0"));
            // 5.
            for (int i = 0; i <= GC.MaxGeneration; i++)
            {
                int count = GC.CollectionCount(i) - gcCounts[i];
                Console.WriteLine("\tGen " + i + ": \t\t" + count);
            }
            Console.WriteLine();
        }
        public static void Time(string name, int iteration, Action action)
        {
            if (string.IsNullOrEmpty(name))
            {
                return;
            }
            // 1.
            ConsoleColor currentForeColor = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine(name);
            // 2.
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            int[] gcCounts = new int[GC.MaxGeneration + 1];
            for (int i = 0; i <= GC.MaxGeneration; i++)
            {
                gcCounts[i] = GC.CollectionCount(i);
            }
            // 3.
            Stopwatch watch = new Stopwatch();
            watch.Start();
            ulong cycleCount = GetCycleCount();
            for (int i = 0; i < iteration; i++)
            {
                action();
            }
            ulong cpuCycles = GetCycleCount() - cycleCount;
            watch.Stop();
            // 4.
            Console.ForegroundColor = currentForeColor;
            Console.WriteLine("\tTime Elapsed:\t" + watch.ElapsedMilliseconds.ToString("N0") + "ms");
            Console.WriteLine("\tCPU Cycles:\t" + cpuCycles.ToString("N0"));
            // 5.
            for (int i = 0; i <= GC.MaxGeneration; i++)
            {
                int count = GC.CollectionCount(i) - gcCounts[i];
                Console.WriteLine("\tGen " + i + ": \t\t" + count);
            }
            Console.WriteLine();
        }
        private static ulong GetCycleCount()
        {
            ulong cycleCount = 0;
            QueryThreadCycleTime(GetCurrentThread(), ref cycleCount);
            return cycleCount;
        }
        [DllImport("kernel32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool QueryThreadCycleTime(IntPtr threadHandle, ref ulong cycleTime);
        [DllImport("kernel32.dll")]
        static extern IntPtr GetCurrentThread();
    }
}
//===============================================================================================================
namespace Test.Share
{
    using System;
    using System.Collections.Generic;
    public class ObjectMsgPack
    {
        public string SenderID;
        public string TimeStamp;
        public byte[] Signature;
        public byte[] Header;
        public byte[] Body;
    }
    public class ObjectMsgPackHeader
    {
        //public ObjectMsgPack Container;
        //public ObjectMsgPackHeader Instance;
        public string From;
        public string[] ToList;
    }
    public class ObjectMsgPackBody
    {
        //public ObjectMsgPack Container;
        //public ObjectMsgPackBody Instance;
        public string Parameter1 = "aaaa";
        public int Parameter2 = 100;
        public string Parameter3 = "aaaa";
        public int Parameter4 = 100;
        public string Parameter5 = "aaaa";
        public int Parameter6 = 100;
        public string Parameter7 = "aaaa";
        public int Parameter8 = 100;
        public string Parameter9 = "aaaa";
        public int Parameter10 = 100;
        public string Parameter11 = "aaaa";
        public int Parameter12 = -101;
        public List<string> ParameterX;
        public ComplexType FF = new ComplexType();
    }
    public class ComplexType
    {
        public string F1 = "asdasd";
        public DateTime F2 = DateTime.Parse("2012-03-30 00:00:00.00000");
    }
}
//================================================================================================
namespace MsgPack
{
    //
    // 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.
    //
    public enum TypePrefixes : byte
    {
        PositiveFixNum = 0x00, // 0x00 - 0x7f
        NegativeFixNum = 0xe0, // 0xe0 - 0xff
        Nil = 0xc0,
        False = 0xc2,
        True = 0xc3,
        Float = 0xca,
        Double = 0xcb,
        UInt8 = 0xcc,
        UInt16 = 0xcd,
        UInt32 = 0xce,
        UInt64 = 0xcf,
        Int8 = 0xd0,
        Int16 = 0xd1,
        Int32 = 0xd2,
        Int64 = 0xd3,
        Raw16 = 0xda,
        Raw32 = 0xdb,
        Array16 = 0xdc,
        Array32 = 0xdd,
        Map16 = 0xde,
        Map32 = 0xdf,
        FixRaw = 0xa0,        // 0xa0 - 0xbf
        FixArray = 0x90,    // 0x90 - 0x9f
        FixMap = 0x80,        // 0x80 - 0x8f
    }
}
namespace MsgPack
{
    //
    // 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.Reflection;
    public class ReflectionCacheEntry
    {
        const BindingFlags FieldBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField | BindingFlags.SetField;
        public ReflectionCacheEntry(Type t)
        {
            FieldInfo[] fields = t.GetFields(FieldBindingFlags);
            IDictionary<string, FieldInfo> map = new Dictionary<string, FieldInfo>(fields.Length);
            for (int i = 0; i < fields.Length; i++)
            {
                FieldInfo f = fields[i];
                string name = f.Name;
                int pos;
                if (name[0] == '<' && (pos = name.IndexOf('>')) > 1)
                {
                    name = name.Substring(1, pos - 1); // Auto-Property (\<.+\>) <ab>
                }
                map[name] = f;
            }
            FieldMap = map;
        }
        public IDictionary<string, FieldInfo> FieldMap { get; private set; }
    }
}
namespace MsgPack
{
    //
    // 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;
    public static class ReflectionCache
    {
        static Dictionary<Type, ReflectionCacheEntry> _cache;
        static ReflectionCache()
        {
            _cache = new Dictionary<Type, ReflectionCacheEntry>();
        }
        public static ReflectionCacheEntry Lookup(Type type)
        {
            ReflectionCacheEntry entry;
            lock (_cache)
            {
                if (_cache.TryGetValue(type, out entry))
                {
                    return entry;
                }
            }
            entry = new ReflectionCacheEntry(type);
            lock (_cache)
            {
                _cache[type] = entry;
            }
            return entry;
        }
        public static void RemoveCache(Type type)
        {
            lock (_cache)
            {
                _cache.Remove(type);
            }
        }
        public static void Clear()
        {
            lock (_cache)
            {
                _cache.Clear();
            }
        }
    }
}
namespace MsgPack
{
    //
    // 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.Runtime.Serialization;
    using System.Text;
    public class ObjectPacker
    {
        byte[] _buf = new byte[64];
        Encoding _encoding = Encoding.UTF8;
        static Dictionary<Type, PackDelegate> PackerMapping;
        static Dictionary<Type, UnpackDelegate> UnpackerMapping;
        delegate void PackDelegate(ObjectPacker packer, MsgPackWriter writer, object o);
        delegate object UnpackDelegate(ObjectPacker packer, MsgPackReader reader);
        static ObjectPacker()
        {
            PackerMapping = new Dictionary<Type, PackDelegate>();
            UnpackerMapping = new Dictionary<Type, UnpackDelegate>();
            PackerMapping.Add(typeof(string), StringPacker);
            UnpackerMapping.Add(typeof(string), StringUnpacker);
        }
        public byte[] Pack(object o)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                Pack(ms, o);
                return ms.ToArray();
            }
        }
        public void Pack(Stream strm, object o)
        {
            if (o != null && o.GetType().IsPrimitive)
                throw new NotSupportedException();
            MsgPackWriter writer = new MsgPackWriter(strm);
            Pack(writer, o);
        }
        void Pack(MsgPackWriter writer, object o)
        {
            if (o == null)
            {
                writer.WriteNil();
                return;
            }
            Type t = o.GetType();
            if (t.IsPrimitive)
            {
                if (t.Equals(typeof(int)))
                {
                    writer.Write((int)o);
                }
                else if (t.Equals(typeof(uint)))
                {
                    writer.Write((uint)o);
                }
                else if (t.Equals(typeof(float)))
                {
                    writer.Write((float)o);
                }
                else if (t.Equals(typeof(double)))
                {
                    writer.Write((double)o);
                }
                else if (t.Equals(typeof(long)))
                {
                    writer.Write((long)o);
                }
                else if (t.Equals(typeof(ulong)))
                {
                    writer.Write((ulong)o);
                }
                else if (t.Equals(typeof(bool)))
                {
                    writer.Write((bool)o);
                }
                else if (t.Equals(typeof(byte)))
                {
                    writer.Write((byte)o);
                }
                else if (t.Equals(typeof(sbyte)))
                {
                    writer.Write((sbyte)o);
                }
                else if (t.Equals(typeof(short)))
                {
                    writer.Write((short)o);
                }
                else if (t.Equals(typeof(ushort)))
                {
                    writer.Write((ushort)o);
                }
                else if (t.Equals(typeof(char)))
                {
                    writer.Write((ushort)(char)o);
                }
                else
                {
                    throw new NotSupportedException();
                }
                return;
            }
            PackDelegate packer;
            if (PackerMapping.TryGetValue(t, out packer))
            {
                packer(this, writer, o);
                return;
            }
            if (t.IsArray)
            {
                Array ary = (Array)o;
                writer.WriteArrayHeader(ary.Length);
                for (int i = 0; i < ary.Length; i++)
                    Pack(writer, ary.GetValue(i));
                return;
            }
            ReflectionCacheEntry entry = ReflectionCache.Lookup(t);
            writer.WriteMapHeader(entry.FieldMap.Count);
            foreach (KeyValuePair<string, FieldInfo> pair in entry.FieldMap)
            {
                writer.Write(pair.Key, _buf);
                object v = pair.Value.GetValue(o);
                if (pair.Value.FieldType.IsInterface && v != null)
                {
                    writer.WriteArrayHeader(2);
                    writer.Write(v.GetType().FullName);
                }
                Pack(writer, v);
            }
        }
        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)
        {
            if (typeof(T).IsPrimitive)
                throw new NotSupportedException();
            MsgPackReader reader = new MsgPackReader(strm);
            return (T)Unpack(reader, typeof(T));
        }
        public object Unpack(Type type, byte[] buf)
        {
            return Unpack(type, buf, 0, buf.Length);
        }
        public object Unpack(Type type, byte[] buf, int offset, int size)
        {
            using (MemoryStream ms = new MemoryStream(buf, offset, size))
            {
                return Unpack(type, ms);
            }
        }
        public object Unpack(Type type, Stream strm)
        {
            if (type.IsPrimitive)
                throw new NotSupportedException();
            MsgPackReader reader = new MsgPackReader(strm);
            return Unpack(reader, type);
        }
        object Unpack(MsgPackReader reader, Type t)
        {
            if (t.IsPrimitive)
            {
                if (!reader.Read()) throw new FormatException();
                if (t.Equals(typeof(int)) && reader.IsSigned()) return reader.ValueSigned;
                else if (t.Equals(typeof(uint)) && reader.IsUnsigned()) return reader.ValueUnsigned;
                else if (t.Equals(typeof(float)) && reader.Type == TypePrefixes.Float) return reader.ValueFloat;
                else if (t.Equals(typeof(double)) && reader.Type == TypePrefixes.Double) return reader.ValueDouble;
                else if (t.Equals(typeof(long)))
                {
                    if (reader.IsSigned64())
                        return reader.ValueSigned64;
                    if (reader.IsSigned())
                        return (long)reader.ValueSigned;
                }
                else if (t.Equals(typeof(ulong)))
                {
                    if (reader.IsUnsigned64())
                        return reader.ValueUnsigned64;
                    if (reader.IsUnsigned())
                        return (ulong)reader.ValueUnsigned;
                }
                else if (t.Equals(typeof(bool)) && reader.IsBoolean()) return (reader.Type == TypePrefixes.True);
                else if (t.Equals(typeof(byte)) && reader.IsUnsigned()) return (byte)reader.ValueUnsigned;
                else if (t.Equals(typeof(sbyte)) && reader.IsSigned()) return (sbyte)reader.ValueSigned;
                else if (t.Equals(typeof(short)) && reader.IsSigned()) return (short)reader.ValueSigned;
                else if (t.Equals(typeof(ushort)) && reader.IsUnsigned()) return (ushort)reader.ValueUnsigned;
                else if (t.Equals(typeof(char)) && reader.IsUnsigned()) return (char)reader.ValueUnsigned;
                else throw new NotSupportedException();
            }
            UnpackDelegate unpacker;
            if (UnpackerMapping.TryGetValue(t, out unpacker))
                return unpacker(this, reader);
            if (t.IsArray)
            {
                if (!reader.Read() || (!reader.IsArray() && reader.Type != TypePrefixes.Nil))
                    throw new FormatException();
                if (reader.Type == TypePrefixes.Nil)
                    return null;
                Type et = t.GetElementType();
                Array ary = Array.CreateInstance(et, (int)reader.Length);
                for (int i = 0; i < ary.Length; i++)
                    ary.SetValue(Unpack(reader, et), i);
                return ary;
            }
            if (!reader.Read())
                throw new FormatException();
            if (reader.Type == TypePrefixes.Nil)
                return null;
            if (t.IsInterface)
            {
                if (reader.Type != TypePrefixes.FixArray && reader.Length != 2)
                    throw new FormatException();
                if (!reader.Read() || !reader.IsRaw())
                    throw new FormatException();
                CheckBufferSize((int)reader.Length);
                reader.ReadValueRaw(_buf, 0, (int)reader.Length);
                t = Type.GetType(Encoding.UTF8.GetString(_buf, 0, (int)reader.Length));
                if (!reader.Read() || reader.Type == TypePrefixes.Nil)
                    throw new FormatException();
            }
            if (!reader.IsMap())
                throw new FormatException();
            object o = FormatterServices.GetUninitializedObject(t);
            ReflectionCacheEntry entry = ReflectionCache.Lookup(t);
            int members = (int)reader.Length;
            for (int i = 0; i < members; i++)
            {
                if (!reader.Read() || !reader.IsRaw())
                    throw new FormatException();
                CheckBufferSize((int)reader.Length);
                reader.ReadValueRaw(_buf, 0, (int)reader.Length);
                string name = Encoding.UTF8.GetString(_buf, 0, (int)reader.Length);
                FieldInfo f;
                if (!entry.FieldMap.TryGetValue(name, out f))
                    throw new FormatException();
                f.SetValue(o, Unpack(reader, f.FieldType));
            }
            IDeserializationCallback callback = o as IDeserializationCallback;
            if (callback != null)
                callback.OnDeserialization(this);
            return o;
        }
        void CheckBufferSize(int size)
        {
            if (_buf.Length < size)
                Array.Resize<byte>(ref _buf, size);
        }
        static void StringPacker(ObjectPacker packer, MsgPackWriter writer, object o)
        {
            writer.Write(Encoding.UTF8.GetBytes((string)o));
        }
        static object StringUnpacker(ObjectPacker packer, MsgPackReader reader)
        {
            if (!reader.Read())
                throw new FormatException();
            if (reader.Type == TypePrefixes.Nil)
                return null;
            if (!reader.IsRaw())
                throw new FormatException();
            packer.CheckBufferSize((int)reader.Length);
            reader.ReadValueRaw(packer._buf, 0, (int)reader.Length);
            return Encoding.UTF8.GetString(packer._buf, 0, (int)reader.Length);
        }
    }
}
namespace MsgPack
{
    //
    // 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.IO;
    using System.Text;
    public class MsgPackWriter
    {
        Stream _strm;
        Encoding _encoding = Encoding.UTF8;
        Encoder _encoder = Encoding.UTF8.GetEncoder();
        byte[] _tmp = new byte[9];
        byte[] _buf = new byte[64];
        public MsgPackWriter(Stream strm)
        {
            _strm = strm;
        }
        public void Write(byte x)
        {
            if (x < 128)
            {
                _strm.WriteByte(x);
            }
            else
            {
                byte[] tmp = _tmp;
                tmp[0] = 0xcc;
                tmp[1] = x;
                _strm.Write(tmp, 0, 2);
            }
        }
        public void Write(ushort x)
        {
            if (x < 0x100)
            {
                Write((byte)x);
            }
            else
            {
                byte[] tmp = _tmp;
                tmp[0] = 0xcd;
                tmp[1] = (byte)(x >> 8);
                tmp[2] = (byte)x;
                _strm.Write(tmp, 0, 3);
            }
        }
        public void Write(char x)
        {
            Write((ushort)x);
        }
        public void Write(uint x)
        {
            if (x < 0x10000)
            {
                Write((ushort)x);
            }
            else
            {
                byte[] tmp = _tmp;
                tmp[0] = 0xce;
                tmp[1] = (byte)(x >> 24);
                tmp[2] = (byte)(x >> 16);
                tmp[3] = (byte)(x >> 8);
                tmp[4] = (byte)x;
                _strm.Write(tmp, 0, 5);
            }
        }
        public void Write(ulong x)
        {
            if (x < 0x100000000)
            {
                Write((uint)x);
            }
            else
            {
                byte[] tmp = _tmp;
                tmp[0] = 0xcf;
                tmp[1] = (byte)(x >> 56);
                tmp[2] = (byte)(x >> 48);
                tmp[3] = (byte)(x >> 40);
                tmp[4] = (byte)(x >> 32);
                tmp[5] = (byte)(x >> 24);
                tmp[6] = (byte)(x >> 16);
                tmp[7] = (byte)(x >> 8);
                tmp[8] = (byte)x;
                _strm.Write(tmp, 0, 9);
            }
        }
        public void Write(sbyte x)
        {
            if (x >= -32 && x <= -1)
            {
                _strm.WriteByte((byte)(0xe0 | (byte)x));
            }
            else if (x >= 0 && x <= 127)
            {
                _strm.WriteByte((byte)x);
            }
            else
            {
                byte[] tmp = _tmp;
                tmp[0] = 0xd0;
                tmp[1] = (byte)x;
                _strm.Write(tmp, 0, 2);
            }
        }
        public void Write(short x)
        {
            if (x >= sbyte.MinValue && x <= sbyte.MaxValue)
            {
                Write((sbyte)x);
            }
            else
            {
                byte[] tmp = _tmp;
                tmp[0] = 0xd1;
                tmp[1] = (byte)(x >> 8);
                tmp[2] = (byte)x;
                _strm.Write(tmp, 0, 3);
            }
        }
        public void Write(int x)
        {
            if (x >= short.MinValue && x <= short.MaxValue)
            {
                Write((short)x);
            }
            else
            {
                byte[] tmp = _tmp;
                tmp[0] = 0xd2;
                tmp[1] = (byte)(x >> 24);
                tmp[2] = (byte)(x >> 16);
                tmp[3] = (byte)(x >> 8);
                tmp[4] = (byte)x;
                _strm.Write(tmp, 0, 5);
            }
        }
        public void Write(long x)
        {
            if (x >= int.MinValue && x <= int.MaxValue)
            {
                Write((int)x);
            }
            else
            {
                byte[] tmp = _tmp;
                tmp[0] = 0xd3;
                tmp[1] = (byte)(x >> 56);
                tmp[2] = (byte)(x >> 48);
                tmp[3] = (byte)(x >> 40);
                tmp[4] = (byte)(x >> 32);
                tmp[5] = (byte)(x >> 24);
                tmp[6] = (byte)(x >> 16);
                tmp[7] = (byte)(x >> 8);
                tmp[8] = (byte)x;
                _strm.Write(tmp, 0, 9);
            }
        }
        public void WriteNil()
        {
            _strm.WriteByte(0xc0);
        }
        public void Write(bool x)
        {
            _strm.WriteByte((byte)(x ? 0xc3 : 0xc2));
        }
        public void Write(float x)
        {
            byte[] raw = BitConverter.GetBytes(x); // unsafeコードを使う?
            byte[] tmp = _tmp;
            tmp[0] = 0xca;
            if (BitConverter.IsLittleEndian)
            {
                tmp[1] = raw[3];
                tmp[2] = raw[2];
                tmp[3] = raw[1];
                tmp[4] = raw[0];
            }
            else
            {
                tmp[1] = raw[0];
                tmp[2] = raw[1];
                tmp[3] = raw[2];
                tmp[4] = raw[3];
            }
            _strm.Write(tmp, 0, 5);
        }
        public void Write(double x)
        {
            byte[] raw = BitConverter.GetBytes(x); // unsafeコードを使う?
            byte[] tmp = _tmp;
            tmp[0] = 0xcb;
            if (BitConverter.IsLittleEndian)
            {
                tmp[1] = raw[7];
                tmp[2] = raw[6];
                tmp[3] = raw[5];
                tmp[4] = raw[4];
                tmp[5] = raw[3];
                tmp[6] = raw[2];
                tmp[7] = raw[1];
                tmp[8] = raw[0];
            }
            else
            {
                tmp[1] = raw[0];
                tmp[2] = raw[1];
                tmp[3] = raw[2];
                tmp[4] = raw[3];
                tmp[5] = raw[4];
                tmp[6] = raw[5];
                tmp[7] = raw[6];
                tmp[8] = raw[7];
            }
            _strm.Write(tmp, 0, 9);
        }
        public void Write(byte[] bytes)
        {
            WriteRawHeader(bytes.Length);
            _strm.Write(bytes, 0, bytes.Length);
        }
        public void WriteRawHeader(int N)
        {
            WriteLengthHeader(N, 32, 0xa0, 0xda, 0xdb);
        }
        public void WriteArrayHeader(int N)
        {
            WriteLengthHeader(N, 16, 0x90, 0xdc, 0xdd);
        }
        public void WriteMapHeader(int N)
        {
            WriteLengthHeader(N, 16, 0x80, 0xde, 0xdf);
        }
        void WriteLengthHeader(int N, int fix_length, byte fix_prefix, byte len16bit_prefix, byte len32bit_prefix)
        {
            if (N < fix_length)
            {
                _strm.WriteByte((byte)(fix_prefix | N));
            }
            else
            {
                byte[] tmp = _tmp;
                int header_len;
                if (N < 0x10000)
                {
                    tmp[0] = len16bit_prefix;
                    tmp[1] = (byte)(N >> 8);
                    tmp[2] = (byte)N;
                    header_len = 3;
                }
                else
                {
                    tmp[0] = len32bit_prefix;
                    tmp[1] = (byte)(N >> 24);
                    tmp[2] = (byte)(N >> 16);
                    tmp[3] = (byte)(N >> 8);
                    tmp[4] = (byte)N;
                    header_len = 5;
                }
                _strm.Write(tmp, 0, header_len);
            }
        }
        public void Write(string x)
        {
            Write(x, false);
        }
        public void Write(string x, bool highProbAscii)
        {
            Write(x, _buf, highProbAscii);
        }
        public void Write(string x, byte[] buf)
        {
            Write(x, buf, false);
        }
        public unsafe void Write(string x, byte[] buf, bool highProbAscii)
        {
            Encoder encoder = _encoder;
            fixed (char* pstr = x)
            fixed (byte* pbuf = buf)
            {
                if (highProbAscii && x.Length <= buf.Length)
                {
                    bool isAsciiFullCompatible = true;
                    for (int i = 0; i < x.Length; i++)
                    {
                        int v = (int)pstr[i];
                        if (v > 0x7f)
                        {
                            isAsciiFullCompatible = false;
                            break;
                        }
                        buf[i] = (byte)v;
                    }
                    if (isAsciiFullCompatible)
                    {
                        WriteRawHeader(x.Length);
                        _strm.Write(buf, 0, x.Length);
                        return;
                    }
                }
                WriteRawHeader(encoder.GetByteCount(pstr, x.Length, true));
                int str_len = x.Length;
                char* p = pstr;
                int convertedChars, bytesUsed;
                bool completed = true;
                while (str_len > 0 || !completed)
                {
                    encoder.Convert(p, str_len, pbuf, buf.Length, false, out convertedChars, out bytesUsed, out completed);
                    _strm.Write(buf, 0, bytesUsed);
                    str_len -= convertedChars;
                    p += convertedChars;
                }
            }
        }
    }
}
namespace MsgPack
{
    //
    // 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.IO;
    using System.Text;
    public class MsgPackReader
    {
        Stream _strm;
        byte[] _tmp0 = new byte[8];
        byte[] _tmp1 = new byte[8];
        Encoding _encoding = Encoding.UTF8;
        Decoder _decoder = Encoding.UTF8.GetDecoder();
        byte[] _buf = new byte[64];
        public MsgPackReader(Stream strm)
        {
            _strm = strm;
        }
        public TypePrefixes Type { get; private set; }
        public bool ValueBoolean { get; private set; }
        public uint Length { get; private set; }
        public uint ValueUnsigned { get; private set; }
        public ulong ValueUnsigned64 { get; private set; }
        public int ValueSigned { get; private set; }
        public long ValueSigned64 { get; private set; }
        public float ValueFloat { get; private set; }
        public double ValueDouble { get; private set; }
        public bool IsSigned()
        {
            return this.Type == TypePrefixes.NegativeFixNum ||
                this.Type == TypePrefixes.PositiveFixNum ||
                this.Type == TypePrefixes.Int8 ||
                this.Type == TypePrefixes.Int16 ||
                this.Type == TypePrefixes.Int32;
        }
        public bool IsBoolean()
        {
            return this.Type == TypePrefixes.True || this.Type == TypePrefixes.False;
        }
        public bool IsSigned64()
        {
            return this.Type == TypePrefixes.Int64;
        }
        public bool IsUnsigned()
        {
            return this.Type == TypePrefixes.PositiveFixNum ||
                this.Type == TypePrefixes.UInt8 ||
                this.Type == TypePrefixes.UInt16 ||
                this.Type == TypePrefixes.UInt32;
        }
        public bool IsUnsigned64()
        {
            return this.Type == TypePrefixes.UInt64;
        }
        public bool IsRaw()
        {
            return this.Type == TypePrefixes.FixRaw || this.Type == TypePrefixes.Raw16 || this.Type == TypePrefixes.Raw32;
        }
        public bool IsArray()
        {
            return this.Type == TypePrefixes.FixArray || this.Type == TypePrefixes.Array16 || this.Type == TypePrefixes.Array32;
        }
        public bool IsMap()
        {
            return this.Type == TypePrefixes.FixMap || this.Type == TypePrefixes.Map16 || this.Type == TypePrefixes.Map32;
        }
        public bool Read()
        {
            byte[] tmp0 = _tmp0, tmp1 = _tmp1;
            int x = _strm.ReadByte();
            if (x < 0)
                return false; // EOS
            if (x >= 0x00 && x <= 0x7f)
            {
                this.Type = TypePrefixes.PositiveFixNum;
            }
            else if (x >= 0xe0 && x <= 0xff)
            {
                this.Type = TypePrefixes.NegativeFixNum;
            }
            else if (x >= 0xa0 && x <= 0xbf)
            {
                this.Type = TypePrefixes.FixRaw;
            }
            else if (x >= 0x90 && x <= 0x9f)
            {
                this.Type = TypePrefixes.FixArray;
            }
            else if (x >= 0x80 && x <= 0x8f)
            {
                this.Type = TypePrefixes.FixMap;
            }
            else
            {
                this.Type = (TypePrefixes)x;
            }
            switch (this.Type)
            {
                case TypePrefixes.Nil:
                    break;
                case TypePrefixes.False:
                    ValueBoolean = false;
                    break;
                case TypePrefixes.True:
                    ValueBoolean = true;
                    break;
                case TypePrefixes.Float:
                    _strm.Read(tmp0, 0, 4);
                    if (BitConverter.IsLittleEndian)
                    {
                        tmp1[0] = tmp0[3];
                        tmp1[1] = tmp0[2];
                        tmp1[2] = tmp0[1];
                        tmp1[3] = tmp0[0];
                        ValueFloat = BitConverter.ToSingle(tmp1, 0);
                    }
                    else
                    {
                        ValueFloat = BitConverter.ToSingle(tmp0, 0);
                    }
                    break;
                case TypePrefixes.Double:
                    _strm.Read(tmp0, 0, 8);
                    if (BitConverter.IsLittleEndian)
                    {
                        tmp1[0] = tmp0[7];
                        tmp1[1] = tmp0[6];
                        tmp1[2] = tmp0[5];
                        tmp1[3] = tmp0[4];
                        tmp1[4] = tmp0[3];
                        tmp1[5] = tmp0[2];
                        tmp1[6] = tmp0[1];
                        tmp1[7] = tmp0[0];
                        ValueDouble = BitConverter.ToDouble(tmp1, 0);
                    }
                    else
                    {
                        ValueDouble = BitConverter.ToDouble(tmp0, 0);
                    }
                    break;
                case TypePrefixes.NegativeFixNum:
                    ValueSigned = (x & 0x1f) - 0x20;
                    break;
                case TypePrefixes.PositiveFixNum:
                    ValueSigned = x & 0x7f;
                    ValueUnsigned = (uint)ValueSigned;
                    break;
                case TypePrefixes.UInt8:
                    x = _strm.ReadByte();
                    if (x < 0)
                        throw new FormatException();
                    ValueUnsigned = (uint)x;
                    break;
                case TypePrefixes.UInt16:
                    if (_strm.Read(tmp0, 0, 2) != 2)
                        throw new FormatException();
                    ValueUnsigned = ((uint)tmp0[0] << 8) | (uint)tmp0[1];
                    break;
                case TypePrefixes.UInt32:
                    if (_strm.Read(tmp0, 0, 4) != 4)
                        throw new FormatException();
                    ValueUnsigned = ((uint)tmp0[0] << 24) | ((uint)tmp0[1] << 16) | ((uint)tmp0[2] << 8) | (uint)tmp0[3];
                    break;
                case TypePrefixes.UInt64:
                    if (_strm.Read(tmp0, 0, 8) != 8)
                        throw new FormatException();
                    ValueUnsigned64 = ((ulong)tmp0[0] << 56) | ((ulong)tmp0[1] << 48) | ((ulong)tmp0[2] << 40) | ((ulong)tmp0[3] << 32) | ((ulong)tmp0[4] << 24) | ((ulong)tmp0[5] << 16) | ((ulong)tmp0[6] << 8) | (ulong)tmp0[7];
                    break;
                case TypePrefixes.Int8:
                    x = _strm.ReadByte();
                    if (x < 0)
                        throw new FormatException();
                    ValueSigned = (sbyte)x;
                    break;
                case TypePrefixes.Int16:
                    if (_strm.Read(tmp0, 0, 2) != 2)
                        throw new FormatException();
                    ValueSigned = (short)((tmp0[0] << 8) | tmp0[1]);
                    break;
                case TypePrefixes.Int32:
                    if (_strm.Read(tmp0, 0, 4) != 4)
                        throw new FormatException();
                    ValueSigned = (tmp0[0] << 24) | (tmp0[1] << 16) | (tmp0[2] << 8) | tmp0[3];
                    break;
                case TypePrefixes.Int64:
                    if (_strm.Read(tmp0, 0, 8) != 8)
                        throw new FormatException();
                    ValueSigned64 = ((long)tmp0[0] << 56) | ((long)tmp0[1] << 48) | ((long)tmp0[2] << 40) | ((long)tmp0[3] << 32) | ((long)tmp0[4] << 24) | ((long)tmp0[5] << 16) | ((long)tmp0[6] << 8) | (long)tmp0[7];
                    break;
                case TypePrefixes.FixRaw:
                    Length = (uint)(x & 0x1f);
                    break;
                case TypePrefixes.FixArray:
                case TypePrefixes.FixMap:
                    Length = (uint)(x & 0xf);
                    break;
                case TypePrefixes.Raw16:
                case TypePrefixes.Array16:
                case TypePrefixes.Map16:
                    if (_strm.Read(tmp0, 0, 2) != 2)
                        throw new FormatException();
                    Length = ((uint)tmp0[0] << 8) | (uint)tmp0[1];
                    break;
                case TypePrefixes.Raw32:
                case TypePrefixes.Array32:
                case TypePrefixes.Map32:
                    if (_strm.Read(tmp0, 0, 4) != 4)
                        throw new FormatException();
                    Length = ((uint)tmp0[0] << 24) | ((uint)tmp0[1] << 16) | ((uint)tmp0[2] << 8) | (uint)tmp0[3];
                    break;
                default:
                    throw new FormatException();
            }
            return true;
        }
        public int ReadValueRaw(byte[] buf, int offset, int count)
        {
            return _strm.Read(buf, offset, count);
        }
        public string ReadRawString()
        {
            return ReadRawString(_buf);
        }
        public unsafe string ReadRawString(byte[] buf)
        {
            if (this.Length < buf.Length)
            {
                if (ReadValueRaw(buf, 0, (int)this.Length) != this.Length)
                {
                    throw new FormatException();
                }
                return _encoding.GetString(buf, 0, (int)this.Length);
            }
            // Poor implementation
            byte[] tmp = new byte[(int)this.Length];
            if (ReadValueRaw(tmp, 0, tmp.Length) != tmp.Length)
                throw new FormatException();
            return _encoding.GetString(tmp);
        }
    }
}
namespace MsgPack
{
    //
    // 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;
    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
    }
}
namespace MsgPack
{
    //
    // 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;
    using System.Collections.Generic;
    using System.IO;
    using System.Reflection;
    public class BoxingPacker
    {
        static Type KeyValuePairDefinitionType;
        static BoxingPacker()
        {
            KeyValuePairDefinitionType = typeof(KeyValuePair<object, object>).GetGenericTypeDefinition();
        }
        public void Pack(Stream strm, object o)
        {
            MsgPackWriter writer = new MsgPackWriter(strm);
            Pack(writer, o);
        }
        public byte[] Pack(object o)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                Pack(ms, o);
                return ms.ToArray();
            }
        }
        void Pack(MsgPackWriter writer, object o)
        {
            if (o == null)
            {
                writer.WriteNil();
                return;
            }
            Type t = o.GetType();
            if (t.IsPrimitive)
            {
                if (t.Equals(typeof(int))) writer.Write((int)o);
                else if (t.Equals(typeof(uint))) writer.Write((uint)o);
                else if (t.Equals(typeof(float))) writer.Write((float)o);
                else if (t.Equals(typeof(double))) writer.Write((double)o);
                else if (t.Equals(typeof(long))) writer.Write((long)o);
                else if (t.Equals(typeof(ulong))) writer.Write((ulong)o);
                else if (t.Equals(typeof(bool))) writer.Write((bool)o);
                else if (t.Equals(typeof(byte))) writer.Write((byte)o);
                else if (t.Equals(typeof(sbyte))) writer.Write((sbyte)o);
                else if (t.Equals(typeof(short))) writer.Write((short)o);
                else if (t.Equals(typeof(ushort))) writer.Write((ushort)o);
                else throw new NotSupportedException();  // char?
                return;
            }
            IDictionary dic = o as IDictionary;
            if (dic != null)
            {
                writer.WriteMapHeader(dic.Count);
                foreach (System.Collections.DictionaryEntry e in dic)
                {
                    Pack(writer, e.Key);
                    Pack(writer, e.Value);
                }
                return;
            }
            if (t.IsArray)
            {
                Array ary = (Array)o;
                Type et = t.GetElementType();
                // KeyValuePair<K,V>[] (Map Type)
                if (et.IsGenericType && et.GetGenericTypeDefinition().Equals(KeyValuePairDefinitionType))
                {
                    PropertyInfo propKey = et.GetProperty("Key");
                    PropertyInfo propValue = et.GetProperty("Value");
                    writer.WriteMapHeader(ary.Length);
                    for (int i = 0; i < ary.Length; i++)
                    {
                        object e = ary.GetValue(i);
                        Pack(writer, propKey.GetValue(e, null));
                        Pack(writer, propValue.GetValue(e, null));
                    }
                    return;
                }
                // Array
                writer.WriteArrayHeader(ary.Length);
                for (int i = 0; i < ary.Length; i++)
                    Pack(writer, ary.GetValue(i));
                return;
            }
        }
        public object Unpack(Stream strm)
        {
            MsgPackReader reader = new MsgPackReader(strm);
            return Unpack(reader);
        }
        public object Unpack(byte[] buf, int offset, int size)
        {
            using (MemoryStream ms = new MemoryStream(buf, offset, size))
            {
                return Unpack(ms);
            }
        }
        public object Unpack(byte[] buf)
        {
            return Unpack(buf, 0, buf.Length);
        }
        object Unpack(MsgPackReader reader)
        {
            if (!reader.Read())
            {
                throw new FormatException();
            }
            switch (reader.Type)
            {
                case TypePrefixes.PositiveFixNum:
                case TypePrefixes.NegativeFixNum:
                case TypePrefixes.Int8:
                case TypePrefixes.Int16:
                case TypePrefixes.Int32:
                    return reader.ValueSigned;
                case TypePrefixes.Int64:
                    return reader.ValueSigned64;
                case TypePrefixes.UInt8:
                case TypePrefixes.UInt16:
                case TypePrefixes.UInt32:
                    return reader.ValueUnsigned;
                case TypePrefixes.UInt64:
                    return reader.ValueUnsigned64;
                case TypePrefixes.True:
                    return true;
                case TypePrefixes.False:
                    return false;
                case TypePrefixes.Float:
                    return reader.ValueFloat;
                case TypePrefixes.Double:
                    return reader.ValueDouble;
                case TypePrefixes.Nil:
                    return null;
                case TypePrefixes.FixRaw:
                case TypePrefixes.Raw16:
                case TypePrefixes.Raw32:
                    byte[] raw = new byte[reader.Length];
                    reader.ReadValueRaw(raw, 0, raw.Length);
                    return raw;
                case TypePrefixes.FixArray:
                case TypePrefixes.Array16:
                case TypePrefixes.Array32:
                    object[] ary = new object[reader.Length];
                    for (int i = 0; i < ary.Length; i++)
                        ary[i] = Unpack(reader);
                    return ary;
                case TypePrefixes.FixMap:
                case TypePrefixes.Map16:
                case TypePrefixes.Map32:
                    IDictionary<object, object> dic = new Dictionary<object, object>((int)reader.Length);
                    int count = (int)reader.Length;
                    for (int i = 0; i < count; i++)
                    {
                        object k = Unpack(reader);
                        object v = Unpack(reader);
                        dic.Add(k, v);
                    }
                    return dic;
                default:
                    throw new FormatException();
            }
        }
    }
}
namespace MsgPack.Compiler
{
    //
    // 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.
    //
    public enum VariableType
    {
        Local,
        Arg
    }
}
namespace MsgPack.Compiler
{
    //
    // 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.Reflection.Emit;
    public class Variable
    {
        Variable(VariableType type, int index)
        {
            this.VarType = type;
            this.Index = index;
        }
        public static Variable CreateLocal(LocalBuilder local)
        {
            return new Variable(VariableType.Local, local.LocalIndex);
        }
        public static Variable CreateArg(int idx)
        {
            return new Variable(VariableType.Arg, idx);
        }
        public VariableType VarType { get; set; }
        public int Index { get; set; }
    }
}
namespace MsgPack.Compiler
{
    //
    // 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.Reflection;
    using System.Reflection.Emit;
    using System.Runtime.Serialization;
    static class PackILGenerator
    {
        #region Pack IL Generator
        public static void EmitPackCode
                        (
                            Type type
                            , MethodInfo mi
                            , ILGenerator il
                            , Func<Type, MemberInfo[]> targetMemberSelector
                            , Func<MemberInfo, string> memberNameFormatter
                            , Func<Type, MethodInfo> lookupPackMethod
                        )
        {
            if (type.IsPrimitive || type.IsInterface)
            {
                throw new NotSupportedException();
            }
            Variable arg_writer = Variable.CreateArg(0);
            Variable arg_obj = Variable.CreateArg(1);
            Variable local_i = Variable.CreateLocal(il.DeclareLocal(typeof(int)));
            if (!type.IsValueType)
            {
                // null check
                Label notNullLabel = il.DefineLabel();
                il.EmitLd(arg_obj);
                il.Emit(OpCodes.Brtrue_S, notNullLabel);
                il.EmitLd(arg_writer);
                il.Emit(OpCodes.Call, typeof(MsgPackWriter).GetMethod("WriteNil", new Type[0]));
                il.Emit(OpCodes.Ret);
                il.MarkLabel(notNullLabel);
            }
            if (type.IsArray)
            {
                EmitPackArrayCode(mi, il, type, arg_writer, arg_obj, local_i, lookupPackMethod);
                goto FinallyProcess;
            }
            // MsgPackWriter.WriteMapHeader
            MemberInfo[] members = targetMemberSelector(type);
            il.EmitLd(arg_writer);
            il.EmitLdc(members.Length);
            il.Emit(OpCodes.Callvirt, typeof(MsgPackWriter).GetMethod("WriteMapHeader", new Type[] { typeof(int) }));
            for (int i = 0; i < members.Length; i++)
            {
                MemberInfo m = members[i];
                Type mt = m.GetMemberType();
                // write field-name
                il.EmitLd(arg_writer);
                il.EmitLdstr(memberNameFormatter(m));
                il.EmitLd_True();
                il.Emit(OpCodes.Call, typeof(MsgPackWriter).GetMethod("Write", new Type[] { typeof(string), typeof(bool) }));
                // write value
                EmitPackMemberValueCode(mt, il, arg_writer, arg_obj, m, null, type, mi, lookupPackMethod);
            }
        FinallyProcess:
            il.Emit(OpCodes.Ret);
        }
        static void EmitPackArrayCode
                            (
                                MethodInfo mi
                                , ILGenerator il
                                , Type t
                                , Variable var_writer
                                , Variable var_obj
                                , Variable var_loop
                                , Func<Type, MethodInfo> lookupPackMethod
                            )
        {
            Type et = t.GetElementType();
            il.EmitLd(var_writer, var_obj);
            il.Emit(OpCodes.Ldlen);
            il.Emit(OpCodes.Call, typeof(MsgPackWriter).GetMethod("WriteArrayHeader", new Type[] { typeof(int) }));
            Label beginLabel = il.DefineLabel();
            Label exprLabel = il.DefineLabel();
            // for-loop: init loop counter
            il.EmitLdc(0);
            il.EmitSt(var_loop);
            // jump
            il.Emit(OpCodes.Br_S, exprLabel);
            // mark begin-label
            il.MarkLabel(beginLabel);
            // write element
            EmitPackMemberValueCode(et, il, var_writer, var_obj, null, var_loop, t, mi, lookupPackMethod);
            // increment loop-counter
            il.EmitLd(var_loop);
            il.Emit(OpCodes.Ldc_I4_1);
            il.Emit(OpCodes.Add);
            il.EmitSt(var_loop);
            // mark expression label
            il.MarkLabel(exprLabel);
            // expression
            il.EmitLd(var_loop, var_obj);
            il.Emit(OpCodes.Ldlen);
            il.Emit(OpCodes.Blt_S, beginLabel);
        }
        /// <param name="m">(optional)</param>
        /// <param name="elementIdx">(optional)</param>
        static void EmitPackMemberValueCode(Type type, ILGenerator il, Variable var_writer, Variable var_obj,
            MemberInfo m, Variable elementIdx, Type currentType, MethodInfo currentMethod, Func<Type, MethodInfo> lookupPackMethod)
        {
            MethodInfo mi;
            il.EmitLd(var_writer, var_obj);
            if (m != null)
                il.EmitLdMember(m);
            if (elementIdx != null)
            {
                il.EmitLd(elementIdx);
                il.Emit(OpCodes.Ldelem, type);
            }
            if (type.IsPrimitive)
            {
                mi = typeof(MsgPackWriter).GetMethod("Write", new Type[] { type });
            }
            else
            {
                if (currentType == type)
                {
                    mi = currentMethod;
                }
                else
                {
                    mi = lookupPackMethod(type);
                }
            }
            il.Emit(OpCodes.Call, mi);
        }
        #endregion
        #region Unpack IL Generator
        public static void EmitUnpackCode(Type type, MethodInfo mi, ILGenerator il,
            Func<Type, MemberInfo[]> targetMemberSelector,
            Func<MemberInfo, string> memberNameFormatter,
            Func<Type, MethodInfo> lookupUnpackMethod,
            Func<Type, IDictionary<string, int>> lookupMemberMapping,
            MethodInfo lookupMemberMappingMethod)
        {
            if (type.IsArray)
            {
                EmitUnpackArrayCode(type, mi, il, targetMemberSelector, memberNameFormatter, lookupUnpackMethod);
            }
            else
            {
                EmitUnpackMapCode(type, mi, il, targetMemberSelector, memberNameFormatter, lookupUnpackMethod, lookupMemberMapping, lookupMemberMappingMethod);
            }
        }
        static void EmitUnpackMapCode(Type type, MethodInfo mi, ILGenerator il,
            Func<Type, MemberInfo[]> targetMemberSelector,
            Func<MemberInfo, string> memberNameFormatter,
            Func<Type, MethodInfo> lookupUnpackMethod,
            Func<Type, IDictionary<string, int>> lookupMemberMapping,
            MethodInfo lookupMemberMappingMethod)
        {
            MethodInfo failedMethod = typeof(PackILGenerator).GetMethod("UnpackFailed", BindingFlags.Static | BindingFlags.NonPublic);
            MemberInfo[] members = targetMemberSelector(type);
            IDictionary<string, int> member_mapping = lookupMemberMapping(type);
            for (int i = 0; i < members.Length; i++)
                member_mapping.Add(memberNameFormatter(members[i]), i);
            Variable msgpackReader = Variable.CreateArg(0);
            Variable obj = Variable.CreateLocal(il.DeclareLocal(type));
            Variable num_of_fields = Variable.CreateLocal(il.DeclareLocal(typeof(int)));
            Variable loop_idx = Variable.CreateLocal(il.DeclareLocal(typeof(int)));
            Variable mapping = Variable.CreateLocal(il.DeclareLocal(typeof(IDictionary<string, int>)));
            Variable switch_idx = Variable.CreateLocal(il.DeclareLocal(typeof(int)));
            Variable var_type = Variable.CreateLocal(il.DeclareLocal(typeof(Type)));
            // if (!MsgPackReader.Read()) UnpackFailed ();
            // if (MsgPackReader.Type == TypePrefixes.Nil) return null;
            // if (!MsgPackReader.IsMap ()) UnpackFailed ();
            EmitUnpackReadAndTypeCheckCode(il, msgpackReader, typeof(MsgPackReader).GetMethod("IsMap"), failedMethod, true);
            // type = typeof (T)
            il.Emit(OpCodes.Ldtoken, type);
            il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
            il.EmitSt(var_type);
            // mapping = LookupMemberMapping (typeof (T))
            il.EmitLd(var_type);
            il.Emit(OpCodes.Call, lookupMemberMappingMethod);
            il.EmitSt(mapping);
            // object o = FormatterServices.GetUninitializedObject (Type);
            il.EmitLd(var_type);
            il.Emit(OpCodes.Call, typeof(FormatterServices).GetMethod("GetUninitializedObject"));
            il.Emit(OpCodes.Castclass, type);
            il.EmitSt(obj);
            // num_of_fields = (int)reader.Length
            il.EmitLd(msgpackReader);
            il.Emit(OpCodes.Call, typeof(MsgPackReader).GetProperty("Length").GetGetMethod());
            il.EmitSt(num_of_fields);
            // Loop labels
            Label lblLoopStart = il.DefineLabel();
            Label lblLoopExpr = il.DefineLabel();
            // i = 0;
            il.EmitLdc(0);
            il.EmitSt(loop_idx);
            il.Emit(OpCodes.Br, lblLoopExpr);
            il.MarkLabel(lblLoopStart);
            /* process */
            // if (!MsgPackReader.Read() || !MsgPackReader.IsRaw()) UnpackFailed();
            EmitUnpackReadAndTypeCheckCode(il, msgpackReader, typeof(MsgPackReader).GetMethod("IsRaw"), failedMethod, false);
            // MsgPackReader.ReadRawString ()
            // if (!Dictionary.TryGetValue (,)) UnpackFailed();
            Label lbl3 = il.DefineLabel();
            il.EmitLd(mapping);
            il.EmitLd(msgpackReader);
            il.Emit(OpCodes.Call, typeof(MsgPackReader).GetMethod("ReadRawString", new Type[0]));
            il.Emit(OpCodes.Ldloca_S, (byte)switch_idx.Index);
            il.Emit(OpCodes.Callvirt, typeof(IDictionary<string, int>).GetMethod("TryGetValue"));
            il.Emit(OpCodes.Brtrue, lbl3);
            il.Emit(OpCodes.Call, failedMethod);
            il.MarkLabel(lbl3);
            // switch
            Label[] switchCases = new Label[members.Length];
            for (int i = 0; i < switchCases.Length; i++)
                switchCases[i] = il.DefineLabel();
            Label switchCaseEndLabel = il.DefineLabel();
            il.EmitLd(switch_idx);
            il.Emit(OpCodes.Switch, switchCases);
            il.Emit(OpCodes.Call, failedMethod);
            for (int i = 0; i < switchCases.Length; i++)
            {
                il.MarkLabel(switchCases[i]);
                MemberInfo minfo = members[i];
                Type mt = minfo.GetMemberType();
                MethodInfo unpack_method = lookupUnpackMethod(mt);
                il.EmitLd(obj);
                il.EmitLd(msgpackReader);
                il.Emit(OpCodes.Call, unpack_method);
                il.EmitStMember(minfo);
                il.Emit(OpCodes.Br, switchCaseEndLabel);
            }
            il.MarkLabel(switchCaseEndLabel);
            // i ++
            il.EmitLd(loop_idx);
            il.EmitLdc(1);
            il.Emit(OpCodes.Add);
            il.EmitSt(loop_idx);
            // i < num_of_fields;
            il.MarkLabel(lblLoopExpr);
            il.EmitLd(loop_idx);
            il.EmitLd(num_of_fields);
            il.Emit(OpCodes.Blt, lblLoopStart);
            // return
            il.EmitLd(obj);
            il.Emit(OpCodes.Ret);
        }
        static void EmitUnpackArrayCode(Type arrayType, MethodInfo mi, ILGenerator il,
            Func<Type, MemberInfo[]> targetMemberSelector,
            Func<MemberInfo, string> memberNameFormatter,
            Func<Type, MethodInfo> lookupUnpackMethod)
        {
            Type elementType = arrayType.GetElementType();
            MethodInfo failedMethod = typeof(PackILGenerator).GetMethod("UnpackFailed", BindingFlags.Static | BindingFlags.NonPublic);
            Variable msgpackReader = Variable.CreateArg(0);
            Variable obj = Variable.CreateLocal(il.DeclareLocal(arrayType));
            Variable num_of_elements = Variable.CreateLocal(il.DeclareLocal(typeof(int)));
            Variable loop_idx = Variable.CreateLocal(il.DeclareLocal(typeof(int)));
            Variable type = Variable.CreateLocal(il.DeclareLocal(typeof(Type)));
            // if (!MsgPackReader.Read() || !MsgPackReader.IsArray ()) UnpackFailed ();
            EmitUnpackReadAndTypeCheckCode(il, msgpackReader, typeof(MsgPackReader).GetMethod("IsArray"), failedMethod, true);
            // type = typeof (T)
            il.Emit(OpCodes.Ldtoken, elementType);
            il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
            il.EmitSt(type);
            // num_of_elements = (int)reader.Length
            il.EmitLd(msgpackReader);
            il.Emit(OpCodes.Call, typeof(MsgPackReader).GetProperty("Length").GetGetMethod());
            il.EmitSt(num_of_elements);
            // object o = Array.CreateInstance (Type, Length);
            il.EmitLd(type);
            il.EmitLd(num_of_elements);
            il.Emit(OpCodes.Call, typeof(Array).GetMethod("CreateInstance", new Type[] { typeof(Type), typeof(int) }));
            il.Emit(OpCodes.Castclass, arrayType);
            il.EmitSt(obj);
            // Unpack element method
            MethodInfo unpack_method = lookupUnpackMethod(elementType);
            // Loop labels
            Label lblLoopStart = il.DefineLabel();
            Label lblLoopExpr = il.DefineLabel();
            // i = 0;
            il.EmitLdc(0);
            il.EmitSt(loop_idx);
            il.Emit(OpCodes.Br, lblLoopExpr);
            il.MarkLabel(lblLoopStart);
            /* process */
            il.EmitLd(obj, loop_idx);
            il.EmitLd(msgpackReader);
            il.Emit(OpCodes.Call, unpack_method);
            il.Emit(OpCodes.Stelem, elementType);
            // i ++
            il.EmitLd(loop_idx);
            il.EmitLdc(1);
            il.Emit(OpCodes.Add);
            il.EmitSt(loop_idx);
            // i < num_of_fields;
            il.MarkLabel(lblLoopExpr);
            il.EmitLd(loop_idx);
            il.EmitLd(num_of_elements);
            il.Emit(OpCodes.Blt, lblLoopStart);
            // return
            il.EmitLd(obj);
            il.Emit(OpCodes.Ret);
        }
        static void EmitUnpackReadAndTypeCheckCode(ILGenerator il, Variable msgpackReader, MethodInfo typeCheckMethod, MethodInfo failedMethod, bool nullCheckAndReturn)
        {
            Label lblFailed = il.DefineLabel();
            Label lblNullReturn = nullCheckAndReturn ? il.DefineLabel() : default(Label);
            Label lblPassed = il.DefineLabel();
            il.EmitLd(msgpackReader);
            il.Emit(OpCodes.Call, typeof(MsgPackReader).GetMethod("Read"));
            il.Emit(OpCodes.Brfalse_S, lblFailed);
            if (nullCheckAndReturn)
            {
                il.EmitLd(msgpackReader);
                il.Emit(OpCodes.Call, typeof(MsgPackReader).GetProperty("Type").GetGetMethod());
                il.EmitLdc((int)TypePrefixes.Nil);
                il.Emit(OpCodes.Beq_S, lblNullReturn);
            }
            il.EmitLd(msgpackReader);
            il.Emit(OpCodes.Call, typeCheckMethod);
            il.Emit(OpCodes.Brtrue_S, lblPassed);
            il.Emit(OpCodes.Br, lblFailed);
            if (nullCheckAndReturn)
            {
                il.MarkLabel(lblNullReturn);
                il.Emit(OpCodes.Ldnull);
                il.Emit(OpCodes.Ret);
            }
            il.MarkLabel(lblFailed);
            il.Emit(OpCodes.Call, failedMethod);
            il.MarkLabel(lblPassed);
        }
        /// <summary>Exception Helper</summary>
        internal static void UnpackFailed()
        {
            throw new FormatException();
        }
        #endregion
        #region Misc
        static Type GetMemberType(this MemberInfo mi)
        {
            if (mi.MemberType == MemberTypes.Field)
                return ((FieldInfo)mi).FieldType;
            if (mi.MemberType == MemberTypes.Property)
                return ((PropertyInfo)mi).PropertyType;
            throw new ArgumentException();
        }
        #endregion
    }
}
namespace MsgPack.Compiler
{
    //
    // 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.Reflection;
    using System.Reflection.Emit;
    public static class EmitExtensions
    {
        public static void EmitLd(this ILGenerator il, Variable v)
        {
            switch (v.VarType)
            {
                case VariableType.Arg:
                    EmitLdarg(il, v);
                    break;
                case VariableType.Local:
                    EmitLdloc(il, v);
                    break;
                default:
                    throw new ArgumentException();
            }
        }
        public static void EmitLd(this ILGenerator il, params Variable[] list)
        {
            for (int i = 0; i < list.Length; i++)
                EmitLd(il, list[i]);
        }
        public static void EmitLdarg(this ILGenerator il, Variable v)
        {
            if (v.VarType != VariableType.Arg)
                throw new ArgumentException();
            switch (v.Index)
            {
                case 0: il.Emit(OpCodes.Ldarg_0); return;
                case 1: il.Emit(OpCodes.Ldarg_1); return;
                case 2: il.Emit(OpCodes.Ldarg_2); return;
                case 3: il.Emit(OpCodes.Ldarg_3); return;
            }
            if (v.Index <= byte.MaxValue)
            {
                il.Emit(OpCodes.Ldarg_S, (byte)v.Index);
            }
            else if (v.Index <= short.MaxValue)
            {
                il.Emit(OpCodes.Ldarg, v.Index);
            }
            else
            {
                throw new FormatException();
            }
        }
        public static void EmitLdloc(this ILGenerator il, Variable v)
        {
            if (v.VarType != VariableType.Local)
                throw new ArgumentException();
            switch (v.Index)
            {
                case 0: il.Emit(OpCodes.Ldloc_0); return;
                case 1: il.Emit(OpCodes.Ldloc_1); return;
                case 2: il.Emit(OpCodes.Ldloc_2); return;
                case 3: il.Emit(OpCodes.Ldloc_3); return;
            }
            if (v.Index <= byte.MaxValue)
            {
                il.Emit(OpCodes.Ldloc_S, (byte)v.Index);
            }
            else if (v.Index <= short.MaxValue)
            {
                il.Emit(OpCodes.Ldloc, v.Index);
            }
            else
            {
                throw new FormatException();
            }
        }
        public static void EmitSt(this ILGenerator il, Variable v)
        {
            switch (v.VarType)
            {
                case VariableType.Arg:
                    EmitStarg(il, v);
                    break;
                case VariableType.Local:
                    EmitStloc(il, v);
                    break;
                default:
                    throw new ArgumentException();
            }
        }
        public static void EmitStarg(this ILGenerator il, Variable v)
        {
            if (v.VarType != VariableType.Arg)
                throw new ArgumentException();
            if (v.Index <= byte.MaxValue)
            {
                il.Emit(OpCodes.Starg_S, (byte)v.Index);
            }
            else if (v.Index <= short.MaxValue)
            {
                il.Emit(OpCodes.Starg, v.Index);
            }
            else
            {
                throw new FormatException();
            }
        }
        public static void EmitStloc(this ILGenerator il, Variable v)
        {
            if (v.VarType != VariableType.Local)
                throw new ArgumentException();
            switch (v.Index)
            {
                case 0: il.Emit(OpCodes.Stloc_0); return;
                case 1: il.Emit(OpCodes.Stloc_1); return;
                case 2: il.Emit(OpCodes.Stloc_2); return;
                case 3: il.Emit(OpCodes.Stloc_3); return;
            }
            if (v.Index <= byte.MaxValue)
            {
                il.Emit(OpCodes.Stloc_S, (byte)v.Index);
            }
            else if (v.Index <= short.MaxValue)
            {
                il.Emit(OpCodes.Stloc, v.Index);
            }
            else
            {
                throw new FormatException();
            }
        }
        public static void EmitLdc(this ILGenerator il, int v)
        {
            switch (v)
            {
                case 0: il.Emit(OpCodes.Ldc_I4_0); return;
                case 1: il.Emit(OpCodes.Ldc_I4_1); return;
                case 2: il.Emit(OpCodes.Ldc_I4_2); return;
                case 3: il.Emit(OpCodes.Ldc_I4_3); return;
                case 4: il.Emit(OpCodes.Ldc_I4_4); return;
                case 5: il.Emit(OpCodes.Ldc_I4_5); return;
                case 6: il.Emit(OpCodes.Ldc_I4_6); return;
                case 7: il.Emit(OpCodes.Ldc_I4_7); return;
                case 8: il.Emit(OpCodes.Ldc_I4_8); return;
                case -1: il.Emit(OpCodes.Ldc_I4_M1); return;
            }
            if (v <= sbyte.MaxValue && v >= sbyte.MinValue)
            {
                il.Emit(OpCodes.Ldc_I4_S, (sbyte)v);
            }
            else
            {
                il.Emit(OpCodes.Ldc_I4, v);
            }
        }
        public static void EmitLd_False(this ILGenerator il)
        {
            il.Emit(OpCodes.Ldc_I4_1);
        }
        public static void EmitLd_True(this ILGenerator il)
        {
            il.Emit(OpCodes.Ldc_I4_1);
        }
        public static void EmitLdstr(this ILGenerator il, string v)
        {
            il.Emit(OpCodes.Ldstr, v);
        }
        public static void EmitLdMember(this ILGenerator il, MemberInfo m)
        {
            if (m.MemberType == MemberTypes.Field)
            {
                il.Emit(OpCodes.Ldfld, (FieldInfo)m);
            }
            else if (m.MemberType == MemberTypes.Property)
            {
                il.Emit(OpCodes.Callvirt, ((PropertyInfo)m).GetGetMethod(true));
            }
            else
            {
                throw new ArgumentException();
            }
        }
        public static void EmitStMember(this ILGenerator il, MemberInfo m)
        {
            if (m.MemberType == MemberTypes.Field)
            {
                il.Emit(OpCodes.Stfld, (FieldInfo)m);
            }
            else if (m.MemberType == MemberTypes.Property)
            {
                il.Emit(OpCodes.Callvirt, ((PropertyInfo)m).GetSetMethod(true));
            }
            else
            {
                throw new ArgumentException();
            }
        }
    }
}