Exemple #1
0
        /// <summary>
        /// 生成指定类型的 Switch 代码块,使用 Hash 匹配算法。
        /// </summary>
        /// <typeparam name="T">指定类型</typeparam>
        /// <param name="ilGen">ILGenerator IL 指令生成器</param>
        /// <param name="emitLoadValue">生成加载 Switch 参数的指令的委托</param>
        /// <param name="comparer">哈希比较器</param>
        /// <param name="cases">case 标签块集合</param>
        /// <param name="defaultLabel">默认标签块</param>
        /// <param name="emitLoadItem">生成加载指定 Case 块值的指定的委托</param>
        public static void HashSwitch <T>(this ILGenerator ilGen,
                                          Action <ILGenerator> emitLoadValue,
                                          Action <ILGenerator, T> emitLoadItem,
                                          IHashComparer <T> comparer,
                                          CaseInfo <T>[] cases,
                                          Label defaultLabel)
        {
            cases = (CaseInfo <T>[])cases.Clone();

            foreach (var item in cases)
            {
                item.HashCode = comparer.GetHashCode(item.Value);
            }

            Array.Sort(cases);

            var groupedCases = new Dictionary <int, List <CaseInfo <T> > >();

            foreach (var item in cases)
            {
                groupedCases.TryGetValue(item.HashCode, out var items);

                if (items is null)
                {
                    items = new List <CaseInfo <T> >
                    {
                        item
                    };

                    groupedCases.Add(item.HashCode, items);
                }
                else
                {
                    items.Add(item);
                }
            }

            var hashCodeLocal = ilGen.DeclareLocal(typeof(int));

            emitLoadValue(ilGen);
            comparer.EmitGetHashCode(ilGen);
            ilGen.StoreLocal(hashCodeLocal);

            SwitchObject(
                ilGen,
                emitLoadValue,
                il => il.LoadLocal(hashCodeLocal),
                emitLoadItem,
                comparer,
                groupedCases.ToList(),
                defaultLabel,
                0,
                (groupedCases.Count - 1) / 2,
                groupedCases.Count - 1);
        }
Exemple #2
0
        private static void SwitchObject <T>(this ILGenerator ilGen,
                                             Action <ILGenerator> emitLoadValue,
                                             Action <ILGenerator> emitLoadHashCode,
                                             Action <ILGenerator, T> emitLoadItem,
                                             IHashComparer <T> comparer,
                                             List <KeyValuePair <int, List <CaseInfo <T> > > > cases,
                                             Label defaultLabel,
                                             int begin,
                                             int index,
                                             int end)
        {
            if (begin > end)
            {
                return;
            }

            if (begin == end)
            {
                emitLoadHashCode(ilGen);
                ilGen.LoadConstant(cases[begin].Key);
                ilGen.BranchIfNotEqualUnsigned(defaultLabel);

                if (SwitchDoNotVerify && cases[begin].Value.Count == 1)
                {
                    ilGen.Branch(cases[begin].Value[0].Label);
                }
                else
                {
                    foreach (var item in cases[begin].Value)
                    {
                        emitLoadValue(ilGen);
                        emitLoadItem(ilGen, item.Value);
                        comparer.EmitEquals(ilGen);
                        ilGen.BranchTrue(item.Label);
                    }
                }

                ilGen.Branch(defaultLabel);

                return;
            }

            if (begin + 1 == end)
            {
                var endLabel = ilGen.DefineLabel();

                emitLoadHashCode(ilGen);
                ilGen.LoadConstant(cases[begin].Key);
                ilGen.BranchIfNotEqualUnsigned(endLabel);

                if (SwitchDoNotVerify && cases[begin].Value.Count == 1)
                {
                    ilGen.Branch(cases[begin].Value[0].Label);
                }
                else
                {
                    foreach (var item in cases[begin].Value)
                    {
                        emitLoadValue(ilGen);
                        emitLoadItem(ilGen, item.Value);
                        comparer.EmitEquals(ilGen);
                        ilGen.BranchTrue(item.Label);
                    }
                }

                ilGen.MarkLabel(endLabel);

                emitLoadHashCode(ilGen);
                ilGen.LoadConstant(cases[end].Key);
                ilGen.BranchIfNotEqualUnsigned(defaultLabel);

                if (SwitchDoNotVerify && cases[end].Value.Count == 1)
                {
                    ilGen.Branch(cases[end].Value[0].Label);
                }
                else
                {
                    foreach (var item in cases[end].Value)
                    {
                        emitLoadValue(ilGen);
                        emitLoadItem(ilGen, item.Value);
                        comparer.EmitEquals(ilGen);
                        ilGen.BranchTrue(item.Label);
                    }
                }

                ilGen.Branch(defaultLabel);

                return;
            }

            var greaterLabel = ilGen.DefineLabel();

            emitLoadHashCode(ilGen);
            ilGen.LoadConstant(cases[index].Key);
            ilGen.BranchIfGreater(greaterLabel);

            SwitchObject(
                ilGen,
                emitLoadValue,
                emitLoadHashCode,
                emitLoadItem,
                comparer,
                cases,
                defaultLabel,
                begin,
                (begin + index) / 2,
                index);

            ilGen.MarkLabel(greaterLabel);

            SwitchObject(
                ilGen,
                emitLoadValue,
                emitLoadHashCode,
                emitLoadItem,
                comparer,
                cases,
                defaultLabel,
                index + 1,
                (index + 1 + end) / 2,
                end);
        }