/* Use of included script samples are subject to the terms specified at http://www.microsoft.com/resources/sharedsource/licensingbasics/permissivelicense.mspx Written by Jomo Fisher */ using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; namespace FastSwitch { static class SwitchCompiler { public static Func CreateSwitch(IDictionary caseDict) { var cases = new List>(caseDict.Count); foreach (var kv in caseDict) cases.Add(new Case { C = kv.Key, R = kv.Value }); var p = Expression.Parameter(typeof(string), "p"); var expr = Expression.Lambda>( BuildStringLength(p, cases.ToOrderedArray(s => s.C.Length), 0, cases.Count - 1), new ParameterExpression[] { p } ); var del = expr.Compile(); return del; } static Expression BuildStringLength(ParameterExpression p, Case[] pairs, int lower, int upper) { int middle = MidPoint(lower, upper); if (pairs[lower].C.Length == pairs[middle].C.Length) return BuildStringChar(p, pairs.TakePage(lower, upper).ToOrderedArray(c => c.C), 0, 0, upper - lower); middle = pairs.FirstDifferentDown(middle, (c) => c.C.Length); return Expression.Condition( Expression.LessThan(Expression.Call(p, StringLength), Expression.Constant(pairs[middle + 1].C.Length)), BuildStringLength(p, pairs, lower, middle), BuildStringLength(p, pairs, middle + 1, upper) ); } static Expression BuildStringChar(ParameterExpression p, Case[] pairs, int index, int lower, int upper) { if (pairs.TakePage(lower, upper).All(c => c.R.Equals(pairs[lower].R))) return Expression.Constant(pairs[lower].R); int middle = MidPoint(lower, upper); if (pairs[middle].C[index] == pairs[lower].C[index]) return BuildStringChar(p, pairs, index + 1, lower, upper); middle = pairs.FirstDifferentDown(middle, (c) => c.C[index]); return Expression.Condition( Expression.LessThan(Expression.Call(p, StringIndex, Expression.Constant(index)), Expression.Constant(pairs[middle + 1].C[index])), BuildStringChar(p, pairs, index, lower, middle), BuildStringChar(p, pairs, index, middle + 1, upper) ); } struct Case { public string C { get; set; } public TR R { get; set; } public override string ToString() { return C.ToString() + " " + R.ToString(); } } static int MidPoint(int lower, int upper) { return ((upper - lower + 1) / 2) + lower; } static MethodInfo StringLength = typeof(string).GetMethod("get_Length"); static MethodInfo StringIndex = typeof(string).GetMethod("get_Chars"); } static class Helpers { internal static T[] ToOrderedArray(this IEnumerable seq, Func keySelector) { return seq.OrderBy(keySelector).ToArray(); } internal static IEnumerable TakePage(this IEnumerable seq, int first, int last) { return seq.Skip(first).Take(last - first + 1); } internal static int FirstDifferentDown(this T[] arr, int searchFrom, Func selector) where TV : IComparable { TV tvfrom = selector(arr[searchFrom]); for (int i = searchFrom - 1; i >= 0; --i) if (tvfrom.CompareTo(selector(arr[i])) != 0) return i; throw new Exception("No match"); } } }