typedef char *sds;
struct sdshdr {
long len;
long free;
char buf[0];
};
注意:sizeof(struct sdshdr)的大小只包括 sizeof(len)和sizeof(free),不含buf数组,buf数组的内存紧随这个结构体后面。
函数名称 | 作用 | 复杂度 |
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) |
sds sdsempty(void) {
return sdsnewlen("",0);
}
sds sdsnew(const char *init) {
size_t initlen = (init == NULL) ? 0 : strlen(init);
return sdsnewlen(init, initlen);
}
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;
}
size_t sdslen(const sds s) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
return sh->len;
}
size_t sdsavail(sds s) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
return sh->free;
}
void sdsupdatelen(sds s) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
int reallen = strlen(s);
sh->free += (sh->len-reallen);
sh->len = reallen;
}
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 大小是两倍长度 - 原先的数据大小。
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;
}
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 的相同。
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 追加。
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个是一样的,则较长的为大。
/* 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。
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;
}
返回一个新的sds,内容与给定的s 相同。
sds sdsdup(const sds s) {
return sdsnewlen(s, sdslen(s));
}
void sdsfree(sds s) {
if (s == NULL) return;
free(s-sizeof(struct sdshdr));
}