一、简介

对于C语言而言,没有String类型,只有char * 和 char [],对于一些操作不便。

  • 字符串是二进制不安全的
    在C语言中,操作字符串的API函数都是以’\0’来作为结束符,而图片或在其他二进制数据中经常会出现’\0’,这样将导致出现被截断等错误。
  • 获取字符串长度时间复杂度O(N)

在C语言中,获取字符串的长度需要遍历整个字符串。

因此nginx自己实现了String类型,对字符串的封装。相比于redis的SDS,nginx的String更简单一些,毕竟不是将所有数据都存储在内存中,不用对内存的使用精打细算。

涉及到的文件ngx_string.h, ngx_string.c

二、数据结构

typedef struct {
    size_t      len;
    u_char     *data;
} ngx_str_t;
  • len 表示字符串的长度
  • data表示实际的数据

nginx 输出文字 nginx字符集_nginx 输出文字

与redis对比:

  1. len的类型是固定的size_t类型,这样更简单;而redis根据字符串的长度的不同而使用不同的类型,这样不浪费一点内存,精打细算,减少不必要的内存开销
  2. 相比与redis的SDS,String中只有一个len字段;而redis还有free,alloc等字段,可以预分配等操作减少向操作系统申请内存的次数;但是nginx使用的是内存池,相比redis直接向操作系统申请资源更有优势

三、相关API

3.1 定义并初始化str

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

比如:

static ngx_str_t ngx_http_gzip_no_cache = ngx_string("no-cache");

3.2 定义空的str

声明并初始化一个空的str

#define ngx_null_string     { 0, NULL }

3.3 对str赋值

首先被赋值的str必须先定义,才能被这里赋值。

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

3.4 清空str

首先被清空的str必须先定义,才能被清空。清空只是设置了空置,并未作释放的操作,看样子是需要调用方处理,或是使用的内存池方式,不需要释放空间。

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

3.5 字符串转小写

#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)

英文大小写字符之间相差32,一般自己实现都是加减32,这里使用了位运算,更高效。32 = 2^6 ,刚好是第6个比特位。

void
ngx_strlow(u_char *dst, u_char *src, size_t n)
{
    while (n) {
        *dst = ngx_tolower(*src);
        dst++;
        src++;
        n--;
    }
}

3.6 字符串比较

#define ngx_strncmp(s1, s2, n)  strncmp((const char *) s1, (const char *) s2, n)

#define ngx_strcmp(s1, s2)  strcmp((const char *) s1, (const char *) s2)

实际上使用的是系统的strncmp/strcmp函数。

3.7 字符串长度

#define ngx_strlen(s)       strlen((const char *) s)

看这里还是使用的是系统的strlen函数,并没有使用ngx_str_t类型。

3.8 字符串子串

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

3.9 字符串中某个字符位置

#define ngx_strchr(s1, c)   strchr((const char *) s1, (int) c)

3.10 字符串指定范围内的字符位置

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

在[p,last)之间查找字符c,并返回地址,如果没有找到则返回NULL。

3.11 字符串中NULL结尾的长度

size_t
ngx_strnlen(u_char *p, size_t n)
{
    size_t  i;

    for (i = 0; i < n; i++) {

        if (p[i] == '\0') {
            return i;
        }
    }

    return n;
}

给定长度n,如果在[0,n) 范围内找到’\0’则范围当前位置,否则返回n。

3.12 总结

对于字符串的操作大部分都是简单的对C的API进行了一个宏的封装,这样在nginx中使用的API都统一,好维护,后期替换更简单。而对于ngx_str_t的使用,更多的是直接使用var.len/var->len,这样减少了对字符串求长度的操作。