redis源码浅见之sds

sds是redis项目封装一个基于字符串操作的库,其官方解释为:"SDSLib 2.0 -- A C dynamic strings library"。

官方下载源码后上传至码云Redis,头文件和源文件连接如下:

sds.h

sds.c 

其想法是在保留C语言对字符串操作对同时,还实现了内存管理,节省了使用成本。

实现原理:变长struct。

typedef char *sds;
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[];
};

总共五个结构,其他四个大同小异,sdshdr5没有len和alloc两个成员,仅做访问flag获取类型用。

其他三个的区别在于len和alloc两个成员的类型不一样,对应的buf成员的存储数据量也不一样,在某些机型下sdshdr32和sdshdr64可能是相同的。

为了操作的统一性,每个struct对应一个唯一的flag存储在flags成员的低三位中。

成员解析:

- len:字符串的长度,不包含'\0','\0'由自己维护,用户不需要关心;

- alloc:申请的内存空间大小;

- flags:类型,当前仅使用低三位,后续可能会有扩展,用于判断当前sds对应的header的类型;

- buf:变长部分,存储数据;

 

巧妙之处:

1. 为了保证C语言原生对字符串对操作,buf定义为'char *',创建函数返回的是buf成员对应的内存地址,可直接访问操控,只不过使用typedef重命名为sds。

2. buf和flags类型相同,根据地址排列关系,buf[-1]正好是flags对应的内存空间,通过flags得到struct类型,进而推算出buf对应的header。当header获取到时,所有成员可任意访问。

3. sds定义了一系列的操作函数,考虑到print族函数比较重,结合stdarg.h实现了建议版本的print系列。

 

sds代码的阅读难处有三点:变长strcut、地址转换、define

变长结构:C语言特点,使用方法网上可搜到一大堆;

地址转换:struct的内存布局;

define:sdshdr为数字系列,结合‘##’可拼接出对应的结构名

#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T)));
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))

 

PS:阅读redis源码,特此记忆,望坚持。
 

你可能感兴趣的:(C,Redis)