概述

Redis并没有使用c语言本身的字符串类型,而是自己构建了一种名为 简单动态字符串(simple dynamic string,SDS)的抽象类型,并将 SDS 作为 Redis的默认字符串表示。

数据结构

源码在redis/src/sds.h

Redis3.0底层结构

1
2
3
4
5
6
typedef char *sds;
struct sdshdr {
unsigned int len;
unsigned int free;
char buf[];
};

Redis5.0底层结构,提供了不同数据长度的结构

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

主要有以下属性:

  1. 字符串长度
  2. 字符数组总长度
  3. flag标志位
  4. 字符串

主要方法

判断该类型的数据长度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static inline int sdsHdrSize(char type) {
switch(type&SDS_TYPE_MASK) {
case SDS_TYPE_5:
return sizeof(struct sdshdr5);
case SDS_TYPE_8:
return sizeof(struct sdshdr8);
case SDS_TYPE_16:
return sizeof(struct sdshdr16);
case SDS_TYPE_32:
return sizeof(struct sdshdr32);
case SDS_TYPE_64:
return sizeof(struct sdshdr64);
}
return 0;
}

判断类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static inline char sdsReqType(size_t string_size) {
if (string_size < 1<<5)
return SDS_TYPE_5;
if (string_size < 1<<8)
return SDS_TYPE_8;
if (string_size < 1<<16)
return SDS_TYPE_16;
#if (LONG_MAX == LLONG_MAX)
if (string_size < 1ll<<32)
return SDS_TYPE_32;
return SDS_TYPE_64;
#else
return SDS_TYPE_32;
#endif
}

创建

创建SDS

mystring = sdsnewlen(“abc”,3);

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
sds sdsnewlen(const void *init, size_t initlen) {
void *sh;
sds s;
// 判断类型,空字符串会使用SDS_TYPE_8
char type = sdsReqType(initlen);
/* Empty strings are usually created in order to append. Use type 8
* since type 5 is not good at this. */
if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;
// 判断类型的大小
int hdrlen = sdsHdrSize(type);
unsigned char *fp; /* flags pointer. */
// 分配内存,长度是数据结构长度+字符串长度+1
sh = s_malloc(hdrlen+initlen+1);
if (init==SDS_NOINIT)
init = NULL;
else if (!init) // 将sh所有字节中填充数据0
memset(sh, 0, hdrlen+initlen+1);
if (sh == NULL) return NULL;
// s指向sh中存放数据后的第一个空位
s = (char*)sh+hdrlen;
// fp指向sh中存放数据的最后一位
fp = ((unsigned char*)s)-1;
// 根据类型构建
switch(type) {
case SDS_TYPE_5: {
*fp = type | (initlen << SDS_TYPE_BITS);
break;
}
case SDS_TYPE_8: {
SDS_HDR_VAR(8,s);
sh->len = initlen;
sh->alloc = initlen;
*fp = type;
break;
}
case SDS_TYPE_16: {
SDS_HDR_VAR(16,s);
sh->len = initlen;
sh->alloc = initlen;
*fp = type;
break;
}
case SDS_TYPE_32: {
SDS_HDR_VAR(32,s);
sh->len = initlen;
sh->alloc = initlen;
*fp = type;
break;
}
case SDS_TYPE_64: {
SDS_HDR_VAR(64,s);
sh->len = initlen;
sh->alloc = initlen;
*fp = type;
break;
}
}
// 字符串填充
if (initlen && init)
memcpy(s, init, initlen);
//设置结束位
s[initlen] = '\0';
return s;
}

其他方法

  1. sds sdsnewlen(const void *init, size_t initlen); //用长度为initlen的字符串创建sds
    1. sds sdsempty(void); //创建一个长度为0的sds
    2. sds sdsnew(const char *init); //用null结尾的字符串创建sds
    3. sds sdsdup(const sds s); //拷贝一个sds
    4. void sdsfree(sds s); //释放sds所占的内存空间
    5. void sdsupdatelen(sds s); //更新sds中的len为实际的字符串长度
    6. void sdsclear(sds s); //将sds中的字符串为空串
    7. sds sdsMakeRoomFor(sds s, size_t addlen); //sds字符串所占空间增加addlen个字符(包括free所占的字符)
    8. sds sdsRemoveFreeSpace(sds s); //去除sds中空闲的空间
  2. size_t sdsAllocSize(sds s); //获取sds实际占用空间的大小
  3. void sdsIncrLen(sds s, int incr); //sds实际字符串的长度增加incr
  4. sds sdsgrowzero(sds s, size_t len); //将sds所占的空间增加到len,增加的空间都清零
  5. sds sdscatlen(sds s, const void *t, size_t len); //sds末尾连接一个长度为len的字符串
  6. sds sdscat(sds s, const char *t); //sds末尾连接一个以null结尾的字符串
  7. sds sdscatsds(sds s, const sds t); //sds末尾连接另一个sds
  8. sds sdscpylen(sds s, const char *t, size_t len); //拷贝长度为len的字符串到sds中
  9. sds sdscpy(sds s, const char *t); //拷贝以null结尾的字符串到sds中
  10. sds sdscatvprintf(sds s, const char *fmt, va_list ap); //sds末尾连接一个由可变参数形成的字符串
  11. sds sdscatprintf(sds s, const char *fmt, …); //sds末尾连接一个由可变参数形成的字符串
  12. sds sdstrim(sds s, const char *cset); //去除sds字符串的前后字符,这些字符都是在cset中出现过的
  13. void sdsrange(sds s, int start, int end); //获取sds字符串的一个字串,start和end可以为负数,负数表示从后面往前面索引
  14. void sdstolower(sds s); //将sds字符串中的字符设置为小写
  15. void sdstoupper(sds s); //将sds字符串中的字符设置为大写
  16. int sdscmp(const sds s1, const sds s2); //比较两个字符串的大小
  17. sds sdssplitlen(const char s, int len, const char sep, int seplen, int count); //用字符串sdp分割一个sds为多个sds
  18. void sdsfreesplitres(sds *tokens, int count); //释放由函数sdssplitlen返回的sds数组空间
  19. sds sdsfromlonglong(long long value); //将long long类型的数字转化为一个sds
  20. sds sdscatrepr(sds s, const char *p, size_t len); //sds末尾连接一个长度为len的字符串,并且将其中的不可打印字符显示出来
  21. int is_hex_digit(char c); //判断一个字符释放为16进制数字
  22. int hex_digit_to_int(char c); //将一个16进制数字转化为整数
  23. sds sdssplitargs(const char line, int *argc); //将一行文本分割成多个参数,每个参数可以用类编程语言 REPL格式,如果空格,\n\r\t\0等作为分隔符
  24. sds sdsmapchars(sds s, const char from, const char to, size_t setlen) //将sds中出现在from中的字符替换为to对应的字符
  25. sds sdsjoin(char *argv, int argc, char sep); //将多个字符串用分割符连接起来组成一个sds