Base64 编解码的C语言实现

简介

Base64编码是将任何类型的数据转换成ASCII码的可见字符,然后接收端再反向解码,得到原始的数据。最早的的Base是用于发送Email内容的。

经过Base64转换之后的数据大小变大了,为原数据的4/3大小。但是方便了传输,比如由于base64的编码中没有<>等特殊字符,可以不用转义扫描,直接放在XML中,放在MIME中,甚至直接不经过转义扫描存进数据库中。由于有这些方便的特性,即使数据量变大,base64编码还是被广泛使用。


原理

编码原理

每个字节8位,每次取出3个字节,也就是3 x 8 = 24 位。然后每次从此24位中取出6位,然后在前端补2位0,组成新的8位,也就是一个字节。这样就将3个字节转换成了4个字节。由于前面两位都是0,所以转换后的每个字节能表示的最大数字为63, 也就是说转换后的每个字节只可能是0-63中的一个数字。

然后根据规范给出的Base64索引表,将1-63 这64个数字转换成"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"中的一个。

当最后取出3个字节不够时,不够的位置补0,并且最后少一个字节时编码的最后加一个“=”,少两个字节时加两个"="

解码原理

解码是编码的反向过程,每次取出4个字节,然后将每个字节的字符转换成原始Base64索引表对应的索引数字,也就是编码时3字节转换成4字节的转换结果。然后使用位操作将每字节前2位去掉,重新转换成3字节。需要注意的是最后对于结尾“=”的处理。


代码实现

ENCODE:

static char base64_index[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 

char *base64_encode(const char *input, const size_t length, char *output)
{
    *output = '\0';
    if (input == NULL || length < 1) return output;

    char *p = (char*)input;
    char *p_dst = (char*)output;;
    char *p_end = (char*)input + length;
    int  loop_count = 0;

    // 0x30 -> 00110000
    // 0x3C -> 00111100
    // 0x3F -> 00111111
    while (p_end - p >= 3) {
        *p_dst++ = base64_index[( p[0] >> 2 )];
        *p_dst++ = base64_index[( (p[0] << 4) & 0x30 ) | ( p[1] >> 4 )];
        *p_dst++ = base64_index[( (p[1] << 2) & 0x3C ) | ( p[2] >> 6 )];
        *p_dst++ = base64_index[p[2] & 0x3F];
        p += 3;
    }

    if (p_end - p > 0) {
        *p_dst++ = base64_index[(p[0] >> 2)];
        if (p_end - p == 2) {
            *p_dst++ = base64_index[( (p[0] << 4) & 0x30 ) | ( p[1] >> 4 )];
            *p_dst++ = base64_index[(p[1] << 2) & 0x3C]; 
            *p_dst++ = '=';
        } else if (p_end - p == 1) {
            *p_dst++ = base64_index[(p[1] << 4) & 0x30];
            *p_dst++ = '=';
            *p_dst++ = '=';
        }
    }

    *p_dst = '\0';
    return output;
}

DECODE:

// 16 * 16
static int base64_decode_map[256] = {
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0   - 15
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16  - 31
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, // 32  - 47
    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 48  - 63
    -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, // 64  - 79
    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // 80  - 95
    -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96  - 111
    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, // 112 - 127
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128 - 143
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144 - 159 
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160 - 175
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176 - 191
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192 - 207
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208 - 223
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224 - 239
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 240 - 255
};

char *base64_decode(const char* input, char *output)
{
    output[0] = '\0';
    if (input == NULL || output == NULL) 
        return output;

    int input_len = strlen(input);
    if (input_len < 4 || input_len % 4 != 0) 
        return output;

    // 0xFC -> 11111100
    // 0x03 -> 00000011
    // 0xF0 -> 11110000
    // 0x0F -> 00001111
    // 0xC0 -> 11000000
    char *p = (char*)input;
    char *p_out = output;
    char *p_end = (char*)input + input_len;
    for (; p < p_end; p += 4) {
        *p_out++ = ((base64_decode_map[p[0]] << 2) & 0xFC) | ((base64_decode_map[p[1]] >> 4) & 0x03);
        *p_out++ = ((base64_decode_map[p[1]] << 4) & 0xF0) | ((base64_decode_map[p[2]] >> 2) & 0x0F);
        *p_out++ = ((base64_decode_map[p[2]] << 6) & 0xC0) | (base64_decode_map[p[3]]); 
    }

    if (*(input + input_len - 2) == '=') {
        *(p_out - 2) = '\0';
    } else if (*(input + input_len - 1) == '=') {
        *(p_out - 1) = '\0';
    }

    return output;
}

代码中的base64_decode_map的作用就是将base64索引中的ASCII字符转换成原始的0-63的数字。


其他

base64在不同的场景也有一个差别,有的编码结果中每76个字符加入一个换行。这也是正确的。以上的代码示例中没有加入,如需加入只需计数,定期加入换行符即可。

编码结果的大小在不加入换行符时是原来长度的4/3,但是需要注意精确malloc内存时,需要 (org_len + 3)  * 4 / 3,如果需要增加结尾的'\0',需要再加1位。或者直接org_len *4 / 3 + 2

猜你喜欢

转载自blog.csdn.net/irwin_chen/article/details/9360845