redisソース学習ノート--文字列


redisを学ぶには、文字列が最も基礎的な構造の一つです.
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を復元することができる.
以上が、現在遭遇している操作文字列の関数です.