SDS介绍

Redis自己构建了一种名为简单动态字符串(simple dynamic string, SDS) 的抽象类型,并将其作为Redis的默认字符串表示。

SDS定义

在 Redis7.0 中 定义的代码位于 src/sds.h 中,定义如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/* Note: sdshdr5 is never used, we just access the flags byte directly.
* However is here to document the layout of type 5 SDS strings. */
struct __attribute__ ((__packed__)) sdshdr5 {
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
char buf[]; // 柔性数组
};
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[]; // 柔性数组
};
struct __attribute__ ((__packed__)) sdshdr16 {
uint16_t len; /* used */
uint16_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[]; // 柔性数组
};
struct __attribute__ ((__packed__)) sdshdr32 {
uint32_t len; /* used */
uint32_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[]; // 柔性数组
};
struct __attribute__ ((__packed__)) sdshdr64 {
uint64_t len; /* used */
uint64_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[]; // 柔性数组
};

根据字符串长度不同,用来存放它的sds类型也不同。

其中 len 表示使用的字符串长度,alloc 表示分配的总空间的长度,flags 表示当前结构体类型,因为只有五种类型,所以只使用低三位,有如下枚举值。

1
2
3
4
5
#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

结构前加上了 __attribute__ ((__packed__)) 取消编译器的内存对齐[1]。从图中可以看出,不同的数据类型有不同的对齐方式,flagsbuf 存在填充数据,如果没有取消内存对齐, (char*)buf - 1 无法直接获取数据类型的 flags

SDS的内存分配方式详见 1.8 内存分配。

接口定义

字符串属性接口

1
2
3
4
5
6
7
8
9
10
// len 属性相关
static inline size_t sdslen(const sds s) ;
static inline size_t sdsavail(const sds s);
static inline void sdssetlen(sds s, size_t newlen) ;
static inline void sdsinclen(sds s, size_t inc);

// alloc 属性相关
/* sdsalloc() = sdsavail() + sdslen() */
static inline size_t sdsalloc(const sds s);
static inline void sdssetalloc(sds s, size_t newlen);

sdslen 接口

sdslen 函数返回字符串的当前长度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static inline size_t sdslen(const sds s) {
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;
}

sdstypedef char* sds; 定义。可以看到,sds 实际上就是指向 sds 结构体的 buf 属性的指针。由于取消了内存对齐,flagsbuf 中不含有填充数据,(char *)buf - 1 就是 flags的值。根据 flags 的值判断字符串

1
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))

sdsavail 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static inline size_t sdsavail(const sds s) {
unsigned char flags = s[-1];
switch(flags&SDS_TYPE_MASK) {
case SDS_TYPE_5: {
return 0;
}
case SDS_TYPE_8: {
SDS_HDR_VAR(8,s);
return sh->alloc - sh->len;
}
case SDS_TYPE_16: {
SDS_HDR_VAR(16,s);
return sh->alloc - sh->len;
}
case SDS_TYPE_32: {
SDS_HDR_VAR(32,s);
return sh->alloc - sh->len;
}
case SDS_TYPE_64: {
SDS_HDR_VAR(64,s);
return sh->alloc - sh->len;
}
}
return 0;
}

sdssetlen 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static inline void sdssetlen(sds s, size_t newlen) {
unsigned char flags = s[-1];
switch(flags&SDS_TYPE_MASK) {
case SDS_TYPE_5:
{
unsigned char *fp = ((unsigned char*)s)-1;
*fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
}
break;
case SDS_TYPE_8:
SDS_HDR(8,s)->len = newlen;
break;
case SDS_TYPE_16:
SDS_HDR(16,s)->len = newlen;
break;
case SDS_TYPE_32:
SDS_HDR(32,s)->len = newlen;
break;
case SDS_TYPE_64:
SDS_HDR(64,s)->len = newlen;
break;
}
}

sdsinclen 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static inline void sdsinclen(sds s, size_t inc) {
unsigned char flags = s[-1];
switch(flags&SDS_TYPE_MASK) {
case SDS_TYPE_5:
{
unsigned char *fp = ((unsigned char*)s)-1;
unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc;
*fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
}
break;
case SDS_TYPE_8:
SDS_HDR(8,s)->len += inc;
break;
case SDS_TYPE_16:
SDS_HDR(16,s)->len += inc;
break;
case SDS_TYPE_32:
SDS_HDR(32,s)->len += inc;
break;
case SDS_TYPE_64:
SDS_HDR(64,s)->len += inc;
break;
}
}

参考链接

  1. c - Redis 源码解读——sds_个人文章 - SegmentFault 思否
  2. redis源码解读(一):基础数据结构之SDS_czrzchao的博客-CSDN博客

TODO:

定义##操作符


  1. 如果没有预定义对齐值,那么结构体或者类的自身对齐值等于其成员中自身对齐值最大的那个值。 ↩︎