redis源码阅读——动态字符串sds
Posted 曾经时光
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了redis源码阅读——动态字符串sds相关的知识,希望对你有一定的参考价值。
redis中动态字符串sds相关的文件为:sds.h与sds.c
一、数据结构
redis中定义了自己的数据类型"sds",用于描述 char*,与一些数据结构
1 typedef char *sds; 2 3 /* Note: sdshdr5 is never used, we just access the flags byte directly. 4 * However is here to document the layout of type 5 SDS strings. */ 5 struct __attribute__ ((__packed__)) sdshdr5 { 6 unsigned char flags; /* 3 lsb of type, and 5 msb of string length */ 7 char buf[]; 8 }; 9 struct __attribute__ ((__packed__)) sdshdr8 { 10 uint8_t len; /* used */ 11 uint8_t alloc; /* excluding the header and null terminator */ 12 unsigned char flags; /* 3 lsb of type, 5 unused bits */ 13 char buf[]; 14 }; 15 struct __attribute__ ((__packed__)) sdshdr16 { 16 uint16_t len; /* used */ 17 uint16_t alloc; /* excluding the header and null terminator */ 18 unsigned char flags; /* 3 lsb of type, 5 unused bits */ 19 char buf[]; 20 }; 21 struct __attribute__ ((__packed__)) sdshdr32 { 22 uint32_t len; /* used */ 23 uint32_t alloc; /* excluding the header and null terminator */ 24 unsigned char flags; /* 3 lsb of type, 5 unused bits */ 25 char buf[]; 26 }; 27 struct __attribute__ ((__packed__)) sdshdr64 { 28 uint64_t len; /* used */ 29 uint64_t alloc; /* excluding the header and null terminator */ 30 unsigned char flags; /* 3 lsb of type, 5 unused bits */ 31 char buf[]; 32 };
定义结构体时,加上了 __attribute__ ((__packed__)) 关键字,用于取消字节对齐,使其按照紧凑排列的方式,占用内存。这样做的目的并不仅仅只是为了节约内存的使用。结构体最后有一个 char buf[],查了资料之后了解到,其只是定义一个数组符号,并没有任何成员,不占用结构体的内存空间,其真实地址紧随结构体之后,可实现变长结构体。由此可以只根据sds字符串的真实地址,取到sds结构体的任意成员变量数据。如flags:
1 void func(const sds s) 2 { 3 unsigned char flags = s[-1]; 4 }
这个flags,从源码的注释上看,其低三位用于表示sds类型,高五位是当sds类型为sdshdr5时,表明字符串长度的。对于非sdshdr5的类型,有专门的成员变量描述当前已使用的长度,及总buffer长度。
sds类型:
1 #define SDS_TYPE_5 0 2 #define SDS_TYPE_8 1 3 #define SDS_TYPE_16 2 4 #define SDS_TYPE_32 3 5 #define SDS_TYPE_64 4
sds定义了两个宏,用于获取sds结构体首地址:
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))))
由此可见sds结构体的大致结构为:
1 /* 2 sdshdr5 3 +--------+----...---+ 4 |00011000|abc | 5 +--------+----...---+ 6 |flags |buf 7 8 sdshdr8 9 +--------+--------+--------+----...---+ 10 |00000011|00000011|00000001|abc | 11 +--------+--------+--------+----...---+ 12 |len |alloc |flags |buf 13 */
sds的一些常规操作,如获取字符串长度、获取剩余buf长度等,都是其于以上操作,首先根据sds字符串地址获取其flags的值,根据flags低三位判断sds类型,接着使用宏SDS_HDR_VAR或SDS_HDR进行操作。如:
1 #define SDS_TYPE_MASK 7 //0000,0111 2 3 static inline size_t sdslen(const sds s) { 4 //获取flags 5 unsigned char flags = s[-1]; 6 //根据flags低三位取类型,根据类型做不同处理 7 switch(flags&SDS_TYPE_MASK) { 8 case SDS_TYPE_5: 9 return SDS_TYPE_5_LEN(flags); 10 case SDS_TYPE_8: 11 return SDS_HDR(8,s)->len; 12 case SDS_TYPE_16: 13 return SDS_HDR(16,s)->len; 14 case SDS_TYPE_32: 15 return SDS_HDR(32,s)->len; 16 case SDS_TYPE_64: 17 return SDS_HDR(64,s)->len; 18 } 19 return 0; 20 }
关于sds结构体中的len与alloc,len表示的是sds字符串的当前长度,alloc表示的是buf的总长度。
二、一些操作。
首先是一个根据字符串长度来决定sds类型的方法
1 static inline char sdsReqType(size_t string_size) { 2 if (string_size < 1<<5) //flags高五位最大数字为 1<<5 - 1 3 return SDS_TYPE_5; 4 if (string_size < 1<<8) //uint8_t 最大数字为 1<<8 - 1 5 return SDS_TYPE_8; 6 if (string_size < 1<<16) //uint16_t 最大数字为 1<<16 - 1 7 return SDS_TYPE_16; 8 #if (LONG_MAX == LLONG_MAX) //区分32位/64位系统 9 if (string_size < 1ll<<32) 10 return SDS_TYPE_32; 11 return SDS_TYPE_64; 12 #else 13 return SDS_TYPE_32; 14 #endif 15 }
创建一个新的sds结构体:
1 sds sdsnewlen(const void *init, size_t initlen) { 2 void *sh; 3 sds s; 4 char type = sdsReqType(initlen); 5 if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8; 6 int hdrlen = sdsHdrSize(type); 7 unsigned char *fp; /* flags pointer. */ 8 9 sh = s_malloc(hdrlen+initlen+1); 10 if (init==SDS_NOINIT) 11 init = NULL; 12 else if (!init) 13 memset(sh, 0, hdrlen+initlen+1); 14 if (sh == NULL) return NULL; 15 s = (char*)sh+hdrlen; 16 fp = ((unsigned char*)s)-1; 17 switch(type) { 18 case SDS_TYPE_5: { 19 *fp = type | (initlen << SDS_TYPE_BITS); 20 break; 21 } 22 case SDS_TYPE_8: { 23 SDS_HDR_VAR(8,s); 24 sh->len = initlen; 25 sh->alloc = initlen; 26 *fp = type; 27 break; 28 } 29 case SDS_TYPE_16: { 30 //同SDS_TYPE_8,略 31 } 32 case SDS_TYPE_32: { 33 //同SDS_TYPE_8,略 34 } 35 case SDS_TYPE_64: { 36 //同SDS_TYPE_8,略 37 } 38 } 39 if (initlen && init) 40 memcpy(s, init, initlen); 41 s[initlen] = ‘