redis版本 3.0.0
1. sds值结构(sds.h)
struct sdshdr {
//buf中已使用的长度unsigned int len;
//buf中未使用的长度unsigned int free;
//字节数组 用于保存字符串, 最后一位是 \0char buf[];
};
2.常数获取字符串的长度
c语言获取字符串的长度时,需要遍历字符串,复杂度为O(n), SDS的len属性记录了长度,所以获取字符串的
复杂度为O(1)
3.杜绝缓冲区溢出
c语言使用strcat函数时,需要先为变量分配足够的内存,才可以正常复制,如果忘记分配或者分配不足,就会产生缓冲区溢出
但SDS的空间分配策略可以杜绝溢出的课程。当对sds进行操作时,会先检查sds的空间是否满足需求,不满足会自动将空扩展至
需要的大小
4.减少修改字符串时带来的内存重新分配次数
c语言中每次增长或者缩短c字符串,需要先对c字符串数组进行内存分配
sds采用空间预分配,sds在分配所需的空间时,还会分配额外的未使用空间,如果满足下次的使用,那么下次就不用再分配内存
5.惰性释放
对sds保存的字符串进行删减时,不会立即重新分配内存,而是使用free记录下来,方便下次使用(也要修改len)
6.二进制安全
c语言字符串除了末尾外,不能包含空字符,否则在程序读入空字符后认为字符串已经结束,导致后面的字符串丢失。因此c语言的字符串只能保存文本,不能保存图片、音频等
但是sds就没有这个问题,sds使用len属性判断字符串是否结束
7.兼容部分c字符串函数
sds的buf属性以空字符结尾,就可以复用c语言的部分函数
8.sds拼接字符串函数源码
sds sdscat(sds s, const char *t) {
//调用了另外一个函数,额外传入了 t的长度return sdscatlen(s, t, strlen(t));
}
sds sdscatlen(sds s, const void *t, size_t len) {struct sdshdr *sh;//获取lensize_t curlen = sdslen(s);//是否扩展空间 满足 两个字符串的拼接s = sdsMakeRoomFor(s,len);if (s == NULL) return NULL;//s是指向buf开始位置的指针, 减去结构体长度 可获取到 sdshdr的指针sh = (void*) (s-(sizeof(struct sdshdr)));//内容复制memcpy(s+curlen, t, len);//长度变为拼接后的长度sh->len = curlen+len;//减少free长度sh->free = sh->free-len;//末尾拼接空字符串,兼容部分c语言函数s[curlen+len] = '\0';return s;
}
//空间分配
sdsMakeRoomFor
sds sdsMakeRoomFor(sds s, size_t addlen) {struct sdshdr *sh, *newsh;//free长度size_t free = sdsavail(s);size_t len, newlen;//如果free的长度满足新增的 直接返回if (free >= addlen) return s;//获取已经使用的lenlen = sdslen(s);sh = (void*) (s-(sizeof(struct sdshdr)));//需要的长度newlen = (len+addlen);//如果小于1m,newLen直接分配2倍 需要16kb分配32kbif (newlen < SDS_MAX_PREALLOC)newlen *= 2;elsenewlen += SDS_MAX_PREALLOC; //超过1m 额外分配1m,减少后续的内存分配newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1);if (newsh == NULL) return NULL;
//重置新的freenewsh->free = newlen - len;return newsh->buf;
}