数据库
首页 > 数据库> > redis-sds简单字符串(sds.h、sds.c)

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