redis sds 实现的几个巧妙的想法

    翻redis 源码的时候,发现有些用法真是很巧妙的,一个是指针变换,一个是内存管理策略,当然后者是有利弊的。    

    sds.h 就这两个数据类型,这里,能够sds 和 sdshdr 相互转化。对外的接口,参数和返回只有char * 。redis 为每个char * 都维护了个数据结构 sdshdr ,防止溢出,又可以减少内存分配。这里char * 怎么跟 sdshdr 关联上?

typedef char *sds;

struct sdshdr {
    long len;
    long free;
    char buf[];
};

    这里 sdshdr 是16个字节,buf 是动态数组,计算偏移的时候size 为0。通过sdshdr 取 buf 地址,强制转成 char * ,也就是sds ,这个数组转字符串指针的操作很好理解。当我们获得sds 的时候,想知道预分配长度,直接向左偏移struct 大小,就是sdshdr的地址,强制转换类型后,就额可以取len。

    看源码 sdslen 函数就可以看出:

size_t sdslen(const sds s) {
    struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); //先偏移struct大小,强制转成sdshdr
    return sh->len;
}

    第二个有意思的是sds 的内存预分配,在对字符串拼接的时候,有个惰性预分配的操作,每次free 为0 ,触发重新拷贝,都将多分配一倍多余内存,备用。这里好处是减少内存分配,当然也浪费了内存。对应底层函数如下:

static sds sdsMakeRoomFor(sds s, size_t addlen) {
    struct sdshdr *sh, *newsh;
    size_t free = sdsavail(s);
    size_t len, newlen;

    if (free >= addlen) return s;
    len = sdslen(s);
    sh = (void*) (s-(sizeof(struct sdshdr)));
    newlen = (len+addlen)*2; //内存空间在要重新分配的情况下,直接加倍
    newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1);
#ifdef SDS_ABORT_ON_OOM
    if (newsh == NULL) sdsOomAbort();
#else
    if (newsh == NULL) return NULL;
#endif

    newsh->free = newlen - len;
    return newsh->buf;
}

   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值