base64.c

さあさあ、ようやくbase64.cです。base64エンコードおよびbase64デコードができるプログラムの説明です。
#ifndef _BASE64
#define _BASE64

// Base64
//           b        a        s        e        6        4        !
// ->       98       97      115      101       54       52       33
// -> 01100010 01100001 01110011 01100101 00110110 00110100 00100001
// -> 011000 100110 000101 110011 011001 010011 011000 110100 001000 01(0000)
// ->     24     38     05     51     25     19     24     52     08     16
// -> 24 38 05 51 | 25 19 24 52 | 08 16 () ()
// ->  Y  m  F  z |  Z  T  Y  0 |  I  Q  =  =


#include <stdlib.h>
#include <string.h>
#include "bit.c"

char *b64_encode(char *s, int size);
char *b64_decode(char *s, int size);
char b64_itoc(int i);
int b64_ctoi(char c);

// encode
char *b64_encode(char *s, int size){
   int i, ensize = (8 * size + 5) / 6; // sizeof encoded char
   bit b[6 * ensize];
   int eqsize = (ensize + 3) / 4 * 4 - ensize; // num of =
   char *en = (char *)malloc(sizeof(char) * (ensize + eqsize + 1));
   char s0[size + 1];
   memcpy(s0, s, size);
   s0[size] = 0;
   ctob(b, s0, 0, 6 * ensize); // read bit sequence from s0 for 6 * ensize
   for(i = 0; i < ensize; ++i) en[i] = b64_itoc((int)btoc(b + i * 6, 6)); // get c from b
   for(i = 0; i < eqsize; ++i) en[ensize + i] = '=';
   en[ensize + eqsize] = '\0';
   return en;
}

// decode
char *b64_decode(char *s, int size){
   int i, tsize = 4 * ((size * 3) / 4);
   char c[tsize];
   for(i = 0; i < size; ++i) c[i] = b64_ctoi(s[i]);
   for(i = size; i < tsize; ++i) c[i] = 0;
   bit b[6 * tsize];
   for(i = 0; i < tsize; ++i) ctob(b + 6 * i, c + i, 2, 6); // read 6 bit for each c with ofst 2
   char *de = (char *)malloc(sizeof(char) * (tsize / 4 * 3 + 1));
   for(i = 0; i < tsize / 4 * 3; ++i) de[i] = btoc(b + 8 * i, 8); // get c from b
   de[tsize / 4 * 3] = '\0';
   return de;
}

char b64_itoc(int i){
   if(i <= 25) return 'A' + i;
   if(i <= 51) return 'a' + i - 26;
   if(i <= 61) return '0' + i - 52;
   if(i == 62) return '+';
   return '/';
}

int b64_ctoi(char c){
   if('A' <= c && c <= 'Z') return c - 'A';
   if('a' <= c && c <= 'z') return c - 'a' + 26;
   if('0' <= c && c <= '9') return c - '0' + 52;
   if(c == '+') return 62;
   if(c == '/') return 63;
   return 0;
}

#endif
base64エンコードがどんなもんかは冒頭のコメントを見ればなんとなくわかるのではないのでしょうか。

encodeのポイントは、うまい関数を見つけて、encodeされた時の文字のサイズensizeと、
そのときつけるべき=の数eqsizeを最初の文字数から求めたことです。
それさえわかれば、あとはbit.cを用いて、
一旦ビット列にし、6ビットごとにcharに戻し、その値を、対応表(b64_itoc関数)を用いて英数字などに直せばOKです。

デコードもその逆をうまいことやってます(適当

いやーbit.c、素晴らしいな、と思うbase64.cでした。

あ、今思いましたが、これはエンコードデコードともに文字列を返しているので、
デコードの際、元データが文字列じゃないばあい、つまり'\0'でデータの終わりを示せない場合、
どこまでデータかわからず大変なことになってしまいそうですね。

まあしばらくデコードは使わないので、そんときゃそんとき考えましょう。

では例のごとくサンプルプログラムをば。
#include <stdio.h>
#include <string.h>
#include "base64.c"

int main(int argc, char *argv[]){
   if(argc != 3){
      printf("usage: %s [-d|-e] string\n", argv[0]);
      exit(1);
   }
   if(argv[1][1] == 'd') printf("[decode]\n%s\n=>\n%s\n", argv[2], b64_decode(argv[2], strlen(argv[2])));
   else printf("[encode]\n%s\n=>\n%s\n", argv[2], b64_encode(argv[2], strlen(argv[2])));
   return 0;
}
これを実行すると、
$ ./a.out -d tororo7
[encode]
tororo7
=>
dG9yb3JvNw==

$ ./a.out -e dG9yb3JvNw==
[decode]
dG9yb3JvNw==
=>
tororo7
と、しっかり動作を確認できます。

コメントをお書きください

コメント: 2
  • #1

    Kisha Schwein (日曜日, 22 1月 2017 16:28)


    Excellent site. Plenty of useful info here. I am sending it to some pals ans additionally sharing in delicious. And certainly, thank you on your effort!

  • #2

    Joe (日曜日, 27 8月 2017 11:54)

    -e -d 逆じゃないでしょうか?