Redis没有使用c语言的字符串结构,自己设计了一个简单的动态字符串。特点是:修改时大小不足则扩容,大小足够直接使用不缩小。末尾使用‘\0’,与c语言字符串兼容。
sds的源代码在sds.h与sds.c中。
sds的定义
1
2
3
4
5
6
7
|
typedef char *sds;// 兼容C
struct sdshdr {
unsigned int len;// 字符串长度
unsigned int free;// 未分配的空间
char buf[];// 末尾'/0'的C风格字符串
};// SDS的实际结构,兼容char*则返回buf地址
|
SDS这样设计的优点:
- 重用部分C字符串库函数的函数。
- 在常数复杂度的情况下获取字符串长度(以下代码)。
- 杜绝缓冲区溢出,通过获取空余空间函数,来进行处理(sdscat函数)。
- 减少字符串内存的重分配。不足则分配更大的空间,足够也不减少空间,而是记录新的len、free值。
- 二进制兼容。C字符串以空字符结尾,而某些二进制数据中间可能存在空字符。SDS兼容该种数据。
SDS API
获取长度
1
2
3
4
5
6
7
8
9
10
11
12
|
// 获取字符串长度
static inline size_t sdslen(const sds s) {
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
return sh->len;
}
// 获取空余空间
static inline size_t sdsavail(const sds s) {
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
return sh->free;
}
|
SDS创建
有两个函数,一个定长创建,一个是不定长创建。
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
|
sds sdsnewlen(const void *init, size_t initlen) {
struct sdshdr *sh;
if (init) {
//为空则使用malloc
sh = zmalloc(sizeof(struct sdshdr)+initlen+1);
} else {
//不为空使用calloc分配
sh = zcalloc(sizeof(struct sdshdr)+initlen+1);
}
if (sh == NULL) return NULL;// 分配失败处理
//设定sds的参数
sh->len = initlen;
sh->free = 0;
//值的复制
if (initlen && init)
memcpy(sh->buf, init, initlen);
sh->buf[initlen] = '\0';// 尾部结束符
return (char*)sh->buf;
}
// 复制一个char*
sds sdsnew(const char *init) {
size_t initlen = (init == NULL) ? 0 : strlen(init);
return sdsnewlen(init, initlen);
}
// 生成一个空sd
sds sdsempty(void) {
return sdsnewlen("",0);
}
// 复制一个sds
sds sdsdup(const sds s) {
return sdsnewlen(s, sdslen(s));
}
|
sds释放函数
先获取sdshdr的首地址,使用zfree释放。
1
2
3
4
5
|
void sdsfree(sds s) {
if (s == NULL) return;
// 获取真实首地址释放
zfree(s-sizeof(struct sdshdr));
}
|
sds动态空间调整
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
|
// 空间增长
sds sdsMakeRoomFor(sds s, size_t addlen) {
struct sdshdr *sh, *newsh;
size_t free = sdsavail(s);
size_t len, newlen;
if (free >= addlen) return s;// 空间足够直接返回
len = sdslen(s);
sh = (void*) (s-(sizeof(struct sdshdr)));
newlen = (len+addlen);// 新的长度
if (newlen < SDS_MAX_PREALLOC)// 不足1MB直接翻倍分配
newlen *= 2;
else// 超过1MB,多分配1MB空余空间
newlen += SDS_MAX_PREALLOC;
newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1);// 分配空间
if (newsh == NULL) return NULL; // 分配失败
// 设置参数
newsh->free = newlen - len;
return newsh->buf;
}
// 空间的重分配
sds sdsRemoveFreeSpace(sds s) {
struct sdshdr *sh;
sh = (void*) (s-(sizeof(struct sdshdr)));
sh = zrealloc(sh, sizeof(struct sdshdr)+sh->len+1);
sh->free = 0;
return sh->buf;
}
|
sds连接操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
sds sdscatlen(sds s, const void *t, size_t len) {
struct sdshdr *sh;
size_t curlen = sdslen(s); // 获取字符串长度
s = sdsMakeRoomFor(s,len);// 扩展字符串
if (s == NULL) return NULL;
sh = (void*) (s-(sizeof(struct sdshdr)));
memcpy(s+curlen, t, len);// 连接字符串到末尾
sh->len = curlen+len;// 设置长度
sh->free = sh->free-len;
s[curlen+len] = '\0';// 设置尾部
return s;
}
sds sdscat(sds s, const char *t) {
return sdscatlen(s, t, strlen(t));
}
|
sds复制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
sds sdscpylen(sds s, const char *t, size_t len) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
size_t totlen = sh->free+sh->len;
// 空间不足,分配空间
if (totlen < len) {
s = sdsMakeRoomFor(s,len-sh->len);
if (s == NULL) return NULL;
sh = (void*) (s-(sizeof(struct sdshdr)));
totlen = sh->free+sh->len;
}
// 复制
memcpy(s, t, len);
s[len] = '\0';
sh->len = len;
sh->free = totlen-len;
return s;
}
sds sdscpy(sds s, const char *t) {
return sdscpylen(s, t, strlen(t));
}
|
一些其他接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
sds sdscatfmt(sds s, char const *fmt, ...);// 格式化输出
sds sdstrim(sds s, const char *cset); // 去除cset中所含字符
void sdsrange(sds s, int start, int end);// 获取指定区间字符串
void sdsupdatelen(sds s); // 更新字符串长度
void sdsclear(sds s); // 清空字符串
int sdscmp(const sds s1, const sds s2); // 字符串比较
// 依据sep将s分割,返回 一个二维数组
sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count);
// 释放由sdssplitlen函数解析的二维数组
void sdsfreesplitres(sds *tokens, int count);
void sdstolower(sds s); // 转小写
void sdstoupper(sds s); // 转大写
sds sdsfromlonglong(long long value);// ll转sds
sds sdsjoin(char **argv, int argc, char *sep); // 以分隔符连接字符串子数组构成新的字符串
|