sds,全稱Simple Dynamic Strings,是Redis自定義的一個字串型別,
typedef char *sds;
看到這你肯定內心覺得Redis在逗你,這不就是一個字符陣列么,怎么就Simple Dynamic Strings了呢 !沒錯,我當時也是這么覺得的,但是仔細閱讀原始碼后發現sds并不是一個人在戰斗,它還有戰友sdshdr,sdshdr是個五胞胎,分別是sdshdr5,sdshdr8,sdshd16,sdshdr32,sdshd64,塊頭從小到大,
sdshdr 全稱 Simple Dynamic Strings Header
/* 因為生的跟別人不一樣(內部結構不一樣),老五(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[];
};
知識點!這個很關鍵!!
__attribute__ ((__packed__))
待會你會看到如下代碼:
(s)-(sizeof(struct sdshdr##T))) 、s[-1]、(char*)s-sdsHdrSize(s[-1])
這些指標之所以可以走位如此風騷,都歸功于 __attribute__ ((__packed__))這個命令的意思是 取消編譯階段的記憶體優化對齊功能.
ps: 關于記憶體補齊如果之前不知道,請自行百度,
所以,該結構在記憶體中的結構如下:

這樣看,之前那些風騷的走位就很明了了,
// s減去sdshdr長度 = 指向sdshdr結構體的指標
(s)-(sizeof(struct sdshdr##T))) 、
// s前一個位置 = flags
s[-1]
// 與1相同效果
(char*)s-sdsHdrSize(s[-1])
有了上面的基礎,看sds.c和sds.h里的代碼就已經很容易了,我們重點看兩個函式
- 創建sds字串
sds sdsnewlen(const void *init, size_t initlen) {
void *sh;
sds s;
/* 根據字串的長度來決定sds的型別 */
char type = sdsReqType(initlen);
/* 老五被歧視了 */
if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;
/* 計算sdsHeader的長度 */
int hdrlen = sdsHdrSize(type);
/* 對應flags */
unsigned char *fp;
/* 開辟記憶體空間,+1是為了最后放一個\0,兼容傳統C語言,入鄉隨俗 */
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;
/* 根據不同的型別,初始化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;
}
- 動態擴展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;
/* 若剩下的空間足夠,就不需要擴了 */
if (avail >= addlen) return s;
len = sdslen(s);
sh = (char*)s-sdsHdrSize(oldtype);
newlen = (len+addlen);
/* Redis認為一旦被擴容了,
* 那這個字串被再次擴容的幾率就很大,所以會在此基礎上多加一些空間,
* 防止頻繁擴容
*/
if (newlen < SDS_MAX_PREALLOC)
newlen *= 2;
else
newlen += SDS_MAX_PREALLOC;
/* 重新計算type */
type = sdsReqType(newlen);
/* 老五又被歧視了 */
if (type == SDS_TYPE_5) type = SDS_TYPE_8;
hdrlen = sdsHdrSize(type);
if (oldtype==type) {
/* 當原型別與新型別一致,則在原有基礎是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;
}
關注公眾號:java寶典
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/263188.html
標籤:Java
上一篇:HTTP/1.1報文詳解
下一篇:bean的生命周期

