sds戳稽,全稱Simple Dynamic Strings,是Redis自定義的一個(gè)字符串類型期升。
typedef char *sds;
看到這你肯定內(nèi)心覺得Redis在逗你惊奇,這不就是一個(gè)字符數(shù)組么,怎么就Simple Dynamic Strings了呢 !沒錯(cuò)播赁,我當(dāng)時(shí)也是這么覺得的颂郎,但是仔細(xì)閱讀源碼后發(fā)現(xiàn)sds并不是一個(gè)人在戰(zhàn)斗,它還有戰(zhàn)友sdshdr容为,sdshdr是個(gè)五胞胎乓序,分別是sdshdr5寺酪,sdshdr8,sdshd16替劈,sdshdr32寄雀,sdshd64。塊頭從小到大陨献。
sdshdr 全稱 Simple Dynamic Strings Header
/* 因?yàn)樯母鷦e人不一樣(內(nèi)部結(jié)構(gòu)不一樣)盒犹,老五(sdshdr5)從來不被使用 */
struct __attribute__ ((__packed__)) sdshdr5 {
unsigned char flags; /* 低三位表示類型, 高五位表示字符串長度 */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; /* 字符串長度*/
uint8_t alloc; /* 分配長度 */
unsigned char flags; /* 低三位表示類型,高五位未使用 */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
uint16_t len; /* 字符串長度*/
uint16_t alloc; /* 分配長度 */
unsigned char flags; /* 低三位表示類型眨业,高五位未使用 */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
uint32_t len; /* 字符串長度*/
uint32_t alloc; /* 分配長度 */
unsigned char flags; /* 低三位表示類型急膀,高五位未使用 */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
uint64_t len; /* 字符串長度*/
uint64_t alloc; /* 分配長度 */
unsigned char flags; /* 低三位表示類型,高五位未使用 */
char buf[];
};
知識(shí)點(diǎn)龄捡!這個(gè)很關(guān)鍵W可!
__attribute__ ((__packed__))
待會(huì)你會(huì)看到如下代碼:
(s)-(sizeof(struct sdshdr##T))) 聘殖、s[-1]晨雳、(char*)s-sdsHdrSize(s[-1])
這些指針之所以可以走位如此風(fēng)騷,都?xì)w功于 __attribute__ ((__packed__))這個(gè)命令的意思是 取消編譯階段的內(nèi)存優(yōu)化對(duì)齊功能.
ps: 關(guān)于內(nèi)存補(bǔ)齊如果之前不知道奸腺,請(qǐng)自行百度餐禁。
所以,該結(jié)構(gòu)在內(nèi)存中的結(jié)構(gòu)如下:
這樣看洋机,之前那些風(fēng)騷的走位就很明了了。
// s減去sdshdr長度 = 指向sdshdr結(jié)構(gòu)體的指針
(s)-(sizeof(struct sdshdr##T))) 洋魂、
// s前一個(gè)位置 = flags
s[-1]
// 與1相同效果
(char*)s-sdsHdrSize(s[-1])
有了上面的基礎(chǔ)绷旗,看sds.c和sds.h里的代碼就已經(jīng)很容易了,我們重點(diǎn)看兩個(gè)函數(shù)
- 創(chuàng)建sds字符串
sds sdsnewlen(const void *init, size_t initlen) {
void *sh;
sds s;
/* 根據(jù)字符串的長度來決定sds的類型 */
char type = sdsReqType(initlen);
/* 老五被歧視了 */
if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;
/* 計(jì)算sdsHeader的長度 */
int hdrlen = sdsHdrSize(type);
/* 對(duì)應(yīng)flags */
unsigned char *fp;
/* 開辟內(nèi)存空間副砍,+1是為了最后放一個(gè)\0衔肢,兼容傳統(tǒng)C語言,入鄉(xiāng)隨俗 */
sh = s_malloc(hdrlen+initlen+1);
if (!init)
memset(sh, 0, hdrlen+initlen+1);
if (sh == NULL) return NULL;
/* 這走位豁翎,指向字符串開始的地方 */
s = (char*)sh+hdrlen;
/* 這走位角骤,到flags了 */
fp = ((unsigned char*)s)-1;
/* 根據(jù)不同的類型,初始化sdsHeader */
switch(type) {
case SDS_TYPE_5: {
*fp = type | (initlen << SDS_TYPE_BITS);
break;
}
case SDS_TYPE_8: {
SDS_HDR_VAR(8,s);
sh->len = initlen;
sh->alloc = initlen;
*fp = type;
break;
}
case SDS_TYPE_16: {
SDS_HDR_VAR(16,s);
sh->len = initlen;
sh->alloc = initlen;
*fp = type;
break;
}
case SDS_TYPE_32: {
SDS_HDR_VAR(32,s);
sh->len = initlen;
sh->alloc = initlen;
*fp = type;
break;
}
case SDS_TYPE_64: {
SDS_HDR_VAR(64,s);
sh->len = initlen;
sh->alloc = initlen;
*fp = type;
break;
}
}
/* 字符串賦值 */
if (initlen && init)
memcpy(s, init, initlen);
s[initlen] = '\0';
return s;
}
- 動(dòng)態(tài)擴(kuò)展sds空間
sds sdsMakeRoomFor(sds s, size_t addlen) {
void *sh, *newsh;
/* avail = alloc-len */
size_t avail = sdsavail(s);
size_t len, newlen;
char type, oldtype = s[-1] & SDS_TYPE_MASK;
int hdrlen;
/* 若剩下的空間足夠心剥,就不需要擴(kuò)了 */
if (avail >= addlen) return s;
len = sdslen(s);
sh = (char*)s-sdsHdrSize(oldtype);
newlen = (len+addlen);
/* Redis認(rèn)為一旦被擴(kuò)容了邦尊,
* 那這個(gè)字符串被再次擴(kuò)容的幾率就很大,所以會(huì)在此基礎(chǔ)上多加一些空間优烧,
* 防止頻繁擴(kuò)容
*/
if (newlen < SDS_MAX_PREALLOC)
newlen *= 2;
else
newlen += SDS_MAX_PREALLOC;
/* 重新計(jì)算type */
type = sdsReqType(newlen);
/* 老五又被歧視了 */
if (type == SDS_TYPE_5) type = SDS_TYPE_8;
hdrlen = sdsHdrSize(type);
if (oldtype==type) {
/* 當(dāng)原類型與新類型一致蝉揍,則在原有基礎(chǔ)是realloc空間即可 */
newsh = s_realloc(sh, hdrlen+newlen+1);
if (newsh == NULL) return NULL;
s = (char*)newsh+hdrlen;
} else {
/* 否則需要重新malloc一整塊空間,然后拷貝 */
newsh = s_malloc(hdrlen+newlen+1);
if (newsh == NULL) return NULL;
memcpy((char*)newsh+hdrlen, s, len+1);
s_free(sh);
s = (char*)newsh+hdrlen;
s[-1] = type;
sdssetlen(s, len);
}
sdssetalloc(s, newlen);
return s;
}