对于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);
}