不仅仅是实现base64工具类

虽然在jmh上测试性能比jdk自带的差点,但是可以通过 实现 接口 Coding 来实现一种 类base64 新的编码方式。(这点很有用,等于是base64加强实现)

当然你也可以直接参考jdk的 encode0 和 decode0 来实现更优的。

package my.code;

public class Base64Plus {

    private static final int BIT = 0x3F;
    private static final int BIT6 = BIT << 6;
    private static final int BIT12 = BIT << 12;
    private static final int BIT18 = BIT << 18;

    /**
     * 这里如果使用原生数组,不使用条件判断,估计能和jdk原生的性能相当。
     * jdk的实现思路: intToChar 64的数组实现,charToByte 256(ASCII码的长度)的数组(下标:ascii码对应整数,值:对应的原数据)实现,
     */
    public static final Coding BASE64 = new Coding() {
        @Override
        public char intToChar(int n) {
            if(n < 26) {
                return (char) (n + 65);
            } else if(n < 52) {
                return (char) (n + 71);
            } else if(n < 62) {
                return (char) (n - 4);
            } else if(n == 62) {
                return '+';
            } else if(n == 63) {
                return '/';
            }
            return '=';
        }

        @Override
        public byte charToByte(char n) {
            if(n >= 'A' && n <= 'Z') {
                return (byte) (n - 65);
            } else if(n >= 'a' && n <= 'z') {
                return (byte) (n - 71);
            } else if(n >= '0' && n <= '9') {
                return (byte) (n + 4);
            } else if(n == '+') {
                return 62;
            } else if(n == '/') {
                return 63;
            }
            throw new RuntimeException("不是base64的字符: " + n);
        }
    };
    public static final Coding MyCoding = new Coding() {
        @Override
        public char intToChar(int n) {
            if(n < 10) {
                return (char) (n + 48);
            } else if(n < 36) {
                return (char) (n + 55);
            } else if(n < 62) {
                return (char) (n + 61);
            } else if(n == 62) {
                return '+';
            } else if(n == 63) {
                return '/';
            }
            return '=';
        }

        @Override
        public byte charToByte(char n) {
            if(n >= 'A' && n <= 'Z') {
                return (byte) (n - 55);
            } else if(n >= 'a' && n <= 'z') {
                return (byte) (n - 61);
            } else if(n >= '0' && n <= '9') {
                return (byte) (n - 48);
            } else if(n == '+') {
                return 62;
            } else if(n == '/') {
                return 63;
            }
            throw new RuntimeException("不是base64的字符: " + n);
        }
    };

    private final Coding coding;

    private final char end;

    public Base64Plus() {
        coding = BASE64;
        end = '=';
    }

    public Base64Plus(Coding coding) {
        this.coding = coding;
        end = '=';
    }

    public Base64Plus(Coding coding, char end) {
        this.coding = coding;
        this.end = end;
    }

    private static int toInt(byte a, byte b, byte c) {
        return ((a & 0xFF) << 16) + ((b & 0xFF) << 8) + (c & 0xFF);
    }


    private void intConvertChars(int n, char[] c, int start) {
        c[start] = coding.intToChar((n & BIT18) >> 18);
        c[start+1] = coding.intToChar((n & BIT12) >> 12);
        c[start+2] = coding.intToChar((n & BIT6) >> 6);
        c[start+3] = coding.intToChar(n & BIT);
    }

    private static int getCharSize(int len, int s) {
        switch (s) {
            case 1:
                return (len * 4) / 3 + 3;
            case 2:
                return (len * 4) / 3 + 2;
            default:
                return (len * 4) / 3;
        }
    }

    private static int getByteSize(int len, int flag) {
        switch (flag) {
            case 1:
                return (len * 3) / 4 - 1;
            case 2:
                return (len * 3) / 4 - 2;
            default:
                return (len * 3) / 4;
        }
    }

    public char[] encoding(byte [] data) {
        int l;
        if(data == null || (l = data.length) == 0) {
            return new char[0];
        }
        int s = l % 3;
        int cyc = (l / 3) * 3;
        char [] c = new char[getCharSize(l, s)];
        int start = 0;
        for(int i = 0;i < cyc;i+=3,start+=4) {
            intConvertChars(toInt(data[i], data[i+1], data[i+2]), c, start);
        }
        if(s == 1) {
            intConvertChars(toInt(data[l-1], (byte) 0, (byte) 0), c, start);
            c[c.length - 2] = c[c.length - 1] = end;
        } else if(s == 2) {
            intConvertChars(toInt(data[l-2], data[l-1], (byte) 0), c, start);
            c[c.length - 1] = end;
        }
        return c;
    }

    private int getFlag(char a, char b) {
        if(a != end && b != end) {
            return 0;
        }
        if(a == end && b == end) {
            return 2;
        }
        return 1;
    }

    public byte[] decoding(char [] data) {
        int l;
        if(data == null || (l = data.length) < 1) {
            return new byte[0];
        }

        int start = 0;
        int flag = getFlag(data[l-1], data[l-2]);
        int cyc = flag == 0 ? (l / 4) * 4 : (l / 4 - 1) * 4;

        byte[] result = new byte[getByteSize(l, flag)];

        for(int i = 0;i < cyc;i+=4,start+=3) {
            byte a = coding.charToByte(data[i]);
            byte b = coding.charToByte(data[i + 1]);
            byte c = coding.charToByte(data[i + 2]);
            byte d = coding.charToByte(data[i + 3]);

            int t = ((a << 18) & BIT18) + ((b << 12) & BIT12) + ((c << 6) & BIT6) + (d & BIT);

            result[start] = (byte) ((t & 0xff0000) >> 16);
            result[start+1] = (byte) ((t & 0x00ff00) >> 8);
            result[start+2] = (byte) (t & 0xff);
        }

        if(flag == 1) {
            byte a = coding.charToByte(data[l - 4]);
            byte b = coding.charToByte(data[l - 3]);
            byte c = coding.charToByte(data[l - 2]);
            int t = (((a & 0xFF) << 12) + ((b & 0xFF) << 6) + c) >> 2;

            result[result.length - 2] = (byte) ((t & 0xFF00) >> 8);
            result[result.length - 1] = (byte) (t & 0xFF);
        } else if(flag == 2) {
            byte a = coding.charToByte(data[l - 4]);
            byte b = coding.charToByte(data[l - 3]);
            result[result.length - 1] = (byte) ((((a & 0xFF) << 6) + (b & 0xFF)) >> 4);
        }

        return result;
    }

    /**
     * 接口必须要通过下面程序测试,才能使用
     * Base64Plus.Coding coding = new 自己的实现类;
     * for(int i = 0;i < 64;i++) {
     *     char t = coding.intToChar(i);
     *     byte a = coding.charToByte(t);
     *     if(a != i) {
     *         throw new RuntimeException(i + " " + t + " " + a);
     *  }
     * }
     */
    public interface Coding {

        /**
         * @param n (0 =< n < 64)
         * @return
         */
        char intToChar(int n);

        /**
         * @param n
         * @return (0 =< return < 64)
         */
        byte charToByte(char n);
    }

}

 

posted @ 2021-12-04 16:01  数学与IT  阅读(34)  评论(0)    收藏  举报