程序源代码共三个文件:base64.c、base64.h、base64_main.c。
该程序能够将指定文件进行base64编码与解码。
base64.c:
/***********************************************************************************************************************************************************************
** @file: base64.c
** @author: huixuan.li guoqiang.zhu
** @date: 2024-11-16 10:42:25
** @brief: 实现base64编码和解码
***********************************************************************************************************************************************************************/
#include "base64.h"
static const unsigned char encode_dict[64] = {
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
};
static const unsigned char decode_dict[] = {
['A'] = 0,['B'] = 1,['C'] = 2,['D'] = 3,['E'] = 4,['F'] = 5,['G'] = 6,['H'] = 7,
['I'] = 8,['J'] = 9,['K'] = 10,['L'] = 11,['M'] = 12,['N'] = 13,['O'] = 14,['P'] = 15,
['Q'] = 16,['R'] = 17,['S'] = 18,['T'] = 19,['U'] = 20,['V'] = 21,['W'] = 22,['X'] = 23,
['Y'] = 24,['Z'] = 25,['a'] = 26,['b'] = 27,['c'] = 28,['d'] = 29,['e'] = 30,['f'] = 31,
['g'] = 32,['h'] = 33,['i'] = 34,['j'] = 35,['k'] = 36,['l'] = 37,['m'] = 38,['n'] = 39,
['o'] = 40,['p'] = 41,['q'] = 42,['r'] = 43,['s'] = 44,['t'] = 45,['u'] = 46,['v'] = 47,
['w'] = 48,['x'] = 49,['y'] = 50,['z'] = 51,['0'] = 52,['1'] = 53,['2'] = 54,['3'] = 55,
['4'] = 56,['5'] = 57,['6'] = 58,['7'] = 59,['8'] = 60,['9'] = 61,['+'] = 62,['/'] = 63
};
unsigned long long get_encode_len(unsigned long long data_len){
return ((data_len + 2ull) / 3ull) << 2;
}
unsigned long long get_decode_len(const unsigned char *base64_code, unsigned long long base64_code_len){
// 计算等号数量
int equal_num = 0;
for (const unsigned char *p = base64_code + base64_code_len;*--p == (unsigned char)'='; equal_num++);
return (base64_code_len >> 2) * 3 - equal_num;
}
bool is_valid_base64_code(const unsigned char *base64_code, unsigned long long base64_code_len){
// 若base_code_len%4!=0则非法
if (base64_code_len & 0x03ull)
return false;
//从末尾开始检测
const unsigned char *p = base64_code + base64_code_len;
//首先跳过连续等号,若等号数量超过两个,则非法
int equal_num = 0;
while (*--p == '='){
equal_num++;
if (equal_num > 2)
return false;
}
// 若base64_code中存在大于122u的元素或(decode_dict[element]==0&&element!='A')则非法
while (p >= base64_code){
if (*p > 'z' || (decode_dict[*p] == 0 && *p != 'A') || *p == '='){
return false;
}
p--;
}
return true;
}
void base64_encode(const unsigned char *data, unsigned long long data_len, unsigned char *result){
const unsigned char *q = data + data_len - data_len % 3;
// 对前最大3的倍数个字节进行处理
while (data != q){
*result++ = encode_dict[*data >> 2];
*result++ = encode_dict[((*data << 4) | (*++data >> 4)) & 0x3fu];
*result++ = encode_dict[((*data << 2) | (*++data >> 6)) & 0x3fu];
*result++ = encode_dict[*data++ & 0x3fu];
}
switch (data_len % 3){
case 1:
*result++ = encode_dict[*data >> 2];
*result++ = encode_dict[*data << 4 & 0x3fu];
*result++ = (unsigned char)'=';
*result = (unsigned char)'=';
break;
case 2:
*result++ = encode_dict[*data >> 2];
*result++ = encode_dict[((*data << 4) | (*++data >> 4)) & 0x3fu];
*result++ = encode_dict[*data << 2 & 0x3fu];;
*result = (unsigned char)'=';
break;
}
}
void base64_decode(const unsigned char *base64_code, unsigned long long base64_code_len, unsigned char *result){
// 计算等号数量
int equal_num = 0;
const unsigned char *p = base64_code + base64_code_len;
while (*--p == '=')
equal_num++;
// 设置锚点
p = equal_num ? base64_code + base64_code_len - 4ull : base64_code + base64_code_len;
while (base64_code < p){
*result++ = (decode_dict[*base64_code] << 2) | (decode_dict[*++base64_code] >> 4);
*result++ = (decode_dict[*base64_code] << 4) | (decode_dict[*++base64_code] >> 2);
*result++ = (decode_dict[*base64_code] << 6) | (decode_dict[*++base64_code]);
base64_code++;
}
switch (equal_num){
case 2:
*result = (decode_dict[*base64_code] << 2) | (decode_dict[*++base64_code] >> 4);
break;
case 1:
*result++ = (decode_dict[*base64_code] << 2) | (decode_dict[*++base64_code] >> 4);
*result = (decode_dict[*base64_code] << 4) | (decode_dict[*++base64_code] >> 2);
break;
}
}
base64.h:
/***********************************************************************************************************************************************************************
** @file: base64.h
** @author: huixuan.li guoqiang.zhu
** @date: 2024-06-06 15:10:46
** @brief: base64头文件
***********************************************************************************************************************************************************************/
#ifndef __CUSTOM_BASE64_H__
#define __CUSTOM_BASE64_H__
#include <stdbool.h>
// 获取对指定长度数据进行base64编码后,base64编码的长度
unsigned long long get_encode_len(unsigned long long data_len);
// 获取对指定base64码解码后,解码后的数据长度
unsigned long long get_decode_len(const unsigned char *base64_code, unsigned long long base64_code_len);
// 检测数据是否为合法的base64编码
bool is_valid_base64_code(const unsigned char *base64_code,unsigned long long base64_code_len);
// 对指定数据进行base64编码
void base64_encode(const unsigned char *data, unsigned long long data_len, unsigned char *result);
// 对指定base64码进行解码
void base64_decode(const unsigned char *base64_code, unsigned long long base64_code_len, unsigned char *result);
#endif
base64_main.c:
/***********************************************************************************************************************************************************************
** @file: base64_main.c
** @author: huixuan.li
** @date: 2024-11-22 11:09:11
** @brief: 对文件进行base64编码或解码
***********************************************************************************************************************************************************************/
// 设置读取大文件所需的宏
#ifndef _LARGEFILE64_SOURCE
#define _LARGEFILE64_SOURCE
#endif
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
#endif
#include "base64.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
/* 返回码定义 */
static enum{ SUCCESS, ARGS_ERROR, HANDLE_ERROR } EXITCODE;
/* 错误信息与帮助信息 */
static const char ARG_ERROR_INFO[] =
"ERROR! Couldn't recognize argument %s, please use option \"--help\" for instructions\n";
static const char HELP_INFO[] =
"The program is capable of base64 encoding and decoding.\n"
"Encoded command:\n"
"\tbase64 -e <inputfile> [-o <outputfile>]\n"
"Decoded command:\n"
"\tbase64 -d <inputfile> -o <outputfile>\n";
/* 全局参数 */
static enum{ UNDEFINE, DECODE, ENCODE } mode=UNDEFINE;
static char *input_file_path = NULL;
static char *output_file_path = NULL;
static FILE *input_file_stream;
static FILE *output_file_stream;
static unsigned long long input_file_size;
static unsigned long long BUFFER_SIZE;
static unsigned char *buffer;
/* 读取参数并根据参数修改全局参数 */
static void analyze_arguments(int argc, char **argv);
/* 根据全局参数设置输入流 */
static void set_input_stream(void);
/* 根据全局参数设置输出流 */
static void set_output_stream(void);
/* 进行转化输出 */
static void handle(void);
/* 主函数 */
int main(int argc, char *argv[]){
analyze_arguments(argc, argv);
set_input_stream();
set_output_stream();
handle();
exit(EXITCODE = SUCCESS);
}
/* 读取参数并根据参数修改全局参数 */
static void analyze_arguments(int argc, char **argv){
if (argc == 1){
printf("%s", HELP_INFO);
exit(EXITCODE = SUCCESS);
}
// 对参数遍历处理
for (int i = 1;i < argc;i++){
char *p = argv[i];
// 若以-开头,则判定为选项
if (*p == '-'){
// -d选项
if (strcmp(p, "-d") == 0){
if (mode){
fputs("Error! Only one of the -d and -e options can be selected!\n", stderr);
exit(EXITCODE = ARGS_ERROR);
}
mode = DECODE;
}
// -e选项
else if (strcmp(p, "-e") == 0){
if (mode){
fputs("Error! Only one of the -d and -e options can be selected!\n", stderr);
exit(EXITCODE = ARGS_ERROR);
}
mode = ENCODE;
}
// --help选项
else if (strcmp(p, "--help") == 0){
fputs(HELP_INFO, stdout);
exit(EXITCODE = SUCCESS);
}
// -o选项
else if (strcmp(p, "-o") == 0){
// 若outputfile已存在
if (output_file_path != NULL){
fprintf(stderr, "Error! An outputfile %s is already given!\n", output_file_path);
exit(EXITCODE = ARGS_ERROR);
}
// 若为参数末尾
if (i == argc - 1){
fputs("Error! Couldn't find outputfile in arguments!\n", stderr);
exit(EXITCODE = ARGS_ERROR);
}
output_file_path = argv[++i];
}
// 未知选项
else{
fprintf(stderr, ARG_ERROR_INFO, p);
exit(EXITCODE = ARGS_ERROR);
}
}
// 若参数不以-开头,则判定为输入文件名
else{
// 若存在多个文件名则报错
if (input_file_path != NULL){
fprintf(stderr, "Error! Multiple input_file set: %s and %s\n", input_file_path, p);
exit(EXITCODE = ARGS_ERROR);
}
input_file_path = p;
}
}
// 检测是否给出mode
if(mode==UNDEFINE){
fputs("Error! An option must be given from -e and -d!\n", stderr);
exit(EXITCODE = ARGS_ERROR);
}
// 检测是否有inputfile
if (input_file_path == NULL){
fputs("Error! An inputfile must be given!\n", stderr);
exit(EXITCODE = ARGS_ERROR);
}
// 检测-d选项下是否有outputfile
if (mode == DECODE && output_file_path == NULL){
fputs("Error! An outputfile must be given under -d option!\n", stderr);
exit(EXITCODE = ARGS_ERROR);
}
}
/* 根据全局参数设置输入流 */
static void set_input_stream(void){
if (mode == ENCODE)
input_file_stream = fopen(input_file_path, "rb");
else
input_file_stream = fopen(input_file_path, "r");
if (input_file_stream == NULL){
fprintf(stderr, "Error occur in open input_file %s!\n", input_file_path);
exit(EXITCODE = HANDLE_ERROR);
}
// 获取文件大小
struct stat input_file_info;
if (fstat(fileno(input_file_stream), &input_file_info) != 0){
fprintf(stderr, "Couldn't get info for file '%s'!\n", input_file_path);
fclose(input_file_stream);
exit(EXITCODE = HANDLE_ERROR);
}
input_file_size = (unsigned long long)input_file_info.st_size;
//文件小于1M,1G,1T时,分别设置缓冲区
if (input_file_size < 1048576ull){
BUFFER_SIZE = 1024ull;
} else if (input_file_size < 1073741824ull){
BUFFER_SIZE = 1048576ull;
} else if (input_file_size < 1099511627776ull){
BUFFER_SIZE = 1073741824ull;
} else{
fprintf(stderr, "file '%s' is to big!\n", input_file_path);
fclose(input_file_stream);
exit(EXITCODE = HANDLE_ERROR);
}
buffer = malloc(BUFFER_SIZE);
if (setvbuf(input_file_stream, buffer, _IOFBF, BUFFER_SIZE) != 0){
fprintf(stderr, "Couldn't set read buffer for file '%s'!\n", input_file_path);
fclose(input_file_stream);
exit(EXITCODE = HANDLE_ERROR);
}
}
/* 根据全局参数设置输出流 */
static void set_output_stream(void){
// 设置输出流
if (output_file_path != NULL){
output_file_stream = fopen(output_file_path, "wb");
if (output_file_stream == NULL){
fprintf(stderr, "Error occur in open output_file %s, please check whether the path exists!\n", output_file_path);
fclose(input_file_stream);
free(buffer);
exit(EXITCODE = HANDLE_ERROR);
}
} else{
output_file_stream = stdout;
}
// 设置缓冲区
if (setvbuf(output_file_stream, buffer, _IOFBF, BUFFER_SIZE) != 0){
fputs("Couldn't set read buffer for output!", stderr);
fclose(input_file_stream);
fclose(output_file_stream);
free(buffer);
exit(EXITCODE = HANDLE_ERROR);
}
}
/* 进行base64编码或解码 */
static void handle(void){
unsigned char *input_file_content;
unsigned char *output_file_content;
unsigned long long output_file_size;
if ((input_file_content = malloc(input_file_size)) == NULL){
fputs("Insufficient memory space!", stderr);
fclose(input_file_stream);
fclose(output_file_stream);
free(buffer);
}
switch (mode){
case ENCODE:
{
fread(input_file_content, 1, input_file_size, input_file_stream);
fclose(input_file_stream);
output_file_size = get_encode_len(input_file_size);
if ((output_file_content = malloc(output_file_size)) == NULL){
fputs("Insufficient memory space!", stderr);
free(input_file_content);
fclose(output_file_stream);
free(buffer);
}
base64_encode(input_file_content, input_file_size, output_file_content);
fwrite(output_file_content, 1, output_file_size, output_file_stream);
}
break;
case DECODE:
{
unsigned char *p = input_file_content;
char c;
while ((c = fgetc(input_file_stream)) != EOF){
if (c != '\n')
*p++ = (unsigned char)c;
}
fclose(input_file_stream);
unsigned long long effective_input_file_size = p - input_file_content;
// 进行解码前首先判断base64码的合法性
if (!is_valid_base64_code(input_file_content, effective_input_file_size)){
fputs("Invalid base64 code!\n", stderr);
free(input_file_content);
fclose(output_file_stream);
free(buffer);
}
output_file_size = get_decode_len(input_file_content, effective_input_file_size);
if ((output_file_content = malloc(output_file_size)) == NULL){
fputs("Insufficient memory space!\n", stderr);
free(input_file_content);
fclose(output_file_stream);
free(buffer);
}
base64_decode(input_file_content, effective_input_file_size, output_file_content);
fwrite(output_file_content, 1, output_file_size, output_file_stream);
}
break;
}
free(input_file_content);
free(output_file_content);
fclose(output_file_stream);
free(buffer);
}
我使用的是编译环境是MinGW64 13.2.0,编译命令:
gcc base64.c base64_main.c -o base64 -O3
运行结果: