redis源码分析(2)-- 基本数据结构sds

 

1、sds格式api

sds header定义:函数

1 struct sdshdr {
2     unsigned int len;
3     unsigned int free;
4     char buf[];
5 };

sizeof(struct sdshdr)= 2*sizeof(unsigned int), char buf[]等价于char buf[0], 仅对编译器有效,并不实际占用存储。优化

其中len是使用的长度,free是剩余的长度,再加上一个C语言中的'\0'结束符spa

sizeof(buf) = len + free + 1, 格式以下:指针

 

2、sds基本操做code

 一、建立sds对象对象

 1 sds sdsnewlen(const void *init, size_t initlen) {
 2     struct sdshdr *sh;
 3 
 4     if (init) {
 5         sh = zmalloc(sizeof(struct sdshdr)+initlen+1);
 6     } else {
 7         sh = zcalloc(sizeof(struct sdshdr)+initlen+1);
 8     }
 9     if (sh == NULL) return NULL;
10     sh->len = initlen;
11     sh->free = 0;
12     if (initlen && init)
13         memcpy(sh->buf, init, initlen);
14     sh->buf[initlen] = '\0';
15     return (char*)sh->buf;
16 }
17 
18 /* Create a new sds string starting from a null terminated C string. */
19 sds sdsnew(const char *init) {
20     size_t initlen = (init == NULL) ? 0 : strlen(init);
21     return sdsnewlen(init, initlen);
22 }

sdsnew根据输入C字符串init,建立新的sds对象,typedef char *sds; sds即char *类型。blog

sds s = sdsnew("abc");
当执行上面这行代码,会在堆内存上面开辟一段连续的空间,具体的空间大小是11个字节的空间。
最前面4个字节存的是len ,也就是sds的长度
其次4个字节存的是free,剩余的空间。
最后才是 3个才是存的是咱们实际的char的信息
也就是每建立一个sds,都会执行上面的操做,都会申请额外的8个字节才存len和free信息。

既然是一段连续的空间,那么只要已知一个指针,那么就能拿出struct结构里面的任何数据。
仍是用上面那个例子
sds s = sdsnew("abc");

这个时候s实际上存的是buf首个char数据的地址。也就是说向前 移8个字节就能到struct sdshdr的len的地址(准确的说是int len的首地址)。8个字节恰好就是struct sdshdr占的空间字节的大小。
因此下面这个是成立的内存

static inline size_t sdslen(const sds s) {
    struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));//把buf的首地址向前移动8个,也是到了len
    return sh->len;
}

s - (sizeof(struct sdshdr)) 表示将指针向前移动到 struct sdshdr 的起点,从而得出一个指向 sdshdr 结构的指针:    
字符串

sds中函数比较简单,这里不作一一介绍,感兴趣能够直接阅读sds.c中的源码。

3、sds特色

一、能够经过O(1)获取字符串长度,直接返回sds->len便可,C字符串用strlen须要O(N)

二、杜绝缓冲区溢出。一样由于 sds已经记录长度信息

三、空间预分配和惰性空间释放

sds中空间预分配相关函数:

 1 sds sdsMakeRoomFor(sds s, size_t addlen) {
 2     struct sdshdr *sh, *newsh;
 3     size_t free = sdsavail(s);
 4     size_t len, newlen;
 5 
 6     if (free >= addlen) return s; // 须要增长的长度小于free,直接返回,避免了频繁内存申请  7     len = sdslen(s);
 8     sh = (void*) (s-(sizeof(struct sdshdr)));
 9     newlen = (len+addlen);
10     if (newlen < SDS_MAX_PREALLOC) // 总长度(len+addlen)小于SDS_MAX_PREALLOC(1MB), 新申请长度扩大为2*newlen 11         newlen *= 2;
12     else
13         newlen += SDS_MAX_PREALLOC; // 不然,只比需求量增长1MB 14     newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1);
15     if (newsh == NULL) return NULL;
16 
17     newsh->free = newlen - len;
18     return newsh->buf;
19 }

惰性空间释放用于优化字符串的缩短操做,不用每次缩短都释放内存,仅修改free和len的大小便可。

例如使用sdstrim操做删除sds中的的特定字符,最终只要修改free/len大小,不涉及free操做。

真正须要释放sds中的free空间,能够调用:

1 sds sdsRemoveFreeSpace(sds s) {
2     struct sdshdr *sh;
3 
4     sh = (void*) (s-(sizeof(struct sdshdr)));
5     sh = zrealloc(sh, sizeof(struct sdshdr)+sh->len+1);
6     sh->free = 0;
7     return sh->buf;
8 }

经过realloc来从新分配一块不包含free的空间,原空间realloc会自动释放。

 

四、sds主要api信息

相关文章
相关标签/搜索