隨便寫個 WebSafeBase64 Encoding 卻比起正經八百寫的 WebSafeBase64 Encoding 慢上許多!!!

摘要:隨便寫個 WebSafeBase64 Encoding 卻比起正經八百寫的 WebSafeBase64 Encoding 慢上許多!!!

先看看隨便寫的 code:

        public static class WebSafeBase64
        {
            public static string ToWebSafeBase64String(byte[] input)
            {
                var result = new StringBuilder(Convert.ToBase64String(input).TrimEnd('='));
                result.Replace('+', '-');
                result.Replace('/', '_');
                return result.ToString();
            }

            public static byte[] FromWebSafeBase64String(string base64ForUrlInput)
            {
                int padChars = (base64ForUrlInput.Length % 4) == 0 ? 0 : (4 - (base64ForUrlInput.Length % 4));
                var result = new StringBuilder(base64ForUrlInput, base64ForUrlInput.Length + padChars);
                result.Append(String.Empty.PadRight(padChars, '='));
                result.Replace('-', '+');
                result.Replace('_', '/');
                return Convert.FromBase64String(result.ToString());
            }
        }

然後看看正經八百寫的 code...

    public static class Base64
    {
        public static char[] websavebase64Chars = new char[]
{ 'A','B','C','D','E','F','G','H','I','J','K','L','M',
'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
'a','b','c','d','e','f','g','h','i','j','k','l','m',
'n','o','p','q','r','s','t','u','v','w','x','y','z',
'0','1','2','3','4','5','6','7','8','9','-','_' };

        public static byte[] CharToByteMapping = GenCharToByteMapp();

        public static byte[] GenCharToByteMapp()
        {
            var r = new byte[256];
            for (int i = 0; i < websavebase64Chars.Length; i++)
            {
                r[(byte)websavebase64Chars[i]] = (byte)i;
            }
            return r;
        }

        public static char[] websavebase64Charsx4 = Genwebsavebase64Charsx4();

        public static char[] Genwebsavebase64Charsx4()
        {
            var r = new char[websavebase64Chars.Length*4];
            Array.Copy(websavebase64Chars, 0,  r, 0, websavebase64Chars.Length);
            Array.Copy(websavebase64Chars, 0,  r, websavebase64Chars.Length, websavebase64Chars.Length);
            Array.Copy(websavebase64Chars, 0,  r, websavebase64Chars.Length*2, websavebase64Chars.Length);
            Array.Copy(websavebase64Chars, 0,  r, websavebase64Chars.Length*3, websavebase64Chars.Length);
            return r;
        }

        public static IEnumerable ToSixBitsChar(IEnumerable bytes)
        {
            int r = 0;
            int len = 0;
            foreach (var b in bytes)
            {
                r = r << 8 | b;
                len++;
                if (len == 3)
                {
                    var m4 = websavebase64Charsx4[(byte)r];
                    r >>= 6;
                    var m3 = websavebase64Charsx4[(byte)r];
                    r >>= 6;
                    var m2 = websavebase64Charsx4[(byte)r];
                    r >>= 6;
                    var m1 = websavebase64Charsx4[(byte)r];
                    yield return m1;
                    yield return m2;
                    yield return m3;
                    yield return m4;
                    r = 0;
                    len = 0;
                }
            }
            switch (len)
            {
                case 1:
                    r <<= 4;
                    var m2 = websavebase64Charsx4[(byte)r];
                    r >>= 6;
                    var m1 = websavebase64Charsx4[(byte)r];
                    yield return m1;
                    yield return m2;
                    break;
                case 2:
                    r <<= 2;
                    var mm3 = websavebase64Charsx4[(byte)r];
                    r >>= 6;
                    var mm2 = websavebase64Charsx4[(byte)r];
                    r >>= 6;
                    var mm1 = websavebase64Charsx4[(byte)r];
                    yield return mm1;
                    yield return mm2;
                    yield return mm3;
                    break;
            }
        }

        private static IEnumerable FromSixBitsChar(IEnumerable chars)
        {
            var CharInput = chars.GetEnumerator();
            while (CharInput.MoveNext())
            {
                var b1 = CharToByteMapping[CharInput.Current] << 2;
                if (CharInput.MoveNext() == false)
                {
                    // this should not happen, but just give up if this happend.
                    yield return (byte)b1;
                    yield break;
                }

                var b2 = CharToByteMapping[CharInput.Current];
                var b2part1 = (b2 & 48) >> 4;
                var b2part2 = (b2 & 15) << 4;
                var r1 = b1 | b2part1;
                yield return (byte)r1;

                if (CharInput.MoveNext() == false)
                    yield break;

                var b3 = CharToByteMapping[CharInput.Current];
                var b3part1 = (b3 & 60) >> 2;
                var b3part2 = (b3 & 3) << 6;

                var r2 = b2part2 | b3part1;
                yield return (byte)r2;

                if (CharInput.MoveNext() == false)
                    yield break;

                var b4 = CharToByteMapping[CharInput.Current];
                var r3 = b3part2 | b4;
                yield return (byte)r3;
            }
        }

        public static string Encode(byte[] data)
        {
            return new String(ToSixBitsChar(data).ToArray());
        }

        public static byte[] Decode(string data)
        {
            return FromSixBitsChar(data).ToArray();
        }
    }

最後...

比賽結果:

WebSafeBase64 Encode with Convert.ToBase64String 100 times cost 51ms
Convert.ToBase64String 100 times cost 16ms
My WebSafeBase64 encode 100 times cost 302ms
 
websafebase64 decode with Convert.ToBase64String 100 times cost 87ms
Convert.FromBase64String 100 times cost 52ms
My WebSafeBase64 decode 100 times cost 231ms
 
就算想用 mono 的 Base64 來改, 也比原來隨便寫的慢 !!! 
mono Base64 Encoding 100 times cost 77ms
 
真的好想哭!