nginx的数据结构设计的非常精巧,高效,学习nginx的数据结构设计可以加深我们对于其后续代码的阅读深度。
nginx的数据结构在源码包的src/core目录中,nginx对应的文件是ngx_string.c和ngx_string.h文件:
[root@rsync core]# ll ngx_string.*
-rw-r--r-- 1 1001 1001 43851 Oct 11 11:03 ngx_string.c
-rw-r--r-- 1 1001 1001 6550 Oct 11 11:03 ngx_string.h
首先我们查看ngx_string.h头文件:
最基本的ngx_str_t定义如下,他有长度和指针两个元素:
typedef struct {
size_t len; //字符串长度
u_char *data; //data是一个指针,在后续中只是起指示左右
} ngx_str_t;
紧接着定义了键值的结构,其实就是包含两个ngx_str_t的结构体:
typedef struct {
ngx_str_t key;
ngx_str_t value;
} ngx_keyval_t;
紧接着定义了nginx的变量类型,其中有很多枚举代表着不同的状态:
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;
nginx为了方便字符串的操作,减少函数调用,使用了很多宏来进行字符串的比较。如下所示:
//下面两个宏是nginx中字符串赋值或者置空时的操作,需要修改ngx_str_t中的长度和指针
#define ngx_string(str) { sizeof(str) - 1, (u_char *) str }
#define ngx_null_string { 0, NULL }
//这个是对标准库的简单封装
#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)
#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)
//内存置位操作
#define ngx_memzero(buf, n) (void) memset(buf, 0, n)
#define ngx_memset(buf, c, n) (void) memset(buf, c, n)
还对格式化的输出进行了封装:
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);
关于格式化输出可以去参考可变参函数的实现。nginx支持的格式化输出除了printf的之外,还包含了自己支持额格式:
/*
* 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
*/
在ngx_string.c文件中实现了很多关于字符串的操作,书写的也十分简练:
//不群分大小写的字符串比较
ngx_int_t ngx_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_int_t ngx_strncasecmp(u_char *s1, u_char *s2, size_t n);
u_char *ngx_strnstr(u_char *s1, char *s2, size_t n);
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_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_t ngx_filename_cmp(u_char *s1, u_char *s2, size_t n);
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);
u_char *ngx_hex_dump(u_char *dst, u_char *src, size_t len);
关于细节可以查看ngx_string.c文件。字符串算是nginx一个基础的数据结构。后边开发自己模块的时候会大量的使用。