typedef char *sds;
雖然它是char指針類型,但是它可以存儲非打印字符。sdshdr一共有5個結構體,其中sdshdr5因爲可以存儲的內容太少不利於擴展,基本上是不用的。一開始看代碼的時候就想到一個問題,爲什麼要有5個結構體,一個結構體不就夠用了麼?通篇看完所有代碼以後發現,劃分5個結構體的好處是可以用最適合的內存結構存儲字符串,不至於浪費。
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; /* used */
uint8_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
sds sdsnewlen(const void *init, size_t initlen)
void *zmalloc(size_t size)
以sdshdr8爲例,結合新建函數理解這個結構體。分配內存的時候有個s_malloc函數,這個函數一共分配PREFIX_SIZE和你需要的內存大小,多餘出來的PREFIX_SIZE是爲了保存你需要的內存的大小,方便釋放的時候取大小。初始化的時候對len和alloc存了一樣的大小,後面修改的時候纔會各自修改。alloc用來存儲字符串容量,len用來存儲字符串長度,這兩個字段都不包括結束字符,flags存儲結構體類型,這裏是SDS_TYPE_8,最後複製字符串。這個結構體定義裏還有一個屬性__packed__需要注意,如果不設置這個屬性,那麼結構體實際的內存結構就會按照尋址方便而分配,就不會是下面的內存結構了,設置了__packed__,內存就可以緊緻排列,由sds取結構體類型的時候就可以直接取地址sds[-1]就可以取得flags了。
/* Enlarge the free space at the end of the sds string so that the caller
* is sure that after calling this function can overwrite up to addlen
* bytes after the end of the string, plus one more byte for nul term.
*
* Note: this does not change the *length* of the sds string as returned
* by sdslen(), but only the free buffer space we have. */
sds sdsMakeRoomFor(sds s, size_t addlen) {
// TODO
/* Return ASAP if there is enough space left. */
if (avail >= addlen) return s;
// TODO
if (newlen < SDS_MAX_PREALLOC)
newlen *= 2;
else
newlen += SDS_MAX_PREALLOC;
// TODO
}
這是用來擴容的函數,這個函數會先進行剩餘內存空間和要增加的內存空間比較,如果不夠纔會進行擴容,新的內存空間如果小於SDS_MAX_PREALLOC的話,會按照新的內存空間進行翻倍,反之就在新的內存空間上加上SDS_MAX_PREALLOC。
/* Identical sdsll2str(), but for unsigned long long type. */
int sdsull2str(char *s, unsigned long long v) {
// TODO
/* Reverse the string. */
p--;
while(s < p) {
aux = *s;
*s = *p;
*p = aux;
s++;
p--;
}
// TODO
}
這個函數裏這段反轉字符串的時候用了頭尾指針來降低了一半的消耗。
/* 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(const char *s, ssize_t len, const char *sep, int seplen, int *count) {
int slots = 5;
sds *tokens;
tokens = s_malloc(sizeof(sds)*slots);
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;
}
}
}
這個函數一開始分配了5個內存,後面再分配的時候都是分配內存翻倍,我感覺分配的其實是有點大的。
/* Like sdscatprintf() but gets va_list instead of being variadic. */
sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
va_list cpy;
char staticbuf[1024], *buf = staticbuf, *t;
size_t buflen = strlen(fmt)*2;
/* We try to start using a static buffer for speed.
* If not possible we revert to heap allocation. */
if (buflen > sizeof(staticbuf)) {
buf = s_malloc(buflen);
if (buf == NULL) return NULL;
} else {
buflen = sizeof(staticbuf);
}
/* Try with buffers two times bigger every time we fail to
* fit the string in the current buffer size. */
while(1) {
buf[buflen-2] = '\0';
va_copy(cpy,ap);
vsnprintf(buf, buflen, fmt, cpy);
va_end(cpy);
if (buf[buflen-2] != '\0') {
if (buf != staticbuf) s_free(buf);
buflen *= 2;
buf = s_malloc(buflen);
if (buf == NULL) return NULL;
continue;
}
break;
}
/* Finally concat the obtained string to the SDS string and return it. */
t = sdscat(s, buf);
if (buf != staticbuf) s_free(buf);
return t;
}
這個函數是翻倍申請空間的,但是有意思的地方是它是根據倒數第二個字符去判斷是否需要擴大,而不是根據最後一個字符,我想可能是爲了餘留空間給後續字符操作。
還有一系列對sds操作的函數,我看下來感覺這幾個函數比較有意思點。