redis sds 實現的幾個巧妙的想法

    翻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;
}
相關文章
相關標籤/搜索