Redis原始碼閱讀一:sds
-
sds的定義:
typedef char *sds;
。所以sds是指向一個C字串的指標。 -
sds所指向的指標之前有一個頭部:
struct __attribute__ ((__packed__)) sdshdr5 { unsigned char flags; /* 3 lsb of type, and 5 msb of string length */ char buf[]; }; struct __attribute__ ((__packed__)) sdshdr8 { uint8_t len; /* used */ uint8_t alloc; /* excluding the header and null terminator */ unsigned char flags; /* 3 lsb of type, 5 unused bits */ char buf[]; }; struct __attribute__ ((__packed__)) sdshdr16 { uint16_t len; /* used */ uint16_t alloc; /* excluding the header and null terminator */ unsigned char flags; /* 3 lsb of type, 5 unused bits */ char buf[]; }; struct __attribute__ ((__packed__)) sdshdr32 { uint32_t len; /* used */ uint32_t alloc; /* excluding the header and null terminator */ unsigned char flags; /* 3 lsb of type, 5 unused bits */ char buf[]; }; struct __attribute__ ((__packed__)) sdshdr64 { uint64_t len; /* used */ uint64_t alloc; /* excluding the header and null terminator */ unsigned char flags; /* 3 lsb of type, 5 unused bits */ char buf[]; };
其中struct __attribute__ ((__packed__))
的意思是不要記憶體對齊,使用緊湊模式。
此外char buf[]
是柔性陣列,在結構體的最後,此前需要至少一個成員。其大小不計入結構體:
$ cat main.c #include <stdio.h> struct Foo { int i; char buf[]; }; int main() { printf("size of int: %lu\n", sizeof(int)); printf("size of struct Foo: %lu\n", sizeof(struct Foo)); } $ cc main.c && ./a.out size of int: 4 size of struct Foo: 4
- sds往前挪一個byte就是flags,用來判斷它到底是什麼型別的。其實sds v1不是這麼設計的,而是簡單的統一用一個 結構體。v2這樣可以節省記憶體。不同大小的字串就用不同大小的頭部。優點在於節省記憶體,缺點在於操作麻煩。 可以看到程式碼中很多地方訪問flags是這樣訪問的:
static inline size_t sdslen(const sds s) { unsigned char flags = s[-1]; switch(flags&SDS_TYPE_MASK) { case SDS_TYPE_5: return SDS_TYPE_5_LEN(flags); case SDS_TYPE_8: return SDS_HDR(8,s)->len; case SDS_TYPE_16: return SDS_HDR(16,s)->len; case SDS_TYPE_32: return SDS_HDR(32,s)->len; case SDS_TYPE_64: return SDS_HDR(64,s)->len; } return 0; }
flags的前三個bit用來表示是何種大小的頭部,後5個bit暫時還沒有用上。
#define SDS_TYPE_50 #define SDS_TYPE_81 #define SDS_TYPE_16 2 #define SDS_TYPE_32 3 #define SDS_TYPE_64 4 #define SDS_TYPE_MASK 7 #define SDS_TYPE_BITS 3 #define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T))); #define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)))) #define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)
-
sds相比與原生的C字串不同之處在於:
- 儲存了長度
- 可以動態擴充套件
- 二進位制安全:因為不依賴字串內部的某位來判斷是否結束,而是依賴頭部裡儲存的長度