basex.h
45a13a97
 /*
  * $Id$
  *
  * convert/decode to/from ascii using various bases
  *
  * Copyright (C) 2008 iptelorg GmbH
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
3ca1a53f
 
5a03489e
 /*!
  * \file
  * \brief SIP-router core :: convert/decode to/from ascii using various bases
  *
  * \ingroup core
  *
  * Module: \ref core
  *
  *
45a13a97
  * Functions:
5a03489e
  *  - base16_enc(src, src_len, dst, dst_len)    : encode to standard hex
  *  - base16_dec(src, src_len, dst, dst_len)    : decode from standard hex
  *  - base16_enc_len(len)                       : length needed to encode len bytes (macro)
  *  - base16_max_dec_len(len)                   : length needed to decode a string of size len
45a13a97
  *
5a03489e
  *  - base64_enc(src, src_len, dst, dst_len)    : encode to base64, standard alphabet
  *  - base64_dec(src, src_len, dst, dst_len)    : decode from base64, standard  alphabet
  *  - base64_enc_len(len)                       : length needed to encode len bytes (macro)
  *  - base64_max_dec_len(len)                   : maximum length needed to decode len bytes (macro)
  *  - base64_dec_len(str, len)                  : size of the decoded str 
  *  - q_base64_enc(src, src_len, dst, dst_len)  : encode to special base64 alphabet (non standard)
  *  - q_base64_dec(src, src_len, dst, dst_len)  - decode from special non-standard base64 alphabet
45a13a97
  *
  *  All the above functions return the size used (in dst) on success and
  *   0 or a negative number (which is -1*size_needed) on error.
  *
  * There are close to no checks for validity, an unexpected char will lead
  * to a corrupted result, but the functions won't return error.
  *
  * Notes:
  *  on a core2 duo the versions with lookup tables are way faster (see
  *  http://www.experts-exchange.com/Programming/Languages/CPP/Q_21988706.html
  *  for some interesting tests and ideeas).
  *
  *  Test results for 40 bytes  (typical ser nounce) in average cpu cycles:
3ca1a53f
 \verbatim
45a13a97
  *                    lookup   lookup_large lookup8k no-lookup
  *  base16_enc           211/231  218/199      -       1331
  *  base16_dec           252/251  236          -       1226
  *  base64_enc           209      186         156      1005
  *  base64_dec           208      207         207      1242
  *  q_base64_enc         -                              288
  *  q_base64_dec         -                              281
  *  (see test/basex.txt for more results)
3ca1a53f
 \endverbatim
45a13a97
  *
  * Defines:
5a03489e
  *  - BASE64_LOOKUP_TABLE/NO_BASE64_LOOKUP_TABLE : use (default)/don't use
45a13a97
  *     small lookup tables for conversions (faster in general).
5a03489e
  *  - BASE64_LOOKUP_LARGE    : use large lookup tables (2560 bytes for 
45a13a97
  *    encoding and 256 bytes for decoding; without it 64 bytes are used for
  *    encoding and 85 bytes for decoding.
5a03489e
  *  - BASE64_LOOKUP_8K : use even larger lookup tables (8K for encoding and
45a13a97
  *    256 for decoding); also try to write 2 bytes at a time (short) if
  *    the destination is 2 byte aligned
  *
5a03489e
  *  - BASE16_LOOKUP_TABLE/NO_BASE16_LOOKUP_TABLE : use (default)/don't use
45a13a97
  *     small lookup tables for conversions (faster in general).
5a03489e
  *  - BASE16_LOOKUP_LARGE  : use large lookup tables (512 bytes for 
45a13a97
  *    encoding and 256 bytes for decoding
5a03489e
  *  - BASE16_READ_WHOLE_INTS : read an int at a time
45a13a97
  *
  * History:
  * --------
  *  2008-06-11  created by andrei
  */
  
 
 
 #ifndef _basex_h
 #define _basex_h
 
 #include "compiler_opt.h"
 
 /* defaults */
 #ifndef NO_BASE16_LOOKUP_TABLE
 #define BASE16_LOOKUP_TABLE
 #endif
 
 #ifndef NO_BASE64_LOOKUP_TABLE
 #define BASE64_LOOKUP_TABLE
 #endif
 
 #ifndef NO_BASE64_LOOKUP_8K
 #define BASE64_LOOKUP_8K
 #endif
 
 #ifndef NO_BASE16_LOOKUP_LARGE
 #define BASE16_LOOKUP_LARGE
 #endif
 
 #if !defined NO_BASE64_LOOKUP_LARGE && !defined BASE64_LOOKUP_8K
 #define BASE64_LOOKUP_LARGE
 #endif
 
 
 
 #if defined BASE16_READ_WHOLE_INTS || defined BASE64_READ_WHOLE_INTS || \
 	defined BASE64_LOOKUP_8K
 #include "endianness.h"
 
5a03489e
 /*! \brief aligns p to a type* pointer, type must have a 2^k size */
45a13a97
 #define ALIGN_POINTER(p, type) \
 	((type*) ((long)((char*)(p)+sizeof(type)-1)&~(long)(sizeof(type)-1)))
 
 #define ALIGN_UINT_POINTER(p) ALIGN_POINTER(p, unsigned int)
 
 #endif
 
 
 #ifdef BASE16_LOOKUP_TABLE
 
 #ifdef BASE16_LOOKUP_LARGE
5a03489e
 /*! \brief use large tables: 512 for lookup and 256 for decode */
45a13a97
 
 extern unsigned char _bx_hexdig_hi[256];
 extern unsigned char _bx_hexdig_low[256];
 
3ca1a53f
 /*! \brief returns the first 4 bits of c converted to a hex digit */
45a13a97
 #define HEX_HI(h)	_bx_hexdig_hi[(unsigned char)(h)]
3ca1a53f
 /*! \brief returns the low 4 bits of converted to a hex digit */
45a13a97
 #define HEX_LOW(h)	_bx_hexdig_low[(unsigned char)(h)]
 
 extern unsigned char _bx_unhexdig256[256];
 
3ca1a53f
 /*! \brief  converts hex_digit to a number (0..15); it might
  *      \return 0xff for invalid digit (but with some compile
  *      option it won't check)
  */
45a13a97
 #define UNHEX(h)	_bx_unhexdig256[(h)]
 
 #else /* BASE16_LOOKUP_LARGE */
5a03489e
 /*! \brief use small tabes: 16 bytes for lookup and 32 for decode */
45a13a97
 
 extern unsigned char _bx_hexdig[16+1];
 
 #define HEX_4BITS(h) _bx_hexdig[(h)]
 #define HEX_HI(h)	HEX_4BITS(((unsigned char)(h))>>4)
 #define HEX_LOW(h)	HEX_4BITS((h)&0xf)
 
 extern unsigned char _bx_unhexdig32[32];
 #define UNHEX(h) _bx_unhexdig32[(((h))-'0')&0x1f]
 
 #endif /* BASE16_LOOKUP_LARGE */
 
 #else /* BASE16_LOOKUP_TABLE */
 /* no lookup tables */
 #if 0
 #define HEX_4BITS(h) (unsigned char)((unlikely((h)>=10))?((h)-10+'A'):(h)+'0')
 #define UNHEX(c) (unsigned char)((unlikely((c)>='A'))?(c)-'A'+10:(c)-'0')
 #else
 #define HEX_4BITS(hc) (unsigned char)( ((((hc)>=10)-1)&((hc)+'0')) | \
 									((((hc)<10)-1)&((hc)+'A')) )
 #define UNHEX(c) (unsigned char) ( ((((c)>'9')-1)& ((c)-'0')) | \
 								((((c)<='9')-1)&((c)-'A')) )
 #endif 
 
 #define HEX_HI(h)	HEX_4BITS(((unsigned char)(h))>>4)
 #define HEX_LOW(h)	HEX_4BITS((h)&0xf)
 
 #endif /* BASE16_LOOKUP_TABLE */
 
 
 #ifdef BASE64_LOOKUP_TABLE
 #ifdef BASE64_LOOKUP_LARGE
 /* large lookup tables, 2.5 k */
 
 extern unsigned char _bx_b64_first[256];
 extern unsigned char _bx_b64_second[4][256];
 extern unsigned char _bx_b64_third[4][256];
 extern unsigned char _bx_b64_fourth[256];
 
 #define BASE64_1(a) _bx_b64_first[(a)]
 #define BASE64_2(a,b) _bx_b64_second[(a)&0x3][(b)]
 #define BASE64_3(b,c) _bx_b64_third[(c)>>6][(b)]
 #define BASE64_4(c) _bx_b64_fourth[(c)]
 
 extern unsigned char _bx_ub64[256];
 #define UNBASE64(v) _bx_ub64[(v)]
 
 #elif defined BASE64_LOOKUP_8K
 /* even larger encode tables: 8k */
 extern unsigned short _bx_b64_12[4096];
 
 /* return a word (16 bits) */
 #define BASE64_12(a,b)	_bx_b64_12[((a)<<4)|((b)>>4)]
 #define BASE64_34(b,c)	_bx_b64_12[(((b)&0xf)<<8)|(c)]
 #ifdef __IS_LITTLE_ENDIAN
 #define FIRST_8B(s)	((unsigned char)(s))
 #define LAST_8B(s)	((s)>>8)
 #elif defined __IS_BIG_ENDIAN
 #define FIRST_8B(s)	((s)>>8)
 #define LAST_8B(s)	((unsigned char)(s))
 #else
 #error neither __IS_LITTLE_ENDIAN nor __IS_BIG_ENDIAN are defined
 #endif
 
 
 extern unsigned char _bx_ub64[256];
 #define UNBASE64(v) _bx_ub64[(v)]
 
 #else /* BASE64_LOOKUP_LARGE */
 /* small lookup tables */
 extern unsigned char _bx_b64[64+1];
 
 #define BASE64_DIG(v)	_bx_b64[(v)]
 
 #define BASE64_1(a)		BASE64_DIG((a)>>2)
 #define BASE64_2(a, b)	BASE64_DIG( (((a)<<4)&0x3f) | ((b)>>4))
 #define BASE64_3(b, c)	BASE64_DIG( (((b)<<2)&0x3f) | ((c)>>6))
 #define BASE64_4(c)		BASE64_DIG((c)&0x3f)
 
 extern unsigned char _bx_ub64[0x54+1];
 #define UNBASE64(v) _bx_ub64[(((v)&0x7f)-0x2b)]
 
 #endif /* BASE64_LOOKUP_LARGE */
 
 
 #else /* BASE64_LOOKUP_TABLE */
 
 #define BASE64_DIG(v) base64_enc_char(v)
 #define BASE64_1(a)		BASE64_DIG((a)>>2)
 #define BASE64_2(a, b)	BASE64_DIG( (((a)<<4)&0x3f) | ((b)>>4))
 #define BASE64_3(b, c)	BASE64_DIG( (((b)<<2)&0x3f) | ((c)>>6))
 #define BASE64_4(c)		BASE64_DIG((c)&0x3f)
 
 #define UNBASE64(v) base64_dec_char(v)
 
 #endif /* BASE64_LOOKUP_TABLE */
 
 
 
5a03489e
 /*! \brief lenght needed for encoding l bytes */
45a13a97
 #define base16_enc_len(l) (l*2)
5a03489e
 /*! \brief maximum lenght needed for decoding l bytes */
45a13a97
 #define base16_max_dec_len(l) (l/2)
5a03489e
 /*! \brief actual space needed for decoding a string b of size l */
45a13a97
 #define base16_dec_len(b, l) base16_max_dec_len(l)
5a03489e
 /*! \brief minimum valid source len for decoding */
45a13a97
 #define base16_dec_min_len() 2
5a03489e
 /*! \brief minimum valid source len for encoding */
45a13a97
 #define base16_enc_min_len() 0
 
5a03489e
 /*! \brief space needed for encoding l bytes */
45a13a97
 #define base64_enc_len(l) (((l)+2)/3*4)
5a03489e
 /*! \brief maximum space needed for encoding l bytes */
45a13a97
 #define base64_max_dec_len(l) ((l)/4*3)
5a03489e
 /*! \brief actual space needed for decoding a string b of size l, l>=4 */
45a13a97
 #define base64_dec_len(b, l) \
 	(base64_max_dec_len(l)-((b)[(l)-2]=='=') -((b)[(l)-1]=='='))
5a03489e
 /*! \brief minimum valid source len for decoding */
45a13a97
 #define base64_dec_min_len() 4
5a03489e
 /*! \brief minimum valid source len for encoding */
45a13a97
 #define base64_enc_min_len() 0
 
 
 #ifdef BASE16_READ_WHOLE_INTS
 
5a03489e
 /*! 
  * \params: 
  * \return: size used from the output buffer (dst) on success,
45a13a97
  *          -size_needed on error
5a03489e
  *
45a13a97
  * WARNING: the output string is not 0-term
  */
5a03489e
 inline static int base16_enc(unsigned char* src, int slen, unsigned char*  dst, int dlen)
45a13a97
 {
 	unsigned int* p;
 	unsigned char* end;
 	int osize;
 	unsigned short us;
 	
 	osize=2*slen;
 	if (unlikely(dlen<osize))
 		return -osize;
 	end=src+slen;
 	p=ALIGN_UINT_POINTER(src);
 	if (likely((unsigned char*)p<end)){
 		switch((unsigned char)((unsigned char*)p-src)){
 			case 3:
 				*dst=HEX_HI(*src);
 				*(dst+1)=HEX_LOW(*src);
 				dst+=2;
 				src++;
 				/* no break */
 			case 2:
 				us=*(unsigned short*)(src);
 #if   defined __IS_LITTLE_ENDIAN
 				*(dst+0)=HEX_HI(us);
 				*(dst+1)=HEX_LOW(us);
 				*(dst+2)=HEX_HI(us>>8);
 				*(dst+3)=HEX_LOW(us>>8);
 #elif defined __IS_BIG_ENDIAN
 				*(dst+2)=HEX_HI(us);
 				*(dst+3)=HEX_LOW(us);
 				*(dst+0)=HEX_HI(us>>8);
 				*(dst+1)=HEX_LOW(us>>8);
 #endif
 				dst+=4;
 				/* no need to inc src */
 				break;
 			case 1:
 				*dst=HEX_HI(*src);
 				*(dst+1)=HEX_LOW(*src);
 				dst+=2;
 				/* no need to inc src */
 			case 0:
 				break;
 		}
 		for(;(unsigned char*)p<=(end-4);p++,dst+=8){
 #if   defined __IS_LITTLE_ENDIAN
 			*(dst+0)=HEX_HI(*p);
 			*(dst+1)=HEX_LOW(*p);
 			*(dst+2)=HEX_HI(((*p)>>8));
 			*(dst+3)=HEX_LOW(((*p)>>8));
 			*(dst+4)=HEX_HI(((*p)>>16));
 			*(dst+5)=HEX_LOW(((*p)>>16));
 			*(dst+6)=HEX_HI(((*p)>>24));
 			*(dst+7)=HEX_LOW(((*p)>>24));
 #elif defined __IS_BIG_ENDIAN
 			*(dst+6)=HEX_HI(*p);
 			*(dst+7)=HEX_LOW(*p);
 			*(dst+4)=HEX_HI(((*p)>>8));
 			*(dst+5)=HEX_LOW(((*p)>>8));
 			*(dst+2)=HEX_HI(((*p)>>16));
 			*(dst+3)=HEX_LOW(((*p)>>16));
 			*(dst+0)=HEX_HI(((*p)>>24));
 			*(dst+1)=HEX_LOW(((*p)>>24));
 #else
 #error neither BIG ro LITTLE endian defined
 #endif /* __IS_*_ENDIAN */
 		}
 		src=(unsigned char*)p;
 		/* src is 2-bytes aligned (short) */
 		switch((unsigned char)((unsigned char*)end-src)){
 			case 3:
 			case 2:
 				us=*(unsigned short*)(src);
 #if   defined __IS_LITTLE_ENDIAN
 				*(dst+0)=HEX_HI(us);
 				*(dst+1)=HEX_LOW(us);
 				*(dst+2)=HEX_HI(us>>8);
 				*(dst+3)=HEX_LOW(us>>8);
 #elif defined __IS_BIG_ENDIAN
 				*(dst+2)=HEX_HI(us);
 				*(dst+3)=HEX_LOW(us);
 				*(dst+0)=HEX_HI(us>>8);
 				*(dst+1)=HEX_LOW(us>>8);
 #endif
 				if ((end-src)==3){
 					*(dst+4)=HEX_HI(*(src+2));
 					*(dst+5)=HEX_LOW(*(src+2));
 				}
 				/* no need to inc anything */
 				break;
 			case 1:
 				*dst=HEX_HI(*src);
 				*(dst+1)=HEX_LOW(*src);
 				/* no need to inc anything */
 			case 0:
 				break;
 		}
 	}else if (unlikely((long)src&1)){
 		/* src is not 2-bytes (short) aligned */
 		switch((unsigned char)((unsigned char*)end-src)){
 			case 3:
 				*dst=HEX_HI(*src);
 				*(dst+1)=HEX_LOW(*src);
 				dst+=2;
 				src++;
 				/* no break */
 			case 2:
 				us=*(unsigned short*)(src);
 #if   defined __IS_LITTLE_ENDIAN
 				*(dst+0)=HEX_HI(us);
 				*(dst+1)=HEX_LOW(us);
 				*(dst+2)=HEX_HI(us>>8);
 				*(dst+3)=HEX_LOW(us>>8);
 #elif defined __IS_BIG_ENDIAN
 				*(dst+2)=HEX_HI(us);
 				*(dst+3)=HEX_LOW(us);
 				*(dst+0)=HEX_HI(us>>8);
 				*(dst+1)=HEX_LOW(us>>8);
 #endif
 				/* no need to inc anything */
 				break;
 			case 1:
 				*dst=HEX_HI(*src);
 				*(dst+1)=HEX_LOW(*src);
 				/* no need to inc anything */
 			case 0:
 				break;
 		}
 	}else{
 		/* src is 2-bytes aligned (short) */
 		switch((unsigned char)((unsigned char*)end-src)){
 			case 3:
 			case 2:
 				us=*(unsigned short*)(src);
 #if   defined __IS_LITTLE_ENDIAN
 				*(dst+0)=HEX_HI(us);
 				*(dst+1)=HEX_LOW(us);
 				*(dst+2)=HEX_HI(us>>8);
 				*(dst+3)=HEX_LOW(us>>8);
 #elif defined __IS_BIG_ENDIAN
 				*(dst+2)=HEX_HI(us);
 				*(dst+3)=HEX_LOW(us);
 				*(dst+0)=HEX_HI(us>>8);
 				*(dst+1)=HEX_LOW(us>>8);
 #endif
 				if ((end-src)==3){
 					*(dst+4)=HEX_HI(*(src+2));
 					*(dst+5)=HEX_LOW(*(src+2));
 				}
 				/* no need to inc anything */
 				break;
 			case 1:
 				*dst=HEX_HI(*src);
 				*(dst+1)=HEX_LOW(*src);
 				/* no need to inc anything */
 			case 0:
 				break;
 		}
 	}
 	
 	return osize;
 }
 
 
 
 #else /* BASE16_READ_WHOLE_INTS */
 
 
5a03489e
 /*!
  * \return : size used from the output buffer (dst) on success,
45a13a97
  *          -size_needed on error
5a03489e
  *
  * \note WARNING: the output string is not 0-term
45a13a97
  */
 inline static int base16_enc(unsigned char* src, int slen,
 							 unsigned char*  dst, int dlen)
 {
 	unsigned char* end;
 	int osize;
 	
 	osize=2*slen;
 	if (unlikely(dlen<osize))
 		return -osize;
 	end=src+slen;
 	for (;src<end; src++,dst+=2){
 		*dst=HEX_HI(*src);
 		*(dst+1)=HEX_LOW(*src);
 	}
 	return osize;
 }
 
 
 #endif /* BASE16_READ_WHOLE_INTS */
 
5a03489e
 inline static int base16_dec(unsigned char* src, int slen, unsigned char* dst, int dlen)
45a13a97
 {
 	unsigned char* end;
 	int osize;
 	
 	osize=slen/2;
 	if (unlikely(dlen<osize))
 		return -osize;
 	end=src+2*osize;
 	for (; src<end; src+=2, dst++)
 		*dst=(UNHEX(*src)<<4) | UNHEX(*(src+1));
 	return osize;
 }
 
 
 
 
 
5a03489e
 /*! \brief helper internal function: encodes v (6 bits value)
  * \return char ascii encoding on success and 0xff on error
45a13a97
  * (value out of range) */
 inline static unsigned char base64_enc_char(unsigned char v)
 {
 	switch(v){
 		case 0x3f:
 			return '/';
 		case 0x3e:
 			return '+';
 		default:
 			if (v<=25)
 				return v+'A';
 			else if (v<=51)
 				return v-26+'a';
 			else if (v<=61)
 				return v-52+'0';
 	}
 	return 0xff;
 }
 
5a03489e
 /*! \brief helper internal function: decodes a base64 "digit",
  * \return value on success (0-63) and 0xff on error (invalid)*/
45a13a97
 inline static unsigned base64_dec_char(unsigned char v)
 {
 	switch(v){
 		case '/':
 			return 0x3f;
 		case '+':
 			return 0x3e;
 		case ':':
 		case ';':
 		case '<':
 		case '=':
 		case '>':
 		case '?':
 		case '@':
 		case '[':
 		case '\\':
 		case ']':
 		case '^':
 		case '_':
 		case '`':
 			return 0xff;
 		default:
 			if ((v)<'0')
 				return 0xff;
 			if ((v)<='9')
 				return (v)-'0'+0x34;
 			else if ((v)<='Z')
 				return (v)-'A';
 			else if ((v) <='z')
 				return (v)-'a'+0x1a;
 	}
 	return 0xff;
 }
 
 
 #ifdef BASE64_LOOKUP_8K
5a03489e
 /*!
  * \return : size used from the output buffer (dst) on success ((slen+2)/3*4)
45a13a97
  *          -size_needed on error
5a03489e
  *
  * \note WARNING: the output string is not 0-term
45a13a97
  */
 inline static int base64_enc(unsigned char* src, int slen,
 							unsigned char* dst,  int dlen)
 {
 	unsigned char* end;
 	int osize;
 	
 	osize=(slen+2)/3*4;
 	if (unlikely(dlen<osize))
 		return -osize;
 	end=src+slen/3*3;
 	if (unlikely((long)dst%2)){
 		for (;src<end; src+=3,dst+=4){
 			dst[0]=FIRST_8B(BASE64_12(src[0], src[1]));
 			dst[1]=LAST_8B(BASE64_12(src[0], src[1]));
 			dst[2]=FIRST_8B(BASE64_34(src[1], src[2]));
 			dst[3]=LAST_8B(BASE64_34(src[1], src[2]));
 		}
 		switch(slen%3){
 			case 2:
 				dst[0]=FIRST_8B(BASE64_12(src[0], src[1]));
 				dst[1]=LAST_8B(BASE64_12(src[0], src[1]));
 				dst[2]=FIRST_8B(BASE64_34(src[1], 0));
 				dst[3]='=';
 				break;
 			case 1:
 				dst[0]=FIRST_8B(BASE64_12(src[0], 0));
 				dst[1]=LAST_8B(BASE64_12(src[0], 0));
 				dst[2]='=';
 				dst[3]='=';
 				break;
 		}
 	}else{
 		for (;src<end; src+=3,dst+=4){
 			*(unsigned short*)(dst+0)=_bx_b64_12[(src[0]<<4)|(src[1]>>4)];
 			*(unsigned short*)(dst+2)=_bx_b64_12[((src[1]&0xf)<<8)|src[2]];
 		}
 		switch(slen%3){
 			case 2:
 				*(unsigned short*)(dst+0)=_bx_b64_12[(src[0]<<4)|(src[1]>>4)];
 				*(unsigned short*)(dst+2)=_bx_b64_12[((src[1]&0xf)<<8)|0];
 				dst[3]='=';
 				break;
 			case 1:
 				*(unsigned short*)(dst+0)=_bx_b64_12[(src[0]<<4)|0];
 				dst[2]='=';
 				dst[3]='=';
 				break;
 		}
 	}
 	return osize;
 }
 #else /*BASE64_LOOKUP_8K*/
5a03489e
 /*! \brief Convert to base64
  * \return size used from the output buffer (dst) on success ((slen+2)/3*4)
45a13a97
  *          -size_needed on error
5a03489e
  * \note WARNING: the output string is not 0-term
45a13a97
  */
 inline static int base64_enc(unsigned char* src, int slen,
 							unsigned char* dst,  int dlen)
 {
 	unsigned char* end;
 	int osize;
 	
 	osize=(slen+2)/3*4;
 	if (unlikely(dlen<osize))
 		return -osize;
 	end=src+slen/3*3;
 	for (;src<end; src+=3,dst+=4){
 		dst[0]=BASE64_1(src[0]);
 		dst[1]=BASE64_2(src[0], src[1]);
 		dst[2]=BASE64_3(src[1], src[2]);
 		dst[3]=BASE64_4(src[2]);
 	}
 	switch(slen%3){
 		case 2:
 			dst[0]=BASE64_1(src[0]);
 			dst[1]=BASE64_2(src[0], src[1]);
 			dst[2]=BASE64_3(src[1], 0);
 			dst[3]='=';
 			break;
 		case 1:
 			dst[0]=BASE64_1(src[0]);
 			dst[1]=BASE64_2(src[0], 0);
 			dst[2]='=';
 			dst[3]='=';
 			break;
 	}
 	return osize;
 }
 #endif /*BASE64_LOOKUP_8K*/
 
 
 
5a03489e
 /*! \brief
  * \return size used from the output buffer (dst) on success (max: slen/4*3)
45a13a97
  *          -size_needed on error or 0 on bad base64 encoded string
5a03489e
  * \note WARNING: the output string is not 0-term
45a13a97
  */
 inline static int base64_dec(unsigned char* src, int slen,
 							unsigned char* dst,  int dlen)
 {
 	
 	unsigned char* end;
 	int osize;
 	register unsigned a, b, c, d; /* more registers used, but allows for
 									 paralles execution */
 	
 	if (unlikely((slen<4) || (slen%4) || 
 				(src[slen-2]=='=' && src[slen-1]!='=')))
 		return 0; /* invalid base64 enc. */
 	osize=(slen/4*3)-(src[slen-2]=='=')-(src[slen-1]=='=');
 	if (unlikely(dlen<osize))
 		return -osize;
 	end=src+slen-4;
 	for (;src<end; src+=4,dst+=3){
 #if 0
 		u=	(UNBASE64(src[0])<<18) | (UNBASE64(src[1])<<12) | 
 			(UNBASE64(src[2])<<6)  |  UNBASE64(src[3]);
 		dst[0]=u>>16;
 		dst[1]=u>>8;
 		dst[3]=u;
 #endif
 		a=UNBASE64(src[0]);
 		b=UNBASE64(src[1]);
 		c=UNBASE64(src[2]);
 		d=UNBASE64(src[3]);
 		dst[0]=(a<<2) | (b>>4);
 		dst[1]=(b<<4) | (c>>2);
 		dst[2]=(c<<6) | d;
 	}
 	switch(osize%3){
 		case 0: /* no '=' => 3 output bytes at the end */
 			a=UNBASE64(src[0]);
 			b=UNBASE64(src[1]);
 			c=UNBASE64(src[2]);
 			d=UNBASE64(src[3]);
 			dst[0]=(a<<2) | (b>>4);
 			dst[1]=(b<<4) | (c>>2);
 			dst[2]=(c<<6) | d;
 			break;
 		case 2: /* 1  '=' => 2 output bytes at the end */
 			a=UNBASE64(src[0]);
 			b=UNBASE64(src[1]);
 			c=UNBASE64(src[2]);
 			dst[0]=(a<<2) | (b>>4);
 			dst[1]=(b<<4) | (c>>2);
 			break;
 		case 1: /* 2  '=' => 1 output byte at the end */
 			a=UNBASE64(src[0]);
 			b=UNBASE64(src[1]);
 			dst[0]=(a<<2) | (b>>4);
 			break;
 	}
 	return osize;
 }
 
 
 
 
5a03489e
 /*! \brief
  * same as \ref base64_enc() but with a different alphabet, that allows simpler and
45a13a97
  *  faster enc/dec
5a03489e
  * \return size used from the output buffer (dst) on success ((slen+2)/3*4)
45a13a97
  *          -size_needed on error
5a03489e
  * \note WARNING: the alphabet includes ":;<>?@[]\`", so it might not be suited
45a13a97
  *  in all cases (e.g. encoding something in a sip uri).
  */
 inline static int q_base64_enc(unsigned char* src, int slen,
 							unsigned char* dst,  int dlen)
 {
 #define q_b64_base	'0'
 #define q_b64_pad	'z'
 #define Q_BASE64(v)	(unsigned char)((v)+q_b64_base)
 	unsigned char* end;
 	int osize;
 	
 	osize=(slen+2)/3*4;
 	if (unlikely(dlen<osize))
 		return -osize;
 	end=src+slen/3*3;
 	for (;src<end; src+=3,dst+=4){
 		dst[0]=Q_BASE64(src[0]>>2);
 		dst[1]=(Q_BASE64((src[0]<<4)&0x3f) | (src[1]>>4));
 		dst[2]=(Q_BASE64((src[1]<<2)&0x3f) | (src[2]>>6) );
 		dst[3]=Q_BASE64(src[2]&0x3f);
 	}
 	switch(slen%3){
 		case 2:
 			dst[0]=Q_BASE64(src[0]>>2);
 			dst[1]=(Q_BASE64((src[0]<<4)&0x3f) | (src[1]>>4));
 			dst[2]=Q_BASE64((src[1]<<2)&0x3f);
 			dst[3]=q_b64_pad;
 			break;
 		case 1:
 			dst[0]=Q_BASE64(src[0]>>2);
 			dst[1]=Q_BASE64((src[0]<<4)&0x3f);
 			dst[2]=q_b64_pad;
 			dst[3]=q_b64_pad;
 			break;
 	}
 	return osize;
 #undef Q_BASE64
 }
 
 
 
5a03489e
 /*! \brief
  * same as \ref base64_enc() but with a different alphabet, that allows simpler and
45a13a97
  *  faster enc/dec
5a03489e
  *
  * \return size used from the output buffer (dst) on success (max: slen/4*3)
45a13a97
  *          -size_needed on error or 0 on bad base64 encoded string
5a03489e
  * \note WARNING: the output string is not 0-term
45a13a97
  */
 inline static int q_base64_dec(unsigned char* src, int slen,
 							unsigned char* dst,  int dlen)
 {
 #define Q_UNBASE64(v) (unsigned char)((v)-q_b64_base)
 	
 	unsigned char* end;
 	int osize;
 #ifdef SINGLE_REG
 	register unsigned u;
 #else
 	register unsigned a, b, c, d; /* more registers used, but allows for
 									 paralles execution */
 #endif
 	
 	if (unlikely((slen<4) || (slen%4) || 
 				(src[slen-2]==q_b64_pad && src[slen-1]!=q_b64_pad)))
 		return 0; /* invalid base64 enc. */
 	osize=(slen/4*3)-(src[slen-2]==q_b64_pad)-(src[slen-1]==q_b64_pad);
 	if (unlikely(dlen<osize))
 		return -osize;
 	end=src+slen-4;
 	for (;src<end; src+=4,dst+=3){
 #ifdef SINGLE_REG
 		u=	(Q_UNBASE64(src[0])<<18) | (Q_UNBASE64(src[1])<<12) | 
 			(Q_UNBASE64(src[2])<<6)  |  Q_UNBASE64(src[3]);
 		dst[0]=u>>16;
 		dst[1]=u>>8;
 		dst[2]=u;
 #else
 		a=Q_UNBASE64(src[0]);
 		b=Q_UNBASE64(src[1]);
 		c=Q_UNBASE64(src[2]);
 		d=Q_UNBASE64(src[3]);
 		dst[0]=(a<<2) | (b>>4);
 		dst[1]=(b<<4) | (c>>2);
 		dst[2]=(c<<6) | d;
 #endif
 	}
 	switch(osize%3){
 		case 0: /* no '=' => 3 output bytes at the end */
 #ifdef SINGLE_REG
 			u=	(Q_UNBASE64(src[0])<<18) | (Q_UNBASE64(src[1])<<12) | 
 				(Q_UNBASE64(src[2])<<6)  |  Q_UNBASE64(src[3]);
 			dst[0]=u>>16;
 			dst[1]=u>>8;
 			dst[2]=u;
 #else
 			a=Q_UNBASE64(src[0]);
 			b=Q_UNBASE64(src[1]);
 			c=Q_UNBASE64(src[2]);
 			d=Q_UNBASE64(src[3]);
 			dst[0]=(a<<2) | (b>>4);
 			dst[1]=(b<<4) | (c>>2);
 			dst[2]=(c<<6) | d;
 #endif
 			break;
 		case 2: /* 1  '=' => 2 output bytes at the end */
 #ifdef SINGLE_REG
 			u=	(Q_UNBASE64(src[0])<<12) | (Q_UNBASE64(src[1])<<6) | 
 				(Q_UNBASE64(src[2]));
 			dst[0]=u>>10;
 			dst[1]=u>>2;
 #else
 			a=Q_UNBASE64(src[0]);
 			b=Q_UNBASE64(src[1]);
 			c=Q_UNBASE64(src[2]);
 			dst[0]=(a<<2) | (b>>4);
 			dst[1]=(b<<4) | (c>>2);
 #endif
 			break;
 		case 1: /* 2  '=' => 1 output byte at the end */
 #ifdef SINGLE_REG
 			dst[0]=(Q_UNBASE64(src[0])<<2) | (Q_UNBASE64(src[1])>>4); 
 #else
 			a=Q_UNBASE64(src[0]);
 			b=Q_UNBASE64(src[1]);
 			dst[0]=(a<<2) | (b>>4);
 #endif
 			break;
 	}
 	return osize;
 #undef q_b64_base
 #undef q_b64_pad
 }
 
3ca1a53f
 /*! \brief inits internal lookup tables */
45a13a97
 int init_basex();
 
 
 #endif /* _basex_h */