[Redis][データ構造]sdsの学習

5102 ワード

最近Redisのソースコードを勉強するのに多くの時間を費やすつもりで、主にhuangzの「redis設計と実現」といくつかのブログを参考にして、堅持して、この困難な任務を完成するように努力します.
このblogはsdsを読み終えたことを記録している.hとsds.c 2つのファイルの後のいくつかの疑問と総括.
     1.struct sdshdr*sh=(void*)(s-(sizeof(struct sdshdr));ということになります.
まずS初期化のコードを見てみましょう.

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;
}

Sは実際にはbufヘッダアドレスを指し、s−(sizeof(struct sdshdr)、すなわちアドレスは8バイト前方に移動し、sdsのstruct sdshdrヘッダアドレスを指す.
sdsは本質的にcharタイプの再構成であり,charに基づいて2つのint空間記憶文字列長と残りの空間を加えた.
      2.なぜtypedef char*sdsなのか.typedef struct sdshdr*sdsではありません;?
sdsはcharを指すポインタであるが、sdshdrのstructを構築し、bufメモリ空間を指すことによって実現される.
sdsのこのような設計の利点は本の中で詳しく述べられている.
      3.sdscatvprintf関数の学習:
/*                s   */
//s:    ,fmt:      s        
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. */
    //  fmt   2       1024   staticbuf,             ,         srarixbuf
    if (buflen > sizeof(staticbuf)) {
        buf = zmalloc(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';//        2   '\0'
        va_copy(cpy,ap);
        // T = O(N)
	//         
        vsnprintf(buf, buflen, fmt, cpy);//buf:      ,buflen:    ,fmt:  ,ap:      
        if (buf[buflen-2] != '\0') {//          ,           2 
            if (buf != staticbuf) zfree(buf);
            buflen *= 2;
            buf = zmalloc(buflen);
            if (buf == NULL) return NULL;
            continue;
        }
        break;
    }

    /* Finally concat the obtained string to the SDS string and return it. */
    //  sdscat buf   s  
    t = sdscat(s, buf);
    if (buf != staticbuf) zfree(buf);
    return t;
}
    
このコードは可変パラメータの概念に関し,学習ノートを簡潔に記録する.
可変パラメータの手順は次のとおりです:1.まず関数にVAを定義しますパラメータを指すポインタであるLIST型の変数.      2.そしてVA_STARTマクロ初期化変数定義されたVA_LIST変数;      3.そしてVA_ARGは可変パラメータを返し、VA_ARGの2番目のパラメータは、あなたが返すパラメータのタイプです(関数に複数の可変パラメータがある場合は、VA_ARGを順次呼び出して各パラメータを取得します).4.最後にVA_ENDマクロで可変パラメータの取得を終了します.
各関数の具体的な役割:
      va_list ap ;vaを定義するList変数ap va_start(ap,v) ;実行ap=(va_list)&v+INTSIZOF(v)は、apがパラメータvの後のパラメータのアドレス、すなわちapがスタックの最初の可変パラメータのアドレスを指す.      va_arg(ap,t) , ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZOF(t))は,現在のapポインタが指す値を取り出し,apを次のパラメータに向ける.ap+=sizeof(tタイプ)は、apが次のパラメータのアドレスを指すようにします.次にap-sizeof(tタイプ)のtタイプ*ポインタを返します.これはスタック内の最初の可変パラメータのアドレスです.そして*でこのアドレスの内容を取得します.      va_end(ap) ; va_をクリアlist ap.
上記の関数の機能と使い方を例に挙げて説明します.
/*         */
#include//             
#include
int AveInt(int v,...){
    int ReturnValue=0;
    int i=v;/v         
    va_list ap;//  va_list  
    va_start(ap,v);//   ,ap    v          
    while (i>0) {
        ReturnValue+=va_arg(ap,int);//    ap      ,  ap      
        i--;
    }
    va_end(ap);//  va_list
    return ReturnValue/=v;
}

int main(){
    printf("%d\t",AveInt(2,2,3));
    printf("%d\t",AveInt(4,2,4,6,8));
    return 0;
}
     
ソースコードにはvsnprintf関数の使い方も含まれています.私も以下にまとめます.
関数プロトタイプ:int vsnprintf(char*str,size_t size,const char*format,va_list ap);関数の説明:可変パラメータを文字配列パラメータにフォーマット出力します:strが出力した配列、sizeがサイズを指定し、境界を越えないようにします.formatフォーマットパラメータ、ap可変パラメータリストの例:
#include
#include
void SYSTEM(const char *format,...){
    char buff[4069];
    va_list list;
    va_start(list,format);//list  format         
    vsnprintf(buff,4069,format,list);
    va_end(list);
    printf("%s
",buff); } int main(){ SYSTEM("%d %s",6,"abc"); return 0; }

    4.sdssplitlen関数を勉強して、疑問があります
if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0))
なぜ1文字と複数文字の比較に分けるのか.