Redis源码学习基本数据结构之动态字符串sds
2019-04-02 本文已影响0人
lixin_karl
一、sds是什么
字符串是Redis中最为常见的数据存储类型,其底层实现是简单动态字符串sds(simple dynamic string),是可以修改的字符串。它的内存管理模式类似于c++中的vector,它采用预分配冗余空间的方式来减少内存的频繁分配。关联源码sds.c/sds.h
typedef char *sds
sds实际上就是一个char指针。它来自于跟它相关的结构体的一部分,其中一共有5个结构体相关根据以下5个值判断属于哪个结构体
#define SDS_TYPE_5 0
#define SDS_TYPE_8 1
#define SDS_TYPE_16 2
#define SDS_TYPE_32 3
#define SDS_TYPE_64 4
其中,SDS_TYPE_5 目前没有用,会用SDS_TYPE_8来代替。它是根据数据的长度来决定使用什么结构体的,对于SDS_TYPE_8 对应的结构体为
struct sdshdr8{
uint8_t len; /*使用了多少 即数据长度 类似vector size()*/
uint8_t alloc; /* 申请了多少内存 类似于vector capacity()*/
unsigned char flags; /* 具体的sdshdr类型 可以节省内存*/
char buf[];/* 实际数据也是sds开始的地址 */
}
二、sds特点
- SDS是在C字符串的基础上进行了一些包装,使得它更符合Redis的使用场景。sds获取长度的时间复杂度为O(1)。
- 减少修改字符串时内存重分配的次数,其内存增加模式类似与c++的vector每次增长两倍但是每次最多增长1024*1024字节。
- SDS对于存储的数据没有任何限制,二进制安全。 普通的c字符串与到'\0'则结束。所以sds是可以存储任意类型的数据的。
三 sds.h/sds.c源码
sds sdsnewlen(const void *init, size_t initlen);//申请内存申请内存的长度为(对应结构体的长度+1+initlen) 最后一个字节会赋值'\0',这点可以保证对普通字符串的兼容
sds sdsnew(const char *init);//根据一个普通字符串在创建sds
sds sdsempty(void);//创建一个空的sds
sds sdsdup(const sds s);//复制一个sds 重新申请内存
void sdsfree(sds s);//释放申请出来的内存
sds sdsgrowzero(sds s, size_t len);//sds动态空间增加到特定的长度,最多申请SDS_MAX_PREALLOC
sds sdscatlen(sds s, const void *t, size_t len);//字符串拼接
sds sdscat(sds s, const char *t);//字符串拼接
sds sdscatsds(sds s, const sds t);//sds拼接
sds sdscpylen(sds s, const char *t, size_t len);//sds复制
sds sdscpy(sds s, const char *t);//sds复制
sds sdscatvprintf(sds s, const char *fmt, va_list ap);//字符串拼接
sds sdscatfmt(sds s, char const *fmt, ...);//字符串拼接
sds sdstrim(sds s, const char *cset);//移除s中是 cset中的字符
void sdsrange(sds s, ssize_t start, ssize_t end);//sds子字符串
void sdsupdatelen(sds s);//字符串实际长度
void sdsclear(sds s);//字符串清理,相关数据赋值为0
int sdscmp(const sds s1, const sds s2);//字符串比较
sds *sdssplitlen(const char *s, ssize_t len, const char *sep, int seplen, int *count);//s中以sep为分隔标志,返回分隔后的sds数组
void sdsfreesplitres(sds *tokens, int count);//将分隔出来的字符串数组释放掉内存
void sdstolower(sds s);//都变成小写字符
void sdstoupper(sds s);//都变成大写字符
sds sdsfromlonglong(long long value);//longlong变成sds
sds sdscatrepr(sds s, const char *p, size_t len);//一些特殊字符的拼接
sds *sdssplitargs(const char *line, int *argc);//将line分割argc个sds数组
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 sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);//字符串连接
/* 底层用户调用api */
sds sdsMakeRoomFor(sds s, size_t addlen);//重新申请内存
void sdsIncrLen(sds s, ssize_t incr);
sds sdsRemoveFreeSpace(sds s);//移除冗余的空间,增加内存的效率
size_t sdsAllocSize(sds s);//返回总的申请的内存
void *sdsAllocPtr(sds s);//返回最初申请的一段内存的起始位置
void *sds_malloc(size_t size);///申请内存
void *sds_realloc(void *ptr, size_t size);//重新申请内存
void sds_free(void *ptr);//释放内存