nginx源码分析(一)-基础数据结构ngx_str_t

近来想拜读一下优秀的开源代码设计,之前也阅读过qt以及opencv的源码,不过学习nginx源码的目的是想试着用设计模式以及C++的思想来封装一次nginx的源码,但是第一步还得好好熟悉一下nginx,大师们都说过,最好的办法就是看源码,万能的开源。

  • ngx_str_t定义

ngx_str_t是nginx中定义的类似于字符串的结构,其中包含了字符串的长度(不包含终止字符'\0')以及字符串的首地址,数据结构定义如下:

typedef struct {
    size_t      len;
    u_char     *data;
} ngx_str_t;
细心的人都会觉得,以首地址的方式指向一块内存,如果在改变ngx_str_t定义的字符串的时候,可能会出错。因为它指向的是一块内存的首地址,如果改变字符串内容,有可能使得指向内存的区域进入到别的字符串的区域,这样就会出现差错。所以,对于ngx_str_t最好的方式是以只读的方式进行。


  • 操作函数

1、初始化或赋值函数

#define ngx_string(str)     { sizeof(str) - 1, (u_char *) str }
#define ngx_null_string     { 0, NULL }
#define ngx_str_set(str, text)                                               \
    (str)->len = sizeof(text) - 1; (str)->data = (u_char *) text
#define ngx_str_null(str)   (str)->len = 0; (str)->data = NULL

  • ngx_string(str)用于接受一个常量字符串数据,然后将该字符串的长度以及首地址传给ngx_str_t的结构体。
  • ngx_null_string定义一个字符长度为0的结构体
  • ngx_str_set用于将结构体ngx_str_t的长度以及首地址赋值为text的长度以及首地址。
  • ngx_str_null(null)用于将结构体ngx_str_t初始化为长度为0,首地址为空的初始序列。
  • ngx_str_set传入的是结构体的地址,因为下面的引用成员变量的方式是以指针的方式进行的,ngx_str_null亦是如此。

ngx_str_t s1=ngx_null_string;
ngx_str_t s2=ngx_string("hello nginx");
ngx_str_set(&s2,"hello again hello");
ngx_str_null(&s2);

2、字符处理函数

很多是封装的c下面的string.h文件中的函数:

#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)  //转大写字符
void ngx_strlow(u_char *dst, u_char *src, size_t n);           //从src复制n个字符到dst中,同时将相应的字符转为小写字符
#define ngx_strncmp(s1, s2, n)  strncmp((const char *) s1, (const char *) s2, n)  //字符比较函数,只比较前面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)                      //第一次出现c的位置


自定义函数:

比较函数:

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_strcmp,ngx_strncmp,大小写不敏感

格式打印函数:

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,

ngx_cdecl是一个宏定义,原型为_cdecl,表示函数的参数从从右到左依次入账,是默认的函数调用方法,用到这个是因为以上三个参数用到了省略号参数,参数个数是可变的。以ngx_snprintf为例进行讲解:

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

对于 va_listva_start以及 va_end的讲解,请参考改博客内容: http://www.cnblogs.com/hanyonglu/archive/2011/05/07/2039916.html

对于可变参数列表,一般有以下几个步骤:

  • 在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);
  •  然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数;
  • 然后是获取参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置;
  •  获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。说白了,就是让我们的程序具有健壮性。通常va_start和va_end是成对出

ngx_snprintf中,fmt是可变参数,我们都知道,printf里面会有"%d%d%s"这种类似的格式,那么这里的fmt也是一样的。ngx_snprintf的格式参数包含:

/*
 * 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_snprintf的函数实现的是:将ngx_str_t的内容格式化输出到buf里面,最后还是通过调用printf实现输出。最后返回的u_char *指针输出的最后位置。

以上三个函数都调用了函数:

u_char *ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args);
它是实现最终的格式化输出到buf的函数。


复制函数

u_char *ngx_cpystrn(u_char *dst, u_char *src, size_t n);
u_char *ngx_pstrdup(ngx_pool_t *pool, ngx_str_t *src);


使用例子:

#include 
#include 
#include
int main(int args,char *argv[])
{
	u_char *p;
	ngx_str_t str = ngx_null_string;
	unsigned char buffer[1024];
	printf("str長度爲:%d\n",(int)str.len);	
	ngx_str_set(&str,"hello world");                 //注意是&str而不是str
	p=ngx_snprintf(buffer, str.len, "%V", &str);   //注意是&str而不是str
	buffer[str.len]='\0';
	printf("格式化str輸出爲:%s\n",buffer);
	printf("格式化長度爲:%d\n",(int)((p-buffer)/sizeof(char)));   //p返回的是最後輸出的位置,所以由此可以得出最後格式化的字符個數
	return 1;
}
nginx源码分析(一)-基础数据结构ngx_str_t_第1张图片


你可能感兴趣的:(nginx源码分析(一)-基础数据结构ngx_str_t)