全部博文(2759)
分类: C/C++
2013-07-30 23:52:18
原文地址:lighttpd源码阅读笔记(2)----常用结构体 作者:loler_zuan
一、bitset
{size_t *bits;
size_t nbits;}
bitset *bitset_init(size_t nbits);分配空间
void bitset_reset(bitset *set);分配的空间清零
void bitset_free(bitset *set);释放空间
void bitset_clear_bit(bitset *set, size_t pos);pos位设为0
void bitset_set_bit(bitset *set, size_t pos);pos位设为1
int bitset_test_bit(bitset *set, size_t pos); 看pos位是否为1
CHAR_BIT 是一个字节位数
BITSET_BITS是size_t占的位数,BITSET_USED是nbits占用的size_t数,取整
BITSET_WORD 返回pos位在的size_t块
知识点一:
assert宏的用法
assert宏的原型定义在
完成调试后,不必从源代码中删除assert()语句,因为宏NDEBUG有定义时,宏assert()的定义为空
知识点二:
SEGFAULT宏定义自buffer.h。这里面的do-while循环并不是多余的。恒假判断的dowhile保证了循环内容作为一个整体执行一次,防止宏拓展后意外出错
不然 if(flag==1)
SEGFAULT();
这样不论如何abort()都会执行。与初衷不符
二、buffer
struct {
char *ptr;//保存内容,可以保存字符串等不同数据。方便socket发送数据
size_t used;//内容长度,字符串\0也占用一个空间
size_t size;//ptr空间大小} buffer;
typedef struct {
buffer **ptr;
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_init() //分配空间
buffer_init_buffer(buffer *src) //返回一个用src初始化的buffer
buffer_copy_string_buffer(buffer *b, const buffer *src) //复制buffer
buffer_reset //清零
buffer_copy_string_len(buffer *b, const char *s, size_t s_len)//从s复制len个字节到b的字节长
buffer_prepare_copy(buffer *b, size_t size) //若b的空间没有size大,为b的ptr分配msize长度的空间
msize = size+64 - (size % 64);
msize是64的整数倍
buffer_free() 空间释放
buffer_copy_string() //复制字符串到buffer
buffer_prepare_append(buffer *b, size_t size) //如果(已用空间+追加空间>分配空间),在当前存储空间后面追加size长度的内容,同时原存储数据不变(如果小于,说明空间够用,没有追加的必要)
buffer_append_string(buffer *b, const char *s)//buffer b的存储空间追加s字符串
buffer_append_string_rfill(buffer *b, const char *s, size_t maxlen)//追加s字符串中maxlen的长度的内容,如果maxlen比s长度大,补空格
高群凯书上说,这个函数有漏洞,但是没有被调用。buffer_prepare_append(b, maxlen + 1);这一步,如果s_len远大于maxlen,申请到的空间不一定装的下s_len
其实没有,如果s_len大于maxlen,那么s字符串会复制maxlen长度的内容。因为s_len=maxlen
buffer_append_string_len(buffer *b, const char *s, size_t s_len)//作用是在b后面追加len长度的s的子串倒是个函数我感觉有漏洞, 按照linux实现memcpy的方式,如果s_len超过了s的空间范围,仍会访问。比如s是char[10],s_len是100,那就会访问到s[99],明显是越界了
linux实现memcpy
void *memcpy(void *dest, const void *src, size_t count)
{
assert(dest != NULL && src != NULL);
char *tmp = dest;
const char *s = src;
while (count--)
*tmp++ = *s++ ;
return dest;
}
测试:
char a[10000];
char b[10]="hello";
memcpy(a,b,9999);
printf("%s\n",a);
//段错误
buffer_append_memory(buffer *b, const char *s, size_t s_len) //把s内容部分s_len长度的放在b的数据部分后面,如果是字符串,放在\0后面。同样有漏洞,如果s_len远大于s,那么对s的访问越界 。
buffer_copy_memory(buffer *b, const char *s, size_t s_len) //把用s替代b的数据部分 。这个函数调用buffer_append_memory,有漏洞
buffer_append_long_hex(buffer *b, unsigned long value) // 转化为十六进制字符串
LI_ltostr(char *buf, long val) //十进制转为字符串,返回字符串长度
buffer_append_long(buffer *b, long val) //把long型转化为字符串添加在buffer中
buffer_copy_long(buffer *b, long val) //把long转化为字符串覆盖b数据部分
buffer_append_off_t //off_t型十进制,和上面long操作一样
buffer_copy_off_t//同上
int2hex //char字符低4位对应的16进制值
hex2int //16进制字符转化为int
buffer_array* buffer_array_init(void) //buffer数组初始化
reset//置0
free//释放空间
buffer_array_append_get_buffer//如果没有任何空间,追加16个没如果空间全部使用,添加16个
buffer_search_string_len //查找具有该字符串len位的第一个buffer
buffer_is_empty//buffer是否为空
buffer_caseless_compare//对两个字符串做不区分大小写的比较
buffer_is_equal//比较两个buffer数据部分是否相等
buffer_is_equal_string//buffer数据部分和字符串比较
buffer_is_equal_right_len//两个buffer末尾len位内容是否相等
buffer_copy_string_hex(buffer *b, const char *in, size_t in_len) //把in字符串转化为16进制存在b中
buffer_path_simplify(buffer *dest, buffer *src)//把字符串路径转化为等同的简单形式 疑问pre = ('/' << 8) | pre1;//这里移8位不久清0了么,char共8位?
buffer_append_string_encoded(buffer *b, const char *s, size_t s_len, buffer_encoding_t encoding)//s取s_len长度的子串,转化编码,追加在b后面
知识点一:realloc()
先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照 newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域(注意:原来指针是自动 释放,不需要使用free),同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。
知识点二:
#if 0作为注释使用,比/**/方便,因为这个取消注释,之需要把0改为1,而且,还可以嵌套注释
知识点三:学习借鉴
int buffer_append_long_hex(buffer *b, unsigned long value) {
char *buf;
int shift = 0;
unsigned long copy = value;
while (copy) {
copy >>= 4;
shift++;
}
if (shift == 0) //也就是value是0,也需要转化一次
shift++;
if (shift & 0x01)//如果末位是1,也就是奇数,再加一次转化。保证转化的次数为偶数次。比如14==》e。应该是0e
shift++;
buffer_prepare_append(b, shift + 1);
if (b->used == 0)
b->used++;
buf = b->ptr + (b->used - 1);
b->used += shift;
shift <<= 2;//移动4*shift位可以全部转换
while (shift > 0) {
shift -= 4;
*(buf++) = hex_chars[(value >> shift) & 0x0F];//& 0x0F,屏蔽高位,保证每次取后四位
}
*buf = '\0';
return 0;
}
知识点四:
if (hex > 9) {
hex = (hex + '0' - 1) | 0x20;/*大于9,说明不是数字部分,因为ascii中0-9和a不连续,+'0'并取低8位。可能是A或者a
a-A=0x20 。 经过|0x20,大写转化为小写,*/
hex = hex - 'a' + 11;
}
为什么先-1后+11呢,因为
A和a的前一个字符比较特殊
拿@来说
@ 01000000 | 020x → 01100000
01100000-'a'+10=9
这是A为01000001,如果A为01000010,那这里就是要+2了。
使得a和A前15个字符,|0x20后不再是'a'的前15个字符就OK
知识点五:巧妙用法,每次比较四个字符
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;
/* is the alignment correct ? */
if ( ((size_t)al & mask) == 0 &&
//如果低三位是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++) {
int a1 = *a++, b1 = *b++;
if (a1 != b1) {
if (a1 >= 'A' && a1 <= 'Z') a1 |= 32;
if (b1 >= 'A' && b1 <= 'Z') b1 |= 32;
if ((a1 - b1) != 0) return (a1 - b1);
}
}
if (a_len == b_len) return 0;
return (a_len - b_len);
}
知识点五:编码转化
http://www.ruanyifeng.com/blog/2010/02/url_encoding.html
http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
后续的结构体看完了追加....