標籤:
0. 前言
這裡對Redis底層字串的實現分析,但是看完其實現還沒有完整的一個概念,即不太清楚作者為什麼要這樣子設計,只能窺知一點,需要看完redis如何使用再回頭來體會,有不足之處還望告知。
涉及檔案:sds.h/sds.c
1. 資料結構:
1 typedef char *sds;2 3 struct sdshdr {4 unsigned int len; //buf中已使用的位元組數5 unsigned int free; //buf中未使用的位元組數6 char buf[]; //緩衝區7 };
這裡向外提供的api所返回的類型都是sds類型(字串),這樣的話也能夠複用一部分的C字串函數。
這裡採用sdshdr結構,存放了字串長度資訊,保證了位元據安全,即不僅可以存放字串,也可用於存放其它位元據
2. API實現:
只提取幾個API,該檔案完整的注釋在GitHud上(使用者名稱:jabnih)
a. sdsnewlen
建立一個sds字串,其它幾個建立API都是基於這個API。
建立時採用一次性分配其所需要的空間,即對於buf不進行再次分配,減少了malloc等的調用,同時在釋放的時候也減少free次數
1 //建立一個sds字串,初始內容為init所指向的內容,buf空間為initlen大小 2 sds sdsnewlen(const void *init, size_t initlen) { 3 struct sdshdr *sh; 4 5 //這裡需要注意 6 if (init) { 7 //init不為空白,則使用malloc,所申請的空間不會初始化 8 sh = zmalloc(sizeof(struct sdshdr)+initlen+1); 9 } else {10 //init為空白,使用calloc,所申請的空間會被初始化為011 sh = zcalloc(sizeof(struct sdshdr)+initlen+1);12 }13 14 if (sh == NULL) return NULL;15 16 sh->len = initlen;17 sh->free = 0;18 //這裡如果init為NULL,則該buf的內容均為019 if (initlen && init)20 memcpy(sh->buf, init, initlen);21 22 sh->buf[initlen] = ‘\0‘;23 24 return (char*)sh->buf;25 }
b. sdsMakeRoomFor
該API的記憶體配置策略為:在小於SDS_MAX_PREALLOC(即1M)時,會預分配出多一倍的空間,在大於該閾值時,每次只預分配多SDS_MAX_PREALLOC記憶體。
1 //保證sds字串有足夠的剩餘未使用空間(大於或等於addlen) 2 sds sdsMakeRoomFor(sds s, size_t addlen) { 3 struct sdshdr *sh, *newsh; 4 size_t free = sdsavail(s); 5 size_t len, newlen; 6 7 //其剩餘的空間滿足addlen大小 8 if (free >= addlen) return s; 9 10 //不滿足addlen大小,需要重新分配11 len = sdslen(s);12 sh = (void*) (s-(sizeof(struct sdshdr)));13 //新空間所需使用的大小為當前sds使用的長度加上addlen14 newlen = (len+addlen);15 //如果新空間大小比設定的閾值小,則以2倍的增長速度預分配一些空間16 if (newlen < SDS_MAX_PREALLOC)17 newlen *= 2;18 else19 //比設定閾值大,則只增加PREALLOC預分配大小20 newlen += SDS_MAX_PREALLOC;21 //重新分配空間22 newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1);23 if (newsh == NULL) return NULL;24 25 newsh->free = newlen - len;26 return newsh->buf;27 }
c. sdsRemoveFreeSpace
1 //去除sds字串中未使用的空間,一般在記憶體緊張的時候使用 2 sds sdsRemoveFreeSpace(sds s) { 3 struct sdshdr *sh; 4 5 sh = (void*) (s-(sizeof(struct sdshdr))); 6 sh = zrealloc(sh, sizeof(struct sdshdr)+sh->len+1); 7 sh->free = 0; 8 9 return sh->buf;10 }
d. sdsclear
1 //清空sds字串,但是不釋放空間2 void sdsclear(sds s) {3 struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));4 5 sh->free += sh->len;6 sh->len = 0;7 sh->buf[0] = ‘\0‘;8 }
3. 總結:
1. 位元據安全
2. 預分配空間,可以懶惰釋放,在記憶體緊張的時候也可以縮減不需要的記憶體
3. 使用該API可以實現記憶體動態擴充(即不需要考慮記憶體空間是否足夠)
4. 邊界檢查
Redis學習——SDS字串實現