BASE64

BASE64エンコード/デコードをC言語で書いた。

/**
 * BASE64変換プログラム(マイコン用)
 *
 * マイコン用なのでメモリの動的確保(malloc)はあえてしません。
 * また実行時の計算とRAM使用を極力減らしています。
 */

//! BASE64エンコードテーブル
static const char BASE64_ENC[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

//! BASE64デコードテーブル
static const char BASE64_DEC[] = {
                                                62, -1, -1, -1, 63, // +, /
    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1,  0, -1, -1, // 0,1,2,...,9, =
    -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, // A,B,C,...
    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // ...,X,Y,Z
    -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // a,b,c,...
    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51                      // ...,x,y,z
};

/**
 * BASE64でエンコードする
 *
 * @param bin_buff バイナリデータ(入力)
 * @param asc_buff アスキーデータ(出力)
 * @param size バイナリデータのサイズ(バイト数)
 * @return アスキーデータのサイズ(バイト数)
 */
int encodeBase64(unsigned char* bin_buff, char* asc_buff, int size)
{
    int i;
    int j = 0;
    unsigned short reg = 0;  // シフトレジスタ
    int   len = 0;  // シフトレジスタに入ったデータのビット数
    short six = 0;  // 6ビットデータ
    
    // バイナリデータを8ビットずつ
    for(i = 0; i < size; i++)
    {
        // 8ビットのデータをシフトレジスタに突っ込む
        reg = (reg << 8) | bin_buff[i];
        len += 8;
        
        // シフトレジスタから6ビットずつ取り出す
        while(len >= 6)
        {
            six = (reg >> (len - 6)) & 0x3F;
            len -= 6;
            // 6ビットデータをBASE64のアスキー文字に変換
            asc_buff[j] = BASE64_ENC[six];
            j++;
        }
    }
    // シフトレジスタに半端のビットが残っている場合
    if(len > 0)
    {
        // 半端ビットを6ビットの上位にそろえて取り出す
        six = (reg << (6 - len)) & 0x3F;
        // 6ビットデータをBASE64のアスキー文字に変換
        asc_buff[j] = BASE64_ENC[six];
        j++;
    }
    // 変換後の文字数が4の倍数でないなら '=' で埋める
    while( (j % 4) != 0 )
    {
        asc_buff[j] = '=';
        j++;
    }
    // 変換後の文字数を返す
    return j;
}

/**
 * BASE64でデコードする
 *
 * @param bin_buff バイナリデータ(出力)
 * @param asc_buff アスキーデータ(入力)
 * @param size アスキーデータのサイズ(バイト数)
 * @return バイナリデータのサイズ(バイト数) ただし、異常のとき-1
 */
int decodeBase64(unsigned char* bin_buff, char* asc_buff, int size)
{
    int i = 0;
    int j = 0;
    int m;
    char asc;
    char reg[4];
    
    // アスキーデータを指定文字数まで
    while(i < size)
    {
        // アスキーデータを4文字ずつ (24ビットぶんのバイナリデータに相当)
        for(m = 0; m < 4; m++)
        {
            asc = asc_buff[i++];
            // BASE64のアスキー文字を6ビットデータに逆変換
            reg[m] = BASE64_DEC[ asc - '+'];
            // 不正な文字ならエラー終了
            if(reg[m] < 0) return -1;
        }
        // 3バイト(24ビット)のバイナリーデータを格納
        bin_buff[j++] = ((reg[0] & 0x3F) << 2) | ((reg[1] & 0x30) >> 4);
        bin_buff[j++] = ((reg[1] & 0x0F) << 4) | ((reg[2] & 0x3C) >> 2);
        bin_buff[j++] = ((reg[2] & 0x03) << 6) | ((reg[3] & 0x3F) >> 0);
    }
    // ゴミを捨てる
    if(asc_buff[i-1] == '=') j--;
    if(asc_buff[i-2] == '=') j--;

    // 変換後のバイト数を返す
    return j;
}

#if 1

#include<stdio.h>
#define MAX_INPUT_SIZE  15000
#define MAX_OUTPUT_SIZE  20000

/**
 * エンコードのテスト用main関数(PC用)
 */
int main(void)
{
    // 入力バイナリファイルを読み込む
    FILE *infile;
    infile = fopen("input.bin", "rb");
    if(infile == NULL){
        printf("Can not open input file!\n");
        return 1;
    }
    unsigned char inbuff[MAX_INPUT_SIZE];
    int inputsize = fread(inbuff, 1, MAX_INPUT_SIZE, infile);
    printf("input size = %d bytes\n", inputsize);
    fclose(infile);
    
    // BASE64エンコード
    char outbuff[MAX_OUTPUT_SIZE];
    int outputsize = encodeBase64(inbuff, outbuff, inputsize);
    printf("output size = %d bytes\n", outputsize);

    // 出力アスキーファイルを書きだす
    FILE *outfile;
    outfile = fopen("output.txt", "w");
    if(outfile == NULL){
        printf("Can not open output file!\n");
        return 2;
    }
    int i;
    for(i=0;i<outputsize;i++){
        fputc(outbuff[i], outfile);
    }
    fclose(outfile);
    
    return 0;
}

#endif

#if 0
#define MAX_INPUT_SIZE  20000
#define MAX_OUTPUT_SIZE  15000

#include<stdio.h>
/**
 * デコードのテスト用main関数(PC用)
 */
int main(void)
{
    // 入力アスキーファイルを読み込む
    FILE *infile;
    infile = fopen("input.txt", "rb");
    if(infile == NULL){
        printf("Can not open input file!\n");
        return 1;
    }
    char inbuff[MAX_INPUT_SIZE];
    int inputsize = fread(inbuff, 1, MAX_INPUT_SIZE, infile);
    printf("input size = %d bytes\n", inputsize);
    fclose(infile);
    
    // BASE64デコード
    unsigned char outbuff[MAX_OUTPUT_SIZE];
    int outputsize = decodeBase64(outbuff, inbuff, inputsize);
    printf("output size = %d bytes\n", outputsize);

    // 出力バイナリファイルを書きだす
    FILE *outfile;
    outfile = fopen("output.bin", "wb");
    if(outfile == NULL){
        printf("Can not open output file!\n");
        return 2;
    }
    int i;
    fwrite(outbuff,1,outputsize,outfile);
    fclose(outfile);
    
    return 0;
}

#endif