redis 源码剖析 1 sds

最近开始看redis源码,本来想直接看redis源码设计与实现,这是一本国人写的不错的源码剖析书,介绍了redis的原理,我准备自己来写下redis源码的注释 也是学习

先看sds这个文件 ,sds本质是一个动态字符串但是不是以'\0'结束,这个文件很简单基本看下内存布局就知道作用

struct sdshdr {
    unsigned int len;  //已经用了的长度
    unsigned int free; //还有多少长度可以用
    char buf[];        //数据区
};


这是典型的一个c技巧 buf 是一个不定长数组

内存布局如下

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 = (int)initlen;                                                     WIN_PORT_FIX /* cast (int) */
    sh->free = 0;
    if (initlen && init)
        memcpy(sh->buf, init, initlen);
    sh->buf[initlen] = '\0';
    return (char*)sh->buf;
}


用法如下 mystring = sdsnewlen("abc",3); mystring 指向了数据

函数功能就是创建了一个新的包含init 和initlen长度的动态字符串 假如 initlen大于init那鬼知道会发生什么

比如mystring = sdsnewlen("ab",3);

sds sdsempty(void) {
    return sdsnewlen("",0);
}

char *ret = sdsempty()

内存布局如下

sds sdsdup(const sds s) {
    return sdsnewlen(s, sdslen(s));
}

 复制一份同样的数据到新的字符串

void sdsupdatelen(sds s) {
    struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
    int reallen = (int)strlen(s);                                               WIN_PORT_FIX /* cast (int) */
    sh->free += (sh->len-reallen);
    sh->len = reallen;
}

这个函数功能也很简单 就是按照'\0'进行截断
比如有一个sds s = sdsnew("foobar");
那这时候取他的长度 是6
那强制 s[2] = '\0'取长度还是6 因为取长度是根据len来决定 ,这个函数重新根据设置了len
调用这个函数后再取长度就是2了

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);
    if (newlen < SDS_MAX_PREALLOC)
        newlen *= 2;
    else
        newlen += SDS_MAX_PREALLOC;
    newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1);
    if (newsh == NULL) return NULL;

    newsh->free = (int)(newlen - len);                                          WIN_PORT_FIX /* cast (int) */
    return newsh->buf;
}

这里主要是动态扩展,当新增的长度free不够时候,看需要的内存是不是超过1M,超过直接分配1M + 需要的内存 否则直接翻倍

void sdsIncrLen(sds s, int incr) {
    struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));

    if (incr >= 0)
        assert(sh->free >= (unsigned int)incr);
    else
        assert(sh->len >= (unsigned int)(-incr));
    sh->len += incr;
    sh->free -= incr;
    s[sh->len] = '\0';
}

函数功能很简单, 就是扩大或者缩小对应的使用,并且在使用长度填充'\0' 所以会改变原来的内容
sds sdsgrowzero(sds s, size_t len) {
    struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
    size_t totlen, curlen = sh->len;

    if (len <= curlen) return s;
    s = sdsMakeRoomFor(s,len-curlen);
    if (s == NULL) return NULL;

    /* Make sure added region doesn't contain garbage */
    sh = (void*)(s-(sizeof(struct sdshdr)));
    memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */
    totlen = sh->len+sh->free;
    sh->len = (int)len;                                                         WIN_PORT_FIX /* cast (int) */
    sh->free = (int)(totlen-sh->len);                                           WIN_PORT_FIX /* cast (int) */
    return s;
}

函数功能是 在s后面添加len个0  假如空间不够重新申请内存
sds sdscatlen(sds s, const void *t, size_t len) {
    struct sdshdr *sh;
    size_t curlen = sdslen(s);

    s = sdsMakeRoomFor(s,len);
    if (s == NULL) return NULL;
    sh = (void*) (s-(sizeof(struct sdshdr)));
    memcpy(s+curlen, t, len);
    sh->len = (int)(curlen+len);                                                WIN_PORT_FIX /* cast (int) */
    sh->free = (int)(sh->free-len);                                             WIN_PORT_FIX /* cast (int) */
    s[curlen+len] = '\0';
    return s;
}

和c标准库功能类似 strcat 拼接数据但是不限于字符串 所以可以存储二进制数据

还有一些函数 功能都很简单 这里可以总结下这个文件作用
解决了二进制存储的问题
缓冲区溢出问题 而且不用管理字符串内存 当然需要管理sds 每次返回去的都是数据指针,可以直接当作字符串使用 比如打印只要符合c语言字符串结束符
为了性能考虑 每次分配内存会根据需要内存大小尽量多分配一些 避免增长时候重新分配内存 减少内存分配调用
当不需要内存时候也可以通过sdsRemoveFreeSpace来缩小内存。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值