Nginx-string解析

来源:互联网 发布:python 自动化测试 编辑:程序博客网 时间:2024/05/17 13:13

我们先看看ngx_string中定义的几个数据结构

typedef struct {    size_t      len;    u_char     *data;} ngx_str_t;    
这个结构体就是最基础的nginx中的字符串结构了,其中 len表示这个字符串的长度, *data是一个指向无符号char类型的指针。

从这个结构体可以看出,后续关于字符串长度的操作strlen()都可以直接饮用len这个字段。 

除了这个最基本的结构体之外,还定义了另外两个字符串相关的结构体:

typedef struct {    ngx_str_t   key;    ngx_str_t   value;} ngx_keyval_t;
这个结构体从定义来看就可以知道,这是一个关于字符串类型的key,value对的子结构,具体再哪能用到,还得往后看才能知道。

typedef struct {    unsigned    len:28;    unsigned    valid:1;    unsigned    no_cacheable:1;    unsigned    not_found:1;    unsigned    escape:1;    u_char     *data;} ngx_variable_value_t;

这个结构体有点点奇怪,从定义来看,前面5个成员才占用了一个int,看上去只是ngx_str_t的一个扩展版本,其中

len: 只用了28bit来表示长度即最大字符串的长度不超过1-2^29.

valid:  

no_cacheable:

not_fount:

escape:

data: 这个字段和ngx_str_t里的对应字段含义一样。

这里就是ngx_string 里所有的关于字符串的结构体了,下面来看一些关于对于字符串操作的接口和宏定义

#define ngx_string(str)     { sizeof(str) - 1, (u_char *) str }

这个宏可以映射到c++中的构造函数,传入一个字符串,然后构造出一个ngx_str_t的对象。

#define ngx_null_string { 0, NULL }

这个宏实际上是上一个宏的特例,构造了一个空的ngx_str_t。 
#define ngx_str_set(str, text)                                               \    (str)->len = sizeof(text) - 1; (str)->data = (u_char *) text
这个宏也可以看成是一个队ngx_str_t的构造函数,但是与前面ngx_string()宏的区别是,ngx_string()宏里传入的变量必须是一个字符串常量,但是这里的参数text可以是一个变量。 
#define ngx_str_null(str)   (str)->len = 0; (str)->data = NULL
这个宏和之前的ngx_null_string的作用也挺像,也是构造一个空的字符串常量,但是这里是显示的把某一个ngx_str_t对象置空或者可以看出是初始化。
#define ngx_tolower(c)      (u_char) ((c >= 'A' && c <= 'Z') ? (c | 0x20) : c)#define ngx_toupper(c)      (u_char) ((c >= 'a' && c <= 'z') ? (c & ~0x20) : c)

这两个宏,顾名思义,是将字符c进行大小写转换。

voidngx_strlow(u_char *dst, u_char *src, size_t n){    while (n) {        *dst = ngx_tolower(*src);        dst++;        src++;        n--;     }    }
这个函数是将字符串src里的字符全部转成小写字符,值得注意的是,这个函数里没有对指针的有效性进行判断,所以需要在外围传入的时候进行保证,否则就会出现段错误。这个函数就使用到了刚刚定义的ngx_tolower这个宏。 同样的办法也可以定义一个转换成大写的函数。

#define ngx_strncmp(s1, s2, n)  strncmp((const char *) s1, (const char *) s2, n)/* msvc and icc7 compile strcmp() to inline loop */#define ngx_strcmp(s1, s2)  strcmp((const char *) s1, (const char *) s2)#define ngx_strstr(s1, s2)  strstr((const char *) s1, (const char *) s2)#define ngx_strlen(s)       strlen((const char *) s)                                                         #define ngx_strchr(s1, c)   strchr((const char *) s1, (int) c)
这一系列的宏定义,实际上是对string.h里一些常见的结构进行了封装,这么做的好处就保证了再Nginx中保持了命名风格保持了一致。

static ngx_inline u_char *ngx_strlchr(u_char *p, u_char *last, u_char c){    while (p < last) {        if (*p == c) {            return p;        }                                                                                                            p++;    }       return NULL;}
这个函数的一个奇怪修饰符 ngx_inline这个是一个宏定义,实际上就是c语言中的inline,同样是为了命名规范。这个函数的作用从代码可以看出从指针p位置到last位置开始循环寻找字符c,如果找到了那么返回指针所在的位置,否则就返回空指针

#define ngx_memzero(buf, n)       (void) memset(buf, 0, n)#define ngx_memset(buf, c, n)     (void) memset(buf, c, n)
这两个宏定义,是对memset的封装,主要是对内存初始化的操作。

#if (NGX_MEMCPY_LIMIT)void *ngx_memcpy(void *dst, const void *src, size_t n);                                                      #define ngx_cpymem(dst, src, n)   (((u_char *) ngx_memcpy(dst, src, n)) + (n))#else/* * gcc3, msvc, and icc7 compile memcpy() to the inline "rep movs". * gcc3 compiles memcpy(d, s, 4) to the inline "mov"es. * icc8 compile memcpy(d, s, 4) to the inline "mov"es or XMM moves. */#define ngx_memcpy(dst, src, n)   (void) memcpy(dst, src, n)#define ngx_cpymem(dst, src, n)   (((u_char *) memcpy(dst, src, n)) + (n))#endif
这段代码首先涉及到在makefile中用宏定义进行条件编译的知识,如果在makefile中编译的时候有 -D NGX_MEMCPY_LIMIT=1 这个参数,那么运行的时候就会执行if后面的声明,否则是使用的else里面的声明。

#if (NGX_MEMCPY_LIMIT)void *ngx_memcpy(void *dst, const void *src, size_t n){    if (n > NGX_MEMCPY_LIMIT) {        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "memcpy %uz bytes", n);        ngx_debug_point();    }        return memcpy(dst, src, n);}#endif           
看到这段代码就明白NGX_MEMCPY_LIMIT这个参数的意思了,它是对内存拷贝长度的一个限制,如果拷贝的长度超过了这个限制,那么是会报错的。这个函数实际上就是对系统函数memcpy的一个封装返回的是dst的起始地址。感兴趣的可以进一步的去考察memcpy这个函数的实现方式,也比较有意思。

#define ngx_cpymem(dst, src, n)   (((u_char *) ngx_memcpy(dst, src, n)) + (n))
这个宏定义很有意思,调用了ngx_memcpy,最后宏返回的结果是dst+n,也就是拷贝后的最后一个指针的位置,具体为什么要这么做还需要往后看才知道具体的应用场景。

#define ngx_memcpy(dst, src, n)   (void) memcpy(dst, src, n)#define ngx_cpymem(dst, src, n)   (((u_char *) memcpy(dst, src, n)) + (n))
如果编译的时候没有定义NGX_MEMCPY_LIMIT,那么 上面定义的ngx_memcpy以及ngx_cpymem实现是对memcpy的一个简单封装。但是都做到了接口统一。

#if ( __INTEL_COMPILER >= 800 )/* * the simple inline cycle copies the variable length strings up to 16 * bytes faster than icc8 autodetecting _intel_fast_memcpy() */static ngx_inline u_char *ngx_copy(u_char *dst, u_char *src, size_t len){    if (len < 17) {        while (len) {            *dst++ = *src++;            len--;        }           return dst;    } else {        return ngx_cpymem(dst, src, len);    }   }#else#define ngx_copy                  ngx_cpymem#endif                       
这段代码可以看出,源码作者对于nginx的性能真的是苦心孤诣。从实现来看,如果字符串的长度不超过16个字节并且要符合某个cpu架构的条件下直接循环逐个字符的复制,比直接调用系统函数要快!

#define ngx_memmove(dst, src, n)   (void) memmove(dst, src, n)#define ngx_movemem(dst, src, n)   (((u_char *) memmove(dst, src, n)) + (n))
这两个宏是关于内存的移动,其实前面有不少关于内存移动的函数了,比如ngx_memcpy,但是为什么这里还要对memmove进行封装呢? 原因是:当dst和src在内存出现重叠的时候,memmove能保证dst和src一样,但是src的内容会被修改

用一小段代码来说明下:

#include<stdio.h>#include <string.h>int main(){    char s[]="hello word!";    memmove(s,s+3,4);    printf("%s\n",s);                                                                                            return 0;}

这个代码的输出为:lo wo word!。

#define ngx_memcmp(s1, s2, n)  memcmp((const char *) s1, (const char *) s2, n) 
这个宏定义是对两段长度为n的内存的大小比较,是对系统函数memcmp的一个简单封装。

u_char *ngx_cpystrn(u_char *dst, u_char *src, size_t n){    if (n == 0) {        return dst;    }    while (--n) {        *dst = *src;        if (*dst == '\0') {            return dst;        }        dst++;        src++;    }    *dst = '\0';    return dst;}
这个函数是一个字符串的拷贝函数,从src中拷贝n个字符到dst中。

u_char *ngx_pstrdup(ngx_pool_t *pool, ngx_str_t *src){    u_char  *dst;    dst = ngx_pnalloc(pool, src->len);    if (dst == NULL) {        return NULL;    }    ngx_memcpy(dst, src->data, src->len);    return dst;}
这个函数是想从pool中分配一个和str同样长度的空间,然后把src中的数据dump到pool中来。

u_char * ngx_cdecl ngx_sprintf(u_char *buf, const char *fmt, ...);u_char * ngx_cdecl ngx_snprintf(u_char *buf, size_t max, const char *fmt, ...);                              u_char * ngx_cdecl ngx_slprintf(u_char *buf, u_char *last, const char *fmt,    ...);u_char *ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args);#define ngx_vsnprintf(buf, max, fmt, args)                                   \    ngx_vslprintf(buf, buf + (max), fmt, args)
这几个函数是一系列的print函数,是通过格式化的方式将一些变量写入到另外的一个字符串中

u_char * ngx_cdeclngx_sprintf(u_char *buf, const char *fmt, ...)                                                               {    u_char   *p;    va_list   args;    va_start(args, fmt);    p = ngx_vslprintf(buf, (void *) -1, fmt, args);    va_end(args);    return p;}
这个函数是处理动态参数个数的典型例子,关于函数动态参数的可以参考【函数动态参数处理详解】 。 另外ngx_vslprintf这个函数实际上是对自动机,对于各种格式化输出的处理,可以自行参考代码。 

ngx_int_t ngx_strcasecmp(u_char *s1, u_char *s2);                                                            ngx_int_t ngx_strncasecmp(u_char *s1, u_char *s2, size_t n);
这两个函数是比较两个字符串而且是大小写不敏感。 

ngx_int_tngx_strcasecmp(u_char *s1, u_char *s2) {    ngx_uint_t  c1, c2;    for ( ;; ) {        c1 = (ngx_uint_t) *s1++;        c2 = (ngx_uint_t) *s2++;        c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;        c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;        if (c1 == c2) {            if (c1) {                continue;            }                return 0;        }            return c1 - c2;    }    }
看下ngx_strcasecmp这个函数,实际上在函数内部把u_char字符转换成了ngx_uint. 而ngx_uint实际上是 uintptr_t. 这里把u_char转换成ngx_unit的原因是:转换后比较操作能稍微快一些。 

而uintptr_t的定义在/usr/includ/stdint.h头文件中

#if __WORDSIZE == 64# ifndef __intptr_t_definedtypedef long int                intptr_t;#  define __intptr_t_defined# endiftypedef unsigned long int       uintptr_t;                                                                                          #else# ifndef __intptr_t_definedtypedef int                     intptr_t;#  define __intptr_t_defined# endiftypedef unsigned int            uintptr_t;#endif

从这段代码可以看出,uintptr_t要么是int,要么是unsigned int。 对于64位的机器是  long 或者unsigned long。

u_char *ngx_strstrn(u_char *s1, char *s2, size_t n);                                                         u_char *ngx_strcasestrn(u_char *s1, char *s2, size_t n); u_char *ngx_strlcasestrn(u_char *s1, u_char *last, u_char *s2, size_t n); 
/* * ngx_strstrn() and ngx_strcasestrn() are intended to search for static * substring with known length in null-terminated string. The argument n * must be length of the second substring - 1. */u_char *ngx_strstrn(u_char *s1, char *s2, size_t n){    u_char  c1, c2;    c2 = *(u_char *) s2++;    do {        do {            c1 = *s1++;            if (c1 == 0) {                return NULL;            }        } while (c1 != c2);    } while (ngx_strncmp(s1, (u_char *) s2, n) != 0);    return --s1;}
这个函数的功能是在s1字符串中找到第一个s2子串出现的位置,没有找到就不返回空指针,否则返回出现的位置。

ngx_strstrn这个函数上面有段注释,很有意思,主要是有两点需要注意

1,s2必须是一个长度已知的静态的字符串。

2, n这个参数必须是s2长度-1,这里为什么要-1呢? 这里还是需要仔细看代码才能明白,因为在内层的do while循环了已经比较了一个字符了,所以就不需要比较开头的字符了,所以需要减掉1,效率,效率,一切都是为了效率。

ngx_strcasestrn这个函数只是一个大小写不敏感的函数而已。

ngx_strlcasestrn这个函数增加了一个参数last,这个函数实现的功能是在s1到last这个区间内找到第一个出现s2子串的位置并且大小写不敏感。

ngx_int_t ngx_rstrncmp(u_char *s1, u_char *s2, size_t n);                                                    ngx_int_t ngx_rstrncasecmp(u_char *s1, u_char *s2, size_t n); ngx_int_t ngx_memn2cmp(u_char *s1, u_char *s2, size_t n1, size_t n2);ngx_int_t ngx_dns_strcmp(u_char *s1, u_char *s2);
这几个函数从函数名的后缀来看就可以知道是比较函数,那么先来看看第一个的实现

ngx_int_tngx_rstrncmp(u_char *s1, u_char *s2, size_t n){    if (n == 0) {         return 0;    }        n--;     for ( ;; ) {        if (s1[n] != s2[n]) {            return s1[n] - s2[n];        }            if (n == 0) {             return 0;        }            n--;     }    }
从代码可以看出,这是一个字符串的逆向比较函数,在实现上也没有有什么特别的地方。 

从这个代码基本可以可以推测出ngx_rstrncasecmp这个函数的实现了,基于上一个函数增加大小写不敏感的处理。

ngx_int_tngx_memn2cmp(u_char *s1, u_char *s2, size_t n1, size_t n2){    size_t     n;    ngx_int_t  m, z;    if (n1 <= n2) {        n = n1;        z = -1;    } else {        n = n2;        z = 1;    }    m = ngx_memcmp(s1, s2, n);    if (m || n1 == n2) {        return m;    }    return z;}
这个函数从实现上来看一开始从n1和n2中取到了一个 较小的数,然后比较s1和s2的前缀的大小,最后的一个if语句实际上处理了前缀相同但是字符串的总长度不相同的情况,这里看到的是 当前缀相同,但是长度不同的时候,较长的大。

ngx_int_tngx_dns_strcmp(u_char *s1, u_char *s2){    ngx_uint_t  c1, c2;    for ( ;; ) {        c1 = (ngx_uint_t) *s1++;        c2 = (ngx_uint_t) *s2++;        c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;        c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;        if (c1 == c2) {            if (c1) {                continue;            }            return 0;        }        /* in ASCII '.' > '-', but we need '.' to be the lowest character */        c1 = (c1 == '.') ? ' ' : c1;        c2 = (c2 == '.') ? ' ' : c2;        return c1 - c2;    }}
这是一个域名比较的函数,其中需要特别注意的地方是在遇到 dot和minus这两个符号进行比较的时候,实际需要的是 minus > dot,所以在遇到 dot的时候做了特殊处理,转化成了 space, 这三个符号的ASCII值为 {'.':46, '-':45,' ':32}。

ngx_int_t ngx_atoi(u_char *line, size_t n);ngx_int_t ngx_atofp(u_char *line, size_t n, size_t point);ssize_t ngx_atosz(u_char *line, size_t n);off_t ngx_atoof(u_char *line, size_t n);time_t ngx_atotm(u_char *line, size_t n);ngx_int_t ngx_hextoi(u_char *line, size_t n);
这几个函数都是关于字符串到数字的转换。

ngx_int_tngx_atofp(u_char *line, size_t n, size_t point){    ngx_int_t   value;    ngx_uint_t  dot;    if (n == 0) {        return NGX_ERROR;    }    dot = 0;    for (value = 0; n--; line++) {        if (point == 0) {            //小数点每往后移动一位,point要减掉1,如果point都变成0了,这个循环还没有结束,那么和这个函数的本意就相违背了。            return NGX_ERROR;        }        if (*line == '.') {            if (dot) {                //多次找到了dot,那么这个浮点数是非法的。                return NGX_ERROR;            }    //字符串中找到了dot,那么就进行标记            dot = 1;            continue;        }        if (*line < '0' || *line > '9') {            return NGX_ERROR;        }        value = value * 10 + (*line - '0');        point -= dot;    }          while (point--) {        value = value * 10;    }    if (value < 0) {        return NGX_ERROR;    } else {        return value;    }     


这个函数需要认真琢磨下,先说下函数各个参数的含义

line: 这个比较明了就是这个字符串。

n: 这里是表示输出整数的位数。

point: 是指浮点字符串dot往后移动的位数。

这里可以有一个隐含的条件是n-point应该就是line中整数部分位数,如果不相等,那么参数错误了。

这个函数的场景其实还是很少见的,后续见到了再回过头来看看。

void ngx_encode_base64(ngx_str_t *dst, ngx_str_t *src);                                                      ngx_int_t ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src);ngx_int_t ngx_decode_base64url(ngx_str_t *dst, ngx_str_t *src);static ngx_int_t ngx_decode_base64_internal(ngx_str_t *dst, ngx_str_t *src, const u_char *basis)

这一组函数是关于base64的编解码的接口

voidngx_encode_base64(ngx_str_t *dst, ngx_str_t *src){    u_char         *d, *s;    size_t          len;    /*编码后输出的字符集*/    static u_char   basis64[] =            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";     len = src->len;    s = src->data;    d = dst->data;    while (len > 2) {        /*因为3个字节刚好是24位可以组合成4个编码后的字符,所以循环判断的时候是len>2*/        *d++ = basis64[(s[0] >> 2) & 0x3f]; //第一个字符的高6位        *d++ = basis64[((s[0] & 3) << 4) | (s[1] >> 4)]; //第一个字符的低2位和第二个字符的高4位        *d++ = basis64[((s[1] & 0x0f) << 2) | (s[2] >> 6)]; //第二个字符的低4位和第三个字符的高2位        *d++ = basis64[s[2] & 0x3f]; //第三个字符的低6位        s += 3;        len -= 3;    }    if (len) {        //字符长度不是3的倍数        *d++ = basis64[(s[0] >> 2) & 0x3f]; //剩下字符的第一个字符的高6位        if (len == 1) { //字符串的长度膜3余1            *d++ = basis64[(s[0] & 3) << 4]; //剩下一个字符的低2位变成高2位,后面四位补0            *d++ = '='; //特殊标记        } else { //字符串长度模3余2            *d++ = basis64[((s[0] & 3) << 4) | (s[1] >> 4)]; //剩下字符的第一个字符的低2位和第二个字符的高4位            *d++ = basis64[(s[1] & 0x0f) << 2]; //剩下字符的第二个字符的低4位,不足补0        }        *d++ = '='; //特殊标记。    }    //从整个编码来看,如果最后没有'='那么原字符串的长度刚好是3的倍数,如果有1个那么模3余2,如果有1个那么是模3余1.    dst->len = d - dst->data;}
这个是标准的base64的编码方式,具体说明可以参考代码中的注释。

ngx_int_t ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src);ngx_int_t ngx_decode_base64url(ngx_str_t *dst, ngx_str_t *src);static ngx_int_t ngx_decode_base64_internal(ngx_str_t *dst, ngx_str_t *src, const u_char *basis)
前面两个函数,统一调用的是最下面接口,参数唯一区别是解码表有点点区别。先来看看decode函数

static ngx_int_tngx_decode_base64_internal(ngx_str_t *dst, ngx_str_t *src, const u_char *basis){    size_t          len;    u_char         *d, *s;    for (len = 0; len < src->len; len++) {        if (src->data[len] == '=') { //找到'=',那么后面的任何字符都不是编码得到的了。            break;        }        /*字符的有效性验证,因为之前都是把字符串当成bit串然后6位为一组进行编码的,那么得到的编码结果每个字符都应该映射回去,如果映射到77了,那么说明出现了错误。*/        if (basis[src->data[len]] == 77) {             return NGX_ERROR;        }    }    /*这里为什么要判定模4余1为非法,需要先看看编码结尾部分的处理,就很好理解了。*/    if (len % 4 == 1) {        return NGX_ERROR;    }    s = src->data;    d = dst->data;    while (len > 3) {        *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4);//第一个字符的低6位左移2位,和第二个字符的低6位右移4位,这样就还原了第一个字符。        *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2);//第二个字符的        *d++ = (u_char) (basis[s[2]] << 6 | basis[s[3]]);        s += 4;        len -= 4;    }    if (len > 1) {        *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4);    }    if (len > 2) {        *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2);    }    dst->len = d - dst->data;    return NGX_OK;}

这里再简单介绍下编解码的过程。

解码: 解码的时候需要将字符串看成bit串(高位在左,低位在右),然后从左到右依次6bit的去取,因为6bit的数字大小在0-63之间,所以在编码的时候需要制定一个64个字符的编码表,然后用着6bit的数字作为下标取到对应的字符,需要特别注意的是,最后6bit不够的时候是采用低位补0的办法处理。

解码: 编码的时候有一个编码表,那么解码的时候同样是需要的,比如: 在编码的时候第一个6bit假设都是1,那么得到的下标是63,取到的字符是'/',那么在解码的时候用'/'的ASCII值作为下标在解码表中取到的值应该是63,这样就完成了解码。

需要特别注意的是,在对url进行base64的decode的时候,解码表中值为62,63的位置和标准的解码表位置不一样了,分别是'_' 和'-'ASCII值下标对应的值为63和62. 这是因为当url中传递的字符串在传递之前做base64encode的时候编码表示有差异的,如下:

标准的base64编码表: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

url做base64的编码表:  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";

之所以对最后两个字符做了变化是因为在'+'和'/'在url中传递的时候是会被urlencode成%xx这样的字符。

这里很奇怪的是,没有提供url参数做base64编码的函数。

uint32_t ngx_utf8_decode(u_char **p, size_t n); size_t ngx_utf8_length(u_char *p, size_t n); u_char *ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len);
这几个函数是关于字符串的在utf8编码上的一些处理,这里没有utf8的encode函数,但是提供了utf8的decode函数,要了解utf的decode函数需要先看看utf8的编码规则,可以先参考维基百科-UTF8, 另外也可以参考一篇写的比较好的blog:UTF8编码规则

uint32_tngx_utf8_decode(u_char **p, size_t n){    size_t    len;    uint32_t  u, i, valid;    u = **p;    if (u >= 0xf0) {        //leading byte = 11110xxx,  第一个字节不小于0xf0的话,说明这个字符是4字节组成        u &= 0x07;        valid = 0xffff;        len = 3;    } else if (u >= 0xe0) {        //leading byte = 1110xxxx,  第一个字节不小于0xe0的话,说明这个字符是3字节组成        u &= 0x0f;        valid = 0x7ff;        len = 2;    } else if (u >= 0xc2) {        //leading byte = 110xxxxx,  第一个字节不小于0xc2的话,说明这个字符是2字节组成,       // 从utf编码的方式来看这里判断条件应该是 u>=0xc0? 已经在nginx论坛提问了 , 持续关注中。        u &= 0x1f;        valid = 0x7f;        len = 1;    } else {        //leading byte = 0xxxxxxx,          (*p)++;        return 0xffffffff;    }    if (n - 1 < len) {        return 0xfffffffe;    }    (*p)++;    //continue byte    while (len) {        i = *(*p)++;        if (i < 0x80) {            return 0xffffffff;        }        u = (u << 6) | (i & 0x3f);        len--;    }    if (u > valid) {        return u;    }    return 0xffffffff;  }

size_tngx_utf8_length(u_char *p, size_t n){    u_char  c, *last;    size_t  len;    last = p + n;    for (len = 0; p < last; len++) {        c = *p;        if (c < 0x80) {            p++;            continue;        }        //UTF-8 : [0-0x10FFFF]        if (ngx_utf8_decode(&p, n) > 0x10ffff) {            /* invalid UTF-8 */            return n;        }    }    return len;}

这个函数是实现,计算一个字符串中含有的字符的个数,以为是utf8编码,所以不能简单计算字节数来计算字符个数。


最后string部分还有几个关于url escape的接口和红黑树的接口,这部分后面其他部分再来看看。


原创粉丝点击