redis源码剖析-字符串
redis实现了自己的字符串结构。在文件sds.h/dsd.c中定义。redis中的字符串叫sds(simple dynamic string)。
sds实质是char*:
typedef char *sds;
sds通过sdsnewlen()函数来创建,sds sdsnewlen(const void *init, size_t initlen)。该函数内部会创建一个sdshdr的结构,返回值sds,即char *,该结构定义如下:
struct sdshdr
{
int len;
int free;
char buf[];
};
其中len存储当前字符串的长度,free存储该结构体剩余可存储字节数,buf存储字符串值。sdsnewlen()函数在创建sdshdr后,会返回buf的地址(sdshdr->buf)。
sdsnewlen()函数创建了字符串,并返回字符串地址sds,要使用sdshdr中的len和free,则需要获取sdshdr结构体的地址。如何根据获得的sds得到sdshdr结构体的地址呢?
redis中参考了linux内核关于通用list(list_head)的实现机制,实现方法如下:
struct sdshdr *sh = (void*) (sds-(sizeof(struct sdshdr)));
sds是返回的字符串地址,即sdshdr->buf,用sds的地址减去其在结构体中的偏移,即可得到sdshdr的地址。由于buf在sdshdr结构体的最后,所以其偏移就是sizeof(len)+sizeof(free),该偏移恰好是sizeof(struct sdshdr)。
如果buf在sdshdr中的位置是任意的,如何根据buf的地址获取sdshdr的地址呢?实现如下:
假设现在sdshdr声明如下:
struct sdshdr
{
int len;
char buf[];
int free;
};
则sdshdr地址如下获取:
int offset_buf = (int)((struct sdshdr *)0)->buf;
struct sdshdr *sh =(struct sdshdr *)( (int)sdsbuf - offset_buf);
其中sdsbuf是调用sdsnewlen()返回的buf地址。即 sds sdsbuf = sdsnewlen(...);
((struct sdshdr *)0)->buf 表示当结构体sdshdr在地址0时,buf相对于sdshdr首地址的偏移。