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首地址的偏移。