redis中动态字符串sds相关的文件为:sds.h与sds.c
一、数据结构
redis中定义了自己的数据类型"sds",用于描述 char*,与一些数据结构
1 typedef char *sds;
2
3 /* Note: sdshdr5 is never used, we just access the flags byte directly.
4 * However is here to document the layout of type 5 SDS strings. */
5 struct __attribute__ ((__packed__)) sdshdr5 {
6 unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
7 char buf[];
8 };
9 struct __attribute__ ((__packed__)) sdshdr8 {
10 uint8_t len; /* used */
11 uint8_t alloc; /* excluding the header and null terminator */
12 unsigned char flags; /* 3 lsb of type, 5 unused bits */
13 char buf[];
14 };
15 struct __attribute__ ((__packed__)) sdshdr16 {
16 uint16_t len; /* used */
17 uint16_t alloc; /* excluding the header and null terminator */
18 unsigned char flags; /* 3 lsb of type, 5 unused bits */
19 char buf[];
20 };
21 struct __attribute__ ((__packed__)) sdshdr32 {
22 uint32_t len; /* used */
23 uint32_t alloc; /* excluding the header and null terminator */
24 unsigned char flags; /* 3 lsb of type, 5 unused bits */
25 char buf[];
26 };
27 struct __attribute__ ((__packed__)) sdshdr64 {
28 uint64_t len; /* used */
29 uint64_t alloc; /* excluding the header and null terminator */
30 unsigned char flags; /* 3 lsb of type, 5 unused bits */
31 char buf[];
32 };
定义结构体时,加上了 __attribute__ ((__packed__)) 关键字,用于取消字节对齐,使其按照紧凑排列的方式,占用内存。这样做的目的并不仅仅只是为了节约内存的使用。结构体最后有一个 char buf[],查了资料之后了解到,其只是定义一个数组符号,并没有任何成员,不占用结构体的内存空间,其真实地址紧随结构体之后,可实现变长结构体。由此可以只根据sds字符串的真实地址,取到sds结构体的任意成员变量数据。如flags:
1 void func(const sds s)
2 {
3 unsigned char flags = s[-1];
4 }
这个flags,从源码的注释上看,其低三位用于表示sds类型,高五位是当sds类型为sdshdr5时,表明字符串长度的。对于非sdshdr5的类型,有专门的成员变量描述当前已使用的长度,及总buffer长度。
sds类型:
1 #define SDS_TYPE_5 0
2 #define SDS_TYPE_8 1
3 #define SDS_TYPE_16 2
4 #define SDS_TYPE_32 3
5 #define SDS_TYPE_64 4
sds定义了两个宏,用于获取sds结构体首地址:
1 #define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T)));
2 #define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
由此可见sds结构体的大致结构为
1 /*
2 sdshdr5
3 +--------+----...---+
4 |00011000|abc |
5 +--------+----...---+
6 |flags |buf
7
8 sdshdr8
9 +--------+--------+--------+----...---+
10 |00000011|00000011|00000001|abc |
11 +--------+--------+--------+----...---+
12 |len |alloc |flags |buf
13 */
二、相关操作
2.1 字符串长度
sds的一些常规操作,如获取字符串长度、获取剩余buf长度等,都是其于以上操作,首先根据sds字符串地址获取其flags的值,根据flags低三位判断sds类型,接着使用宏SDS_HDR_VAR或SDS_HDR进行操作。如:
1 #define SDS_TYPE_MASK 7 //0000,0111
2
3 static inline size_t sdslen(const sds s) {
4 //获取flags
5 unsigned char flags = s[-1];
6 //根据flags低三位取类型,根据类型做不同处理
7 switch(flags&SDS_TYPE_MASK) {
8 case SDS_TYPE_5:
9 return SDS_TYPE_5_LEN(flags);
10 case SDS_TYPE_8:
11 return SDS_HDR(8,s)->len;
12 case SDS_TYPE_16:
13 return SDS_HDR(16,s)->len;
14 case SDS_TYPE_32:
15 return SDS_HDR(32,s)->len;
16 case SDS_TYPE_64:
17 return SDS_HDR(64,s)->len;
18 }
19 return 0;
20 }
关于sds结构体中的len与alloc,len表示的是sds字符串的当前长度,alloc表示的是buf的总长度。
2.2 字符串类型
首先是一个根据字符串长度来决定sds类型的方法
1 static inline char sdsReqType(size_t string_size) {
2 if (string_size < 1<<5) //flags高五位最大数字为 1<<5 - 1
3 return SDS_TYPE_5;
4 if (string_size < 1<<8) //uint8_t 最大数字为 1<<8 - 1
5 return SDS_TYPE_8;
6 if (string_size < 1<<16) //uint16_t 最大数字为 1<<16 - 1
7 return SDS_TYPE_16;
8 #if (LONG_MAX == LLONG_MAX) //区分32位/64位系统
9 if (string_size < 1ll<<32)
10 return SDS_TYPE_32;
11 return SDS_TYPE_64;
12 #else
13 return SDS_TYPE_32;
14 #endif
15 }
2.3 字符串创建
创建一个新的sds结构体:
1 sds sdsnewlen(const void *init, size_t initlen) {
2 void *sh;
3 sds s;
4 //根据长度获取sds类型
5 char type = sdsReqType(initlen);
6 /* Empty strings are usually created in order to append. Use type 8
7 * since type 5 is not good at this. */
8 /**
9 * 如果初始长度为0的情况下,并且类型为SDS_TYPE_5,则会被强制转为SDS_TYPE_8
10 */
11 if (type == SDS_TYPE_5 && initlen == 0)
12 type = SDS_TYPE_8;
13
14 //除了buf[]变量其它变量所占的空间
15 int hdrlen = sdsHdrSize(type);
16 unsigned char *fp; /* flags pointer. */
17
18 //sdshdrx(x:5、8、16、32、64)变量所占空间+字符串本身所占空间
19 sh = s_malloc(hdrlen+initlen+1);
20 if (init==SDS_NOINIT)
21 init = NULL;
22 else if (!init)
23 memset(sh, 0, hdrlen+initlen+1);
24 if (sh == NULL) return NULL;
25 //获取buf[]数组符号
26 s = (char*)sh+hdrlen;
27 //得到flags变量
28 fp = ((unsigned char*)s)-1;
29 switch(type) {//根据相应类型设置类型(flag前3位)以及len、alloc
30 case SDS_TYPE_5: {
31 *fp = type | (initlen << SDS_TYPE_BITS);
32 break;
33 }
34 case SDS_TYPE_8: {
35 SDS_HDR_VAR(8,s);
36 sh->len = initlen;
37 sh->alloc = initlen;
38 *fp = type;
39 break;
40 }
41 case SDS_TYPE_16: {
42 SDS_HDR_VAR(16,s);
43 sh->len = initlen;
44 sh->alloc = initlen;
45 *fp = type;
46 break;
47 }
48 case SDS_TYPE_32: {
49 SDS_HDR_VAR(32,s);
50 sh->len = initlen;
51 sh->alloc = initlen;
52 *fp = type;
53 break;
54 }
55 case SDS_TYPE_64: {
56 SDS_HDR_VAR(64,s);
57 sh->len = initlen;
58 sh->alloc = initlen;
59 *fp = type;
60 break;
61 }
62 }
63 if (initlen && init)
64 memcpy(s, init, initlen);//将字符串拷贝到buf[]中
65 //指针指向的字符串节位处理
66 s[initlen] = '