文章目录
- 前言
- 代码位置
- 核心类型
- SDS结构
- 获取sds字符串的元数据的宏
- 获取字符串长度
- 重新设置sds长度
- 创建字符串
- 感悟
- 最后
前言
Redis中实现了sds(simple dynamic string)这种字符串,它比c语言标准库的char*字符串更加实用
代码位置
src/sdc.h
src/sdc.c
核心类型
// 底层字符数组
typedef char *sds;
// 不同大小的sds的元数据和底层字符数组
/* Note: sdshdr5 is never used, we just access the flags byte directly.
* However is here to document the layout of type 5 SDS strings. */
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[];
};
SDS结构
类型别名sds实际就是底层的字符数组
获取sds字符串的元数据的宏
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
获取字符串长度
static inline size_t sdslen(const sds s) {
// 根据偏移获取sds类型
unsigned char flags = s[-1];
// 根据相应sds类型获取sds对应的元数据,最后获取长度
switch(flags&SDS_TYPE_MASK) {
case SDS_TYPE_5:
return SDS_TYPE_5_LEN(flags);
case SDS_TYPE_8:
return SDS_HDR(8,s)->len;
case SDS_TYPE_16:
return SDS_HDR(16,s)->len;
case SDS_TYPE_32:
return SDS_HDR(32,s)->len;
case SDS_TYPE_64:
return SDS_HDR(64,s)->len;
}
return 0;
}
重新设置sds长度
// newlen: sds新长度
static inline void sdssetlen(sds s, size_t newlen) {
// 根据偏移获取sds类型
unsigned char flags = s[-1];
// 根据相应sds类型获取sds对应的元数据,然后设置其长度
switch(flags&SDS_TYPE_MASK) {
case SDS_TYPE_5:
{
unsigned char *fp = ((unsigned char*)s)-1;
*fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
}
break;
case SDS_TYPE_8:
SDS_HDR(8,s)->len = newlen;
break;
case SDS_TYPE_16:
SDS_HDR(16,s)->len = newlen;
break;
case SDS_TYPE_32:
SDS_HDR(32,s)->len = newlen;
break;
case SDS_TYPE_64:
SDS_HDR(64,s)->len = newlen;
break;
}
}
创建字符串
// initlen: 初始化的长度
sds _sdsnewlen(const void *init, size_t initlen, int trymalloc) {
//sds指针
void *sh;
// sds变量
sds s;
// 判定sds的类型
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. */
size_t usable;
assert(initlen + hdrlen + 1 > initlen); /* Catch size_t overflow */
// 分配内存
sh = trymalloc?
s_trymalloc_usable(hdrlen+initlen+1, &usable) :
s_malloc_usable(hdrlen+initlen+1, &usable);
if (sh == NULL) return NULL;
if (init==SDS_NOINIT)
init = NULL;
else if (!init)
memset(sh, 0, hdrlen+initlen+1);
// 指向sh底层的字符数组
s = (char*)sh+hdrlen;
fp = ((unsigned char*)s)-1;
usable = usable-hdrlen-1;
if (usable > sdsTypeMaxSize(type))
usable = sdsTypeMaxSize(type);
// 根据类型进行相应元数据的设置
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 = usable;
*fp = type;
break;
}
case SDS_TYPE_16: {
SDS_HDR_VAR(16,s);
sh->len = initlen;
sh->alloc = usable;
*fp = type;
break;
}
case SDS_TYPE_32: {
SDS_HDR_VAR(32,s);
sh->len = initlen;
sh->alloc = usable;
*fp = type;
break;
}
case SDS_TYPE_64: {
SDS_HDR_VAR(64,s);
sh->len = initlen;
sh->alloc = usable;
*fp = type;
break;
}
}
if (initlen && init)
// 拷贝指定长度的字符串给s
memcpy(s, init, initlen);
// 设置结束符
s[initlen] = '\0';
// 返回底层字符数组
return s;
}
// 创建指定长度的字符串
sds sdsnewlen(const void *init, size_t initlen) {
return _sdsnewlen(init, initlen, 0);
}
感悟
- 相比于c语言char*,sds更加易用,可以通过元数据获取信息,无需像char*每次获取长度时进行遍历
- sds可以表示含有“\0”的数据,因为sds是通过长度来界定字符串的,而不是像char*通过"\0"来确定字符串
- sds使用不同类型来表示不同大小的字符串,不同类型的sds使用attribute ((packed))的紧凑型内存布局来节省内存
最后
我是醉墨居士,我会继续分享Redis源码相关内容😊