Redis源码分析:数据结构之简单动态字符串(SDS)

Redis源码分析:数据结构之简单动态字符串(SDS)

本节的分析基于redis-6.0.0

内容在 src/sds.h 和 src/sds.c 中

C语言中的字符串是以空字符(\0)结尾的字符数组,被称为 C 字符串,如果字符串中间混入了空字符,就会产生截断的后果,这就是非二进制安全。而且。C字符串无法自动扩容。Redis实现了一个可以自动扩容且二进制安全的字符串,叫做简单动态字符串(simple dynamic string),简称 SDS。SDS在Redis中广泛使用,意义重大。在不需要修改字符串值的地方,仍在继续使用C字符串。

SDS的结构

早先的SDS结构的定义很直观,如下:

struct sdshdr {
    // 记录 buf 数组中已使用字节的数量
    // 等于 SDS 所保存字符串的长度
    int len;
    
    // 记录 buf 数组中未使用字节的数量
    int free;
    
    // 字节数组,用于保存字符串
    char buf[];
};

注意,保存空字符的1字节空间不计算在 SDS 的 len 属性里面,并且为空字符分配额外的1字节空间,以及添加空字符到字符串末尾等操作,都是由 SDS 函数自动完成的。

buf 是个柔性数组,只能放在结构体的末尾。

redis-6.0.0中的设计是这样的:对于不同长度的字符串,Redis底层使用了不同的SDS结构体。

sdshdr5、sdshdr8、sdshdr16、sdshdr32、sdshdr64,定义为:

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[];
};

len:表示 buf中已占用字节数。

alloc:表示buf中已分配字节数,不同于free,记录的是为buf分配的总长度。

flags:标识当前结构体的类型,低3位用作标识位,高5位预留。

buf:柔性数组,存储数据的地方。

通过对buf指针减一,能方便定位到flags。

相关API的实现

*sds sdsnewlen(const void init, size_t initlen) ;

创建SDS

实现方式:

sds sdsnewlen(const void *init, size_t initlen) {
    void *sh;
    sds s;
    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. */

    sh = s_malloc(hdrlen+initlen+1);
    if (sh == NULL) return NULL;
    if (init==SDS_NOINIT)
        init = NULL;
    else if (!init)
        memset(sh, 0, hdrlen+initlen+1);
    s = (char*)sh+hdrlen;
    fp = ((unsigned char*)s)-1;
    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 = 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;
        }
    }
    if (initlen && init)
        memcpy(s, init, initlen);
    s[initlen] = '\0';
    return s;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值