nginx基本数据类型--字符串

在nginx源码目录的src/core/ngx_string.h|c里面,包含了字符串的封装以及字符串相关操作的api。nginx提供了一个带长度的字符串结构ngx_str_t,它的原型如下:

typedef struct {
    size_t      len;    // 字符串长度
    u_char     *data;   // 字符串数据的开头指针
} ngx_str_t;

nginx字符串和普通的C字符串实现基本一致, 区别在于,普通的字符串是以'\0'作为字符串的结束符号, 字符串的长度是需要自己通过strlen(str)来计算的。而ngx_str_t是通过字符串对象的len属性来决定字符串的结束位置, data来表示字符串的指针开始位置。

nginx字符串的优点:

  1. 通过长度来表示字符串长度,减少计算字符串长度的次数。
  2. nginx可以重复引用一段字符串内存,data可以指向任意内存,长度表示结束,而不用去copy一份自己的字符串。 (标准C的字符串以'\0'结束, 因为不能改变原字符串, 势必要拷贝一份作为新的字符串。) 这样可以减少不必要的内存分配和拷贝。

可以找到很多字符串引用一段内存的例子,比如request_line、uri、args等等,这些字符串的data部分,都是指向在接收数据时创建buffer所指向的内存中,uri,args就没有必要copy一份出来。

nginx字符串的缺点:

也正是因为nginx字符串有上述的优点是得益于不使用‘\0'表示字符串的结束, 这样就导致, 在ngx_str_t调用标准字符串api的时候, 需要自己进行手工转换,才能使用。 通常的转换方式有:

  1. 通过申请一份str->len + 1的内存,然后将data拷贝到里边, 最后以'\0'结尾, 然后传入标准API。
  2. hack方法, 将ngx_str_t的len+1位进行备份, 然后修改其为'\0',然后将data传入, 这样操作标准api后恢复这个位置的原始内容。但不推荐使用该方法。
  3. 修改ngx_str_t的data域的时候必须谨慎的去修改一个字符串。在修改字符串时需要认真的去考虑:是否可以修改该字符串;字符串修改后,是否会对其它的引用造成影响。

下面顺带介绍ngx_string.h|c中定义的其他几种数据结构:

1. ngx_keyval_t : key , value对数据结构

typedef struct {
    ngx_str_t   key;
    ngx_str_t   value;
} ngx_keyval_t;

该结构由两个域组成, key, value, 这两个域都是ngx_str_t类型,有点像PHP中的联系数组。

2. ngx_variable_value_t : 可变值类型

typedef struct {
    unsigned    len:28;                  // 字符串长度 位域28?

    unsigned    valid:1;                 // 有效性 位域1
    unsigned    no_cacheable:1;          // 无可缓存的 位域1
    unsigned    not_found:1;             // 未找到标志 位域1
    unsigned    escape:1;                // 是否escape

    u_char     *data;                    // 数据区域
} ngx_variable_value_t;

下面看看ngx_str_t的相关API:


1. ngx_string(str) 将给定的标准C字符串转换为ngx_str_t类型:

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

注意, 上面的data域使用的是sizeof(), 因此传入宏的字符串必须是常量字符串。

2. ngx_null_string() 空的ngx_str_t的宏

#define ngx_null_string     { 0, NULL }

以上两个宏定义的时候使用{,}, 因此只能用于ngx_str_t定义的时候初始化。 而不能用于赋值。下面介绍两个用于赋值的宏定义。

3. ngx_str_set(str, text) : 用于ngx_str_t类型的变量定义后的赋值, 其原型如下:

#define ngx_str_set(str, text)                                               \
    (str)->len = sizeof(text) - 1; (str)->data = (u_char *) text

注意上面的宏是两个语句, 中间用‘;’分割, 因此在应用到循环语句或者条件语句的时候, 一定要用{}将其包围住。否则会出现编译错误。

eg : ngx_str_t str1;

       if(xx) {

              ngx_str_set(str1, 'abcd');

       }

4. ngx_str_null(str) : 将一个ngx_str_t类型重置为空的状态。

#define ngx_str_null(str)   (str)->len = 0; (str)->data = NULL

这个宏的应用也需要注意和3中的一样, 这个宏是两个语句, 在条件语句中调用需要使用括号。

5. ngx_tolower(c) : 将字符c转换为小写的对应字符

#define ngx_tolower(c)      (u_char) ((c >= 'A' && c <= 'Z') ? (c | 0x20) : c)

6. ngx_toupper(c) : 将字符c转换为相应的大写字符

#define ngx_toupper(c)      (u_char) ((c >= 'a' && c <= 'z') ? (c & ~0x20) : c)

7. ngx_strncmp(s1, s2, n) : 比较字符串s1, s2的前面n位

#define ngx_strncmp(s1, s2, n)  strncmp((const char *) s1, (const char *) s2, n)
参数s1, s2都是标准的C字符串, n为size_t。此宏仅仅是对标准api strncmp的包装。

8. ngx_strcmp(s1, s2) : 比较s1, s2字符串, 普通字符串的比较, 对strcmp的包装。

#define ngx_strcmp(s1, s2)  strcmp((const char *) s1, (const char *) s2)
9. ngx_strstr(s1, s2) : 查找s1字符串中出现完整s2字符串的第一个位置, 如果没有找到返回null指针。strstr的包装。

#define ngx_strstr(s1, s2)  strstr((const char *) s1, (const char *) s2)

10. ngx_strlen(str) : 获取C字符串str的长度, 即strlen()的包装。

#define ngx_strlen(s)       strlen((const char *) s)
11. ngx_strchr(s1, c) :  查找C字符串s1中第一次出现c字符串的位置, 没有找到返回null指针。strchar的包装。

#define ngx_strchr(s1, c)   strchr((const char *) s1, (int) c)
12. ngx_strlchr(u_char *p, u_char *last, u_char c) : 查找字符串p处到last位置处第一次出现字符c的位置, 未发现返回null. 该方法为静态内联函数。

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;
}

13. ngx_memzero(buf, n) : 将buf字符串的前n位置为0, 该宏是对memset(str, value, n)的包装。其中value使用0.

#define ngx_memzero(buf, n)       (void) memset(buf, 0, n)

ngx_memset(buf, value, n) : 是对memset(buf, value, n)的包装。更具一般性。

14. ngx_memcpy(dest, src, n) : memcpy(dest, src, n) 的封装宏. 从src指针指向的内存拷贝n位到dest所指的内存里边, 最后返回dest.

memcpy()对于dest, src有重叠部分的拷贝是不安全的, 更加安全的方法是memmove()方法, 两个的区别可单独百度查找。

#define ngx_memcpy(dst, src, n)   (void) memcpy(dst, src, n)

15. ngx_cpymem(dest, src, n) : 功能和上面的类似, 只是返回的时候,返回的是拷贝后的dest结束位置, 而非开始位置。

#define ngx_cpymem(dst, src, n)   (((u_char *) memcpy(dst, src, n)) + (n))

16. ngx_copy(u_char *dst, u_char *src, size_t len) : 该函数为简单内联循环拷贝可变字符串到16字节, 比_intel_fast_memcpy()自动检测的icc8函数快点。

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);
    }
}

对于可变字符串dest如果不足16位, 则拷贝满16位结束, 超出16位的直接拷贝, 最终返回拷贝结束后的位置。

另外相应的memmove的两个宏:

ngx_memmove(dst, src, n)  : 拷贝后返回dst指针的位置
ngx_movemem(dst, src, n) : 拷贝后返回dst拷贝后的位置指针

17. ngx_memcmp(s1, s2, n) :memcmp(s1, s2, n)的包装, 返回0 表示s1, s2的前n位内容相同, 大于0, s1的前n位比s2的前n位大, 小于0相反。

#define ngx_memcmp(s1, s2, n)  memcmp((const char *) s1, (const char *) s2, n)

ngx_str_t相关的方法:

1. ngx_strlow(u_char *dst, u_char *src, size_t n) : 将src的前n个字符转换为小写保存于dst中:

void
ngx_strlow(u_char *dst, u_char *src, size_t n)
{
    while (n) {
        *dst = ngx_tolower(*src);   // 调用转换小写字符的宏
        dst++;
        src++;
        n--;
    }
}

2. ngx_cpystrn(u_char *dst, u_char *src, size_t n) : 拷贝src的前n位到dst中, 遇到字符串结束符号即返回, 如果拷贝完没有遇到字符串结束符, 则自动添加结束符'\0'. 返回dst.

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;
}

3. ngx_pstrdup(ngx_pool_t *pool, ngx_str_t *src) : 在将ngx_str_t类型的字符串src拷贝至给定的内存池pool中, 并返回其在内存池中的句柄位置。

首先在内存池pool中为src申请一片内存, 然后将src的数据拷贝进去。

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;
}
4. ngx_sprintf(u_char *buf, const char *fmt, ...) :nginx自己实现的字符串格式化方法。类似于sprintf方法。

/*
 * supported formats:
 *    %[0][width][x][X]O        off_t
 *    %[0][width]T              time_t
 *    %[0][width][u][x|X]z      ssize_t/size_t
 *    %[0][width][u][x|X]d      int/u_int
 *    %[0][width][u][x|X]l      long
 *    %[0][width|m][u][x|X]i    ngx_int_t/ngx_uint_t
 *    %[0][width][u][x|X]D      int32_t/uint32_t
 *    %[0][width][u][x|X]L      int64_t/uint64_t
 *    %[0][width|m][u][x|X]A    ngx_atomic_int_t/ngx_atomic_uint_t
 *    %[0][width][.width]f      double, max valid number fits to %18.15f
 *    %P                        ngx_pid_t
 *    %M                        ngx_msec_t
 *    %r                        rlim_t
 *    %p                        void *
 *    %V                        ngx_str_t *
 *    %v                        ngx_variable_value_t *
 *    %s                        null-terminated string
 *    %*s                       length and string
 *    %Z                        '\0'
 *    %N                        '\n'
 *    %c                        char
 *    %%                        %
 *
 *  reserved:
 *    %t                        ptrdiff_t
 *    %S                        null-terminated wchar string
 *    %C                        wchar
 */
u_char * ngx_cdecl
ngx_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_snprintf(u_char *buf, size_t max, const char *fmt, ...) : 最多展示buf开始到max的位置的字符串格式化

ngx_slprintf(u_char *buf, u_char *last, const char *fmt, ...) : 直到last的位置的字符串格式化。

具体代码如下:

u_char * ngx_cdecl
ngx_snprintf(u_char *buf, size_t max, const char *fmt, ...)
{
    u_char   *p;
    va_list   args;

    va_start(args, fmt);
    p = ngx_vslprintf(buf, buf + max, fmt, args);
    va_end(args);

    return p;
}


u_char * ngx_cdecl
ngx_slprintf(u_char *buf, u_char *last, const char *fmt, ...)
{
    u_char   *p;
    va_list   args;

    va_start(args, fmt);
    p = ngx_vslprintf(buf, last, fmt, args);
    va_end(args);

    return p;
}
5. ngx_strcasecmp(u_char *s1, u_char *s2) :


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值