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

ironiris의 이미지

강좌라고 하기엔 좀 그렇고요.
샤워하다가 갑자기 떠오른 생각을 코드로 옮긴 것입니다.
유니온, 구조체 등을 이용해서,
쉬프트연산등을 활용한 기존 인터넷에서 쉽게 구할수 있는 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 확장으로 만들어서 제출해보고 싶네요.ㅎㅎ

emerge money
http://wiki.kldp.org/wiki.php/GentooInstallSimple - 명령어도 몇 개 안돼요~
http://xenosi.de/

시지프스의 이미지

그냥 떠오른 생각입니다만...
1. padding bit이 들어가는 문제
2. align의 문제
3. little endian과 big endian의 차이로 발생하는 문제
가 어쩌면 혹시 있을지도 모르겠네요.
테스트 해 본 것은 아닙니다.
혹시 문제 없으면 알려주세요.

begin{signature}
THIS IS SPARTA!!!!!n.
end{signature}

Hyun의 이미지

빅 엔디언이랑 리틀엔디언 머신에서 각기 다른 인코딩 결과가 나오는데, 이건 비정상인 듯 합니다. 경험상 빅 엔디언 머신에서는 구조체의 비트필드가 거꾸로 잡혀서 그런거 같습니다.

int main( int argc, char **argv )
{
	FILE *in = stdin;
	char buf_in[256], buf_out[512];
	size_t len;
 
	if( argc > 1 )
	{
		in = fopen( argv[1], "r" );
		if( in == NULL )
			return 1;
	}
 
	while( (len=fread(buf_in, 1, sizeof(buf_in), in)) != 0 )
	{
		memset( buf_out, 0, sizeof(buf_out) );
		base64e( buf_in, buf_out, len );
		fputs( buf_out, stdout );
 
		if( len != sizeof(buf_in) )
			break;
	}
	fputs( "\n", stdout );
 
	if( in != stdin )
		fclose( in );
 
	return 0;
}

위의 프로그램으로 각기 "abc"를 인코딩 했습니다.

[parkhw00@rose0 parkhw00]$ ./a.out 
abc
hJ2YK8==
[parkhw00@rose0 parkhw00]$ uname -m
sun4u
[parkhw00@rose0 parkhw00]$ 

[hyun@hyun ~]$ ./a.out 
abc
YWJjCg==
[hyun@hyun ~]$ uname -m
x86_64
[hyun@hyun ~]$ 

나도 세벌식을 씁니다

나도 세벌식을 씁니다
ironiris의 이미지

네. 엔디안은 생각하지 않고 x86 호환pc에서만 작동하도록 했습니다. pc말고는 테스트할 놈이 없으니... --;;
사실 프로그래밍하면서 엔디안을 신경써서 짠 프로그램은 이번이 처음 같네요.
이상하게.. 유니온에서 3byte를 넣고 6bit 씩 자르는데.. 원하는 값이 안나오더군요.
뒤집어야지 정상적으로 나오고 그게 x86의 엔디안 때문이라고 하더군요. 쩝...

Hyun의 이미지

인디안 문제는 엔디안을 구별할 수 있는 gcc에 predefine 된 선언이 있었는데, 지금 찾아보니깐 뭔지 잘 모르겠네요... :( 대부분 네트웍 프로그램이나 비트필드 구조체를 사용하는데서 그렇게 사용하는 것 같았습니다.


나도 세벌식을 씁니다

나도 세벌식을 씁니다
ironiris의 이미지

#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 환경에서 컴파일할수 있으신 분들 테스트 부탁드려요.
Hyun의 이미지

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

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> 헤더파일이 없네요. 오래된 컴터라서 그런가...


나도 세벌식을 씁니다

나도 세벌식을 씁니다
ironiris의 이미지

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

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

ymir의 이미지

__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 』

Hyun의 이미지

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

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


나도 세벌식을 씁니다

나도 세벌식을 씁니다
ymir의 이미지

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 함수를 아래와 같이 해야 정상적으로 동작합니다.

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+1) >= length) result[j+2] = '=';
            if((i+2) >= 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+1) >= length) result[j+2] = '=';
            if((i+2) >= length) result[j+3] = '=';
        }
    }
}
김정균의 이미지

ㅎㅎ 이 코드의 라이센스는 어떻게 되나요?

항상 코드 올리시는 분들의 심경이 궁금합니다. ^^; '좋으니까 써줘'인지.. 아니면 '쓰기만 해 봐라.. x 되는거야' 인지 말이죠 ^^;

ironiris의 이미지

뭐 그리 내세울만한 코드도 아닌지라..
그건 그렇고 KLDP에 참 오랜만에 왔네요. 리눅스에 몇년간 손떼고 있었는데..

익명 사용자의 이미지

좋은 내용 고맙습니다~
제 블로그에 정리해서 올려도 될까요?
http://arodream.wordpress.com

ironiris의 이미지

좋은 정보는 공유해야 제맛이죠~

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.