Redis源码学习 --> 基本数据结构 --> Intset

1. 什么是Intset?

Intset​​顾名思义就是整数集合。是Set数据类型的底层结构之一。

2. 为什么用Intset?

核心目的是节省空间,且只能在元素数量少的情况使用。

3. Intset结构分析

Intset的结构很简单,代码如下:

typedef struct intset {
    uint32_t encoding;   // 编码方式, 即元素占用的字节数(只能取2、4、8)
    uint32_t length;     // 元素个数
    int8_t contents[];   // 有符号整数 数组,类型取决于encoding
} intset;

contents内的元素是同一类型,实际只有{INT16、INT32、INT64}三种。元素按从小到大排列,方便二分法的查找元素。
结构图如下:

Intset-1

编码时尽可能选择最小的一种。

  • 当新增元素时,如果元素值超过类型的上限值,则编码升级。(如: INT16 编码插入了1000000超过 INT16_MAX)
  • 当删除元素时,任何情况不进行降级,防止编码反复切换。

4. 增删查的操作代码

查询操作:

static uint8_t intsetSearch(intset *is, int64_t value, uint32_t *pos) {
    int min = 0, max = intrev32ifbe(is->length)-1, mid = -1;
    int64_t cur = -1;

    /* The value can never be found when the set is empty */
    if (intrev32ifbe(is->length) == 0) {
        if (pos) *pos = 0;
        return 0;
    } else {
        /* Check for the case where we know we cannot find the value,
         * but do know the insert position. */
        if (value > _intsetGet(is,max)) {
            if (pos) *pos = intrev32ifbe(is->length);
            return 0;
        } else if (value < _intsetGet(is,0)) {
            if (pos) *pos = 0;
            return 0;
        }
    }

    /* 典型的二分法查找 */
    while(max >= min) {
        mid = ((unsigned int)min + (unsigned int)max) >> 1;
        cur = _intsetGet(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;
    }
}

增加操作:

intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {
    uint8_t valenc = _intsetValueEncoding(value);
    uint32_t pos;
    if (success) *success = 1;

    if (valenc > intrev32ifbe(is->encoding)) {
        /* 需要升级则重新分配空间, 拷贝数据, 并将新值添加到末尾 */
        return intsetUpgradeAndAdd(is,value);
    } else {
        /* 查找元素, 如果存在则返回失败 */
        if (intsetSearch(is,value,&pos)) {
            if (success) *success = 0;
            return is;
        }

        /* 扩容放新元素, 将插入位置后面的元素memmove()右移一个数 */
        is = intsetResize(is,intrev32ifbe(is->length)+1);
        if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);
    }

    _intsetSet(is,pos,value);
    is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
    return is;
}

删除操作:

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 (success) *success = 1;

        /* 将删除的元素后的所有元素memmove()左移一个数 */
        if (pos < (len-1)) intsetMoveTail(is,pos+1,pos);
        is = intsetResize(is,len-1);  // 缩容节省空间
        is->length = intrev32ifbe(len-1);
    }
    return is;
}
posted @ 2025-09-17 22:15  -蓝蜗牛-  阅读(52)  评论(0)    收藏  举报