redisソース分析(1)----文字列sds

8385 ワード

1.1 Redis文字列の特徴
    1. バイナリは安全で、文字列の間に'0'文字を含めることができます.
   2. redisは文字列の末尾に'0'文字を追加するので、redis文字列は標準Cの文字列と互換性があります.
   3. 文字列の長さに応じて異なるタイプを使用し、コンパイル中の最適化位置合わせをキャンセルし、スペースを節約します.
   4. 文字列成長の効率を向上させるため、redisは、文字列長操作(sdsMakeRoomFor)を拡張する際の余分な空間を予め割り当て、次の拡張長さの場合、空間を再割り当てする必要がない.redisは、文字列の新規作成時に追加のスペースを割り当てていないことに注意してください.redisは、1つの文字列に拡張長の操作がある場合、その後も拡張長が拡張される可能性が高いため、空間が予め割り当てられるという仮定に基づいている.redisの場合、ほとんどの文字列は長さを拡張する必要がないため、文字列の作成時にスペースを事前に割り当てません.
 1 struct __attribute__ ((__packed__)) sdshdr5 {  2     unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
 3     char buf[];  4 };  5 struct __attribute__ ((__packed__)) sdshdr8 {  6     uint8_t len; /* used */
 7     uint8_t alloc; /* excluding the header and null terminator */
 8     unsigned char flags; /* 3 lsb of type, 5 unused bits */
 9     char buf[]; 10 }; 11 struct __attribute__ ((__packed__)) sdshdr16 { 12     uint16_t len; /* used */
13     uint16_t alloc; /* excluding the header and null terminator */
14     unsigned char flags; /* 3 lsb of type, 5 unused bits */
15     char buf[]; 16 }; 17 struct __attribute__ ((__packed__)) sdshdr32 { 18     uint32_t len; /* used */
19     uint32_t alloc; /* excluding the header and null terminator */
20     unsigned char flags; /* 3 lsb of type, 5 unused bits */
21     char buf[]; 22 }; 23 struct __attribute__ ((__packed__)) sdshdr64 { 24     uint64_t len; /* used */
25     uint64_t alloc; /* excluding the header and null terminator */
26     unsigned char flags; /* 3 lsb of type, 5 unused bits */
27     char buf[]; 28 };

   __attrubte__ ((packed))の役割は、コンパイラに構造のコンパイル中の最適化された位置合わせをキャンセルし、実際の占有バイト数に従って割り当てることです.
1.2実装で使用されるテクニック
  1.2.1計算構造体のアドレス
文字列を使用する場合、sdshdr 32などの文字列構造体を頻繁に使用するのではなく、文字列量(buf)を使用するため、bufに基づいて構造体のアドレスを見つける必要があります.
1 #define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T)));
2 #define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
3 #define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)

  1.2.2 bufに基づいてflagsのアドレスを計算する
負の数としてラベルを付けてflagsのアドレスを取得する
 1 static inline void sdssetalloc(sds s, size_t newlen) {  2     unsigned char flags = s[-1];  3     switch(flags&SDS_TYPE_MASK) {  4         case SDS_TYPE_5:  5             /* Nothing to do, this type has no total allocation info. */
 6             break;  7         case SDS_TYPE_8:  8             SDS_HDR(8,s)->alloc = newlen;  9             break; 10         case SDS_TYPE_16: 11             SDS_HDR(16,s)->alloc = newlen; 12             break; 13         case SDS_TYPE_32: 14             SDS_HDR(32,s)->alloc = newlen; 15             break; 16         case SDS_TYPE_64: 17             SDS_HDR(64,s)->alloc = newlen; 18             break; 19  } 20 }

  1.2.3ストレージ領域の事前割り当て
 1 sds sdsMakeRoomFor(sds s, size_t addlen) {  2     void *sh, *newsh;  3     size_t avail = sdsavail(s);  4  size_t len, newlen;  5     char type, oldtype = s[-1] & SDS_TYPE_MASK;  6     int hdrlen;  7 
 8     /* Return ASAP if there is enough space left. */
 9     if (avail >= addlen) return s; 10 
11     len = sdslen(s); 12     sh = (char*)s-sdsHdrSize(oldtype); 13     newlen = (len+addlen); 14     //     SDS_MAX_PREALLOC,             2 
15     if (newlen < SDS_MAX_PREALLOC) 16         newlen *= 2; 17     else
18         /*     SDS_MAX_PREALLOC */
19         newlen += SDS_MAX_PREALLOC; 20 
21     //           
22     type = sdsReqType(newlen); 23 
24     /* Don't use type 5: the user is appending to the string and type 5 is 25  * not able to remember empty space, so sdsMakeRoomFor() must be called 26  * at every appending operation. */
27     if (type == SDS_TYPE_5) type = SDS_TYPE_8; 28 
29     hdrlen = sdsHdrSize(type); 30     if (oldtype==type) { 31         //
32         newsh = s_realloc(sh, hdrlen+newlen+1); 33         if (newsh == NULL) return NULL; 34         s = (char*)newsh+hdrlen; 35     } else { 36         /* Since the header size changes, need to move the string forward, 37  * and can't use realloc */
38         newsh = s_malloc(hdrlen+newlen+1); 39         if (newsh == NULL) return NULL; 40         memcpy((char*)newsh+hdrlen, s, len+1); 41  s_free(sh); 42         s = (char*)newsh+hdrlen; 43         s[-1] = type; 44  sdssetlen(s, len); 45  } 46  sdssetalloc(s, newlen); 47     return s; 48 }