Base64 的另一种解码方法
常规的方法一般有设定一个 127 字节的反向转换表. 这种查表法网上有很多,这里不再讲述.
下面将要使用的是另一种方法, 只需要使用18字节的反向驱动表.我不知道还有没有其他
人想到, 不过即然我了解了, 也让大家分享一下这个想法.
第一步. 观察
Base64 编码里面只会出现以下的字符集:
A to Z, a to Z, 0 to 9, +, -, =
至于 = 是属于PADDING 字符,出现的话必然会在最后出现,所以我们先不考虑它.
好的, 我们利用ASCII 标准码表可以知道下面的事实.
A to Z: 65 to 90, 二进制即( 01000001 to 01011010 )
a to z: 97 to 122, 二进制即( 01100001 to 01111010 )
0 to 9: 48 to 57, 二进制即( 00110000 to 00111001 )
+: 43 二进制即( 00101011 )
-: 47 二进制即( 00101111 )
上面的二进制有个特性,你注意到了吗? 对,请注意高4位.
0 to 9: 高4位为 0011 ( 3 )
a to z: 高4位为 0110 和 0111 ( 6, 7 )
A to Z: 高4位为 0100 和 0101 ( 4, 5 )
+ , - : 高4位为 0010 ( 2 )
根据BASE64编码表,我们必须根据编码还原出在表中的序号.
A to Z: 需要减去 65
a to z: 需要减去 71
0 to 9: 需要加上 4
+ :需要加上 19
- :需要加上 19 - 3 = 16
设定一个加减驱动表,根据它们的高4位来得到应该加减的值, 索引即是高4位减去2.于是,它们可能的取值就是:
0,1,2,3,4,5
于是有: { 19, 4, -65, -65, -71, -71 }
好了,还有一个麻烦就是 +,- 虽然在同一组,但是 - 需要在加上 19后再减去3 修正.所以我们不得不再次来考虑这个问题.
+,- 两个符号的第2位是有区别的, 所以我们制定了一个附加条件转换序列来根据第2位来得到一个修正值. 当然, 除了+,- 以外
其他的组根本不需要, 附加修正值为0.所以我们有了根据第2位来加载的附加修正表:
{ 0,-3},{0,0},{0,0},{0,0},{0,0},{0,0}
将两张表结合起来,有了反向转换表:
char Base64Code_RevTbl[6][3] = {{ 19,0,-3},{4,0,0},{-65,0,0},{-65,0,0},{-97,0,0},{-97,0,0}}
第二步, 使用
如何使用上面的反向转换表呢? 编码后的BASE64 是4的倍数. 假定有4个字节的已经编码的BASE64码.我们需要将其转换出来.
( 注意,这里不考虑这4个字节中最后二个字符可能是=符号的问题,因为那个问题比效简单,而且与当前的话题不相关 )
我们假定有下面的定义:
char r0,r1,r2,r3
sp[] 为输入的BASE64编码字.
r0 = ( sp[ 0 ] >> 4 ) & 15 - 2; r1 = ( sp[ 1 ] >> 4 ) & 15 - 2;
r2 = ( sp[ 2 ] >> 4 ) & 15 - 2; r3 = ( sp[ 3 ] >> 4 ) & 15 - 2;
r0 = sp[ 0 ] + Base64Code_RevTbl[ r0 ][ 0 ] + Base64Code_RevTbl[ r0 ][ (( sp[ 0 ] >> 2 ) & 1 ) + 1 ];
r1 = sp[ 1 ] + Base64Code_RevTbl[ r1 ][ 0 ] + Base64Code_RevTbl[ r1 ][ (( sp[ 0 ] >> 2 ) & 1 ) + 1 ];
r2 = sp[ 2 ] + Base64Code_RevTbl[ r2 ][ 0 ] + Base64Code_RevTbl[ r2 ][ (( sp[ 0 ] >> 2 ) & 1 ) + 1 ];
r3 = sp[ 3 ] + Base64Code_RevTbl[ r3 ][ 0 ] + Base64Code_RevTbl[ r3 ][ (( sp[ 0 ] >> 2 ) & 1 ) + 1 ];
明显的,为了得到序号, 我们进行了三次加法. 如上面的 r0:
r0 = ( sp[ 0 ] >> 4 ) & 15; 我们得到了sp[0]所属的组.
sp[0] + Base64Code_RevTbl[ r0 ][ 0 ] 得到了没有修正的序号, 如果 sp[0]是 +或者-符号的话,序号会出错. , 我们再加上修正值,
Base64Code_RevTbl[ r0 ][ (( sp[ 0 ] >> 2 ) & 1 ) + 1 ] 根据符号的第2位来得到符号修正值, ((sp[0]>>2)&1)+1 的结果只可能是 1
或者2. 我们就根据它所属的组r0,再得到修正值.
从反向转换表来说,只有当符号为-时,才会有附加值-3的出现,其他均为0.
既然序号得到了, 那么, 下面最简单的事就是将4个序号合并成三个字节了.
题外话: 对于=符号,其实不必担心,根据源数据的最后2个字节,只会有三种情况出现。
第一种情况:没有=符号。这种情况下,r0,r1,r2,r3都是正确的。只需要将它们组成三个字节即可。
第二种情况:有一个=符号。这种情况下,r3就是错误的。只需要使用 r0,r1,r2产生二个字节即可。
第三种情况:有二个=符号。哦太好了,只需要使用 r0,r1产生一个字节即可。
其他情况:....源数据都是错误的。
而根据 r0,r1,r2,r3 产生三个字节的方法也简单,如下:
dp[ 0 ] = ( r0 << 2 ) | (( r1 >> 4 ) & 0x03 );
dp[ 1 ] = ( r1 << 4 ) | (( r2 >> 2 ) & 0x0f );
dp[ 2 ] = ( r2 << 6 ) | r3;