处理字符串时如何选择buffer的大小和分配方式 - 看看 redis 是怎么做的

最近在阅读 redis 代码, 觉得 如何阅读 Redis 源码 提供了的阅读顺序还不错, 因为之前有读过 lua 代码, 也有一点经验, 感觉作者建议的顺序还是比较合理的. 所以先从 redis 的内部字符串的实现开始看起, 在阅读过程中看到一个我个人认为很赞的技巧. 先贴代码:

(不得不说, redis 的代码注释真是详尽到了极致 … lua的注释很本没法比啊)

sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
    va_list cpy;
    char staticbuf[1024], *buf = staticbuf, *t;
    size_t buflen = strlen(fmt)*2;

    /* We try to start using a static buffer for speed.
     * If not possible we revert to heap allocation. */
    if (buflen > sizeof(staticbuf)) {
        buf = zmalloc(buflen);
        if (buf == NULL) return NULL;
    } else {
        buflen = sizeof(staticbuf);
    }

    /* Try with buffers two times bigger every time we fail to
     * fit the string in the current buffer size. */
    while(1) {
        buf[buflen-2] = '\0';
        va_copy(cpy,ap);
        vsnprintf(buf, buflen, fmt, cpy);
        va_end(cpy);
        if (buf[buflen-2] != '\0') {
            if (buf != staticbuf) zfree(buf);
            buflen *= 2;
            buf = zmalloc(buflen);
            if (buf == NULL) return NULL;
            continue;
        }
        break;
    }

    /* Finally concat the obtained string to the SDS string and return it. */
    t = sdscat(s, buf);
    if (buf != staticbuf) zfree(buf);
    return t;
}

sds 是 redis 的内部字符串类型, 其实就是 char * 的 typedef, 区别在其存取方式上, 这里把它当成 char * 就可以了.

几乎从函数名我们就能知道这个函数功能是将 字符串 s 和 格式化后的字符串 拼接在一起, 并返回这个新的字符串.

要实现这个功能不难, 但有一个问题需要考虑, 因为 ‘格式化的字符串’ 的长度你是不知道的, 所以我们需要用到 buffer, 但 buffer 应该选多大, 是直接放在函数栈上还是放在堆上? 这是个问题.

redis 采用的办法是:

  1. 先在栈上开一个 1024 字节 staticbuf 数组;
  2. 估计一下可能的 ‘格式化字符串’ 的大小 ( strlen(fmt) *2 );
  3. 如果 ‘格式化字符串’ 长度大于 staticbuf 数组长度, 那么就用 zmalloc (可以看作 malloc) 在堆上开一块相应大小的内存;
  4. 如果实际的 ‘格式化字符串’ 比估计大怎么办? 先 zfree (可以看作 free) 之前在堆上开的内存, 然后再在堆上开一块 2倍于之前大小的内存;
  5. 如果还是不够, 重复 4;
  6. 最后记得释放堆内存 (如果开辟了的话), 防止内存泄露.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值