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