redis 源码intset
intset是集合键的底层实现之一, 保存的元素是有序的。
redis中intset是一个整数集合, 只能存储整数类型的数据, 可以是16位, 32位, 或者是64位, 是以升序排列的数组进行保存数据,下面会介绍具体数据结构和对其操作过程.
数据结构
typedef struct intset {
// 编码方式
uint32_t encoding;
// 集合包含的元素数量
uint32_t length;
// 保存元素的数组
int8_t contents[];
} intset;
**个人理解encoding:**如果想读懂源码,必须读懂这一段
因为redis中intset是一个整数集合, 只能存储整数类型的数据, 可以是16位, 32位, 或者是64位,但是存元素的数组类型是int8_t,所以需要encoding表明数组中存的元素是多少位的,不然存取元素会出错。
这里需要理解编解码函数
void memrev16(void *p) {
unsigned char *x = p, t;
t = x[0];
x[0] = x[1];
x[1] = t;
}
/* Toggle the 32 bit unsigned integer pointed by *p from little endian to
* big endian */
void memrev32(void *p) {
unsigned char *x = p, t;
t = x[0];
x[0] = x[3];
x[3] = t;
t = x[1];
x[1] = x[2];
x[2] = t;
}
/* Toggle the 64 bit unsigned integer pointed by *p from little endian to
* big endian */
void memrev64(void *p) {
unsigned char *x = p, t;
t = x[0];
x[0] = x[7];
x[7] = t;
t = x[1];
x[1] = x[6];
x[6] = t;
t = x[2];
x[2] = x[5];
x[5] = t;
t = x[3];
x[3] = x[4];
x[4] = t;
}
uint16_t intrev16(uint16_t v) {
memrev16(&v);
return v;
}
uint32_t intrev32(uint32_t v) {
memrev32(&v);
return v;
}
uint64_t intrev64(uint64_t v) {
memrev64(&v);
return v;
}
其实可以看出来,编解码只是把值按字符型reverse一下。整个结构体中,不仅仅要使用encoding处理数值,还要用encoding处理length。至于为什么要这样做,是因为
简单来说,在redis的RDB持久化方式中,ziplists, intsets等是直接被写到文件中去的,所以这要求他们在内存中是大小端中性(endian-neutral),而大部分环境是小端法的,于是对于这些压缩的数据结构,redis总是采取小端法的方式来存放字节,那么对于大端法的机器来说,想要得到正确的数,便需要端转换。需要注意的是,反转的仅仅是作为参数传入的在栈上的字节,对于结构体自身的字节并未发生改变。
创建intset函数
intset *intsetNew(void) {
// 为整数集合结构分配空间
intset *is = zmalloc(sizeof(intset));
// 设置初始编码
is->encoding = intrev32ifbe(INTSET_ENC_INT16);