redis之sds(simple dynamic string)阅读笔记6-sds之可变参数

redis之sds(simple dynamic string)阅读笔记6-sds之可变参数
**********************************************************************
函数sdscatvprintf接收可变参数
/* Like sdscatprintf() but gets va_list instead of being variadic. */
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 = s_malloc(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'; //在倒数第二的位置放置一个哨兵(如果这个哨兵被覆盖,那么需要拷贝的字符串至少长度为buflen-1或者更长)
        va_copy(cpy,ap);   //用参数ap初始化cpy
        vsnprintf(buf, buflen, fmt, cpy); // 将可变参数中最多拷贝buflen-1的字节到buf中
        va_end(cpy);//释放cpy
        if (buf[buflen-2] != '\0') { 
        //如果哨兵没有被覆盖,那么长度就够了,否则需要重新申请内存来存放变参中的字符串,
        //但是这里有一种临界情况,就是刚好长度是buflen-1,即刚覆盖我们的哨兵,这种情况也是需要扩充内存的,事实上不需要
            if (buf != staticbuf) s_free(buf); //是新分配的内存,不是我们预定义使用的内存staticbuf,需要释放,否则会有没有回收的内存碎片
            buflen *= 2;//通过翻倍的方式扩充内存,试探是否够输入参数长度
            buf = s_malloc(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) s_free(buf); //记得释放动态分配内存空间,免得留下孤魂野鬼
    return t; //返回得到的新字符串
}
**********************************************************************
函数sdscatprintf
/* Append to the sds string 's' a string obtained using printf-alike format
 * specifier.
使用类似printf格式的方法得到的字符串,然后将这个字符串添加到sds字符串‘s’后面
 * After the call, the modified sds string is no longer valid and all the
 * references must be substituted with the new pointer returned by the call.
进过这函数的调用,原sds字符串🏆释放,所有的引用必须用函数调用返回的新指针
 * Example: 
示例如下:
 * s = sdsnew("Sum is: "); //创建新sds字符串
 * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b). //将格式化输入的参数添加到sds字符串后面
 *
 * Often you need to create a string from scratch with the printf-alike
 * format. When this is the need, just use sdsempty() as the target string:
通常如果你需要用格式化的方法创建一个字符串进行连接,你可以使用函数sdsempty(产生的字符串)作为目标字符串
 * s = sdscatprintf(sdsempty(), "... your format ...", args);
 */
sds sdscatprintf(sds s, const char *fmt, ...) {
    va_list ap;
    char *t;
    va_start(ap, fmt); //  初始化参数ap
    t = sdscatvprintf(s,fmt,ap); //调用sdscatvprintf 获取格式化字符串
    va_end(ap); // 释放参数ap
    return t;
}
**********************************************************************
函数sdscatfmt 是自己写的处理可变参数的函数
/* This function is similar to sdscatprintf, but much faster as it does
 * not rely on sprintf() family functions implemented by the libc that
 * are often very slow. Moreover directly handling the sds string as
 * new data is concatenated provides a performance improvement.
这个函数类似sdscatprintf,但是比sdscatprintf要快,主要是因为它不依赖于有库实现的函数家族sprintf,
这类库函数通常比较慢。进一步,直接像新数据串联一样处理sds字符串提供了显著的性能改善
 * However this function only handles an incompatible subset of printf-alike
 * format specifiers:
不过这个函数只是类printf格式方法的一个不兼容子集.
 * %s - C String
 * %S - SDS string
 * %i - signed int
 * %I - 64 bit signed integer (long long, int64_t)
 * %u - unsigned int
 * %U - 64 bit unsigned integer (unsigned long long, uint64_t)
 * %% - Verbatim "%" character. // %本身
 */
sds sdscatfmt(sds s, char const *fmt, ...) {
    size_t initlen = sdslen(s);
    const char *f = fmt;
    long i;
    va_list ap;

    /* To avoid continuous reallocations, let's start with a buffer that
     * can hold at least two times the format string itself. It's not the
     * best heuristic but seems to work in practice. */
为了避免连续分配内存,我们初始化的时候就分配一个格式化字符串两倍长的内存空间,
这个不是最优的分配内存启发式算法,但是在实际应用在足够了
    s = sdsMakeRoomFor(s, initlen + strlen(fmt)*2);//分配两倍长的格式化字符串内存空间
    va_start(ap,fmt); // 初始化ap
    f = fmt;    /* Next format specifier byte to process. */
    i = initlen; /* Position of the next byte to write to dest str. */
    while(*f) { //遍历格式化字符串的每个字符
        char next, *str;
        size_t l;
        long long num;
        unsigned long long unum;

        /* Make sure there is always space for at least 1 char. */
至少需要一个字符的空间,用来存放遍历的格式化字符串的一个字符
        if (sdsavail(s)==0) {
            s = sdsMakeRoomFor(s,1);
        }

        switch(*f) { //对每个字符进行判断
        case '%':    // 如果遇到% 说明遇到格式了
            next = *(f+1); //需要继续取下一个字符判断是什么格式
            f++;//又取了一个,所以需要继续往下+1
            switch(next) {
            case 's':
            case 'S':
                str = va_arg(ap,char*); // 字符类型参数, 赋值给str
                l = (next == 's') ? strlen(str) : sdslen(str); // C or SDS,调用不同的长度函数
                if (sdsavail(s) < l) {  //如果剩余空间不够,那么需要分配足够的空间
                    s = sdsMakeRoomFor(s,l);
                }
                memcpy(s+i,str,l); //将长度为l(字母l)参数内容str拷贝sds字符串中
                sdsinclen(s,l); // 将sds字符串长度修改
                i += l; //变化i指向位置,供下次拷贝字符使用,不会覆盖
                break;
            case 'i':
            case 'I':
                if (next == 'i')
                    num = va_arg(ap,int);
                else
                    num = va_arg(ap,long long);
                {
                    char buf[SDS_LLSTR_SIZE];
                    l = sdsll2str(buf,num); // 长整形转化字符串函数
                    if (sdsavail(s) < l) {
                        s = sdsMakeRoomFor(s,l);
                    }
                    memcpy(s+i,buf,l);
                    sdsinclen(s,l);
                    i += l;
                }
                break;
            case 'u':
            case 'U':
                if (next == 'u')
                    unum = va_arg(ap,unsigned int);
                else
                    unum = va_arg(ap,unsigned long long);
                {
                    char buf[SDS_LLSTR_SIZE];
                    l = sdsull2str(buf,unum); // 无符号长整形转化字符串函数
                    if (sdsavail(s) < l) {
                        s = sdsMakeRoomFor(s,l);
                    }
                    memcpy(s+i,buf,l);
                    sdsinclen(s,l);
                    i += l;
                }
                break;
            default: /* Handle %% and generally %<unknown>. */ //如果后面的字符也是%,那就当做%字符处理
                s[i++] = next;
                sdsinclen(s,1);
                break;
            }
            break;
        default: //非%的情况,直接添加到结尾即可
            s[i++] = *f;
            sdsinclen(s,1);
            break;
        }
        f++; //取下一个继续循环
    }
    va_end(ap); //释放可变参数列表

    /* Add null-term */
    s[i] = '\0'; //设置结尾符/0
    return s;
}

 

posted on 2020-07-30 17:30  子虚乌有  阅读(326)  评论(0)    收藏  举报