redis--sds API 详解

1,sds 数据结构

typedef char *sds;

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

  注意:sizeof(struct sdshdr)的大小只包括 sizeof(len)和sizeof(free),不含buf数组,buf数组的内存紧随这个结构体后面。

2,sds API

函数名称 作用 复杂度
sdsempty 创建一个只包含空字符串””的sds O(N)
sdsnew 根据给定的C字符串,创建一个相应的sds O(N)
sdsnewlen 创建一个指定长度的sds,接受一个指定的C字符串作为初始化值 O(N)
sdsdup 复制给定的sds O(N)
sdsfree 释放给定的sds O(1)
sdsupdatelen 更新给定sds所对应的sdshdr的free与len值 O(1)
sdsMakeRoomFor 对给定sds对应sdshdr的buf进行扩展 O(N)
sdscatlen 将一个C字符串追加到给定的sds对应sdshdr的buf O(N)
sdscpylen 将一个C字符串复制到sds中,需要依据sds的总长度来判断是否需要扩展 O(N)
sdscatprintf 通过格式化输出形式,来追加到给定的sds O(N)
sdstrim 对给定sds,删除前端/后端在给定的C字符串中的字符 O(N)
sdsrange 截取给定sds,[start,end]字符串 O(N)
sdscmp 比较两个sds的大小 O(N)
sdssplitlen 对给定的字符串s按照给定的sep分隔字符串来进行切割 O(N)

3,sds ---创建

   1) 创建一个空的sds,len和free都为0,buf数组也为空。

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

    2) 创建一个sds(空的或非空)。

sds sdsnew(const char *init) {
    size_t initlen = (init == NULL) ? 0 : strlen(init);
    return sdsnewlen(init, initlen);
}

    3) 其中均会调用sdsnewlen,这个函数主要是分配内存和赋值。

sds sdsnewlen(const void *init, size_t initlen) {
    struct sdshdr *sh;

    sh = malloc(sizeof(struct sdshdr)+initlen+1);
#ifdef SDS_ABORT_ON_OOM
    if (sh == NULL) sdsOomAbort();
#else
    if (sh == NULL) return NULL;
#endif
    sh->len = initlen;
    sh->free = 0;
    if (initlen) {
        if (init) memcpy(sh->buf, init, initlen);
        else memset(sh->buf,0,initlen);
    }
    sh->buf[initlen] = '\0';
    return (char*)sh->buf;
}

3,sds---长度操作

    1) 获取sds 的数据长度。

size_t sdslen(const sds s) {
    struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
    return sh->len;
}

    2) 获取sds 的空闲大小。

size_t sdsavail(sds s) {
    struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
    return sh->free;
}

   3) 更新sds 的长度信息。

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

4,sds---扩容操作

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 = realloc(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; //两倍数据-原先数据长度,后面free再减去新增数据长度addlen,即可保证free的大小和len的最终的大小相等
    return newsh->buf;
}

    函数的参数 addlen是要增加的长度,首先它会跟free 比较,如果 addlen小,sds放得下,无需扩容;如果addlen大,则需要扩容,重新分配内存,重新计算的数组大小是原先存放的数据加上新增数据之和的两倍(newlen),因为一倍给free,另外一倍存放最终的数据,目前的新sds中的free 大小是两倍长度 - 原先的数据大小。

5,sds---追加在某个sds的后面

     sdscat 将字符串t 追加在具有sds结构的s 后面,并返回一个sds结构(如果没有扩容就是原先那个sds)。

sds sdscat(sds s, char *t) {
    return sdscatlen(s, t, strlen(t));
}

   其中用到了sdscatlen

sds sdscatlen(sds s, 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 = curlen+len;      //最终大小为原先数据长度加上新增的字符串长度
    sh->free = sh->free-len;   //这个len是新增数据长度,sh->free大小是两倍长度减去原先数据长度,再减个len,即为free字段是原先数据长度加上新增的数据长度,保证和最终的sh->len大小一致
    s[curlen+len] = '\0';
    return s;
}

6,sds---将字符串拷贝到sds中,会覆盖掉原先的数据

sds sdscpy(sds s, char *t) {
    return sdscpylen(s, t, strlen(t));
}
sds sdscpylen(sds s, char *t, size_t len) {
    struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
    size_t totlen = sh->free+sh->len;

    if (totlen < len) {
        s = sdsMakeRoomFor(s,len-totlen); //此处应该是len- sh->len
        if (s == NULL) return NULL;
        sh = (void*) (s-(sizeof(struct sdshdr)));
        totlen = sh->free+sh->len; //新的sh->free为两倍大小除去原先的数据长度,sh->len为原先的数据长度,现在相加,totlen值为两倍大小
    }
    memcpy(s, t, len);
    s[len] = '\0';
    sh->len = len;     //最终的sds中的len为拷贝的字符串的长度
    sh->free = totlen-len;   ////最终的sds中的free也为拷贝的字符串的长度(两倍减去了一倍)
    return s;
}

同样最后会保证free的大小和len 的相同。

7,sds---通过格式化输出的形式,追加到给定的sds 后

sds sdscatprintf(sds s, const char *fmt, ...) {
    va_list ap;
    char *buf, *t;
    size_t buflen = 32;

    va_start(ap, fmt);
    while(1) {
        buf = malloc(buflen);
#ifdef SDS_ABORT_ON_OOM
        if (buf == NULL) sdsOomAbort();
#else
        if (buf == NULL) return NULL;
#endif
        buf[buflen-2] = '\0';     //在倒数第二个打上标记,如果被覆盖了说明分配的内存不够
        vsnprintf(buf, buflen, fmt, ap);
        if (buf[buflen-2] != '\0') {
            free(buf);
            buflen *= 2;
            continue;
        }
        break;
    }
    va_end(ap);
    t = sdscat(s, buf);
    free(buf);
    return t;
}

    此函数是以格式化输出的形式追加到给定的sds 后面,首先默认分配32字节的内存,在倒数第二个字节打上结束标记,然后拷贝格式化输出的内容,如果倒数第二个字节被覆盖,说明分配的大小不够,采取的策略是翻倍尝试,最后调用前面的sdscat 追加。

8,sds---比较两个sds

int sdscmp(sds s1, sds s2) {
    size_t l1, l2, minlen;
    int cmp;

    l1 = sdslen(s1);
    l2 = sdslen(s2);
    minlen = (l1 < l2) ? l1 : l2;
    cmp = memcmp(s1,s2,minlen);
    if (cmp == 0) return l1-l2;
    return cmp;
}

    sds s1和s2保存的字符串长度可能不一样,如果长度一样的话,直接调用memcmp比较即可,如果不一样的话,首先需要计算出较小的长度minlen,调用memcmp 比较前minlen个字符串,如果前minlen个不一样的话,直接返回比较结果(此时已出胜负),如果前minlen个是一样的,则较长的为大。

9,sds---对给定的字符串按给定的sep 分隔符来切割

/* Split 's' with separator in 'sep'. An array
 * of sds strings is returned. *count will be set
 * by reference to the number of tokens returned.
 *
 * On out of memory, zero length string, zero length
 * separator, NULL is returned.
 *
 * Note that 'sep' is able to split a string using
 * a multi-character separator. For example
 * sdssplit("foo_-_bar","_-_"); will return two
 * elements "foo" and "bar".
 *
 * This version of the function is binary-safe but
 * requires length arguments. sdssplit() is just the
 * same function but for zero-terminated strings.
 */
sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count) {
    int elements = 0, slots = 5, start = 0, j;

    sds *tokens = malloc(sizeof(sds)*slots);
#ifdef SDS_ABORT_ON_OOM
    if (tokens == NULL) sdsOomAbort();
#endif
    if (seplen < 1 || len < 0 || tokens == NULL) return NULL;
    for (j = 0; j < (len-(seplen-1)); j++) {
        /* make sure there is room for the next element and the final one */
        if (slots < elements+2) {
            slots *= 2;
            sds *newtokens = realloc(tokens,sizeof(sds)*slots);
            if (newtokens == NULL) {
#ifdef SDS_ABORT_ON_OOM
                sdsOomAbort();
#else
                goto cleanup;
#endif
            }
            tokens = newtokens;
        }
        /* search the separator */
        if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {
            tokens[elements] = sdsnewlen(s+start,j-start);
            if (tokens[elements] == NULL) {
#ifdef SDS_ABORT_ON_OOM
                sdsOomAbort();
#else
                goto cleanup;
#endif
            }
            elements++;
            start = j+seplen;
            j = j+seplen-1; /* skip the separator */
        }
    }
    /* Add the final element. We are sure there is room in the tokens array. */
    tokens[elements] = sdsnewlen(s+start,len-start);
    if (tokens[elements] == NULL) {
#ifdef SDS_ABORT_ON_OOM
                sdsOomAbort();
#else
                goto cleanup;
#endif
    }
    elements++;
    *count = elements;
    return tokens;

#ifndef SDS_ABORT_ON_OOM
cleanup:
    {
        int i;
        for (i = 0; i < elements; i++) sdsfree(tokens[i]);
        free(tokens);
        return NULL;
    }
#endif
}

    比如字符串 foo_-_bar_-_fun使用分割符_-_,分割符长度是3,最后得到3个(count的值)sds *结构的tokens,tokens[0]存放的是sds结构的foo,tokens[1]存放的是sds结构的bar,tokens[2]存放的是sds结构的foo。

10,sds---字符串范围操作

    sdsrange----截取给定的sds,[start, end]字符串。如start为3,表示字符串中第4个字符,字符串下标从0开始,如为-1,表示是最后一个字符,同理-2表示倒数第2个字符。

sds sdsrange(sds s, long start, long end) {
    struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
    size_t newlen, len = sdslen(s);

    if (len == 0) return s;
    if (start < 0) {
        start = len+start;
        if (start < 0) start = 0;
    }
    if (end < 0) {
        end = len+end;
        if (end < 0) end = 0;
    }
    newlen = (start > end) ? 0 : (end-start)+1;
    if (newlen != 0) {
        if (start >= (signed)len) start = len-1;
        if (end >= (signed)len) end = len-1;
        newlen = (start > end) ? 0 : (end-start)+1;
    } else {
        start = 0;
    }
    if (start != 0) memmove(sh->buf, sh->buf+start, newlen);
    sh->buf[newlen] = 0;
    sh->free = sh->free+(sh->len-newlen);
    sh->len = newlen;
    return s;
}

   sdstrim---对给定sds,删除前端/后端在给定的C字符串中出现过的字符。

sds sdstrim(sds s, const char *cset) {
    struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
    char *start, *end, *sp, *ep;
    size_t len;

    sp = start = s;
    ep = end = s+sdslen(s)-1;
    while(sp <= end && strchr(cset, *sp)) sp++;
    while(ep > start && strchr(cset, *ep)) ep--;
    len = (sp > ep) ? 0 : ((ep-sp)+1);
    if (sh->buf != sp) memmove(sh->buf, sp, len);
    sh->buf[len] = '\0';
    sh->free = sh->free+(sh->len-len);
    sh->len = len;
    return s;
}

11,sds---复制操作

       返回一个新的sds,内容与给定的s 相同。

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

12,sds---释放操作

void sdsfree(sds s) {
    if (s == NULL) return;
    free(s-sizeof(struct sdshdr));
}

 

你可能感兴趣的:(redis)