整数集合(Intset)是Redis中的一种紧凑型数据结构,专门用于存储整数集合。这种集合通过数组保存数据,并根据需要动态调整数组的大小和整数的存储格式(16位、32位或64位),从而实现高效的存储和查询。
主要结构
整数集合的基本结构包括一个头部和一个整数数组。头部包含了整数集合的元数据,如编码方式、元素数量等。数组部分则用于存储实际的整数数据。
头部结构
c
typedef struct intset {
uint32_t encoding; // 编码方式:表示当前使用的整数类型(16位、32位或64位)
uint32_t length; // 集合中的元素数量
int8_t contents[]; // 整数数组,实际存储数据的地方
} intset;
主要操作
1. 创建整数集合
创建一个新的整数集合并初始化。
c
intset *intsetNew(void) {
intset *is = zmalloc(sizeof(intset));
is->encoding = intrev32ifbe(INTSET_ENC_INT16); // 默认编码方式为16位整数
is->length = 0; // 初始集合长度为0
return is;
}
2. 查找操作
查找一个整数是否存在于集合中。
c
uint8_t intsetFind(intset *is, int64_t value) {
uint32_t pos;
if (intsetSearch(is, value, &pos)) {
return 1;
}
return 0;
}
int intsetSearch(intset *is, int64_t value, uint32_t *pos) {
int min = 0, max = intrev32ifbe(is->length) - 1, mid = -1;
int64_t cur;
if (intrev32ifbe(is->length) == 0) {
if (pos) *pos = 0;
return 0;
}
while (max >= min) {
mid = (min + max) / 2;
cur = intsetGetEncoded(is, mid);
if (value > cur) {
min = mid + 1;
} else if (value < cur) {
max = mid - 1;
} else {
break;
}
}
if (value == cur) {
if (pos) *pos = mid;
return 1;
} else {
if (pos) *pos = min;
return 0;
}
}
3. 插入操作
向整数集合中插入一个整数。根据需要调整编码方式并重新分配内存。
c
intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {
uint8_t valenc = _intsetValueEncoding(value);
uint32_t pos;
if (success) *success = 1;
// 查找插入位置
if (intsetSearch(is, value, &pos)) {
if (success) *success = 0;
return is;
}
// 如果需要更大的编码格式
if (valenc > intrev32ifbe(is->encoding)) {
return intsetUpgradeAndAdd(is, value);
}
// 为新元素分配空间
is = intsetResize(is, intrev32ifbe(is->length) + 1);
// 移动元素以腾出插入位置
if (pos < intrev32ifbe(is->length)) {
intsetMoveTail(is, pos, intrev32ifbe(is->length) - pos);
}
// 插入新元素
intsetSet(is, pos, value);
is->length = intrev32ifbe(intrev32ifbe(is->length) + 1);
return is;
}
static uint8_t _intsetValueEncoding(int64_t v) {
if (v < INT32_MIN || v > INT32_MAX) return INTSET_ENC_INT64;
if (v < INT16_MIN || v > INT16_MAX) return INTSET_ENC_INT32;
return INTSET_ENC_INT16;
}
4. 删除操作
从整数集合中删除一个整数。
c
intset *intsetRemove(intset *is, int64_t value, int *success) {
uint8_t valenc = _intsetValueEncoding(value);
uint32_t pos;
if (success) *success = 0;
if (valenc <= intrev32ifbe(is->encoding) && intsetSearch(is, value, &pos)) {
uint32_t len = intrev32ifbe(is->length);
// 移动尾部元素以覆盖被删除元素
if (pos < (len - 1)) {
intsetMoveTail(is, pos + 1, len - (pos + 1));
}
is = intsetResize(is, len - 1);
is->length = intrev32ifbe(len - 1);
if (success) *success = 1;
}
return is;
}
辅助函数
获取和设置元素
c
static int64_t intsetGetEncoded(intset *is, int pos) {
int64_t v64;
int32_t v32;
int16_t v16;
uint32_t encoding = intrev32ifbe(is->encoding);
if (encoding == INTSET_ENC_INT64) {
memcpy(&v64, ((int64_t*)is->contents) + pos, sizeof(v64));
return intrev64ifbe(v64);
} else if (encoding == INTSET_ENC_INT32) {
memcpy(&v32, ((int32_t*)is->contents) + pos, sizeof(v32));
return intrev32ifbe(v32);
} else {
memcpy(&v16, ((int16_t*)is->contents) + pos, sizeof(v16));
return intrev16ifbe(v16);
}
}
static void intsetSet(intset *is, int pos, int64_t value) {
uint32_t encoding = intrev32ifbe(is->encoding);
if (encoding == INTSET_ENC_INT64) {
((int64_t*)is->contents)[pos] = intrev64ifbe(value);
} else if (encoding == INTSET_ENC_INT32) {
((int32_t*)is->contents)[pos] = intrev32ifbe(value);
} else {
((int16_t*)is->contents)[pos] = intrev16ifbe(value);
}
}
调整内存大小
c
intset *intsetResize(intset *is, uint32_t len) {
uint32_t size = len * intrev32ifbe(is->encoding);
is = zrealloc(is, sizeof(intset) + size);
return is;
}
示例使用
c
int main() {
intset *is = intsetNew();
uint8_t success;
// 插入整数
is = intsetAdd(is, 10, &success);
is = intsetAdd(is, 20, &success);
is = intsetAdd(is, 30, &success);
// 查找整数
if (intsetFind(is, 20)) {
printf("20 exists in the set.\n");
}
// 删除整数
is = intsetRemove(is, 20, &success);
if (!intsetFind(is, 20)) {
printf("20 has been removed from the set.\n");
}
return 0;
}
总结:整数集合(Intset)通过紧凑的内存布局和灵活的编码方式实现了高效的存储和查询操作。它适用于存储小范围的整数集合,通过精心设计的数据结构,保证了高效的插入、删除和查找操作。