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

  在web服务器中,通常要设计很多字符串的处理。比如客户端请求的 URI地址、发送的 query参数、post 提交的数据等等都是一串字符。因此,提供对字符串的灵活高效的处理,对lighttpd的效率至关重要。
  在lighttpd中,buffer提供了对字符串的处理。在buffer.h中,有如下的数据结构定义:
  //定义buffer

typedef struct 
{
    char *ptr;     //指向存储空间,一个字符串组
    size_t used;     //buffer中数据的长度
    size_t size;     //buffer的长度
} buffer;

  上面的结构体定义了lighttpd中,对字符串处理的基本结构。其具体含义如上。
Code

//定义buffer数组
typedef struct 
{
    buffer **ptr;     //buffer指针数组
    size_t used;     //buffer数组中数据的个数
    size_t size;     //buffer数组的大小
} buffer_array;
/*
 * 这个比较有意思
 */
typedef struct 
{
    char *ptr;
    size_t offset;    /* input-pointer */
    size_t used;    /* output-pointer */
    size_t size;
}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中。

Code
int buffer_append_string(buffer *b, const char *s)
{
    size_t s_len;
    if (!s || !b) return -1;
    s_len = strlen(s);
    buffer_prepare_append(b, s_len + 1);
    /*
     * 如果buffer中原来有数据(字符串),那么最后一个字符是NULL,
     * 在复制的时候,要覆盖这个字符。
      * 但当buffer为空时,就不需要覆盖NULL字符,因此,需要加一,
     * 以便和有数据的情况下处理相同。
     */
    if (b->used == 0)
        b->used++;
    //覆盖原来数据最后一个字符NULL,同时,也将s中的NULL复制到b中。
    memcpy(b->ptr + b->used - 1, s, s_len + 1);
    b->used += s_len;
    return 0;
}

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进制的问题。代码如下:

Code
static const char hex_chars[] = "0123456789abcdef";
int buffer_append_long_hex(buffer *b, unsigned long value) 
{
    char *buf;
    int shift = 0;
    unsigned long copy = value;
    //计算十六进制表示的value的长度
    while (copy) 
    {
        copy >>= 4;
        shift++;
    }
    if (shift == 0)
        shift++;
    /*
     * 保证追加的字符串为偶数位。
     * 如若不是偶数位,则在最前面加一个'0'.
     */
    if (shift & 0x01)
        shift++;
    buffer_prepare_append(b, shift + 1);//最后一个'\0'
    if (b->used == 0)
        b->used++;
    //buf指向开始存放的位置
    buf = b->ptr + (b->used - 1);
    b->used += shift;
    /*
     * 每四位一组,转化value为十六进制形式的字符串
     */
    shift <<= 2;
    while (shift > 0) 
    {
        shift -= 4;
        *(buf++) = hex_chars[(value >> shift) & 0x0F];
    }
    *buf = '\0';
    return 0;
}

20、int buffer_append_long(buffer * b, long val);
  将val以字符串的形式追加大b中。

  下面的宏定义使用来处理off_t和long类型。
  如果long和off_t相同,则用处理long的函数来处理off_t。如果不相同,则另行定义off_t的处理函数。

Code
#if defined(SIZEOF_LONG) && (SIZEOF_LONG == SIZEOF_OFF_T)
#define buffer_copy_off_t(x, y)        buffer_copy_long(x, y)
#define buffer_append_off_t(x, y)    buffer_append_long(x, y)
#else
int buffer_copy_off_t(buffer * b, off_t val);
int buffer_append_off_t(buffer * b, off_t val);
#endif

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,忽略大小写。

Code
/** 
* simple-assumption:
* most parts are equal and doing a case conversion needs time
* 假设比较的部分相同的较多且大小写转换需要时间。 
*/
int buffer_caseless_compare(const char *a, size_t a_len, const char *b, size_t b_len) 
{
        size_t ndx = 0, max_ndx;
        size_t *al, *bl;
        size_t mask = sizeof(*al) - 1;
        al = (size_t *)a;
        bl = (size_t *)b;
                /* 一开始,将字符串数组转化成size_t类型的数组,通过比较size_t类型来比较是否相同 */
        /* libc的字符串比较函数也使用了相同的技巧,可有效的加快比较速度 */
        /* 检查a1和b1的位置是否对齐(size_t的类型长度的倍数?) ? */
        if ( ((size_t)al & mask) == 0 &&
                     ((size_t)bl & mask) == 0 ) 
        {
            /* 确定比较的长度 */
            max_ndx = ((a_len < b_len) ? a_len : b_len) & ~mask;

            for (; ndx < max_ndx; ndx += sizeof(*al)) 
                       {
                if (*al != *bl) break;
                al++; bl++;
            }
        }
        /* 相同的部分比较完毕 */
        /* 开始比较字符串,并忽略大小写 */
        a = (char *)al;
        b = (char *)bl;
        max_ndx = ((a_len < b_len) ? a_len : b_len);
        for (; ndx < max_ndx; ndx++) 
               {
            char a1 = *a++, b1 = *b++;
            /*
                'A'的二进制表示为0100 0001,'a'的二进制表示为0110 0001,
                大写字母比小写字母的ASCII值小了32。
                通过或上一个32,可以使所有的字母全部转换成大写字母。
            */
            if (a1 != b1) 
                       {
                if ((a1 >= 'A' && a1 <= 'Z') && (b1 >= 'a' && b1 <= 'z'))
                    a1 |= 32;
                else if ((a1 >= 'a' && a1 <= 'z') && (b1 >= 'A' && b1 <= 'Z'))
                    b1 |= 32;
                if ((a1 - b1) != 0) return (a1 - b1);
            }
        }
        /* all chars are the same, and the length match too。 they are the same */
        if (a_len == b_len) return 0;
        /* if a is shorter then b, then b is larger */
        return (a_len - b_len);
}

本文章由 http://www.wifidog.pro/2015/04/15/wifidog%E8%AE%A4%E8%AF%81lighttpd-%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86-1.html 整理编辑,转载请注明出处

标签: wifidog认证, wifidog源码