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));
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章