redis代码字符串阅读第一部分

本文深入探讨了Redis中SDS字符串的数据结构实现,解析了sdshdr64结构体的内部布局及其动态内存分配机制,详细说明了如何通过操作SDS来高效管理字符串长度和空间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

redis代码字符串阅读

今天看了些字符串的实现,感觉有点违反之前的认知

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

这段代码里面长度为0的char数组buf的作用是什么?我尝试把char buff[] 放到flags之前,发现编译器报了个错,说不能定义一个长度为0的数组。查完发现是一个trick,如果把空数组放在对象的最末尾,那么在申请内存时,可以这么操作:

auto a = (sdshdr64 *)malloc(sizeof(sdshdr64 )+ alloc);
for( int i = 0 ; i < alloc ; ++ i)
{
   a->buf[i] = i;
}

这么做的话,整个struct的长度就可以控制,动态分配sdshdr64 中buf的大小。

也就是说sdshdr64 的内存布局是这样的:

0-7                    len
8-15                  alloc
16                     flags
17-alloc+17           buf

然后再来看看sdshdr字符串相关的操作,以长度为例:

#define char* sds
static inline size_t sdslen(const sds s) {
    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;
}

太复杂了 把宏展开 截取SDS_TYPE_64部分是这样的:

static inline size_t sdslen(const sds s) {
    unsigned char flags = s[-1];
    switch(flags&SDS_TYPE_MASK) {
        case SDS_TYPE_64:
        	  return ((sdshdr64*)(s-sizeof(sdshdr64)))->len;
    }
    return 0;
}

按照逻辑反推的话,s[-1]是flags,那么s的指向应该是sdshdr64->buff, 加了__attribute__ ((packed)) 这个属性,强制不要进行对齐,这时sizeof(struct sdshdr64)的长度为17,刚好就是对象内存开始的地方。

了解这些后那些直接操作sds的就很直观了,一个经常用的函数:

sds sdsMakeRoomFor(sds s, size_t addlen) {
    void *sh, *newsh;
    size_t avail = sdsavail(s);
    size_t len, newlen;
    char type, oldtype = s[-1] & SDS_TYPE_MASK;
    int hdrlen;

    /* Return ASAP if there is enough space left. */
    if (avail >= addlen) return s;

    len = sdslen(s);
    sh = (char*)s-sdsHdrSize(oldtype);
    newlen = (len+addlen);
    if (newlen < SDS_MAX_PREALLOC)
        newlen *= 2;
    else
        newlen += SDS_MAX_PREALLOC;

    type = sdsReqType(newlen);

    if (type == SDS_TYPE_5) type = SDS_TYPE_8;

    hdrlen = sdsHdrSize(type);
    if (oldtype==type) {
        newsh = s_realloc(sh, hdrlen+newlen+1);
        if (newsh == NULL) return NULL;
        s = (char*)newsh+hdrlen;
    } else {
        /* Since the header size changes, need to move the string forward,
         * and can't use realloc */
        newsh = s_malloc(hdrlen+newlen+1);
        if (newsh == NULL) return NULL;
        memcpy((char*)newsh+hdrlen, s, len+1);
        s_free(sh);
        s = (char*)newsh+hdrlen;
        s[-1] = type;
        sdssetlen(s, len);
    }
    sdssetalloc(s, newlen);
    return s;
}

无非是判断要新加的大小与旧数组大小的关系,决定类型要不要换,分配新的内存(扩大两倍)返回出去。

后面又看了下ad_list相关代码,跟上学时链表的课后作业一样,很基础,没啥好说的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值