字符串内部
Redis 字符串原始实现指南
注意:此文档由 Redis 创建者 Salvatore Sanfilippo 在 Redis 开发早期(约 2010 年)编写。虚拟内存已从 Redis 2.6 版中弃用,因此此文档仅出于历史兴趣而保留。
Redis 字符串的实现包含在 sds.c
中(sds
代表 Simple Dynamic Strings)。该实现作为独立库提供,网址为 https://github.com/antirez/sds。
在 sds.h
中声明的 C 结构 sdshdr
表示一个 Redis 字符串
struct sdshdr {
long len;
long free;
char buf[];
};
buf
字符数组存储实际字符串。
len
字段存储 buf
的长度。这使得获取 Redis 字符串的长度成为一个 O(1) 操作。
free
字段存储可供使用的附加字节数。
len
和 free
字段可以被认为是 buf
字符数组的元数据。
创建 Redis 字符串
在 sds.h
中定义了一个名为 sds
的新数据类型,它是一个字符指针的同义词
typedef char *sds;
在 sds.c
中定义的 sdsnewlen
函数创建了一个新的 Redis 字符串
sds sdsnewlen(const void *init, size_t initlen) {
struct sdshdr *sh;
sh = zmalloc(sizeof(struct sdshdr)+initlen+1);
#ifdef SDS_ABORT_ON_OOM
if (sh == NULL) sdsOomAbort();
#else
if (sh == NULL) return NULL;
#endif
sh->len = initlen;
sh->free = 0;
if (initlen) {
if (init) memcpy(sh->buf, init, initlen);
else memset(sh->buf,0,initlen);
}
sh->buf[initlen] = '\0';
return (char*)sh->buf;
}
记住,Redis 字符串是 struct sdshdr
类型的变量。但是 sdsnewlen
返回一个字符指针!
这是一个技巧,需要一些解释。
假设我使用 sdsnewlen
创建了一个 Redis 字符串,如下所示
sdsnewlen("redis", 5);
这将创建一个新的 struct sdshdr
类型的变量,为 len
和 free
字段以及 buf
字符数组分配内存。
sh = zmalloc(sizeof(struct sdshdr)+initlen+1); // initlen is length of init argument.
在 sdsnewlen
成功创建 Redis 字符串后,结果类似于
-----------
|5|0|redis|
-----------
^ ^
sh sh->buf
sdsnewlen
将 sh->buf
返回给调用者。
如果您需要释放由 sh
指向的 Redis 字符串,该怎么办?
您想要指针 sh
,但您只有指针 sh->buf
。
您可以从 sh->buf
获取指针 sh
吗?
是的。指针运算。从上面的 ASCII 图中注意到,如果您从 sh->buf
中减去两个 long 的大小,您将得到指针 sh
。
碰巧两个 long 的 sizeof
是 struct sdshdr
的大小。
查看 sdslen
函数,看看这个技巧是如何工作的
size_t sdslen(const sds s) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
return sh->len;
}
知道了这个技巧,您可以轻松地浏览 sds.c
中的其余函数。
Redis 字符串实现隐藏在仅接受字符指针的接口后面。Redis 字符串的用户不必关心它是如何实现的,并且可以将 Redis 字符串视为字符指针。