Redis(131)Redis的整数集合(Intset)是如何实现的?

整数集合(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)通过紧凑的内存布局和灵活的编码方式实现了高效的存储和查询操作。它适用于存储小范围的整数集合,通过精心设计的数据结构,保证了高效的插入、删除和查找操作。

相关推荐
q***82912 小时前
Spring Boot 热部署
java·spring boot·后端
yuuki2332333 小时前
【数据结构】栈
c语言·数据结构·后端
程序猿小蒜6 小时前
基于springboot的共享汽车管理系统开发与设计
java·开发语言·spring boot·后端·spring·汽车
q***46527 小时前
在2023idea中如何创建SpringBoot
java·spring boot·后端
hygge9997 小时前
Spring Boot + MyBatis 整合与 MyBatis 原理全解析
java·开发语言·经验分享·spring boot·后端·mybatis
q***13617 小时前
十七:Spring Boot依赖 (2)-- spring-boot-starter-web 依赖详解
前端·spring boot·后端
q***25217 小时前
Spring Boot接收参数的19种方式
java·spring boot·后端
WX-bisheyuange7 小时前
基于Spring Boot的民谣网站的设计与实现
java·spring boot·后端
q***14647 小时前
Spring Boot文件上传
java·spring boot·后端