翻redis 源碼的時候,發現有些用法真是很巧妙的,一個是指針變換,一個是內存管理策略,固然後者是有利弊的。 redis
sds.h 就這兩個數據類型,這裏,可以sds 和 sdshdr 相互轉化。對外的接口,參數和返回只有char * 。redis 爲每一個char * 都維護了個數據結構 sdshdr ,防止溢出,又能夠減小內存分配。這裏char * 怎麼跟 sdshdr 關聯上?數組
typedef char *sds; struct sdshdr { long len; long free; char buf[]; };
這裏 sdshdr 是16個字節,buf 是動態數組,計算偏移的時候size 爲0。經過sdshdr 取 buf 地址,強制轉成 char * ,也就是sds ,這個數組轉字符串指針的操做很好理解。當咱們得到sds 的時候,想知道預分配長度,直接向左偏移struct 大小,就是sdshdr的地址,強制轉換類型後,就額能夠取len。數據結構
看源碼 sdslen 函數就能夠看出:函數
size_t sdslen(const sds s) { struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); //先偏移struct大小,強制轉成sdshdr return sh->len; }
第二個有意思的是sds 的內存預分配,在對字符串拼接的時候,有個惰性預分配的操做,每次free 爲0 ,觸發從新拷貝,都將多分配一倍多餘內存,備用。這裏好處是減小內存分配,固然也浪費了內存。對應底層函數以下:指針
static sds sdsMakeRoomFor(sds s, size_t addlen) { struct sdshdr *sh, *newsh; size_t free = sdsavail(s); size_t len, newlen; if (free >= addlen) return s; len = sdslen(s); sh = (void*) (s-(sizeof(struct sdshdr))); newlen = (len+addlen)*2; //內存空間在要從新分配的狀況下,直接加倍 newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1); #ifdef SDS_ABORT_ON_OOM if (newsh == NULL) sdsOomAbort(); #else if (newsh == NULL) return NULL; #endif newsh->free = newlen - len; return newsh->buf; }