对于C/C++实现Base64的Encode代码的阅读日记

2020-03-16  本文已影响0人  anliner

首先了解了下Base64的实现原理:https://baijiahao.baidu.com/s?id=1644892102150918183&wfr=spider&for=pc
大概内容就是,base64基于一个含有64个字符的编码表(A-Z,a-z,0-9,+,/),将原文进行转码,其步骤就是将原文按每三个字节一组分割,每三个字节又按六位一组分为四组,然后高位补零,所以输出结果里每个字节最大值也就63,也就是对应了base64的编码表。
接下来直接看base64.c文件的代码

int base64_encode(unsigned char *dst, int *dlen, const unsigned char *src, int slen) {
    int i, n;
    int C1, C2, C3;
    unsigned char *p;

    if (slen == 0)
        return (0);

    /**
     * 首先计算传入进来的原文按六位一组,可以分成多少组
     * 所以将slen * 8,然后再除6 (由于8是2的3次方,用乘法算太耗时,所以直接左移3位)
     */
    n = (slen << 3) / 6;

    /**
     * 由于原文长度并不一定是3的倍数,可能多1或2个字节,所以还需要计算是否多出字节
     * 由于是将3字节按4个6位分割,需要以24位一组的规则计算新数组空间
     * 按照8:6 和 16:12的比例,那么多出1个字节,原文总位数除以6,余2;多出2个字节,余4
     * 再结合每组24位的原则,原文多1个字节,在n的基础上,新数组还差18位即3字节空间,原文多2字节,新数组还差12位即2字节空间
     * 所以当余2,n需要自加3,当余4,n需要自加2
     */
    switch ((slen << 3) - (n * 6)) {
        case 2:
            n += 3;
            break;
        case 4:
            n += 2;
            break;
        default:
            break;
    }

    /**
     * 按理说按照以上计算,最终n的值就是新数组的长度,所以dlen只要不小于n即可
     * 但我不明白为什么这里的判断是dlen要大于n,不知道是不是为了字符串结尾\0留位置,但也没看代码里结尾添加字符串的结尾标识啊,啊,不懂c啊
     */
    if (*dlen < n + 1) {
        *dlen = n + 1;
        return (POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL);
    }

    /**
     * 这里是将原文按3字节分组,得到组数,多余的1、2字节后面单独计算
     */
    n = (slen / 3) * 3;

    /**
     * 按组数循环取原文里的字节,每次取3个,这里用了新指针p,指向地址与传参的dst一样,联系上下文,我个人理解是为了计算新数组实际长度
     * 但是用strlen不也可以计算dst的长度么,不明白为什么这样做
     * em....好像直接用dst运算,最后指针已经指向字符串末尾了,不过也不是没有办法回到开头,这个p还是让我很纠结
     */
    for (i = 0, p = dst; i < n; i += 3) {
        C1 = *src++;
        C2 = *src++;
        C3 = *src++;

        /**
         * 首先将第一字节右移2位,得到左6位,与上0x3F,高2位置0,得到第一个6位
         * 然后在编码表里进行转码,得到新组的第一字节
         */
        *p++ = base64_enc_map[(C1 >> 2) & 0x3F];
        /**
         * 接着将第一字节与3,即00000011,得到低2位,然后左移4位,
         * 然后将第二字节右移4位,做加法运算,得到第二个6位,转码得到新组的第二字节
         */
        *p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F];
        /**
         * 将第二字节与15,即00001111,得到低4位,然后左移2位,
         * 然后将第三字节右移6位,得到高2位,做加法运算,得到第三个6位,转码得到新组的第三字节
         */
        *p++ = base64_enc_map[(((C2 & 15) << 2) + (C3 >> 6)) & 0x3F];
        /**
         * 最后将第三字节与上0x3F,清除高2位,得到低6位,即第四个6位,转码得到新组的第四字节
         */
        *p++ = base64_enc_map[C3 & 0x3F];
    }
    //以上将正好可以按3字节一组分割的内容全部转码完毕

    /**
     * 接下来判断原文按3字节分组后,是否还多出不够一组的内容
     */
    if (i < slen) {
        /**
         * 既然多出内容,那么至少多一个,所以第一字节直接取
         * 对第二字节需要判断
         */
        C1 = *src++;
        C2 = ((i + 1) < slen) ? *src++ : 0;

        /**
         * 新组第一二字节可直接取6位然后转码
         */
        *p++ = base64_enc_map[(C1 >> 2) & 0x3F];
        *p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F];

        /**
         * 如果原文只多出1字节,那么新组第三字节肯定为0,那么按照规则,空字符用 '=' 代替
         * 如果多出2字节,则还继续进行取6位,转码的运算
         */
        if ((i + 1) < slen)
            *p++ = base64_enc_map[((C2 & 15) << 2) & 0x3F];
        else *p++ = '=';
        /**
         * 由于最多多出2字节,所以新组第四字节肯定没有值可以取,也就是空字符,所以直接转换为 '='
         */
        *p++ = '=';
    }

    /**
     * 这里我的理解是计算字符数组dst(p)的长度,但还是那句话,为啥不用strlen(dst)计算
     */
    *dlen = p - dst;
    *p = 0;

    return (0);
}
上一篇下一篇

猜你喜欢

热点阅读