一、简介
对于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表示实际的数据
与redis对比:
- len的类型是固定的size_t类型,这样更简单;而redis根据字符串的长度的不同而使用不同的类型,这样不浪费一点内存,精打细算,减少不必要的内存开销
- 相比与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,这样减少了对字符串求长度的操作。