빠른 base64 인코딩/디코딩 소스
글쓴이: ironiris / 작성시간: 금, 2009/10/16 - 4:10오후
강좌라고 하기엔 좀 그렇고요.
샤워하다가 갑자기 떠오른 생각을 코드로 옮긴 것입니다.
유니온, 구조체 등을 이용해서,
쉬프트연산등을 활용한 기존 인터넷에서 쉽게 구할수 있는 base64 코드보다
더욱 빠르게 만들었습니다.
아무거나 base64 소스를 하나 구해서 인코딩 함수만 테스트해보니 약 50배정도 빠르더군요.
(약 35byte정도 문자열)
부끄럽지만 밑에 소스 나갑니다.
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, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 }; typedef union{ struct{ unsigned char c1,c2,c3; }; struct{ unsigned int e1:6,e2:6,e3:6,e4:6; }; } BF; void base64e(char *src, char *result, int length){ int i, j = 0; BF temp; for(i = 0 ; i < length ; i = i+3, j = j+4){ temp.c3 = src[i]; if((i+1) > length) temp.c2 = 0; else temp.c2 = src[i+1]; if((i+2) > length) temp.c1 = 0; 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] = '='; } } void base64d(char *src, char *result, int *length){ int i, j = 0, src_length, blank = 0; BF temp; src_length = strlen(src); 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 = 0; blank++; } else temp.e2 = DecodeMimeBase64[src[i+2]]; if(src[i+3] == '='){ temp.e1 = 0; blank++; } else temp.e1 = DecodeMimeBase64[src[i+3]]; result[j] = temp.c3; result[j+1] = temp.c2; result[j+2] = temp.c1; } *length = j-blank; }
많은 테스트를 안해봐서 버그가 있을지도 모르나 몇몇 문자열을 테스트해보니 잘되네요. :)
Forums:
php 에 들어있는것도
php 에 들어있는것도 그렇고, 제가 델파이에서 쓰고 있는것도 쉬프트연산을 이용하는거군요.
오류만 없다면 이걸로 쓰면 좋겠네요.
누가 오류없는거 확인해주시면 php 확장으로 만들어서 제출해보고 싶네요.ㅎㅎ
emerge money
http://wiki.kldp.org/wiki.php/GentooInstallSimple - 명령어도 몇 개 안돼요~
http://xenosi.de/
https://xenosi.de/
그냥 떠오른
그냥 떠오른 생각입니다만...
1. padding bit이 들어가는 문제
2. align의 문제
3. little endian과 big endian의 차이로 발생하는 문제
가 어쩌면 혹시 있을지도 모르겠네요.
테스트 해 본 것은 아닙니다.
혹시 문제 없으면 알려주세요.
begin{signature}
THIS IS SPARTA!!!!!n.
end{signature}
빅 엔디언이랑
빅 엔디언이랑 리틀엔디언 머신에서 각기 다른 인코딩 결과가 나오는데, 이건 비정상인 듯 합니다. 경험상 빅 엔디언 머신에서는 구조체의 비트필드가 거꾸로 잡혀서 그런거 같습니다.
위의 프로그램으로 각기 "abc"를 인코딩 했습니다.
네. 엔디안은
네. 엔디안은 생각하지 않고 x86 호환pc에서만 작동하도록 했습니다. pc말고는 테스트할 놈이 없으니... --;;
사실 프로그래밍하면서 엔디안을 신경써서 짠 프로그램은 이번이 처음 같네요.
이상하게.. 유니온에서 3byte를 넣고 6bit 씩 자르는데.. 원하는 값이 안나오더군요.
뒤집어야지 정상적으로 나오고 그게 x86의 엔디안 때문이라고 하더군요. 쩝...
인디안 문제는
인디안 문제는 엔디안을 구별할 수 있는 gcc에 predefine 된 선언이 있었는데, 지금 찾아보니깐 뭔지 잘 모르겠네요... :( 대부분 네트웍 프로그램이나 비트필드 구조체를 사용하는데서 그렇게 사용하는 것 같았습니다.
#include #include #include
gcc 일 경우 little endian 과 big endian 을 구분해서 작동하도록 되어있습니다.
big endian 환경에서 컴파일할수 있으신 분들 테스트 부탁드려요.
원래 코드에서
원래 코드에서 구조체를 다음과 같이 엔디언에 따라 다르게 잡으면 빅에서나 리틀에서나 똑같이 작동합니다.
소스코드는 ironiris 님의 코드에서 구조체만 위에서와 같이 엔디언에따라 나누고 코드에서는 리틀엔디언, 빅엔디언에따른 처리를 따로 하지 않았습니다. 그리고, 다른 컴파일러는 모르겠지만, x86의 gcc에서는 LITTLE_ENDIAN 이 선언되어있네요.
----
그리고, 제가 사용한 머신에서는 <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 』
오류 수정
위의 소스에서 오류가 있습니다. base64e 함수를 아래와 같이 해야 정상적으로 동작합니다.
ㅎㅎ 이 코드의 라이센스는 어떻게 되나요? 항상
ㅎㅎ 이 코드의 라이센스는 어떻게 되나요?
항상 코드 올리시는 분들의 심경이 궁금합니다. ^^; '좋으니까 써줘'인지.. 아니면 '쓰기만 해 봐라.. x 되는거야' 인지 말이죠 ^^;
코드를 이용하실때는 그냥 쓰시면 됩니다.
뭐 그리 내세울만한 코드도 아닌지라..
그건 그렇고 KLDP에 참 오랜만에 왔네요. 리눅스에 몇년간 손떼고 있었는데..
퍼가도 될까요?
좋은 내용 고맙습니다~
제 블로그에 정리해서 올려도 될까요?
http://arodream.wordpress.com
이 글 링크를 달아주면서 인용하시면 될것 같습니다.
좋은 정보는 공유해야 제맛이죠~
댓글 달기