Nginx 对字符串编码函数解析
Nginx函数ngx_escape_uri对非普通字符进行编码转换,源码如下:
ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type)
{
ngx_uint_t n;
uint32_t *escape;
static u_char hex[] = "0123456789ABCDEF";
/* " ", "#", "%", "?", %00-%1F, %7F-%FF */
static uint32_t uri[] = {
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
/* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
0x80000029, /* 1000 0000 0000 0000 0000 0000 0010 1001 */
/* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
/* ~}| {zyx wvut srqp onml kjih gfed cba` */
0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */
};
...
...
static uint32_t *map[] =
{ uri, args, uri_component, html, refresh, memcached, memcached };
escape = map[type];
if (dst == NULL) {
/* find the number of the characters to be escaped */
n = 0;
while (size) {
if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
n++;
}
src++;
size--;
}
return (uintptr_t) n;
}
while (size) {
if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
*dst++ = '%';
*dst++ = hex[*src >> 4];
*dst++ = hex[*src & 0xf];
src++;
} else {
*dst++ = *src++;
}
size--;
}
return (uintptr_t) dst;
}
在上述函数中利用位图来判断是否需要编码,其中static uint32_t uri[]用0和1来表示ASCII码表中的字符是否需要编码,每一位表示一个符号,其中值为1表示此符号需要转换,值为0表示不需要转换。
然后利用(escape[*src >> 5] & (1U << (*src & 0x1f)))来判断字符是否需要编码,这个判断分为两步:一是利用escape[*src >> 5] 获取字符所在的分组,注意到uri[]把256个字符分为8组,每组32个字符,所以利用表示字符的ASCII码右移5位(相当于除以32)即可得到所在分组;二是利用1U << (*src & 0x1f)获取字符在分组中的位置,*src & 0x1f相当于利用表示字符的ASCII码对32取余,然后1左移该余数即可得到字符在分组中的位置,最后根据该位置是0还是1来判断是否需要编码。