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

相关推荐
薄雾晚晴10 小时前
大模型Skill暴论:所有Skill终将消亡?最新Skill方法论与模型增强开发实战
前端·后端·架构
MacroZheng10 小时前
面试官皱眉:"你懂 Vibe Coding,那你说superpowers和grill-me怎么选?",我:"小孩才做选择,我全都要!"
java·人工智能·后端
千里码aicood10 小时前
flask-基于注意力机制的异常流量检测与自动防御系统
后端·python·flask
李迟10 小时前
Golang实践录:工程框架实践:搭建工程模板
开发语言·后端·golang
Lynko10 小时前
C语言指针高阶
后端
the局外人10 小时前
学习 FastAPI 的 Day 2:用异步 ORM 完成增删改查
后端·python·fastapi
SamDeepThinking10 小时前
if嵌套最好控制在3层以内
java·后端·程序员
苏渡苇11 小时前
Spring Insight 里上报 Span 为什么用 JDK HttpClient 而不是 RestTemplate
java·后端·spring·spring cloud·springboot·性能监控
tonydf11 小时前
AI驱动历史项目安全审查
后端·ai编程
她的男孩11 小时前
同一个接口,为什么销售只看到自己的订单?我拆了 681 行数据权限拦截器的源码
java·后端·架构