Redis 沒有直接使用 C 語言傳統的字符串表示(以空字符結尾的字符數組,如下簡稱 C 字符串), 而是本身構建了一種名爲簡單動態字符串(simple dynamic string,sds)的抽象類型, 並將sds用做 Redis 的默認字符串表示。c++
sds簡單動態字符串數據結構以下:git
1 typedef char *sds; 2 3 struct sdshdr { 4 int len; 5 int free; 6 char buf[]; 7 };
len記錄字符串的長度,free記錄sds還剩餘的空間,buf指向存儲字符的空間。redis
對應的內存空間以下圖:編程
例如最開始要存放字符串「chenrancc」:數組
通常開始的時候會比初始字符串多申請一個長度的空間放\0,如上圖所示,對應的函數是sdsnewlen。
刪除後面的cc字符後:數據結構
刪除後面的cc字符後,空出兩個字符空間並不會回收,而是用free來記錄。若是要回收者兩個空閒的空間,必須從新分配一個新的sds,作法是將原來的sds經過realloc從新分配成新的sds,對應的函數爲sdsRemoveFreeSpace。若是要增長sds的空間,也是用一樣的方法經過realloc從新分配一個新的sds,對應的函數是sdsMakeRoomFor。編程語言
要回收sds所在的內存空間,能夠經過函數sdsfree,它實際調用的是free函數。ide
除了上面提到的函數,sds中還定義了不少其它的函數來方便上層使用:函數
1 sds sdsnewlen(const void *init, size_t initlen); //用長度爲initlen的字符串建立sds 2 sds sdsempty(void); //建立一個長度爲0的sds 3 sds sdsnew(const char *init); //用null結尾的字符串建立sds 4 sds sdsdup(const sds s); //拷貝一個sds 5 void sdsfree(sds s); //釋放sds所佔的內存空間 6 void sdsupdatelen(sds s); //更新sds中的len爲實際的字符串長度 7 void sdsclear(sds s); //將sds中的字符串爲空串 8 sds sdsMakeRoomFor(sds s, size_t addlen); //sds字符串所佔空間增長addlen個字符(包括free所佔的字符) 9 sds sdsRemoveFreeSpace(sds s); //去除sds中空閒的空間 10 size_t sdsAllocSize(sds s); //獲取sds實際佔用空間的大小 11 void sdsIncrLen(sds s, int incr); //sds實際字符串的長度增長incr 12 sds sdsgrowzero(sds s, size_t len); //將sds所佔的空間增長到len,增長的空間都清零 13 sds sdscatlen(sds s, const void *t, size_t len); //sds末尾鏈接一個長度爲len的字符串 14 sds sdscat(sds s, const char *t); //sds末尾鏈接一個以null結尾的字符串 15 sds sdscatsds(sds s, const sds t); //sds末尾鏈接另外一個sds 16 sds sdscpylen(sds s, const char *t, size_t len); //拷貝長度爲len的字符串到sds中 17 sds sdscpy(sds s, const char *t); //拷貝以null結尾的字符串到sds中 18 sds sdscatvprintf(sds s, const char *fmt, va_list ap); //sds末尾鏈接一個由可變參數造成的字符串 19 sds sdscatprintf(sds s, const char *fmt, ...); //sds末尾鏈接一個由可變參數造成的字符串 20 sds sdstrim(sds s, const char *cset); //去除sds字符串的先後字符,這些字符都是在cset中出現過的 21 void sdsrange(sds s, int start, int end); //獲取sds字符串的一個字串,start和end能夠爲負數,負數表示從後面往前面索引 22 void sdstolower(sds s); //將sds字符串中的字符設置爲小寫 23 void sdstoupper(sds s); //將sds字符串中的字符設置爲大寫 24 int sdscmp(const sds s1, const sds s2); //比較兩個字符串的大小 25 sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count); //用字符串sdp分割一個sds爲多個sds 26 void sdsfreesplitres(sds *tokens, int count); //釋放由函數sdssplitlen返回的sds數組空間 27 sds sdsfromlonglong(long long value); //將long long類型的數字轉化爲一個sds 28 sds sdscatrepr(sds s, const char *p, size_t len); //sds末尾鏈接一個長度爲len的字符串,而且將其中的不可打印字符顯示出來 29 int is_hex_digit(char c); //判斷一個字符釋放爲16進制數字 30 int hex_digit_to_int(char c); //將一個16進制數字轉化爲整數 31 sds *sdssplitargs(const char *line, int *argc); //將一行文本分割成多個參數,每一個參數能夠用類編程語言 REPL格式,若是空格,\n\r\t\0等做爲分隔符 32 sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) //將sds中出如今from中的字符替換爲to對應的字符 33 sds sdsjoin(char **argv, int argc, char *sep); //將多個字符串用分割符鏈接起來組成一個sds
sds和c++中的vector很相似,惟一不一樣的是vector在空間不夠的時候能夠自動增長2倍的空間。spa
瞭解了sds的實現,想一想爲何redis非要本身實現一個字符串,而不是使用c所支持的字符串和相關的操做呢?
相比c語言中的字符串,sds有以下的好處: