保证没有重复;整数集合的结构体如下:

typedef struct intset {  
    uint32_t encoding;   /* 编码方式 */
    uint32_t length;     /* 集合的元素数量 */
    int8_t contents[];   /* 保存元素的数组 */
} intset;

按照大小升序排列,而且要求数组数字的唯一性,不允许重复。

length 记录了整数集合的元素个数,也就是contents[]数组的长度,所以长度获取复杂度为 O(1)。
         contents[] 是一个柔性数组,虽然结构体数组声明的类型为 int8_t,但是存储的时候并不是 int8_t 类型,而是取决于 encoding 属性值,以下为各种属性值对应数字大小范围:
         INTSET_ENC_INT16,那么 contents[] 数组的类型就是 int16_t,那么 数组的内存长度就是 sizeof(int16_t) * length,数组内数字大小范围为 -32 768 ~ 32 767
         INTSET_ENC_INT32,那么 contents[] 数组的类型就是 int32_t,那么 数组的内存长度就是 sizeof(int32_t) * length,数组内数字大小范围为 -2 147 483 648 ~ 2 147 483 647
         INTSET_ENC_INT64,那么 contents[] 数组的类型就是 int64_t,那么 数组的内存长度就是 sizeof(int64_t) * length,数组内数字大小范围为 -9 223 372 036 854 775 808 ~ 9 223 372 036 854 775 807

        其中需要注意的是,intset可能会随着数据的添加而改变它的数据编码:
        最开始,新创建的intset使用占内存最小的INTSET_ENC_INT16(值为2)作为数据编码。每添加一个新元素,则根据元素大小决定是否对数据编码进行升级。

        在添加每个元素的过程中,intset始终保持从小到大有序。
        与ziplist类似,intset也是按小端(little endian)模式存储的。

插入

intset *intsetAdd(intset *is, int64_t value, uint8_t *success)

        获取新数值的类型_intsetValueEncoding
        如果新加入的数值大于原编码类型大小,要升级编码类型intsetUpgradeAndAdd,把原来集合的每个元素取出来,再用新的编码重新写入新的位置
        在集合中查找intsetSearch,如果已存在则终止操作,不存在则返回数值要保存的位置pos
为集合重新分配大小intsetResize,这个操作会引发内存realloc,可能带来一次数据拷贝
        如果在集合中间插入数值,需要移动pos后边元素intsetMoveTail(memmove(dst,src,bytes),memmove保证了拷贝过程中不会造成数据重叠或覆盖)
        插入新元素_intsetSet
        集合元素个数+1
        算法总的时间复杂度O(n)
        注意:intsetAdd返回一个intset指针,调用方必须使用这个返回的intset,替换传进来的旧的intset

升级

static intset *intsetUpgradeAndAdd(intset *is, int64_t value)

        判断新数值是否小于0,新数值肯定是绝对值最大得数,不是放表头就是放表尾
调整数组大小
        从后往前遍历集合,把原来的值往升级后的位置移动

while(length--)
        _intsetSet(is,length+prepend,_intsetGetEncoded(is,length,curenc));

 根据数值的正负判断,将新元素插入表头或表尾

if (prepend)
        _intsetSet(is,0,value);
    else
        _intsetSet(is,intrev32ifbe(is->length),value);
    is->length = intrev32ifbe(intrev32ifbe(is->length)+1);

查找插入位置
static uint8_t intsetSearch(intset *is, int64_t value, uint32_t *pos) 

        如果集合为空,返回pos = 0
        如果value大于集合中最大值,则返回pos = is->length
        如果value小于集合中最小值,则返回pos = 0, 否则二分法查找value插入位置:

while (max >= min) {
    mid = (min + max) >> 1;
    cur = _intsetGet(is, mid);
    if (value > cur) {
        min = mid + 1;
    } else if (value < cur) {
        max = mid - 1;
    } else {
        break;
    }
}

        如果找到了value,用pos存value的位置 pos  = mid
        如果没找到,则pos = value应该插入的位置 pos = min