반응형

빠른 base64 인코딩/디코딩 소스

 

http://kldp.org/node/109436

 

 

필요한 부분만 가져왔습니다.

 

앞부분 생략

 

 

--------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <endian.h>
 
static const char MimeBase64[] = {
    '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 int DecodeMimeBase64[256] = {
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 00-0F */
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 10-1F */
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,  /* 20-2F */
    52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,  /* 30-3F */
    -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,  /* 40-4F */
    15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,  /* 50-5F */
    -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,  /* 60-6F */
    41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,  /* 70-7F */
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 80-8F */
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 90-9F */
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* A0-AF */
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* B0-BF */
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* C0-CF */
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* D0-DF */
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* E0-EF */
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1   /* F0-FF */
};
 
typedef union{
    struct{
        unsigned char c1,c2,c3;
    };
    struct{
        unsigned int e1:6,e2:6,e3:6,e4:6;
    };
} BF;
 
int endian = 0; // little : 0, big : 1
 
void base64e(char *src, char *result, int length){
    int i, j = 0;
    BF temp;
 
    if(endian == 0){ // little endian(intel)
        for(i = 0 ; i < length ; i = i+3, j = j+4){
            temp.c3 = src[i];
            if((i+1) > length) temp.c2 = 0x00;
            else temp.c2 = src[i+1];
            if((i+2) > length) temp.c1 = 0x00;
            else temp.c1 = src[i+2];
 
            result[j]   = MimeBase64[temp.e4];
            result[j+1] = MimeBase64[temp.e3];
            result[j+2] = MimeBase64[temp.e2];
            result[j+3] = MimeBase64[temp.e1];
 
            if((i+2) > length) result[j+2] = '=';
            if((i+3) > length) result[j+3] = '=';
        }
    } else { // big endian(sun)
        for(i = 0 ; i < length ; i = i+3, j = j+4){
            temp.c1 = src[i];
            if((i+1) > length) temp.c2 = 0x00;
            else temp.c2 = src[i+1];
            if((i+2) > length) temp.c3 = 0x00;
            else temp.c3 = src[i+2];
 
            result[j]   = MimeBase64[temp.e4];
            result[j+1] = MimeBase64[temp.e3];
            result[j+2] = MimeBase64[temp.e2];
            result[j+3] = MimeBase64[temp.e1];
 
            if((i+2) > length) result[j+2] = '=';
            if((i+3) > length) result[j+3] = '=';
        }
    }
}
 
void base64d(char *src, char *result, int *length){
    int i, j = 0, src_length, blank = 0;
    BF temp;
 
    src_length = strlen(src);
 
    if(endian == 0){ // little endian(intel)
        for(i = 0 ; i < src_length ; i = i+4, j = j+3){
            temp.e4 = DecodeMimeBase64[src[i]];
            temp.e3 = DecodeMimeBase64[src[i+1]];
            if(src[i+2] == '='){
                temp.e2 = 0x00;
                blank++;
            } else temp.e2 = DecodeMimeBase64[src[i+2]];
            if(src[i+3] == '='){
                temp.e1 = 0x00;
                blank++;
            } else temp.e1 = DecodeMimeBase64[src[i+3]];
 
            result[j]   = temp.c3;
            result[j+1] = temp.c2;
            result[j+2] = temp.c1;
        }
    } else { // big endian(sun)
        for(i = 0 ; i < src_length ; i = i+4, j = j+3){
            temp.e4 = DecodeMimeBase64[src[i]];
            temp.e3 = DecodeMimeBase64[src[i+1]];
            if(src[i+2] == '='){
                temp.e2 = 0x00;
                blank++;
            } else temp.e2 = DecodeMimeBase64[src[i+2]];
            if(src[i+3] == '='){
                temp.e1 = 0x00;
                blank++;
            } else temp.e1 = DecodeMimeBase64[src[i+3]];
 
            result[j]   = temp.c1;
            result[j+1] = temp.c2;
            result[j+2] = temp.c3;
        }
    }
    *length = j-blank;
}
 
int main(void){
    char str1[]="테스트문자열입니다.ABCabc123,./";
    char str2[]="7YWM7Iqk7Yq466y47J6Q7Je07J6F64uI64ukLkFCQ2FiYzEyMywuLw==";
    char *result;
    int src_size;
    struct timespec start,end;
 
    if (__LITTLE_ENDIAN == BYTE_ORDER) endian == 0;
    else endian == 1;
 
    src_size = strlen(str1);
    result = (char *)malloc((4 * (src_size / 3)) + (src_size % 3 ? 4 : 0) + 1);
    clock_gettime(CLOCK_REALTIME, &start);
    base64e(str1, result, src_size);
    clock_gettime(CLOCK_REALTIME, &end);
    float time_dif = (end.tv_sec - start.tv_sec) + ((end.tv_nsec - start.tv_nsec) );
    printf("함수 수행 시간: %f\n", time_dif);
    printf("%s\n%s\n",str1,result);
    free(result);
 
    src_size = strlen(str2);
    result = (char *)malloc(3 * (src_size / 4));
    base64d(str2,result,&src_size);
    printf("%s\n%s\n길이:%d\n",str2,result,src_size);
    free(result);
}

===============================================================================


gcc 일 경우 little endian 과 big endian 을 구분해서 작동하도록 되어있습니다.
big endian 환경에서 컴파일할수 있으신 분들 테스트 부탁드려요.

원래 코드에서

원래 코드에서 구조체를 다음과 같이 엔디언에 따라 다르게 잡으면 빅에서나 리틀에서나 똑같이 작동합니다.

typedef union{
        struct{
#ifdef LITTLE_ENDIAN
                unsigned char c1,c2,c3;
#else
                unsigned char c3,c2,c1;
#endif
        };
        struct{
#ifdef LITTLE_ENDIAN
                unsigned int e1:6,e2:6,e3:6,e4:6;
#else
                unsigned int e4:6,e3:6,e2:6,e1:6;
#endif
        };
} BF;

소스코드는 ironiris 님의 코드에서 구조체만 위에서와 같이 엔디언에따라 나누고 코드에서는 리틀엔디언, 빅엔디언에따른 처리를 따로 하지 않았습니다. 그리고, 다른 컴파일러는 모르겠지만, x86의 gcc에서는 LITTLE_ENDIAN 이 선언되어있네요.

[parkhw00@rose0 parkhw00]$ gcc base64.c 
[parkhw00@rose0 parkhw00]$ ./a.out 
테스트문자열입니다.ABCabc123,./
7YWM7Iqk7Yq466y47J6Q7Je07J6F64uI64ukLkFCQ2FiYzEyMywuLw==
7YWM7Iqk7Yq466y47J6Q7Je07J6F64uI64ukLkFCQ2FiYzEyMywuLw==
테스트문자열입니다.ABCabc123,./
길이:40
[parkhw00@rose0 parkhw00]$ uname -p
sparc
[parkhw00@rose0 parkhw00]$ 

[hyun@hyun tmp]$ gcc base64.c 
[hyun@hyun tmp]$ ./a.out 
테스트문자열입니다.ABCabc123,./
7YWM7Iqk7Yq466y47J6Q7Je07J6F64uI64ukLkFCQ2FiYzEyMywuLw==
7YWM7Iqk7Yq466y47J6Q7Je07J6F64uI64ukLkFCQ2FiYzEyMywuLw==
테스트문자열입니다.ABCabc123,./
길이:40
[hyun@hyun tmp]$ uname -p
x86_64
[hyun@hyun tmp]$ 

----
그리고, 제가 사용한 머신에서는 <endian.h> 헤더파일이 없네요. 오래된 컴터라서 그런가...



나도 세벌식을 씁니다

네.. LITTLE_ENDIAN 으로

네.. LITTLE_ENDIAN 으로 선언되어있는 것을 사용하는 것이 더 좋겠네요.
if 구문도 줄일수 있고요..

그럭저럭 사용할 수 있는 코드가 된 듯 하니 다행이네요. :)

음...

__LITTLE_ENDIAN, __BIG_ENDIAN, __BYTE_ORDER 는 endian.h 에 define 되어 있어서..
#ifdef 로 사용하면 낭패를 볼 수 있습니다.
(__USE_BSD 가 define 되면, LITTLE_ENDIAN, BIG_ENDIAN, BYTE_ORDER 가 추가됩니다)

#if __BYTE_ORDER == __LITTLE_ENDIAN 와 같이 사용하시는게 좋을 것 같습니다.

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

아닙니다. LITTLE_ENDIAN

아닙니다. LITTLE_ENDIAN 은 gcc 컴파일러에서 미리 선언해주는 것 같습니다.
위에서 사용한 헤더는 stdio, stdlib, string 뿐입니다.

----
stdlib 헤더를 빼니깐 LITTLE_ENDIAN 선언이 없어지네요. 아마 저 안쪽 어디에서 endian 헤더를 추가하는 듯 합니다.



나도 세벌식을 씁니다

음..

gcc predefined macros 에는 ENDIAN 에 대한 언급은 없는 걸로 알고 있습니다.
해당 시스템의 endian 정의는 system header 에 의존적이구요..

혹시나 해서 다시 헤더를 살펴봤습니다.
features.h 를 보니, 별다른 옵션이 없는 경우에는 _BSD_SOURCE (__USE_BSD) 가 켜지는 듯 하네요.
이 경우에는 stdlib.h 가 sys/types.h -> endian.h 을 include 하구요.
이 때 LITTLE_ENDIAN 이 1234 로 define 됩니다. (BIG_ENDIAN 은 4321)

$ fih LITTLE_ENDIAN | grep '\# *define'
/usr/include/bits/endian.h:7:#define __BYTE_ORDER __LITTLE_ENDIAN
/usr/include/endian.h:32:#define __LITTLE_ENDIAN 1234
/usr/include/endian.h:46:# define LITTLE_ENDIAN __LITTLE_ENDIAN

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

반응형
Posted by 공간사랑
,