redis-sds简单字符串(sds.h、sds.c)
作者:互联网
sds简单字符串(sds.h、sds.c)
部分源码解读(5.0.8版本)
定义结构:
/* 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 */c
char buf[];
};
内存结构:
结构体成员变量说明:
len:代表已使用的char数组长度(不包含结尾\0)
alloc:代表char数组总长度(不包含\0)
剩余可使用长度= alloc - len;
使用sds而不使用char*的原因:
1:获取字符串长度为 O(N) 级别的操作 → 因为 C 不保存数组的长度,每次都需要遍历一遍整个数组
2:字符串操作不当拼接容易造成缓冲区溢出/内存泄露
3:C字符串仅能保存文本数据,截止\0 则会进行截取,后边的无法识别
主要api
函数 | 作用 | 时间复杂度 |
---|---|---|
sdsnew | 创建一个包含给定C字符串的SDS | O(N) |
sdsempty | 创建一个空SDS | O(1) |
sdsfree | 释放给定的SDS | O(N) |
sdslen | 返回已使用长度 | O(1) |
sdsavail | 返回未使用长度 | O(1) |
sdsdup | 创建给定SDS副本(copy) | O(N) |
sdscpy | 将给定C字符串复制到SDS里面,覆盖原有的字符串 | O(N) |
sdscmp | 比较两个SDS字符串是否相同 | O(N) |
sdsrange | 保留SDS给定区间的数据,非区间内则被覆盖或清除 | O(N) |
简单字符串的创建
/* Create a new sds string with the content specified by the 'init' pointer
* and 'initlen'.
* If NULL is used for 'init' the string is initialized with zero bytes.
*
* The string is always null-termined (all the sds strings are, always) so
* even if you create an sds string with:
*
* mystring = sdsnewlen("abc",3);
*
* You can print the string with printf() as there is an implicit \0 at the
* end of the string. However the string is binary safe and can contain
* \0 characters in the middle, as the length is stored in the sds header. */
sds sdsnewlen(const void *init, size_t initlen) {
void *sh;
sds s;
//根据字符串长度选择类型确定类型(sdshdr8|sdshdr16|sdshdr32|sdshdr64)
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,后边+1是因为要补'\0'
sh = s_malloc(hdrlen+initlen+1);
if (sh == NULL) return NULL;//申请失败返回NULL
if (!init)
memset(sh, 0, hdrlen+initlen+1); //将sh指针指向的地址内容设置为0
s = (char*)sh+hdrlen;//char buf[]数组指针位置
fp = ((unsigned char*)s)-1;//flag指针
//将*sh 转为sdhdr类型指针,并将len、alloc、flag进行初始化
switch(type) {
case SDS_TYPE_5: {
*fp = type | (initlen << SDS_TYPE_BITS);
break;
}
case SDS_TYPE_8: {
SDS_HDR_VAR(8,s)//struct sdshdr8 *sh = (struct sdshdr8 *)((s)-(sizeof(struct sdshdr8)));;
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;
}
}
// 将字符串拷贝到char buf[]
if (initlen && init)
memcpy(s, init, initlen);
s[initlen] = '\0';//末尾补'\0'
return s;
}
计算字符串长度
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
static inline size_t sdslen(const sds s) { //sds指向buf的位置
unsigned char flags = s[-1];
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;
}
说明:
redis进行简单字符串操作时,传递的是sds实际上是sdshdr->buf,因为sdshdr泛型出了几种不同长度的简单字符串,所以通过flag = sds-1可以确定sdshdr的类型,进而获得对应的sdshdr结构体对象。如果直接传递sdshdr不具备通用性,reids 3.0版本之前仅有sdshdr一种类型,使用泛型的好处则避免了内存的浪费
redis 2.X.X中简单字符串的定义
struct sdshdr {
int len;
int free;
char buf[];
};
static inline size_t sdslen(const sds s) {
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
return sh->len;
}
sds sdsnewlen(const void *init, size_t initlen) {
struct sdshdr *sh;
if (init) {
sh = zmalloc(sizeof(struct sdshdr)+initlen+1);
} else {
sh = zcalloc(sizeof(struct sdshdr)+initlen+1);
}
if (sh == NULL) return NULL;
sh->len = initlen;
sh->free = 0;
if (initlen && init)
memcpy(sh->buf, init, initlen);
sh->buf[initlen] = '\0';
return (char*)sh->buf;
}
标签:__,initlen,SDS,sds,redis,char,sh,字符串 来源: https://www.cnblogs.com/houzishangshu/p/14237917.html