Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>

설명

시중에 돌아다니는 base64관련 함수를 짜집기 한겁니다. 출처는 PHP3 에 들어간 Base64 Encode & Decode Source 랍니다.

목적으로 하는 문자열을 입력하면 base64로 인코딩과 디코딩을 시켜줍니다. base64는 웹에서 데이터를 전달(Content-Transfer)하기 위한 목적으로 사용됩니다. 사람이 읽을 수 없는 (바이너리)데이터의 경우 내용과 형태가 멋대로인 경우가 많아서 이 상태 그대로는 HTTP프로토콜을 이용해서 전달하기가 애매모호한데, 이것을 일관성 있는 데이터로 바꿔줍니다. base64인코딩을 할경우 US-ASCII에서 이용하는 65개의 pritable 문자로 데이터를 재구성합니다.

인코딩와 디코딩에 사용되는 알고리즘이 간단하다는 장점이 있지만 데이터를 인코딩할경우 원래 데이터에 비해서 33%정도 크기가 커진다는 단점이 존재 합니다.

<!> 이 함수는 완전하지 않은 버젼입니다. 내부에서 malloc()를 한후 free()하는 과정이 없으므로 메모리 누수가 발생할 수 있습니다. 약간 수정을 해야 될것 같은데, 일단은 그냥 올립니다.

사용방법

unsigned char *__base64_encode(const unsigned char *str, 
		int length, int *ret_length);
unsigned char *__base64_decode(const unsigned char *str, 
		int length, int *ret_length);
  1. str : 인코딩/디코딩을 위한 데이터
  2. length : 인코딩/디코딩을 위한 데이터의 크기
  3. ret_length : 인코딩/디코딩결과 나온 데이터의 크기
#include <encode.h>
#include <stdio.h>

int main()
{
   unsigned char *str, *dst;
   char *source = "hello world";
   int  size;

   str = __base64_encode((unsigned char *)source, strlen(source), &size);
   printf("%s : %d\n", str, size);
   dst = __base64_decode(str, strlen(str), &size);
   printf("%s : %d\n", dst, size);

   free(str);
   free(dst);

}

코드

#include <stdlib.h>
#include <string.h>

// BASE64
static char __base64_table[] ={
   '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', '+', '/', '\0'
};

static char __base64_pad = '=';

unsigned char *__base64_encode(const unsigned char *str, int length, int *ret_length) {
   const unsigned char *current = str;
   int i = 0;
   unsigned char *result = (unsigned char *)malloc(((length + 3 - length % 3) * 4 / 3 + 1) * sizeof(char));

   while (length > 2) { /* keep going until we have less than 24 bits */
      result[i++] = __base64_table[current[0] >> 2];
      result[i++] = __base64_table[((current[0] & 0x03) << 4) + (current[1] >> 4)];
      result[i++] = __base64_table[((current[1] & 0x0f) << 2) + (current[2] >> 6)];
      result[i++] = __base64_table[current[2] & 0x3f];

      current += 3;
      length -= 3; /* we just handle 3 octets of data */
   }

   /* now deal with the tail end of things */
   if (length != 0) {
      result[i++] = __base64_table[current[0] >> 2];
      if (length > 1) {
         result[i++] = __base64_table[((current[0] & 0x03) << 4) + (current[1] >> 4)];
         result[i++] = __base64_table[(current[1] & 0x0f) << 2];
         result[i++] = __base64_pad;
      }
      else {
         result[i++] = __base64_table[(current[0] & 0x03) << 4];
         result[i++] = __base64_pad;
         result[i++] = __base64_pad;
      }
   }
   if(ret_length) {
      *ret_length = i;
   }
   result[i] = '\0';
   return result;
}

/* as above, but backwards. :) */
unsigned char *__base64_decode(const unsigned char *str, int length, int *ret_length) {
   const unsigned char *current = str;
   int ch, i = 0, j = 0, k;
   /* this sucks for threaded environments */
   static short reverse_table[256];
   static int table_built;
   unsigned char *result;

   if (++table_built == 1) {
      char *chp;
      for(ch = 0; ch < 256; ch++) {
         chp = strchr(__base64_table, ch);
         if(chp) {
            reverse_table[ch] = chp - __base64_table;
         } else {
            reverse_table[ch] = -1;
         }
      }
   }

   result = (unsigned char *)malloc(length + 1);
   if (result == NULL) {
      return NULL;
   }

   /* run through the whole string, converting as we go */
   while ((ch = *current++) != '\0') {
      if (ch == __base64_pad) break;

      /* When Base64 gets POSTed, all pluses are interpreted as spaces.
         This line changes them back.  It's not exactly the Base64 spec,
         but it is completely compatible with it (the spec says that
         spaces are invalid).  This will also save many people considerable
         headache.  - Turadg Aleahmad <turadg@wise.berkeley.edu>
      */

      if (ch == ' ') ch = '+';

      ch = reverse_table[ch];
      if (ch < 0) continue;

      switch(i % 4) {
      case 0:
         result[j] = ch << 2;
         break;
      case 1:
         result[j++] |= ch >> 4;
         result[j] = (ch & 0x0f) << 4;
         break;
      case 2:
         result[j++] |= ch >>2;
         result[j] = (ch & 0x03) << 6;
         break;
      case 3:
         result[j++] |= ch;
         break;
      }
      i++;
   }

   k = j;
   /* mop things up if we ended on a boundary */
   if (ch == __base64_pad) {
      switch(i % 4) {
      case 0:
      case 1:
         free(result);
         return NULL;
      case 2:
         k++;
      case 3:
         result[k++] = 0;
      }
   }
   if(ret_length) {
      *ret_length = j;
   }
   result[k] = '\0';
   return result;
}

또다른 버젼

  • [minzkn] 버젼
    • 특징: 고정크기 버퍼를 사용하지 않고 가변적으로 할당하여 넘겨줌.
    • 원문에 대하여 encode 된 문자열의 크기는 정확히 다음과 같은 공식에 의해서 커지거나 작아집니다. (아래의 공식은 정수연산법으로 소숫점은 잘린(버림)다는 조건에 의해서 계산하게 정리해봤습니다.) - [minzkn]
      1. 원문을 encode하였을때 문자열의 길이 = ((<원문의 길이> + 2) / 3) * 4 = ((len + 2) / 3) << 2
      2. encode된 문자열을 decode하였을때 문자열의 길이 = ((<encode된 문자열의 길이> + 3) / 4) + 3 = ((len + 3) >> 2) + 3
    • base64 는 0~255 사이의 문자값을 각각 0~63의 64단계의 문자값만을 사용하여 변환하는 과정을 말하며 2글자가 3글자로 늘어납니다. 이것이 주로 사용되는 이유는 문자열 사이에 공백을 넣어서는 안될경우와 멀티바이트를 사용하지 않아야 할 결우에 많이들 사용합니다. 공백을 넣지 않아야 하는 경우는 대부분 token 이 공백으로 분류될때가 되겠지요. - [minzkn]
      /*
       Copyright (C) Information Equipment co.,LTD.
       All rights reserved.
       Code by JaeHyuk Cho <mailto:minzkn@infoeq.com>
       CVSTAG="$Header: /usr/local/mutihost/joinc/modules/moniwiki/data/text/RCS/Code_2fC_2fbase64,v 1.2 2007/01/24 02:16:30 root Exp root $"
      
       - Simple is best !
      */
      
      #if !defined(__def_mzapi_source_base64_c__)
      #define __def_mzapi_source_base64_c__ "base64.c"
      
      #include <stdio.h>
      #include <stdlib.h>
      #include <memory.h>
      #include <malloc.h>
      
      #if !defined(__mzapi_peek_vector__)
      # define __mzapi_peek_vector__(m_cast,m_base,m_sign,m_offset)         ((m_cast)(((unsigned char *)(m_base)) m_sign (size_t)(m_offset)))
      #endif
      #if !defined(mzapi_peek_byte)
      # define mzapi_peek_byte(m_base,m_offset)                             (*__mzapi_peek_vector__(unsigned char *,m_base,+,m_offset))
      #endif
      #if !defined(mzapi_poke_byte)
      # define mzapi_poke_byte(m_base,m_offset,m_value)                     (*__mzapi_peek_vector__(unsigned char *,m_base,+,m_offset)) = (unsigned char)(m_value)
      #endif
      
      static unsigned long (__mzapi_decode_base64__)(int s_character);
      char * (mzapi_encode_base64)(const char * s_string);
      char * (mzapi_decode_base64)(const char * s_string);
      int main(int s_argc, char **s_argv);
      
      static unsigned long (__mzapi_decode_base64__)(int s_character) 
      {
            if((s_character) >= ((int)'a'))return((((unsigned long)(s_character)) - ((unsigned long)'a')) + 26lu); 
       else if((s_character) >= ((int)'A'))return(((unsigned long)(s_character)) - ((unsigned long)'A'));
       else if((s_character) >= ((int)'0'))return((((unsigned long)(s_character)) - ((unsigned long)'0')) + 52lu);
       else if((s_character) == ((int)'+'))return(62lu);
       else if((s_character) == ((int)'/'))return(63lu);
       return(0lu);
      }
      
      char * (mzapi_encode_base64)(const char * s_string)
      {
       static const unsigned char c_alpha_table[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="};
       char * s_result;
       size_t s_length = strlen(s_string), s_source_offset = (size_t)0, s_target_offset = (size_t)0;
       unsigned long s_value;
       int s_quad, s_trip;
       s_result = (char *)malloc((((s_length + ((size_t)2)) / ((size_t)3)) << 2) + ((size_t)1));
       if(s_result == ((char *)0))return((char *)0);
       while(s_source_offset < s_length)
       {
        s_value = ((unsigned long)(mzapi_peek_byte((void *)s_string, s_source_offset) & ((int)0xff))) << 8; 
        if((s_source_offset + ((size_t)1)) < s_length)
        {
         s_value |= (unsigned long)(mzapi_peek_byte((void *)s_string, s_source_offset + ((size_t)1)) & ((int)0xff)); 
         s_trip = (int)1;
        }
        else s_trip = (int)0;
        s_value <<= 8;
        if((s_source_offset + ((size_t)2)) < s_length)
        {
         s_value |= (unsigned long)(mzapi_peek_byte((void *)s_string, s_source_offset + ((size_t)2)) & ((int)0xff)); 
         s_quad = (int)1;
        }
        else s_quad = (int)0;
        mzapi_poke_byte((void *)s_result, s_target_offset + ((size_t)3), (int)c_alpha_table[(s_quad == (int)1) ? (s_value & 0x3flu) : 64]);
        s_value >>= 6;
        mzapi_poke_byte((void *)s_result, s_target_offset + ((size_t)2), (int)c_alpha_table[(s_trip == (int)1) ? (s_value & 0x3flu) : 64]);
        s_value >>= 6;
        mzapi_poke_byte((void *)s_result, s_target_offset + ((size_t)1), (int)c_alpha_table[s_value & 0x3flu]);
        s_value >>= 6;
        mzapi_poke_byte((void *)s_result, s_target_offset, (int)c_alpha_table[s_value & 0x3flu]);
        s_source_offset += (size_t)3, s_target_offset += (size_t)4;
       }
       mzapi_poke_byte((void *)s_result, s_target_offset, (int)'\0');
       return(s_result);
      }
      
      char * (mzapi_decode_base64)(const char * s_string)
      {
       char * s_result;
       size_t s_length = strlen(s_string), s_source_offset = (size_t)0, s_target_offset = (size_t)0;
       unsigned long s_value;
       s_result = (char *)malloc((((s_length + ((size_t)3)) >> 2) * ((size_t)3)) + ((size_t)1));
       if(s_result == ((char *)0))return((char *)0);
       while(s_source_offset < s_length)
       {
        s_value  = ((__mzapi_decode_base64__(mzapi_peek_byte((void *)s_string, s_source_offset)) & 0x3flu) << 18) |
                   ((__mzapi_decode_base64__(mzapi_peek_byte((void *)s_string, s_source_offset + ((size_t)1))) & 0x3flu) << 12) |
                   ((__mzapi_decode_base64__(mzapi_peek_byte((void *)s_string, s_source_offset + ((size_t)2))) & 0x3flu) << 6) |
                   (__mzapi_decode_base64__(mzapi_peek_byte((void *)s_string, s_source_offset + ((size_t)3))) & 0x3flu);
        mzapi_poke_byte((void *)s_result, s_target_offset++, (int)((s_value >> 16) & 0xfflu));
        if(mzapi_peek_byte((void *)s_string, s_source_offset + ((size_t)2)) != ((int)'='))
        {
         mzapi_poke_byte((void *)s_result, s_target_offset++, (int)((s_value >> 8) & 0xfflu));
         if(mzapi_peek_byte((void *)s_string, s_source_offset + ((size_t)3)) != ((int)'='))mzapi_poke_byte((void *)s_result, s_target_offset++, (int)(s_value & 0xfflu));
        } 
        s_source_offset += (size_t)4;
       }
       mzapi_poke_byte((void *)s_result, s_target_offset, (int)'\0');
       return(s_result);
      }
      
      int main(int s_argc, char **s_argv)
      {
       static char s_default[] = {"This is base64 test function - by minzkn"};
       char *s_this, *s_encode, *s_decode;
       if(s_argc >= 2)s_this = (char *)(&s_argv[1][0]);
       else s_this = s_default;
       (void)fprintf(stdout, "original: \"%s\" (%d)\n", s_this, (int)strlen(s_this));
       s_encode = mzapi_encode_base64(s_this);
       if(s_encode != ((char *)0))
       {
        (void)fprintf(stdout, "encode  : \"%s\" (%d)\n", s_encode, (int)strlen(s_encode));
        s_decode = mzapi_decode_base64(s_encode);
        if(s_decode != ((char *)0))
        {
         (void)fprintf(stdout, "decode  : \"%s\" (%d)\n", s_decode, (int)strlen(s_decode));
         free((void *)s_decode);
        }
        free((void *)s_encode);
       }
       return(0);
      }
      
      #endif
      
      /* End of source */

변경사항

  1. 출처 내용 : PHP3 에 들어간 Base64 Encode & Decode Source
2. 형변환(Visual C++ 6.0 SP6 에서 에러남)