redisソース学習ノート--文字列
3900 ワード
redisを学ぶには、文字列が最も基礎的な構造の一つです.
redis文字列の設計は比較的巧みである.彼はc言語に基づいているため、C++文字列クラスのように様々な構造関数、構造関数などを実現するのではなく、全体的な設計が精巧である.しかし、注意すべき点もいくつかあります.
文字列には長さlen、使用可能な長さfreeなどのデータがあります.もちろん最も重要なのはデータbufです.データは一定長ではないので、len、freeを構造体の一番前に置いて、最後にデータを置きます.
新しい文字列を作成します.
sds c = sdsnew("hello world");
sdsnewの定義は次のとおりです.
実はsdsはchar*の別名です.上の関数はcharポインタだけを返していますが、どうやってcの長さを知っていますか?strlenを呼び出すのは難しいですが、文字列を設計するのは意味がありません.長さなどのデータはbufデータの前にあるので、lenフィールドを位置決めすればわかりますが、実際にsdslenはこのように実現されています.
すべてのchar*データがこのように長さを求めることができるのではなく、sdshdr構造のchar*データだけがそうすることができます.そうしないと、char*データの前にsdshdr構造のフィールドがないため、未知になります.
クライアント・オブジェクトredisClientのデータ・バッファ・フィールドquerybufタイプはchar*ですが、querybufはsdsnewlen()で作成された空の文字列であり、sdsnewlen()でスタックにスペースが割り当てられているため、いつでも長さを求めることができます.
文字列を拡張するには、sds sdsMakeRoomFor(sds,size_t addlen)が呼び出され、新しいデータが割り当てられます.データbufにデータを書き込むstrcpyを呼び出すか、bufポインタを指定すればいいので、buf関連sdshdr構造を更新する操作が必要です.sdsIncrLen:
データを空にするとsdsrangeを呼び出すことができますが、実際にはデータブロックを移動する操作です.
sdsrange(s,len,−1)を呼び出すと,構造体のデータを空にすることができ,実際には最初のバイトを0にし,長さを0にし,freeを復元することができる.
以上が、現在遭遇している操作文字列の関数です.
redis文字列の設計は比較的巧みである.彼はc言語に基づいているため、C++文字列クラスのように様々な構造関数、構造関数などを実現するのではなく、全体的な設計が精巧である.しかし、注意すべき点もいくつかあります.
文字列には長さlen、使用可能な長さfreeなどのデータがあります.もちろん最も重要なのはデータbufです.データは一定長ではないので、len、freeを構造体の一番前に置いて、最後にデータを置きます.
struct sdshdr {
// buf
int len;
// buf
int free;
//
char buf[];
};
新しい文字列を作成します.
sds c = sdsnew("hello world");
sdsnewの定義は次のとおりです.
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;
if (init) {
sh = zmalloc(sizeof(struct sdshdr)+initlen+1);
} else {
sh = zcalloc(sizeof(struct sdshdr)+initlen+1);
}
if (sh == NULL) return NULL;
sh->len = initlen; //
sh->free = 0;
if (initlen && init)
memcpy(sh->buf, init, initlen);
sh->buf[initlen] = '\0'; // \0
return (char*)sh->buf; // char *
}
実はsdsはchar*の別名です.上の関数はcharポインタだけを返していますが、どうやってcの長さを知っていますか?strlenを呼び出すのは難しいですが、文字列を設計するのは意味がありません.長さなどのデータはbufデータの前にあるので、lenフィールドを位置決めすればわかりますが、実際にsdslenはこのように実現されています.
static inline size_t sdslen(const sds s) {
struct sdshdr *sh = (void*)(s - (sizeof(struct sdshdr)));
return sh->len;
}
すべてのchar*データがこのように長さを求めることができるのではなく、sdshdr構造のchar*データだけがそうすることができます.そうしないと、char*データの前にsdshdr構造のフィールドがないため、未知になります.
クライアント・オブジェクトredisClientのデータ・バッファ・フィールドquerybufタイプはchar*ですが、querybufはsdsnewlen()で作成された空の文字列であり、sdsnewlen()でスタックにスペースが割り当てられているため、いつでも長さを求めることができます.
sds sdsnewlen(const void *init, size_t initlen) {
struct sdshdr *sh;
// ,
// T = O(N)
if (init) {
// zmalloc
sh = zmalloc(sizeof(struct sdshdr) + initlen + 1);
}
else {
// zcalloc 0
sh = zcalloc(sizeof(struct sdshdr) + initlen + 1);
}
// ,
if (sh == NULL) return NULL;
//
sh->len = initlen;
// sds
sh->free = 0;
// , sdshdr buf
// T = O(N)
if (initlen && init)
memcpy(sh->buf, init, initlen);
// \0
sh->buf[initlen] = '\0';
// buf , sdshdr
return (char*)sh->buf;
}
文字列を拡張するには、sds sdsMakeRoomFor(sds,size_t addlen)が呼び出され、新しいデータが割り当てられます.データbufにデータを書き込むstrcpyを呼び出すか、bufポインタを指定すればいいので、buf関連sdshdr構造を更新する操作が必要です.sdsIncrLen:
void sdsIncrLen(sds s, int incr) {
struct sdshdr *sh = (void*)(s - (sizeof(struct sdshdr)));
// sds
assert(sh->free >= incr);
//
sh->len += incr;
sh->free -= incr;
// assert
// assert sh->free - incr >= 0
assert(sh->free >= 0);
//
s[sh->len] = '\0';
}
データを空にするとsdsrangeを呼び出すことができますが、実際にはデータブロックを移動する操作です.
void sdsrange(sds s, int start, int end) {
struct sdshdr *sh = (void*)(s - (sizeof(struct sdshdr)));
size_t newlen, len = sdslen(s);
if (len == 0) return;
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) {
newlen = 0;
}
else if (end >= (signed)len) {
end = len - 1;
newlen = (start > end) ? 0 : (end - start) + 1;
}
}
else {
start = 0;
}
// ,
// T = O(N)
if (start && newlen) memmove(sh->buf, sh->buf + start, newlen);
//
sh->buf[newlen] = 0;
//
sh->free = sh->free + (sh->len - newlen);
sh->len = newlen;
}
sdsrange(s,len,−1)を呼び出すと,構造体のデータを空にすることができ,実際には最初のバイトを0にし,長さを0にし,freeを復元することができる.
以上が、現在遭遇している操作文字列の関数です.