Redis源码(2):简单动态字符串SDS
概述
Redis并没有使用c语言本身的字符串类型,而是自己构建了一种名为 简单动态字符串(simple dynamic string,SDS)的抽象类型,并将 SDS 作为 Redis的默认字符串表示。
数据结构
源码在redis/src/sds.h
Redis3.0底层结构1
2
3
4
5
6typedef char *sds;
struct sdshdr {
unsigned int len;
unsigned int free;
char buf[];
};
Redis5.0底层结构,提供了不同数据长度的结构
1 | typedef char *sds; |
主要有以下属性:
- 字符串长度
- 字符数组总长度
- flag标志位
- 字符串
主要方法
判断该类型的数据长度
1 | static inline int sdsHdrSize(char type) { |
判断类型
1 | static inline char sdsReqType(size_t string_size) { |
创建
创建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;
}
其他方法
- sds sdsnewlen(const void *init, size_t initlen); //用长度为initlen的字符串创建sds
- sds sdsempty(void); //创建一个长度为0的sds
- sds sdsnew(const char *init); //用null结尾的字符串创建sds
- sds sdsdup(const sds s); //拷贝一个sds
- void sdsfree(sds s); //释放sds所占的内存空间
- void sdsupdatelen(sds s); //更新sds中的len为实际的字符串长度
- void sdsclear(sds s); //将sds中的字符串为空串
- sds sdsMakeRoomFor(sds s, size_t addlen); //sds字符串所占空间增加addlen个字符(包括free所占的字符)
- sds sdsRemoveFreeSpace(sds s); //去除sds中空闲的空间
- size_t sdsAllocSize(sds s); //获取sds实际占用空间的大小
- void sdsIncrLen(sds s, int incr); //sds实际字符串的长度增加incr
- sds sdsgrowzero(sds s, size_t len); //将sds所占的空间增加到len,增加的空间都清零
- sds sdscatlen(sds s, const void *t, size_t len); //sds末尾连接一个长度为len的字符串
- sds sdscat(sds s, const char *t); //sds末尾连接一个以null结尾的字符串
- sds sdscatsds(sds s, const sds t); //sds末尾连接另一个sds
- sds sdscpylen(sds s, const char *t, size_t len); //拷贝长度为len的字符串到sds中
- sds sdscpy(sds s, const char *t); //拷贝以null结尾的字符串到sds中
- sds sdscatvprintf(sds s, const char *fmt, va_list ap); //sds末尾连接一个由可变参数形成的字符串
- sds sdscatprintf(sds s, const char *fmt, …); //sds末尾连接一个由可变参数形成的字符串
- sds sdstrim(sds s, const char *cset); //去除sds字符串的前后字符,这些字符都是在cset中出现过的
- void sdsrange(sds s, int start, int end); //获取sds字符串的一个字串,start和end可以为负数,负数表示从后面往前面索引
- void sdstolower(sds s); //将sds字符串中的字符设置为小写
- void sdstoupper(sds s); //将sds字符串中的字符设置为大写
- int sdscmp(const sds s1, const sds s2); //比较两个字符串的大小
- sds sdssplitlen(const char s, int len, const char sep, int seplen, int count); //用字符串sdp分割一个sds为多个sds
- void sdsfreesplitres(sds *tokens, int count); //释放由函数sdssplitlen返回的sds数组空间
- sds sdsfromlonglong(long long value); //将long long类型的数字转化为一个sds
- sds sdscatrepr(sds s, const char *p, size_t len); //sds末尾连接一个长度为len的字符串,并且将其中的不可打印字符显示出来
- int is_hex_digit(char c); //判断一个字符释放为16进制数字
- int hex_digit_to_int(char c); //将一个16进制数字转化为整数
- sds sdssplitargs(const char line, int *argc); //将一行文本分割成多个参数,每个参数可以用类编程语言 REPL格式,如果空格,\n\r\t\0等作为分隔符
- sds sdsmapchars(sds s, const char from, const char to, size_t setlen) //将sds中出现在from中的字符替换为to对应的字符
- sds sdsjoin(char *argv, int argc, char sep); //将多个字符串用分割符连接起来组成一个sds