Table Of Contents

Previous topic

ハッシュ型

Next topic

プロトコル仕様

This Page

文字列のハック

Redisの文字列の実装は、 sds.c の中に書かれています。sdsはSimple Dynamic Stringsの略です。

sds.h の中で宣言されている、 sdshdr 構造体が、Redisの文字列を表現しています。

struct sdshdr {
    long len;
    long free;
    char buf[];
};

buf 文字配列が、実際の文字列を格納しています。

len 変数は、 buf の長さを格納しています。このため、Redisの文字列の長さはO(1)の操作で取得できます。

free 変数はあと、何バイトの文字列が格納できるのかを保持しています。

lenfree も、 buf のメタデータを保持していると考えることができます。

Redisの文字列の作成

sds という名前の新しいデータ型が、 sds.h の中で定義されています。これは文字のポインタの別名です。

typedef char *sds;

新しい文字列を作成する、 sdsnewlen 関数が、 sds.c の中で定義されています。

sds sdsnewlen(const void *init, size_t initlen) {
    struct sdshdr *sh;

    sh = zmalloc(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;
}

Redisの文字列は sdshdr 構造体の変数ですが、 この関数は文字のポインタを返しています。

これは一種のトリックですが、多様の説明を要するでしょう。

次のようにRedis文字列を作ったとします。

sdsnewlen("redis", 5);

この関数は新しい sdshdr 構造体の変数を作り、 buf と同じようにして、 lenfree にもメモリを割り当てます。

sh = zmalloc(sizeof(struct sdshdr)+initlen+1); // initlenはinit引き数の長さ

sdsnewlen が成功すると、Redis文字列は次のように作られます。

-----------
|5|0|redis|
-----------
^   ^
sh  sh->buf

sdsnewlensh->buf を呼び出し元に返します。

sh が指しているRedis文字列を解放したい場合にはどうすればよいでしょうか?

必要なポインタは sh ですが、手元にあるポインタは、 sh->buf です。

sh->buf から、 sh ポインタを取得することができるでしょうか?

はい。ポインタ演算を使うことで求めることができます。上の図を見てお分かりの通り、 sh->buf から、 long 型2つ分のサイズを引くと sh のポインタを得ることができます。

2つの long の大きさは、偶然ですが、 sdshdr の大きさと同じです。

sdslen 関数の中の、このトリックが使われているコードを見てみましょう。

size_t sdslen(const sds s) {
    struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
    return sh->len;
}

このトリックを知っていると、 sds.c の他の関数も、簡単に読むことができるでしょう。

Redis文字列の実装は、文字のポインタのみを受け取るインタフェース関数の裏に隠れています。Redis文字列を使用するだけであれば、どのように実装しているかを気にするひつようはなく、単に文字のポインタとして扱うことができます。