• Lighttpd1.4.20源码分析之buffer.c(h)字符串内存管理


    在web服务器中,通常要设计很多字符串的处理。比如客户端请求的 URI地址、发送的 query参数、post 提交的数据等等都是一串字符。因此,提供对字符串的灵活高效的处理,对lighttpd的效率至关重要。
    在lighttpd中,buffer提供了对字符串的处理。在buffer.h中,有如下的数据结构定义:
    //定义buffer
    1 typedef struct 
    2 {
    3     char *ptr;     //指向存储空间,一个字符串组
    4     size_t used;     //buffer中数据的长度
    5     size_t size;     //buffer的长度
    6 } buffer;
    上面的结构体定义了lighttpd中,对字符串处理的基本结构。其具体含义如上。

     1 //定义buffer数组
     2 typedef struct 
     3 {
     4     buffer **ptr;     //buffer指针数组
     5     size_t used;     //buffer数组中数据的个数
     6     size_t size;     //buffer数组的大小
     7 } buffer_array;
     8 /*
     9  * 这个比较有意思
    10  */
    11 typedef struct 
    12 {
    13     char *ptr;
    14     size_t offset;    /* input-pointer */
    15     size_t used;    /* output-pointer */
    16     size_t size;
    17 }read_buffer
    这个结构体比较有意思,具体干什么的我还没有发现。。。不过从其定义中猜测,应该和输入输出缓冲有关。

    围绕buffer结构体和buffer_array结构体,在buffer.h中定义了很多操作函数,其具体的作用将在下文中一一说明。其中比较有意思有技巧的函数还将就其实现代码进行分析。大部分的函数都很简单,读者可以自行阅读。
    首先是buffer_array的操作函数:
    1、buffer_array *buffer_array_init(void);
    初始化一个buffer_array,返回其指针并分配空间。
    2、void buffer_array_free(buffer_array * b);
    释放b指向的buffer_array的空间。
    3、void buffer_array_reset(buffer_array * b);
    重置buffer_array。并递归重置数组中的数据。
    4、buffer *buffer_array_append_get_buffer(buffer_array * b);
    返回数组中第一个未使用的buffer结构体的指针。如果数组已满,则对数组进行扩容,并初始化第一个为使用的buffer的指针。

    下面是buffer的操作函数:
    1、buffer *buffer_init(void);
    初始化一个buffer。
    2、buffer *buffer_init_buffer(buffer * b);
    用b初始化一个buffer。相当于复制b。
    3、buffer *buffer_init_string(const char *str);
    用str初始化一个buffer。把str指向的字符串复制到buffer中。
    4、void buffer_free(buffer * b);
    释放buffer的空间。
    5、void buffer_reset(buffer * b);
    这个比较有意思。重置b所指向的buffer结构。一般情况下,都是把buffer数据区中ptr指向的字符数组的第一个元素ptr[0]设置为’\0’,然后把buffer的size设置为0。但当buffer的大小超过BUFFER_MAX_REUSE_SIZE时,则直接释放buffer的ptr指向的空间并把size设置为0。
    6、int buffer_prepare_copy(buffer * b, size_t size);
    为复制准备size大小的空间。如果b的空间大于size则仅仅将b的使用空间used设置为0.如果b的空间小于size,则重新分配size大小的空间。另外,为了防止内存碎片,在每次重新分配空间时,都将所分配的空间凑成BUFFER_PIECE_SIZE的整数倍:
    1 b->size += BUFFER_PIECE_SIZE - (b->size % BUFFER_PIECE_SIZE);
    7、int buffer_prepare_append(buffer * b, size_t size);
    为追加size大小的数据准备空间。操作和上一个函数查不多。
    8、int buffer_copy_string(buffer * b, const char *s);
    将字符串s复制到b中。

     1 int buffer_append_string(buffer *b, const char *s)
     2 {
     3     size_t s_len;
     4     if (!|| !b) return -1;
     5     s_len = strlen(s);
     6     buffer_prepare_append(b, s_len + 1);
     7     /*
     8      * 如果buffer中原来有数据(字符串),那么最后一个字符是NULL,
     9      * 在复制的时候,要覆盖这个字符。
    10       * 但当buffer为空时,就不需要覆盖NULL字符,因此,需要加一,
    11      * 以便和有数据的情况下处理相同。
    12      */
    13     if (b->used == 0)
    14         b->used++;
    15     //覆盖原来数据最后一个字符NULL,同时,也将s中的NULL复制到b中。
    16     memcpy(b->ptr + b->used - 1, s, s_len + 1);
    17     b->used += s_len;
    18     return 0;
    19 }
    9、int buffer_copy_string_len(buffer * b, const char *s, size_t s_len);
    将字符串s复制到b中。s_len是s的长度。s被看作是一个不以'\0'结尾的字符串,s_len是s的长度。最终b中的数据以'\0'结尾。也就是说,如果s的结尾是'\0',那么,最终,b中的数据末尾有两个'\0',而且b中used表示的数据长度,包括其中一个'\0'!
    10、int buffer_copy_string_buffer(buffer * b, const buffer * src);
    将src中的数据复制到b中。
    11、int buffer_copy_string_hex(buffer * b, const char *in ,size_t in_len);
    将字符串In转化成十六进制形式,复制到b中。
    12、int buffer_copy_long(buffer * b, long val);
    将val以字符串的形式复制到b中。
    13、int buffer_copy_memory(buffer * b, const char *s, size_t s_len);
    复制s指向的内存区域中的数据到b中。
    14、int buffer_append_string(buffer * b, const char *s);
    将字符串s追加大b中。
    15、int buffer_append_string_len(buffer * b, const char *s, size_t s_len);
    将字符串s追加到b中。s_len为s的长度。
    具体的处理与上面的复制函数差不多。
    16、int buffer_append_string_buffer(buffer * b, const buffer * src);
    将src的数据追加到b中。
    17、int buffer_append_string_lfill(buffer * b, const char *s, size_t maxlen);
    这个函数在buffer.c中没有实现。
    18、int buffer_append_string_rfill(buffer * b, const char *s, size_t maxlen);
    将字符串s追加到b中。其中maxlen为字符串s的最大长度。如果
    字符串s的长度小于maxlen,那么追加空格,使其长度达到maxlen。在 函数实现中。如果s的长度大于maxlen,则可能溢出。。。
    19、int buffer_append_long_hex(buffer * b, unsigned long len);
    将无符号长整型value转化成对应的十六进制的字符串形式。并将字符串复制到b中。其中涉及到数值转16进制的问题。代码如下:

     1 static const char hex_chars[] = "0123456789abcdef";
     2 int buffer_append_long_hex(buffer *b, unsigned long value) 
     3 {
     4     char *buf;
     5     int shift = 0;
     6     unsigned long copy = value;
     7     //计算十六进制表示的value的长度
     8     while (copy) 
     9     {
    10         copy >>= 4;
    11         shift++;
    12     }
    13     if (shift == 0)
    14         shift++;
    15     /*
    16      * 保证追加的字符串为偶数位。
    17      * 如若不是偶数位,则在最前面加一个'0'.
    18      */
    19     if (shift & 0x01)
    20         shift++;
    21     buffer_prepare_append(b, shift + 1);//最后一个'\0'
    22     if (b->used == 0)
    23         b->used++;
    24     //buf指向开始存放的位置
    25     buf = b->ptr + (b->used - 1);
    26     b->used += shift;
    27     /*
    28      * 每四位一组,转化value为十六进制形式的字符串
    29      */
    30     shift <<= 2;
    31     while (shift > 0
    32     {
    33         shift -= 4;
    34         *(buf++= hex_chars[(value >> shift) & 0x0F];
    35     }
    36     *buf = '\0';
    37     return 0;
    38 }
    39 
    20、int buffer_append_long(buffer * b, long val);
    将val以字符串的形式追加大b中。
     
    下面的宏定义使用来处理off_t和long类型。
    如果long和off_t相同,则用处理long的函数来处理off_t。如果不相同,则另行定义off_t的处理函数。

    1 #if defined(SIZEOF_LONG) && (SIZEOF_LONG == SIZEOF_OFF_T)
    2 #define buffer_copy_off_t(x, y)        buffer_copy_long(x, y)
    3 #define buffer_append_off_t(x, y)    buffer_append_long(x, y)
    4 #else
    5 int buffer_copy_off_t(buffer * b, off_t val);
    6 int buffer_append_off_t(buffer * b, off_t val);
    7 #endif
    8 

    22、int buffer_append_memory(buffer * b, const char *s, size_t s_len);
    将s指向的内存区的数据复制到b中,s_len是s的长度。
    23、char *buffer_search_string_len(buffer * b, const char *needle, size_t len);
    判断b中是否含有字符串needle,needle的长度为len。如果存在,则返回needle在b中的指针位置,否则返回NULL
    24、int buffer_is_empty(buffer * b);
    判断b是否为空。
    25、int buffer_is_equal(buffer * a, buffer * b);
    判断a和b中的数据是相同。
    26、int buffer_is_equal_right_len(buffer * a, buffer * b, size_t len);
    判断b1和b2中,最右边的len个字符是否相同。
    27、int buffer_is_equal_string(buffer * a, const char *s, size_t b_len);
    b中的数据是否等于s,b_len为s的长度。
    28、int buffer_caseless_compare(const char *a, size_t a_len,const char *b, size_t b_len);
    比较字符串a和b,忽略大小写。

     1 /*
     2 * simple-assumption:
     3 * most parts are equal and doing a case conversion needs time
     4 * 假设比较的部分相同的较多且大小写转换需要时间。 
     5 */
     6 int buffer_caseless_compare(const char *a, size_t a_len, const char *b, size_t b_len) 
     7 {
     8         size_t ndx = 0, max_ndx;
     9         size_t *al, *bl;
    10         size_t mask = sizeof(*al) - 1;
    11         al = (size_t *)a;
    12         bl = (size_t *)b;
    13                 /* 一开始,将字符串数组转化成size_t类型的数组,通过比较size_t类型来比较是否相同 */
    14         /* libc的字符串比较函数也使用了相同的技巧,可有效的加快比较速度 */
    15         /* 检查a1和b1的位置是否对齐(size_t的类型长度的倍数?) ? */
    16         if ( ((size_t)al & mask) == 0 &&
    17                      ((size_t)bl & mask) == 0 ) 
    18         {
    19             /* 确定比较的长度 */
    20             max_ndx = ((a_len < b_len) ? a_len : b_len) & ~mask;
    21 
    22             for (; ndx < max_ndx; ndx += sizeof(*al)) 
    23                        {
    24                 if (*al != *bl) break;
    25                 al++; bl++;
    26             }
    27         }
    28         /* 相同的部分比较完毕 */
    29         /* 开始比较字符串,并忽略大小写 */
    30         a = (char *)al;
    31         b = (char *)bl;
    32         max_ndx = ((a_len < b_len) ? a_len : b_len);
    33         for (; ndx < max_ndx; ndx++
    34                {
    35             char a1 = *a++, b1 = *b++;
    36             /*
    37                 'A'的二进制表示为0100 0001,'a'的二进制表示为0110 0001,
    38                 大写字母比小写字母的ASCII值小了32。
    39                 通过或上一个32,可以使所有的字母全部转换成大写字母。
    40             */
    41             if (a1 != b1) 
    42                        {
    43                 if ((a1 >= 'A' && a1 <= 'Z'&& (b1 >= 'a' && b1 <= 'z'))
    44                     a1 |= 32;
    45                 else if ((a1 >= 'a' && a1 <= 'z'&& (b1 >= 'A' && b1 <= 'Z'))
    46                     b1 |= 32;
    47                 if ((a1 - b1) != 0return (a1 - b1);
    48             }
    49         }
    50         /* all chars are the same, and the length match too。 they are the same */
    51         if (a_len == b_len) return 0;
    52         /* if a is shorter then b, then b is larger */
    53         return (a_len - b_len);
    54 }

     /** deprecated */

    一些工具性的函数:
    1、int LI_ltostr(char *buf, long val);
    将长整型val转化成字符串,并存入buf中。

     1 int LI_ltostr(char *buf, long val) 
     2 {
     3     char swap;
     4     char *end;
     5     int len = 1;
     6     //val为负数,加一个负号,然后转化成正数
     7     if (val < 0
     8     {
     9         len++;
    10         *(buf++= '-';
    11         val = -val;
    12     }
    13     end = buf;
    14     /*
    15           这里val必须设置为大于9,并在循环外在做一次转换
    16             (*(end) = '0' + val)!
    17              因为如果val设置为大于0,当val为0时,将不进入循环,那么循
    18            环后面直接在buf中
    19      * 追加'\0'。这样0就被转化成了空串!!
    20      * 这里val转化后的字符串是逆序存放在buf中的,在后面要反转,
    21      * 以得到正确的顺序。
    22      */
    23     while (val > 9
    24     {
    25         *(end++= '0' + (val % 10);
    26         val = val / 10;
    27     }
    28     *(end) = '0' + val;
    29     *(end + 1= '\0';
    30     len += end - buf;
    31     //将字符串反转,
    32     while (buf < end) 
    33     {
    34         swap = *end;
    35         *end = *buf;
    36         *buf = swap;
    37         buf++;
    38         end--;
    39     }
    40     return len;
    41 }
    42 
    2、char hex2int(unsigned char c);
    converts hex char (0-9, A-Z, a-z) to decimal.returns 0xFF on 
    invalid input. 将16进制的字符转化成对应的数字,非法输入返回0xFF。忽略c的大小写。
    3、char int2hex(char i);
    将i转化成对应的16进制形式
    4、int light_isdigit(int c);
    c是否是数字。0-9
    5、int light_isxdigit(int c);
    c是否是十六进制的数字0-9 a-f
    6、int light_isalpha(int c);
    c是否是字母。
    7、int light_isalnum(int c);
    c是否是字母或数字。
    以上几个函数在处理大小写的时候,都使用了c |= 32;将c转换成小写的形式,无论c原来是大写还是小写。原理在函数buffer_caseless_compare中讲解过。
    8、int buffer_to_lower(buffer * b);
    将b中的数据转换成小写。
    9、int buffer_to_upper(buffer * b);
    将b中的数据转换成大写。
    以上两个函数没有在buffer.c中定义。

    下面的几个宏定义一些方便的操作。

     1 #define BUFFER_APPEND_STRING_CONST(x, y) \
     2     buffer_append_string_len(x, y, sizeof(y) - 1)
     3 
     4 #define BUFFER_COPY_STRING_CONST(x, y) \
     5     buffer_copy_string_len(x, y, sizeof(y) - 1)
     6 //在buffer中追加一个‘/’,如果最后一个字符是‘/’,则不追加。
     7 #define BUFFER_APPEND_SLASH(x) \
     8     if (x->used > 1 && x->ptr[x->used - 2!= '/'
     9 { BUFFER_APPEND_STRING_CONST(x, "/"); }
    10 
    11 #define CONST_STR_LEN(x)  x, x ? sizeof(x) - 1 : 0
    12 #define CONST_BUF_LEN(x)  x->ptr, x->used ? x->used - 1 : 0
    13 
    14 #define SEGFAULT() 
    15 do { 
    16 fprintf(stderr, "%s.%d: aborted\n", __FILE__, __LINE__); abort(); 
    17 while(0)
    18 #define
    以下的函数操作涉及到编码问题。在lighttpd中,使用到的编码有六种。具体的类型定义在下面的结构体中。

     1 typedef enum 
     2 {
     3     ENCODING_UNSET,
     4     ENCODING_REL_URI,                /* for coding a rel-uri (/withspace/and%percent) nicely as part of a href */
     5     ENCODING_REL_URI_PART,        /* same as ENC_REL_URL plus coding / too as %2F */
     6     ENCODING_HTML,                /* & becomes &amp; and so on */
     7     ENCODING_MINIMAL_XML,        /* minimal encoding for xml */
     8     ENCODING_HEX,                    /* encode string as hex */
     9     ENCODING_HTTP_HEADER            /* encode \n with \t\n */
    10 
    于此对应的 buffer.c 源文件中给出了 
    const char encoded_chars_rel_uri_part[]
    const char encoded_chars_rel_uri[]
    const char encoded_chars_html[]
    const char encoded_chars_minimal_xml[]
    const char encoded_chars_hex[]
    const char encoded_chars_http_header[] 
    六个标志数组,数组中值为 1 的元素表示对应下标值大小的字符需要被编码转换,否则不需要转换可以直接使用(即编码前和编码后是同一个值)。例如对于 encoded_chars_rel_uri数组,encoded_chars_rel_uri[32]值为1表示该对应的字符(32对应的是空格,因为空格的十进制值为 32)需要被 uri 编码(被编码为“%20”),而对于值为 0的 encoded_chars_rel_uri[48],其对应的字符就不需要编码(48 对应的是字符‘0’,而字符‘0’并不是特殊字符,因此不用编码。)。对于具体的编码方式,请查阅相关资料。
    1、int buffer_append_string_encoded(buffer * b, const char *s,size_t s_len, buffer_encoding_t encoding);
        将字符串s以指定的编码方式存入b中。encoding指定编码的方式。
    Code
    2、static int buffer_urldecode_internal(buffer *url, int is_query)
        将rul中存放的特殊编码的字符转换成正常的字符。这里的编码是指上面六种编码中的ENCODING_REL_RUL_PART.
    Code

    3、int buffer_path_simplify(buffer * dest, buffer * src);
        删除路径字符串中的"/../","//"和"/./",简化路径,并不是简单的删除。
    Code

    总得来说,buffer的内容比较简单,其他的函数读者可以自行查看。
  • 相关阅读:
    C++中的异常
    Hadoop YARN介绍
    js处理层级数据结构的一些总结
    Python数据结构
    Python的编码风格
    Python流程控制
    java中面试可能会问的问题
    深度学习
    Pescal Triangle Two
    Pascal Triangle
  • 原文地址:https://www.cnblogs.com/kernel_hcy/p/1594134.html
Copyright © 2020-2023  润新知