简单动态字符串(SDS, Simple Dynamic String)
redis里面将SDS用作redis的默认字符串表示。包含字符串的键值对在底层的实现都是由SDS实现的。
arduino
//sds.h
/* Note: sdshdr5 is never used, we just access the flags byte directly.
* However is here to document the layout of type 5 SDS strings. */
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[];
};
定义了5种用于实现 Redis 的 SDS (Simple Dynamic String,简单动态字符串) 数据结构的结构体。 所有结构体都使用了 __attribute__ ((__packed__))
,这意味着结构体成员会紧密排列,不进行内存对齐填充,以适应高效的内存管理需求。
内存设计
__attribute__
是关键字,表示这是一个编译器属性, 是 GCC(GNU Compiler Collection) 提供的一个 编译器指令(Compiler Directive) ,用于向编译器传递额外的信息,控制变量、函数、结构体等的编译行为。- 括号
((...))
内部是具体的属性名称和参数(如packed
、aligned
、noreturn
等) packed
是__attribute__
的一个具体属性,用于 取消结构体(struct)或联合体(union)的内存对齐优化 ,使其成员在内存中 紧密排列(packed) ,不插入任何填充字节(padding)
结构体类型
共有 5 种结构体,分别用于不同长度的字符串:
sdshdr5
:未实际使用,仅用于文档说明sdshdr8
:用于短字符串(长度 ≤ 2^8-1)sdshdr16
:用于中等长度字符串(长度 ≤ 2^16-1)sdshdr32
:用于长字符串(长度 ≤ 2^32-1)sdshdr64
:用于超长字符串(长度 ≤ 2^64-1)
arduino
//sds.h
#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
#define SDS_TYPE_MASK 7
#define SDS_TYPE_BITS 3
宏定义
scss
#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(s) (((unsigned char)(s[-1])) >> SDS_TYPE_BITS)
#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T)));
: 用于声明并初始化一个指向 SDS 头结构的指针变量 sh
, T
是 SDS 类型(如 8, 16, 32, 64), s
是指向字符串数据的指针, ##
是连接符,将 sdshdr
和 T
连接起来形成结构体名(如 sdshdr8
), 计算方式:用字符串指针 s
减去头结构的大小,得到头结构的起始地址,结果:声明了一个 sh
变量,指向 SDS 的头结构
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
: 与第一个类似,但不声明变量,直接返回头结构指针,同样通过字符串指针 s
减去头结构大小来计算头结构地址,结果:返回一个指向 SDS 头结构的指针
#define SDS_TYPE_5_LEN(s) (((unsigned char)(s[-1])) >> SDS_TYPE_BITS)
: 专门用于 SDS 类型5(特殊的小字符串优化类型), 它从字符串前一个字节(s[-1]
)中提取长度信息,SDS_TYPE_BITS
是类型占用的位数(对于类型5是3), 右移操作 >> SDS_TYPE_BITS
是为了去掉低3位的类型信息,只保留长度部分,结果:返回类型5 SDS 字符串的长度
结构体成员
对于实际使用的 sdshdr8
到 sdshdr64
,它们包含相同的成员,只是数据类型不同:
len
:当前字符串的实际长度(已使用的字节数)alloc
:分配的总内存大小(不包括头部和空终止符)flags
:标志字节- 低 3 位表示 SDS 类型(0-4 对应 sdshdr5 到 sdshdr64)
- 高 5 位未使用(在 sdshdr5 中用于存储长度)
buf
:柔性数组,实际存储字符串内容(包含空终止符 '\0')
sdshdr5 的特殊性
sdshdr5
没有被实际使用,它的设计是:
flags
字节:- 低 3 位表示类型(0b000)
- 高 5 位存储字符串长度(因此最大长度为 31)
- 没有单独的
len
和alloc
字段