Simple Dynamic String (SDS)
SDS是在redis中用于表示字符串的数据结构,相比C语言中的原生字符串而言,其最大的区别就是多封装了整数变量用于记录当前字符串的长度(包含已保存的字符数量和可用的空余空间),这样做的好处有以下几点:
- 二进制安全
SDS利用自身结构体中的len变量来界定字符串的结尾,而C字符串以'\0'字符标志结尾;当然,显而易见的好处是可以用更短的时间获得字符串的长度,这里就不谈了。但同样重要的是,这样的变化使得在SDS中可以存储'\0'字符数据而不用担心系统会对其误判为字符串结尾了。可以这样说,因为len变量的存在,在SDS的存储空间中,可以存储任何的二进制数据,而且是安全的,但C字符串就不可以,至少它不能存储'\0'字符。在对应的redis源码中的注释也对其有提及:
/* Create a new sds string with the content specified by the 'init' pointer
* and 'initlen'.
* If NULL is used for 'init' the string is initialized with zero bytes.
* If SDS_NOINIT is used, the buffer is left uninitialized;
*
* The string is always null-termined (all the sds strings are, always) so
* even if you create an sds string with:
*
* mystring = sdsnewlen("abc",3);
*
* You can print the string with printf() as there is an implicit \0 at the
* end of the string. However the string is binary safe and can contain
* \0 characters in the middle, as the length is stored in the sds header. */
sds sdsnewlen(const void *init, size_t initlen) { ...
阅读源码时的一些笔记
// SDS在内部就是直接使用其buf属性来进行引用,因此sds就代表buf,也就是char*的别名;(这也是SDS和C字符串兼容的体现)
// 不同的地方就在于SDS还有它的head部分,也就是包括len, alloc, flags这些变量;
// 由于设计成packed压缩排布形式(编译器不会执行对齐优化),所以可以直接用sds变量的负偏移下标去访问head;
// 比如说tmp是一个sds类型的变量,那么tmp[-1]就代表SDS的flag属性
typedef char *sds; // 指向sdshdr的buf变量
/* ************************************************ *
some other code...
* ************************************************ */
#define SDS_TYPE_5 0
#define SDS_TYPE_8 1
#define SDS_TYPE_16 2
#define SDS_TYPE_32 3
#define SDS_TYPE_64 4
#define SDS_TYPE_MASK 7
#define SDS_TYPE_BITS 3
#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)))) // 利用s计算出sdshdr的首地址;注意,sizeof并不会计算buf的内存占用
#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)
static inline size_t sdslen(const sds s) { // 当前sds已经保存的字符数量
// 注意此处使用负数作为下标;因为sdshdr是packed,各属性之间压缩排布,而s指向buf属性,因此s[-1]指向flags属性
unsigned char flags = s[-1];
switch(flags&SDS_TYPE_MASK) {
case SDS_TYPE_5:
return SDS_TYPE_5_LEN(flags);
case SDS_TYPE_8:
return SDS_HDR(8,s)->len;
case SDS_TYPE_16:
return SDS_HDR(16,s)->len;
case SDS_TYPE_32:
return SDS_HDR(32,s)->len;
case SDS_TYPE_64:
return SDS_HDR(64,s)->len;
}
return 0;
}
待补充...
网友评论