号外号外,新建Redis交流讨论群:332160890,欢迎加入!!
类型介绍
sds是redis里面一个字符串的库,从功能上来说,实际上是封装了str的操作,另外统一了从分配内存到字符串操作到内存释放,因为这里面的字符串都是要放到内存中去的,封装后的库函数更加全面,同时也更加灵活,实际运用中不需要考虑内存等操作内幕,这一点和前面的其他类型很相似,另外,新版本又一次区分了字符串实际使用上长度的类型不同运用的头信息也不同,这一点也是为了尽量减少内存占用所考虑;
代码分析
自定义类型
struct __attribute__ ((__packed__)) sdshdr5 {
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; /* used */
uint8_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
uint16_t len; /* used */
uint16_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
uint32_t len; /* used */
uint32_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
uint64_t len; /* used */
uint64_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
定义变量
代码分解
sdsHdrSize,根据类型获取hdr的结构体大小;
static inline int sdsHdrSize(char type) {
switch(type&SDS_TYPE_MASK) {
case SDS_TYPE_5:
return sizeof(struct sdshdr5);
case SDS_TYPE_8:
return sizeof(struct sdshdr8);
case SDS_TYPE_16:
return sizeof(struct sdshdr16);
case SDS_TYPE_32:
return sizeof(struct sdshdr32);
case SDS_TYPE_64:
return sizeof(struct sdshdr64);
}
return 0;
}
sdsReqType,根据类型匹配对应的sds的类型;
static inline char sdsReqType(size_t string_size) {
if (string_size < 1<<5)
return SDS_TYPE_5;
if (string_size < 1<<8)
return SDS_TYPE_8;
if (string_size < 1<<16)
return SDS_TYPE_16;
if (string_size < 1ll<<32)
return SDS_TYPE_32;
return SDS_TYPE_64;
}
sdsnewlen,根据字符串和初始长度新建一个sds,如果init为空,就是申请一个空内存,也就是len=free=0;
sds sdsnewlen(const void *init, size_t initlen) {
void *sh;
sds s;
char type = sdsReqType(initlen); //根据长度信息获取待分配的类型
/* Empty strings are usually created in order to append. Use type 8
* since type 5 is not good at this. */
if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8; //这里面说的是一般情况下空字符都是用来添加数据的,不适合用type5来做
int hdrlen = sdsHdrSize(type); //根据类型获取待分配类型的长度
unsigned char *fp; /* flags pointer. */
sh = s_malloc(hdrlen+initlen+1); //头信息长度+字符串长度+结尾字符
if (!init)
memset(sh, 0, hdrlen+initlen+1); //如果字符串是空的,置0
if (sh == NULL) return NULL;
s = (char*)sh+hdrlen;
fp = ((unsigned char*)s)-1;
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; //返回字符串的首地址
}
sdsempty,创建一个空的字符串;
sds sdsempty(void) {
return sdsnewlen("",0);
}
sdsnew,根据字符串新建一个sds;
sds sdsnew(const char *init) {
size_t initlen = (init == NULL) ? 0 : strlen(init);
return sdsnewlen(init, initlen);
}
sdsdup,复制一个sds,sds其实就是一个字符串,所以跟sdsnew差不多;
sds sdsdup(const sds s) {
return sdsnewlen(s, sdslen(s));
}
sdsfree,释放一个sds,注意释放的时候是需要释放头信息占用空间的;
void sdsfree(sds s) {
if (s == NULL) return;
s_free((char*)s-sdsHdrSize(s[-1]));
}
sdsclear,清空sds,这里面只是将结尾提前,并不是真的释放内存;
void sdsclear(sds s) {
sdssetlen(s, 0);
s[0] = '\0';
}
sdsMakeRoomFor,为sds增加内存空间;
sds sdsMakeRoomFor(sds s, size_t addlen) {
void *sh, *newsh;
size_t avail = sdsavail(s); //获取可用空间
size_t len, newlen;
char type, oldtype = s[-1] & SDS_TYPE_MASK;
int hdrlen;
/* Return ASAP if there is enough space left. */
if (avail >= addlen) return s; //如果可用空间大于所需添加的内存空间,无需扩张,直接返回继续使用该s即可
len = sdslen(s); //获取当前sds占用的实际长度
sh = (char*)s-sdsHdrSize(oldtype); //跳到hdr首地址
newlen = (len+addlen); //现在新的需要长度
if (newlen < SDS_MAX_PREALLOC) //如果没有到最大预分配,乘以2
newlen *= 2;
else
newlen += SDS_MAX_PREALLOC;
type = sdsReqType(newlen); //获取该长度所需的hdr结构类型
/* Don't use type 5: the user is appending to the string and type 5 is
* not able to remember empty space, so sdsMakeRoomFor() must be called
* at every appending operation. */
if (type == SDS_TYPE_5) type = SDS_TYPE_8; //同新建sds里面一样,因为是扩张,不需要TYPE_5
hdrlen = sdsHdrSize(type); //获取hdr结构长度
if (oldtype==type) { //如果类型相同,因为头信息长度相同,可以直接realloc
newsh = s_realloc(sh, hdrlen+newlen+1);
if (newsh == NULL) return NULL;
s = (char*)newsh+hdrlen;
} else {
/* Since the header size changes, need to move the string forward,
* and can't use realloc */
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;
}
sdsRemoveFreeSpace,释放多余的空间;
sds sdsRemoveFreeSpace(sds s) {
void *sh, *newsh;
char type, oldtype = s[-1] & SDS_TYPE_MASK;
int hdrlen;
size_t len = sdslen(s); //获取字符串真实长度
sh = (char*)s-sdsHdrSize(oldtype); //跳到信息头首地址
type = sdsReqType(len); //获取长度对应的hdr类型
hdrlen = sdsHdrSize(type); //根据类型获得头部长度
if (oldtype==type) { //类型一样,头部占用空间一致,realloc即可
newsh = s_realloc(sh, hdrlen+len+1);
if (newsh == NULL) return NULL;
s = (char*)newsh+hdrlen; //字符串首地址
} else {
newsh = s_malloc(hdrlen+len+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, len);
return s;
}
sdsAllocSize,返回sds分配的所有内存大小;
size_t sdsAllocSize(sds s) {
size_t alloc = sdsalloc(s);
return sdsHdrSize(s[-1])+alloc+1;
}
sdsIncrLen,扩展sds占用的内存长度,主要与makeroomfor配合使用;
void sdsIncrLen(sds s, int incr) {
unsigned char flags = s[-1];
size_t len;
switch(flags&SDS_TYPE_MASK) {
case SDS_TYPE_5: {
unsigned char *fp = ((unsigned char*)s)-1;
unsigned char oldlen = SDS_TYPE_5_LEN(flags);
assert((incr > 0 && oldlen+incr < 32) || (incr < 0 && oldlen >= (unsigned int)(-incr)));
*fp = SDS_TYPE_5 | ((oldlen+incr) << SDS_TYPE_BITS);
len = oldlen+incr;
break;
}
case SDS_TYPE_8: {
SDS_HDR_VAR(8,s);
assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));
len = (sh->len += incr);
break;
}
case SDS_TYPE_16: {
SDS_HDR_VAR(16,s);
assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));
len = (sh->len += incr);
break;
}
case SDS_TYPE_32: {
SDS_HDR_VAR(32,s);
assert((incr >= 0 && sh->alloc-sh->len >= (unsigned int)incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));
len = (sh->len += incr);
break;
}
case SDS_TYPE_64: {
SDS_HDR_VAR(64,s);
assert((incr >= 0 && sh->alloc-sh->len >= (uint64_t)incr) || (incr < 0 && sh->len >= (uint64_t)(-incr)));
len = (sh->len += incr);
break;
}
default: len = 0; /* Just to avoid compilation warnings. */
}
s[len] = '\0';
}
sdsgrowzero,将sds的长度增加到len,增加的区域补0;
sds sdsgrowzero(sds s, size_t len) {
size_t curlen = sdslen(s); //获取当前实际占用大小
if (len <= curlen) return s; //如果设定的长度比实际长度小,则不管,直接返回即可
s = sdsMakeRoomFor(s,len-curlen); //否则,重新申请内存
if (s == NULL) return NULL;
/* Make sure added region doesn't contain garbage */
memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */ //新增部分置0
sdssetlen(s, len);
return s;
}
sdscatlen,给sds接上t指向的字符串;
sds sdscatlen(sds s, const void *t, size_t len) {
size_t curlen = sdslen(s);
s = sdsMakeRoomFor(s,len);
if (s == NULL) return NULL;
memcpy(s+curlen, t, len);
sdssetlen(s, curlen+len);
s[curlen+len] = '\0';
return s;
}
sdscpylen,将t复制到sds中;
sds sdscpylen(sds s, const char *t, size_t len) {
if (sdsalloc(s) < len) { //如果当前sds中内存不够
s = sdsMakeRoomFor(s,len-sdslen(s)); //重新申请内容
if (s == NULL) return NULL;
}
memcpy(s, t, len); //拷贝
s[len] = '\0';
sdssetlen(s, len);
return s;
}
sdstrim,删除sds首尾包含cset中的字符;
sds sdstrim(sds s, const char *cset) {
char *start, *end, *sp, *ep;
size_t len;
sp = start = s;
ep = end = s+sdslen(s)-1;
while(sp <= end && strchr(cset, *sp)) sp++;
while(ep > sp && strchr(cset, *ep)) ep--;
len = (sp > ep) ? 0 : ((ep-sp)+1);
if (s != sp) memmove(s, sp, len);
s[len] = '\0';
sdssetlen(s,len);
return s;
}
sdsrange,截取sds从start到end;
void sdsrange(sds s, int start, int end) {
size_t newlen, len = sdslen(s);
if (len == 0) return;
if (start < 0) {
start = len+start;
if (start < 0) start = 0;
}
if (end < 0) {
end = len+end;
if (end < 0) end = 0;
}
newlen = (start > end) ? 0 : (end-start)+1;
if (newlen != 0) {
if (start >= (signed)len) {
newlen = 0;
} else if (end >= (signed)len) {
end = len-1;
newlen = (start > end) ? 0 : (end-start)+1;
}
} else {
start = 0;
}
if (start && newlen) memmove(s, s+start, newlen);
s[newlen] = 0;
sdssetlen(s,newlen);
}