redis6.2源碼解析-簡單動態(tài)字符串

1 SDS的介紹

Redis 沒有直接使用C原生的字符串, 而是自己構建了一種簡單動態(tài)字符串(simple dynamic string), 簡稱SDS. redis只有在一些無須對字符串進行修改的地方, 如打印日志, 才會用原生的C字符串.

sds的優(yōu)化點:

c 字符串 原因 SDS 優(yōu)化
獲取字符串長度復雜度為O(N) 沒有記錄字符串長度, 需要遍歷數組 獲取字符串長度復雜度為O(1) 記錄字符串的長度
API是不安全的, 可能會造成緩沖區(qū)溢出 字符數組沒有長度 API是安全的, 不會造成緩沖區(qū)溢出 SDS 空間分配策略會自動擴展數組的長度
修改字符串長度N次必然會執(zhí)行N次內存重分配 字符串就是字符數組, 增加字符需要重分配空間, 減少字符需要縮減空間 修改字符串長度N次最多需要執(zhí)行N次內存重分配 1. 空間預分配 2. 惰性空間釋放
只能保存文本數據 字符數組以'\0'為字符串結束符 可以保存文本和二進制數據 以len長度為是否結束
可以使用所有的<string.h>庫中的函數 原C生字符串肯定可以用原生的庫 可以使用所有的<string.h>庫中一部分的函數 sds也會在字符最后添加字符串結束符

2 sds.h 源碼解析

2.1 SDS 結構體

redis4.0之后, 開始使用5種header結構體來定義SDS, redis會自動根據字符串的不同的長度選擇不同的結構體來保存字符串, 從而節(jié)省內存的使用.
reids雖然定義了5位長度的sdshdr5, 但是從來不會使用它, 估計是它能存的字符串太少了, 2^5=32個字符.


//為字符數組定義別名, 為sds
typedef char *sds;

/* Note: sdshdr5 is never used, we just access the flags byte directly.
 * However is here to document the layout of type 5 SDS strings. */
//sds結構體從4.0開始, 開始使用這5種sdshdr${n}的定義, 用于更合理地分配內存, 注意, 只是定義了 sdshdr5, 但是不會使用.
// __attribute__ ((__packed__)) 是指定編譯器屬性, 非語言特性, packed屬性的主要目的是讓編譯器更緊湊地使用內存, 具體可以google一下
//最長 2^5-1 長度的 sdshdr
struct __attribute__ ((__packed__)) sdshdr5 {
    //低三位保存類型標志, 高5位用于保存字符串長度. 最多能保存5bit長度的字符串
    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
    char buf[];
};
//最長 2^8-1 長度的 sdshdr
struct __attribute__ ((__packed__)) sdshdr8 {
    //len 表示已使用長度
    uint8_t len; /* used */
    //buf分配的總長度, 也就是數組的總大小, 剩余大小 = alloc - len
    uint8_t alloc; /* excluding the header and null terminator */
    //低3位保存類型標志
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    //字符數組
    char buf[];
};
//最長 2^16-1 長度的 sdshdr
struct __attribute__ ((__packed__)) sdshdr16 {
    uint16_t len; /* used */
    uint16_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
//最長 2^32-1 長度的 sdshdr
struct __attribute__ ((__packed__)) sdshdr32 {
    uint32_t len; /* used */
    uint32_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
//最長 2^64-1 長度的 sdshdr
struct __attribute__ ((__packed__)) sdshdr64 {
    uint64_t len; /* used */
    uint64_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};

//flags 的5種類型值
#define SDS_TYPE_5  0
#define SDS_TYPE_8  1
#define SDS_TYPE_16 2
#define SDS_TYPE_32 3
#define SDS_TYPE_64 4
//低三位的掩碼,  也就是 00000111
#define SDS_TYPE_MASK 7
#define SDS_TYPE_BITS 3
  • sds 的結構體主要由四個屬性組成, 分別是字符串長度len, sds的內存空間大小alloc, sds的類型 flags, 和字符數組 buf[]. 注意, sdshdr5只有兩個屬性, 其中flags低三位表示類型, 高5位表示字符串長度.
  • attribute ((packed)) 是指定編譯器屬性, 非語言特性, packed屬性的主要目的是讓編譯器更緊湊地使用內存, 具體可以google一下

2.2 sds.h其他變量和方法的定義

//宏定義中, 用#于把宏參數變成一個字符串, 用##把兩個宏參數粘合在一起
//獲取 sdshdr 引用地址, 并且將地址放到 sh 變量中
#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T)));
//這里為什么 s - sizeof(struct sdshdr##T) 就能得到 sdshdr##T 呢
//可以看一下 sdshdr##T 的內存結構, 如: sdshdr8, 代表字符數組引用的sds在結構體的最后, 數組名不占空間, 我們可以得到
// 結構體的引用地址 = 字符數組的引用地址 - 結體的大小
//獲取 sdshdr 引用地址
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
//左移三位, 也就是獲取高5位作為返回值
#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)

//內聯(lián)函數, 用于獲取sds字符串的長度
static inline size_t sdslen(const sds s) {
    //獲取數組下標外, 前一個char大小的值, 也就相當于 SDSHDR 的 flag
    unsigned char flags = s[-1];
    switch(flags&SDS_TYPE_MASK) {
        case SDS_TYPE_5:
            return SDS_TYPE_5_LEN(flags);
        case SDS_TYPE_8:
            return SDS_HDR(8,s)->len;
        case SDS_TYPE_16:
            return SDS_HDR(16,s)->len;
        case SDS_TYPE_32:
            return SDS_HDR(32,s)->len;
        case SDS_TYPE_64:
            return SDS_HDR(64,s)->len;
    }
    return 0;
}

//內聯(lián)函數, 用于獲取sds的剩余空間, 剩余空間 = alloc - len
static inline size_t sdsavail(const sds s) {
    unsigned char flags = s[-1];
    switch(flags&SDS_TYPE_MASK) {
        case SDS_TYPE_5: {
            return 0;
        }
        case SDS_TYPE_8: {
            SDS_HDR_VAR(8,s);
            return sh->alloc - sh->len;
        }
        case SDS_TYPE_16: {
            SDS_HDR_VAR(16,s);
            return sh->alloc - sh->len;
        }
        case SDS_TYPE_32: {
            SDS_HDR_VAR(32,s);
            return sh->alloc - sh->len;
        }
        case SDS_TYPE_64: {
            SDS_HDR_VAR(64,s);
            return sh->alloc - sh->len;
        }
    }
    return 0;
}

//設置sds的長度
static inline void sdssetlen(sds s, size_t newlen) {
    unsigned char flags = s[-1];
    switch(flags&SDS_TYPE_MASK) {
        case SDS_TYPE_5:
            {
                unsigned char *fp = ((unsigned char*)s)-1;
                *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
            }
            break;
        case SDS_TYPE_8:
            SDS_HDR(8,s)->len = newlen;
            break;
        case SDS_TYPE_16:
            SDS_HDR(16,s)->len = newlen;
            break;
        case SDS_TYPE_32:
            SDS_HDR(32,s)->len = newlen;
            break;
        case SDS_TYPE_64:
            SDS_HDR(64,s)->len = newlen;
            break;
    }
}

//擴展sds的長度
static inline void sdsinclen(sds s, size_t inc) {
    unsigned char flags = s[-1];
    switch(flags&SDS_TYPE_MASK) {
        case SDS_TYPE_5:
            {
                unsigned char *fp = ((unsigned char*)s)-1;
                unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc;
                *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
            }
            break;
        case SDS_TYPE_8:
            SDS_HDR(8,s)->len += inc;
            break;
        case SDS_TYPE_16:
            SDS_HDR(16,s)->len += inc;
            break;
        case SDS_TYPE_32:
            SDS_HDR(32,s)->len += inc;
            break;
        case SDS_TYPE_64:
            SDS_HDR(64,s)->len += inc;
            break;
    }
}

//獲取數組分配的大小
/* sdsalloc() = sdsavail() + sdslen() */
static inline size_t sdsalloc(const sds s) {
    //獲取 flag 標記
    unsigned char flags = s[-1];
    switch(flags&SDS_TYPE_MASK) {
        case SDS_TYPE_5:
            return SDS_TYPE_5_LEN(flags);
        case SDS_TYPE_8:
            return SDS_HDR(8,s)->alloc;
        case SDS_TYPE_16:
            return SDS_HDR(16,s)->alloc;
        case SDS_TYPE_32:
            return SDS_HDR(32,s)->alloc;
        case SDS_TYPE_64:
            return SDS_HDR(64,s)->alloc;
    }
    return 0;
}

//重置sds已分配容量的大小
static inline void sdssetalloc(sds s, size_t newlen) {
    unsigned char flags = s[-1];
    switch(flags&SDS_TYPE_MASK) {
        case SDS_TYPE_5:
            /* Nothing to do, this type has no total allocation info. */
            break;
        case SDS_TYPE_8:
            SDS_HDR(8,s)->alloc = newlen;
            break;
        case SDS_TYPE_16:
            SDS_HDR(16,s)->alloc = newlen;
            break;
        case SDS_TYPE_32:
            SDS_HDR(32,s)->alloc = newlen;
            break;
        case SDS_TYPE_64:
            SDS_HDR(64,s)->alloc = newlen;
            break;
    }
}

//創(chuàng)建給定長度的 sds
sds sdsnewlen(const void *init, size_t initlen);
sds sdstrynewlen(const void *init, size_t initlen);
//創(chuàng)建字符串數組創(chuàng)建 sds
sds sdsnew(const char *init);
//創(chuàng)建空在的 sds
sds sdsempty(void);
//復制sds對象返回
sds sdsdup(const sds s);
//釋放sds
void sdsfree(sds s);
//用空字符串擴展sds的長度
sds sdsgrowzero(sds s, size_t len);
//將二進制安全的字符串附加到現(xiàn)有的 buf 數組之后
sds sdscatlen(sds s, const void *t, size_t len);
//sds拼接字符串
sds sdscat(sds s, const char *t);
//拼接兩sds
sds sdscatsds(sds s, const sds t);
//丟棄 sds 字符數組中的原內容,將長為 len 的字符串拷貝至 sds 的 buf 中
sds sdscpylen(sds s, const char *t, size_t len);
//將給定的C字符串復制到sds里面, 覆蓋SDS原有的字符串
sds sdscpy(sds s, const char *t);

//將格式化字符串拼接到 sds 之后
sds sdscatvprintf(sds s, const char *fmt, va_list ap);
#ifdef __GNUC__
sds sdscatprintf(sds s, const char *fmt, ...)
    __attribute__((format(printf, 2, 3)));
#else
sds sdscatprintf(sds s, const char *fmt, ...);
#endif

sds sdscatfmt(sds s, char const *fmt, ...);
//接受一個SDS和一個字符串作為參數, 從SDS中移除所有在D字符串中出現(xiàn)過的字符
sds sdstrim(sds s, const char *cset);
//依據 start 和 end 索引下標修剪 sds 字符串
void sdsrange(sds s, ssize_t start, ssize_t end);
//更新sds長度
void sdsupdatelen(sds s);
//清空sds內空
void sdsclear(sds s);
//對比兩個sds是否相同
int sdscmp(const sds s1, const sds s2);
//使用長為 seplen 的二進制安全字符串 sep 作為分隔符,將長為 len 的二進制安全字符串 s 分割成 count 個 sds 字符串
sds *sdssplitlen(const char *s, ssize_t len, const char *sep, int seplen, int *count);
//釋放 sdssplitlen 生成的動態(tài)數組的內存
void sdsfreesplitres(sds *tokens, int count);
//字符串變小寫
void sdstolower(sds s);
//字符串變大小
void sdstoupper(sds s);
//將long long 值轉成 sds
sds sdsfromlonglong(long long value);
//處理特殊字符, 非打印字符會轉成16進制打印, 相當于將字符串變成可打印的
sds sdscatrepr(sds s, const char *p, size_t len);
//解析命令行參數, 返回sds數組和參數個數
sds *sdssplitargs(const char *line, int *argc);
//遍歷 sds 字符串,將在字符串 from 中出現(xiàn)的字符替換成 to 中對應位置的字符
sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);
//使用 C 風格字符串 sep 作為分隔符,將 C 風格字符串數組拼接為一個 sds
sds sdsjoin(char **argv, int argc, char *sep);
//以 sep 字符串為分割符, 將sds數組拼接成字符串
sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);

/* Callback for sdstemplate. The function gets called by sdstemplate
 * every time a variable needs to be expanded. The variable name is
 * provided as variable, and the callback is expected to return a
 * substitution value. Returning a NULL indicates an error.
 */
//模版中變量的處理函數指針
typedef sds (*sdstemplate_callback_t)(const sds variable, void *arg);
//處理字符串模板, 如: "my name is {1}"
sds sdstemplate(const char *template, sdstemplate_callback_t cb_func, void *cb_arg);

/* Low level functions exposed to the user API */
//空間預分配, 減少內存重分配次數
sds sdsMakeRoomFor(sds s, size_t addlen);
//sds增加長度
void sdsIncrLen(sds s, ssize_t incr);
//回收sds空閑的內存空間
sds sdsRemoveFreeSpace(sds s);
//獲取sds分配的空間大小
size_t sdsAllocSize(sds s);
//獲取sds分配內存的指針, 也就是sdsHdr對象的指針
void *sdsAllocPtr(sds s);

/* Export the allocator used by SDS to the program using SDS.
 * Sometimes the program SDS is linked to, may use a different set of
 * allocators, but may want to allocate or free things that SDS will
 * respectively free or allocate. */
//分配sds內存
void *sds_malloc(size_t size);
//重分配sds內存
void *sds_realloc(void *ptr, size_t size);
//釋放sds內存
void sds_free(void *ptr);
  • sds.h 定義了sds相關的方法聲明, 具體每個方法的作用可以看上面的注釋

3 sds.c 的源碼解析

3.1 sdsHdr 相關屬性的獲取

//根據給定的類型獲取 sdsHdr 的結構體的大小
static inline int sdsHdrSize(char type) {
    switch(type&SDS_TYPE_MASK) {
        case SDS_TYPE_5:
            return sizeof(struct sdshdr5);
        case SDS_TYPE_8:
            return sizeof(struct sdshdr8);
        case SDS_TYPE_16:
            return sizeof(struct sdshdr16);
        case SDS_TYPE_32:
            return sizeof(struct sdshdr32);
        case SDS_TYPE_64:
            return sizeof(struct sdshdr64);
    }
    return 0;
}

//根據字符串的大小, 返回對應的sdsHdr的類型
static inline char sdsReqType(size_t string_size) {
    if (string_size < 1<<5)
        return SDS_TYPE_5;
    if (string_size < 1<<8)
        return SDS_TYPE_8;
    if (string_size < 1<<16)
        return SDS_TYPE_16;
#if (LONG_MAX == LLONG_MAX)
    if (string_size < 1ll<<32)
        return SDS_TYPE_32;
    return SDS_TYPE_64;
#else
    return SDS_TYPE_32;
#endif
}

//根據sdsHdr的類型, 返回此類型能存字符串的最大長度
static inline size_t sdsTypeMaxSize(char type) {
    if (type == SDS_TYPE_5)
        return (1<<5) - 1;
    if (type == SDS_TYPE_8)
        return (1<<8) - 1;
    if (type == SDS_TYPE_16)
        return (1<<16) - 1;
#if (LONG_MAX == LLONG_MAX)
    if (type == SDS_TYPE_32)
        return (1ll<<32) - 1;
#endif
    return -1; /* this is equivalent to the max SDS_TYPE_64 or SDS_TYPE_32 */
}

3.2 創(chuàng)建新SDS字符串相關方法

//用于標識, 創(chuàng)建sds時, 不初始化字符數組
extern const char *SDS_NOINIT;

//如果init指針是NULL, 則將內存初始化為0, 如果init是SDS_NOINIT, 則不進行初始化
//trymalloc 用于指定分配內存的方法
sds _sdsnewlen(const void *init, size_t initlen, int trymalloc) {
    void *sh;
    sds s;
    //根據字符串長度, 獲取sdsHdr的類型
    char type = sdsReqType(initlen);
    /* Empty strings are usually created in order to append. Use type 8
     * since type 5 is not good at this. */
    //如果類型是 5, 或者初始字符串長度是 0 , 則默認給 8
    if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;
    //獲取 sdsHdr 的結構體的長度
    int hdrlen = sdsHdrSize(type);
    //flag字段的指針
    unsigned char *fp; /* flags pointer. */
    //字符數組可使用的長度
    size_t usable;

    //確保不會溢出
    assert(initlen + hdrlen + 1 > initlen); /* Catch size_t overflow */
    //分配sds內存, sds內存大小 = sdsHdr + 字符串長度 + 空標識符
    //trymalloc=1, 則嘗試分配內存, 分配不了則返回 NULL, trymalloc=0則強制分配內存, 分配不了, 則直接打錯誤日志, 并且退出程序
    sh = trymalloc?
        s_trymalloc_usable(hdrlen+initlen+1, &usable) :
        s_malloc_usable(hdrlen+initlen+1, &usable);
    //沒分配到, 則返回 NULL
    if (sh == NULL) return NULL;
    //如果指定不初始化字符數組, 則init設置為空
    if (init==SDS_NOINIT)
        init = NULL;
    //如果沒有初始化內容
    else if (!init)
        //將sh的前hdrlen+initlen+1個字節(jié), 初始化成0
        memset(sh, 0, hdrlen+initlen+1);
    //計算出 sds 的指針, 也就是字符數組的指針
    s = (char*)sh+hdrlen;
    //計算出 flag 類型標識的指針
    fp = ((unsigned char*)s)-1;
    //重新計算sdsHdr已經使用的長度
    usable = usable-hdrlen-1;
    //如果可以長度超過sdsHdr類型的最大字符度長度, 則默認取對應類型的最大長度
    if (usable > sdsTypeMaxSize(type))
        usable = sdsTypeMaxSize(type);
    //根據sdsHdr的類型設置 len, alloc, flag 屬性
    switch(type) {
        case SDS_TYPE_5: {
            *fp = type | (initlen << SDS_TYPE_BITS);
            break;
        }
        case SDS_TYPE_8: {
            SDS_HDR_VAR(8,s);
            sh->len = initlen;
            sh->alloc = usable;
            *fp = type;
            break;
        }
        case SDS_TYPE_16: {
            SDS_HDR_VAR(16,s);
            sh->len = initlen;
            sh->alloc = usable;
            *fp = type;
            break;
        }
        case SDS_TYPE_32: {
            SDS_HDR_VAR(32,s);
            sh->len = initlen;
            sh->alloc = usable;
            *fp = type;
            break;
        }
        case SDS_TYPE_64: {
            SDS_HDR_VAR(64,s);
            sh->len = initlen;
            sh->alloc = usable;
            *fp = type;
            break;
        }
    }
    //如果有初始化長度且有初始化內容, 則將內容設置到字符數組中
    if (initlen && init)
        memcpy(s, init, initlen);
    //設置最后一個空串
    s[initlen] = '\0';
    //返回 sds
    return s;
}

//強制分配 sds 的內存
sds sdsnewlen(const void *init, size_t initlen) {
    return _sdsnewlen(init, initlen, 0);
}

//嘗試分配 sds的內存
sds sdstrynewlen(const void *init, size_t initlen) {
    return _sdsnewlen(init, initlen, 1);
}

//創(chuàng)建一個空的sds
/* Create an empty (zero length) sds string. Even in this case the string
 * always has an implicit null term. */
sds sdsempty(void) {
    return sdsnewlen("",0);
}

//根據給定的C字符串創(chuàng)建sds
/* Create a new sds string starting from a null terminated C string. */
sds sdsnew(const char *init) {
    size_t initlen = (init == NULL) ? 0 : strlen(init);
    return sdsnewlen(init, initlen);
}
  • _sdsnewlen()方法創(chuàng)建SDS的思路是, 根據字符串的長度, 計算出SDS類型, 然后申請sdsHdr的內存, 將內容設置到sdsHdr中, 最后返回sds指針

3.3 復制一個SDS

//復制一個sds
/* Duplicate an sds string. */
sds sdsdup(const sds s) {
    return sdsnewlen(s, sdslen(s));
}

3.4 釋放SDS

//釋放sds對象
/* Free an sds string. No operation is performed if 's' is NULL. */
void sdsfree(sds s) {
    if (s == NULL) return;
    //計算出sdsHdr的指針, 然后釋放內存
    s_free((char*)s-sdsHdrSize(s[-1]));
}

3.5 預分配SDS內存

// 1M = 1024 * 1024 * 1 byte
#define SDS_MAX_PREALLOC (1024*1024)

//檢查sds長度是否足夠添加對應長度的字符串, 不夠的則預分配對應的長度
sds sdsMakeRoomFor(sds s, size_t addlen) {
    void *sh, *newsh;
    //獲取sds的剩余空間
    size_t avail = sdsavail(s);
    size_t len, newlen;
    //獲取sds的類型
    char type, oldtype = s[-1] & SDS_TYPE_MASK;
    int hdrlen;
    size_t usable;

    /* Return ASAP if there is enough space left. */
    //如果剩余空間夠用, 則直接返回 sds
    if (avail >= addlen) return s;

    //往下走表示剩余空間不夠用

    //獲取sds字符串的長度
    len = sdslen(s);
    //計算出 sdsHdr 指針
    sh = (char*)s-sdsHdrSize(oldtype);
    //計算出字符串新的長度
    newlen = (len+addlen);
    //斷言新的長度必須大于原有的長度, 避免溢出
    assert(newlen > len);   /* Catch size_t overflow */
    //如果新的長度小于 SDS_MAX_PREALLOC , 也就是小于 1M 時
    if (newlen < SDS_MAX_PREALLOC)
        //預分配2倍長度
        newlen *= 2;
    else
        //每次增加 SDS_MAX_PREALLOC
        newlen += SDS_MAX_PREALLOC;

    //根據新的長度, 獲取 sds 的類型
    type = sdsReqType(newlen);

    /* Don't use type 5: the user is appending to the string and type 5 is
     * not able to remember empty space, so sdsMakeRoomFor() must be called
     * at every appending operation. */
    //不使用 5 位, 最低使用 8 位
    if (type == SDS_TYPE_5) type = SDS_TYPE_8;

    //獲取類型對應的 sdsHdr 的內存大小
    hdrlen = sdsHdrSize(type);
    //斷言避免溢出
    assert(hdrlen + newlen + 1 > len);  /* Catch size_t overflow */
    //如果sds類型不變
    if (oldtype==type) {
        //直接重分配, 擴展了空間
        newsh = s_realloc_usable(sh, hdrlen+newlen+1, &usable);
        //分配不成功, 則返回 NULL
        if (newsh == NULL) return NULL;
        //設置sds的指針
        s = (char*)newsh+hdrlen;
    } else {
        //來到這里, 表示 sds 類型發(fā)生了變化
        /* Since the header size changes, need to move the string forward,
         * and can't use realloc */
        //分配新的sdsHdr
        newsh = s_malloc_usable(hdrlen+newlen+1, &usable);
        //分配失敗返回 NULL
        if (newsh == NULL) return NULL;
        //將s的內容拷貝到新的sds中
        memcpy((char*)newsh+hdrlen, s, len+1);
        //釋放原來的sds
        s_free(sh);
        //將s替換成新的sds
        s = (char*)newsh+hdrlen;
        //設置類型
        s[-1] = type;
        //設置長度
        sdssetlen(s, len);
    }
    //計算出剩余可用空間
    usable = usable-hdrlen-1;
    //如果剩余可以空間大于sds類型所能存的最大長度, 重置為sds類型的最大長度
    if (usable > sdsTypeMaxSize(type))
        usable = sdsTypeMaxSize(type);
    //設置sds已分配的空間
    sdssetalloc(s, usable);
    //返回 sds
    return s;
}
  • 預分配內存, 首先檢查sds剩余的內存空間是否足夠存儲新添加的長度, 足夠則不處理
  • 不夠存, 則根據現(xiàn)有的長度是否小于 SDS_MAX_PREALLOC, 小于則增加一倍, 大于等于則每次增加 SDS_MAX_PREALLOC
  • 然后根據新的字符串容量空間, 獲取sds類型, 如果sds類型不變, 則調用realloc重分配
  • 如果sds類型發(fā)生了變化, 則根據新的類型申請的內存, 然后釋放原來的內存, 最后設置sds內容和屬性, 返回新的sds指針

3.6 回收SDS空閑的內存空間

//回收sds空閑的內存空間
sds sdsRemoveFreeSpace(sds s) {
    void *sh, *newsh;
    //獲取sds的類型
    char type, oldtype = s[-1] & SDS_TYPE_MASK;
    //獲取hdr結構體的大小
    int hdrlen, oldhdrlen = sdsHdrSize(oldtype);
    //獲取sds字符串的長度
    size_t len = sdslen(s);
    //獲取sds剩余長度
    size_t avail = sdsavail(s);
    //獲取sdsHdr舊指針
    sh = (char*)s-oldhdrlen;

    /* Return ASAP if there is no space left. */
    //如果沒有剩余空間, 則不處理
    if (avail == 0) return s;

    /* Check what would be the minimum SDS header that is just good enough to
     * fit this string. */
    //根據字符串長度獲取sds的新的類型
    type = sdsReqType(len);
    //根據新的類型獲取sdsHdr的結構體大小
    hdrlen = sdsHdrSize(type);

    /* If the type is the same, or at least a large enough type is still
     * required, we just realloc(), letting the allocator to do the copy
     * only if really needed. Otherwise if the change is huge, we manually
     * reallocate the string to use the different header type. */
    //為什么類型大于 8 , 還是 realloc 呢, 這里有性能優(yōu)化, realloc 相對 malloc 來說, realloc 更節(jié)省性能, realloc 只是將已分配的內存減少一下就行
    //如果結構體的類型不變, 或者 type 大于 SDS_TYPE_8
    if (oldtype==type || type > SDS_TYPE_8) {
        //直接在原指針上重分配內存, 只保留字符串長度的內存空間, 壓縮了空間
        newsh = s_realloc(sh, oldhdrlen+len+1);
        //分配失敗, 則返回 NULL
        if (newsh == NULL) return NULL;
        //返回新的sds指針
        s = (char*)newsh+oldhdrlen;
    } else {
        //只有變化很大的情況, 才去 malloc, 變化小都直接 realloc
        //oldtype != type && type == SDS_TYPE_8
        //根據新的類型長度分配內存
        newsh = s_malloc(hdrlen+len+1);
        //分配失敗, 則返回 NULL
        if (newsh == NULL) return NULL;
        //將內容拷貝到新的內存中
        memcpy((char*)newsh+hdrlen, s, len+1);
        //釋放原來的sdsHdr指針
        s_free(sh);
        //獲取新的 sdsHdr 指針
        s = (char*)newsh+hdrlen;
        //設置 flag 屬性
        s[-1] = type;
        //設置sds字符串長度
        sdssetlen(s, len);
    }
    //設置已分配容量大小
    sdssetalloc(s, len);
    //返回 sds
    return s;
}
  • 以現(xiàn)在的字符串長度, 獲取sds類型, 然后判斷類型是否變量和類型的值來決定是realloc分配還是malloc分配
  • 只有變化很大, 也就是類型由大變小并且是變成8, 才會進行申請新的內存.
  • 縮減內存, 默認上 realloc 會比 malloc 性能好很多
  • 在最苛刻的條件上, 才會重新申請內存, 是為了避免內存資源浪費

3.4 其他源碼

其他源碼比較簡單, 有需要可以自行探討, 基本都是對字符串的一些常用的操作


微信文章鏈接: https://mp.weixin.qq.com/s?__biz=MzI3NDAxNTAzMw==&mid=2247483716&idx=1&sn=b5cd16e246f8a473310f0bb1e72505be&chksm=eb1b3712dc6cbe04abcd4595c120ef70132189b3fdfa3f2f4ad04eadb4ac980c3c2610394298&token=122962709&lang=zh_CN#rd

個人微信: wolfleong

微信公眾號: wolfleong

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容