- 初看源码做一个记录,打算按照如何阅读redis源码这里的结构慢慢看下去。有兴趣的初学者也可以先看看这篇文章。
- sds简介
sds是redis中最基础的也是最简单的一个数据类型,字符串数据类型。该数据类型的所有函数API都以C字符指针操作为基础,但是由于封装了长度和剩余可用长度,使得和单纯的char*相比,具有以下几点好处:
1.对于频繁的获取字符串长度操作,可以在O(1)时间内完成
2.可以类比STL中vector的实现,对于一个sds结构有实际字符串长度,还有一个free是未使用空间的长度,但对字符串进行修改时,例如追加字符串时,只要追加的长度不大于free值那么就可以直接增加无需重新分配内存,当追加长度大于free时,那么就会根据已有的分配机制去进行一个内存的重新分配。总而言之,减少了大大减少了重新分配的次数。
3.因为显示存储了len,所以是二进制安全的。
- sds实现细节 ``` /*这里之所以使用char* 的别名sds,我认识是为了区分单纯的char*和sdshdr, *当函数api的参数为char*时就代表是一个单纯的char*,当函数参数使用sds类型时, *就说明该指针之前的地址内存有len和free两个值可以使用*/ typedef char *sds; struct sdshdr { unsigned int len; unsigned int free; char buf[]; };
static inline size_t sdslen(const sds s) {
struct sdshdr sh = (void)(s-(sizeof(struct sdshdr)));
return sh->len;
}
static inline size_t sdsavail(const sds s) {
struct sdshdr sh = (void)(s-(sizeof(struct sdshdr)));
return sh->free;
}
<h6> - sds常用api
/用void指针和一个长度来初始化一个sds字符串,如果该指针里面有一个
*字符串就用该字符串初始化,否则就用0来初始化,第一次初始化的时候不
预留空间。同时因为制定了长度,所以即使中间有\0也不影响/
sds sdsnewlen(const void *init, size_t initlen) {
struct sdshdr *sh;
if (init) {
sh = zmalloc(sizeof(struct sdshdr)+initlen+1); //不初始化内存
} else {
sh = zcalloc(sizeof(struct sdshdr)+initlen+1); //初始化内存为0
}
if (sh == NULL) return NULL;
sh->len = initlen;
sh->free = 0;
if (initlen && init)
memcpy(sh->buf, init, initlen);
sh->buf[initlen] = '\0';
return (char*)sh->buf; //返回buf开始的指针 sds是char*别名
}
//创建一个空字符串
sds sdsempty(void) {
return sdsnewlen("",0);
}
//用一个char*创建一个sds
sds sdsnew(const char *init) {
size_t initlen = (init == NULL) ? 0 : strlen(init);
return sdsnewlen(init, initlen);
}
//复制拷贝操作
sds sdsdup(const sds s) {
return sdsnewlen(s, sdslen(s));
}
//释放一个sds
void sdsfree(sds s) {
if (s == NULL) return;
zfree(s-sizeof(struct sdshdr)); //为了从头开始释放,释放掉len和free两个int值
}
//不释放内存情况下清空字符串
void sdsclear(sds s) {
struct sdshdr sh = (void) (s-(sizeof(struct sdshdr))); //每次这一步都是为了将一个字符串指针转换为一个结构体指针,为了取出头部数据(len和free进行更改)
sh->free += sh->len;
sh->len = 0;
sh->buf[0] = ‘\0’;
}
后续还有很多操作函数都比较简单,就不一一介绍,下面给出sds的内存分配机制实现。
<h6> - sds内存扩展
/*类似于vector容器中增加可用容量操作,如果增加的值本身就比原先可用容量小,
那么直接返回即可,如果增加值大于free,则将free+addlen之和直接翻倍返回。
若翻倍之前时若超过最大可用容量,就不翻倍,只加固定的一部分。/
#define SDS_MAX_PREALLOC (10241024)
sds sdsMakeRoomFor(sds s, size_t addlen) {
struct sdshdr *sh, *newsh;
size_t free = sdsavail(s);
size_t len, newlen;
if (free >= addlen) return s;
len = sdslen(s);
sh = (void*) (s-(sizeof(struct sdshdr)));
newlen = (len+addlen);
if (newlen < SDS_MAX_PREALLOC)
newlen *= 2;
else
newlen += SDS_MAX_PREALLOC;
newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1);
if (newsh == NULL) return NULL;
newsh->free = newlen - len;
return newsh->buf;
}