标签:
0. 前言
这里对Redis底层字符串的实现分析,但是看完其实现还没有完整的一个概念,即不太清楚作者为什么要这样子设计,只能窥知一点,需要看完redis如何使用再回头来体会,有不足之处还望告知。
涉及文件:sds.h/sds.c
1. 数据结构:
1 typedef char *sds; 2 3 struct sdshdr { 4 unsigned int len; //buf中已使用的字节数 5 unsigned int free; //buf中未使用的字节数 6 char buf[]; //缓冲区 7 };
这里向外提供的api所返回的类型都是sds类型(字符串),这样的话也能够复用一部分的C字符串函数。
这里采用sdshdr结构,存放了字符串长度信息,保证了二进制数据安全,即不仅可以存放字符串,也可用于存放其它二进制数据
2. API实现:
只提取几个API,该文件完整的注释在GitHud上(用户名:jabnih)
a. sdsnewlen
创建一个sds字符串,其它几个创建API都是基于这个API。
创建时采用一次性分配其所需要的空间,即对于buf不进行再次分配,减少了malloc等的调用,同时在释放的时候也减少free次数
1 //创建一个sds字符串,初始内容为init所指向的内容,buf空间为initlen大小 2 sds sdsnewlen(const void *init, size_t initlen) { 3 struct sdshdr *sh; 4 5 //这里需要注意 6 if (init) { 7 //init不为空,则使用malloc,所申请的空间不会初始化 8 sh = zmalloc(sizeof(struct sdshdr)+initlen+1); 9 } else { 10 //init为空,使用calloc,所申请的空间会被初始化为0 11 sh = zcalloc(sizeof(struct sdshdr)+initlen+1); 12 } 13 14 if (sh == NULL) return NULL; 15 16 sh->len = initlen; 17 sh->free = 0; 18 //这里如果init为NULL,则该buf的内容均为0 19 if (initlen && init) 20 memcpy(sh->buf, init, initlen); 21 22 sh->buf[initlen] = ‘\0‘; 23 24 return (char*)sh->buf; 25 }
b. sdsMakeRoomFor
该API的内存分配策略为:在小于SDS_MAX_PREALLOC(即1M)时,会预分配出多一倍的空间,在大于该阈值时,每次只预分配多SDS_MAX_PREALLOC内存。
1 //保证sds字符串有足够的剩余未使用空间(大于或等于addlen) 2 sds sdsMakeRoomFor(sds s, size_t addlen) { 3 struct sdshdr *sh, *newsh; 4 size_t free = sdsavail(s); 5 size_t len, newlen; 6 7 //其剩余的空间满足addlen大小 8 if (free >= addlen) return s; 9 10 //不满足addlen大小,需要重新分配 11 len = sdslen(s); 12 sh = (void*) (s-(sizeof(struct sdshdr))); 13 //新空间所需使用的大小为当前sds使用的长度加上addlen 14 newlen = (len+addlen); 15 //如果新空间大小比设定的阈值小,则以2倍的增长速度预分配一些空间 16 if (newlen < SDS_MAX_PREALLOC) 17 newlen *= 2; 18 else 19 //比设定阈值大,则只增加PREALLOC预分配大小 20 newlen += SDS_MAX_PREALLOC; 21 //重新分配空间 22 newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1); 23 if (newsh == NULL) return NULL; 24 25 newsh->free = newlen - len; 26 return newsh->buf; 27 }
c. sdsRemoveFreeSpace
1 //去除sds字符串中未使用的空间,一般在内存紧张的时候使用 2 sds sdsRemoveFreeSpace(sds s) { 3 struct sdshdr *sh; 4 5 sh = (void*) (s-(sizeof(struct sdshdr))); 6 sh = zrealloc(sh, sizeof(struct sdshdr)+sh->len+1); 7 sh->free = 0; 8 9 return sh->buf; 10 }
d. sdsclear
1 //清空sds字符串,但是不释放空间 2 void sdsclear(sds s) { 3 struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 4 5 sh->free += sh->len; 6 sh->len = 0; 7 sh->buf[0] = ‘\0‘; 8 }
3. 总结:
1. 二进制数据安全
2. 预分配空间,可以懒惰释放,在内存紧张的时候也可以缩减不需要的内存
3. 使用该API可以实现内存动态扩展(即不需要考虑内存空间是否足够)
4. 边界检查
标签:
原文地址:http://www.cnblogs.com/jabnih/p/4733260.html