SDS介绍
Redis自己构建了一种名为简单动态字符串(simple dynamic string, SDS) 的抽象类型,并将其作为Redis的默认字符串表示。
SDS定义
在 Redis7.0 中 定义的代码位于 src/sds.h
中,定义如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
struct __attribute__ ((__packed__)) sdshdr5 { unsigned char flags; char buf[]; }; struct __attribute__ ((__packed__)) sdshdr8 { uint8_t len; uint8_t alloc; unsigned char flags; char buf[]; }; struct __attribute__ ((__packed__)) sdshdr16 { uint16_t len; uint16_t alloc; unsigned char flags; char buf[]; }; struct __attribute__ ((__packed__)) sdshdr32 { uint32_t len; uint32_t alloc; unsigned char flags; char buf[]; }; struct __attribute__ ((__packed__)) sdshdr64 { uint64_t len; uint64_t alloc; unsigned char flags; char buf[]; };
|
根据字符串长度不同,用来存放它的sds类型也不同。
其中 len
表示使用的字符串长度,alloc
表示分配的总空间的长度,flags
表示当前结构体类型,因为只有五种类型,所以只使用低三位,有如下枚举值。
1 2 3 4 5
| #define SDS_TYPE_5 0 #define SDS_TYPE_8 1 #define SDS_TYPE_16 2 #define SDS_TYPE_32 3 #define SDS_TYPE_64 4
|
结构前加上了 __attribute__ ((__packed__))
取消编译器的内存对齐。从图中可以看出,不同的数据类型有不同的对齐方式,flags
与 buf
存在填充数据,如果没有取消内存对齐, (char*)buf - 1
无法直接获取数据类型的 flags
。
SDS的内存分配方式详见 1.8 内存分配。
接口定义
字符串属性接口
1 2 3 4 5 6 7 8 9 10
| static inline size_t sdslen(const sds s) ; static inline size_t sdsavail(const sds s); static inline void sdssetlen(sds s, size_t newlen) ; static inline void sdsinclen(sds s, size_t inc);
static inline size_t sdsalloc(const sds s); static inline void sdssetalloc(sds s, size_t newlen);
|
sdslen
接口
sdslen
函数返回字符串的当前长度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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; }
|
sds
由 typedef char* sds;
定义。可以看到,sds
实际上就是指向 sds 结构体的 buf 属性的指针。由于取消了内存对齐,flags
和 buf
中不含有填充数据,(char *)buf - 1
就是 flags的值。根据 flags
的值判断字符串
1
| #define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
|
sdsavail
接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| static inline size_t sdsavail(const sds s) { unsigned char flags = s[-1]; switch(flags&SDS_TYPE_MASK) { case SDS_TYPE_5: { return 0; } case SDS_TYPE_8: { SDS_HDR_VAR(8,s); return sh->alloc - sh->len; } case SDS_TYPE_16: { SDS_HDR_VAR(16,s); return sh->alloc - sh->len; } case SDS_TYPE_32: { SDS_HDR_VAR(32,s); return sh->alloc - sh->len; } case SDS_TYPE_64: { SDS_HDR_VAR(64,s); return sh->alloc - sh->len; } } return 0; }
|
sdssetlen
接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| static inline void sdssetlen(sds s, size_t newlen) { unsigned char flags = s[-1]; switch(flags&SDS_TYPE_MASK) { case SDS_TYPE_5: { unsigned char *fp = ((unsigned char*)s)-1; *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); } break; case SDS_TYPE_8: SDS_HDR(8,s)->len = newlen; break; case SDS_TYPE_16: SDS_HDR(16,s)->len = newlen; break; case SDS_TYPE_32: SDS_HDR(32,s)->len = newlen; break; case SDS_TYPE_64: SDS_HDR(64,s)->len = newlen; break; } }
|
sdsinclen
接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| static inline void sdsinclen(sds s, size_t inc) { unsigned char flags = s[-1]; switch(flags&SDS_TYPE_MASK) { case SDS_TYPE_5: { unsigned char *fp = ((unsigned char*)s)-1; unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc; *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); } break; case SDS_TYPE_8: SDS_HDR(8,s)->len += inc; break; case SDS_TYPE_16: SDS_HDR(16,s)->len += inc; break; case SDS_TYPE_32: SDS_HDR(32,s)->len += inc; break; case SDS_TYPE_64: SDS_HDR(64,s)->len += inc; break; } }
|
参考链接
- c - Redis 源码解读——sds_个人文章 - SegmentFault 思否
- redis源码解读(一):基础数据结构之SDS_czrzchao的博客-CSDN博客
TODO:
定义##操作符