intsetAdd
<span style="font-family:Courier New;">//添加一个整数
intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {
uint8_t valenc = _intsetValueEncoding(value); //得到类型的长度
uint32_t pos;
if (success) *success = 1;
/* Upgrade encoding if necessary. If we need to upgrade, we know that
* this value should be either appended (if > 0) or prepended (if < 0),
* because it lies outside the range of existing values. */
//需要升级,那么进行升级并插入新值
if (valenc > intrev32ifbe(is->encoding)) {
/* This always succeeds, so we don't need to curry *success. */
return intsetUpgradeAndAdd(is,value);
} else {//否则
/* Abort if the value is already present in the set.
* This call will populate "pos" with the right position to insert
* the value when it cannot be found. */
//如果该值在集合中已经存在,那么直接返回
if (intsetSearch(is,value,&pos)) {
if (success) *success = 0;
return is;
}
is = intsetResize(is,intrev32ifbe(is->length)+1);
//将从pos位置后面的值全部向后偏移一个位置,为新元素空出位置
if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);
}
_intsetSet(is,pos,value);//添加新元素
is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
return is;
}</span>
intsetAdd函数添加一个元素value时,首先根据value的字节数与当前intset的encoding进行比较,分析intset是否需要升级,若需要升级则调用intsetUpdateAndAdd函数处理,否则如果value已存在intset中直接pass,不存在,那么先resize,接着将插入位置之后的所有元素向后偏移,添加value。
intsetMoveTail
<span style="font-family:Courier New;">/**使用memmove对集合进行向后偏移,下标从0开始,并且已经Resize
例:前 | 1 | 2 | 3 | 4 | 5 | 6 | | |
from = 1, to = 3
length = 6
src = | 2 | 3 | 4 | 5 | 6 |
dst = | 4 | 5 | 6 | | |
bytes = 5 * sizeof(...)
后 | 1 | 2 | 3 | 2 | 3 | 4 | 5 | 6 |
偏移之前肯定需要用intsetResize函数,进行扩容,增加两个容量
如果不理解前后的变化,建议查看memmove源码,这里需要考虑到内存覆盖的问题
也就是为什么必须使用memmove而不能使用memcpy的原因
*/
static void intsetMoveTail(intset *is, uint32_t from, uint32_t to) {
void *src, *dst;
uint32_t bytes = intrev32ifbe(is->length)-from;
uint32_t encoding = intrev32ifbe(is->encoding);
if (encoding == INTSET_ENC_INT64) {
src = (int64_t*)is->contents+from;
dst = (int64_t*)is->contents+to;
bytes *= sizeof(int64_t);
} else if (encoding == INTSET_ENC_INT32) {
src = (int32_t*)is->contents+from;
dst = (int32_t*)is->contents+to;
bytes *= sizeof(int32_t);
} else {
src = (int16_t*)is->contents+from;
dst = (int16_t*)is->contents+to;
bytes *= sizeof(int16_t);
}
memmove(dst,src,bytes);
}</span>
intsetUpdateAndAdd
//对编码类型进行升级,O(n)
//需要插入的值,要么比当前集合中的最大值大,要么比集合中的最小值小,不然不需要升级
//比最大值大还是小,只需要根据value的正负即可判断
static intset *intsetUpgradeAndAdd(intset *is, int64_t value) {
uint8_t curenc = intrev32ifbe(is->encoding); //当前编码类型
uint8_t newenc = _intsetValueEncoding(value);//新的编码类型
int length = intrev32ifbe(is->length);
int prepend = value < 0 ? 1 : 0;//决定新的值插入的位置(1表示头,0表示尾)
/* First set new encoding and resize */
is->encoding = intrev32ifbe(newenc); //设置编码类型
is = intsetResize(is,intrev32ifbe(is->length)+1);//resize
/* Upgrade back-to-front so we don't overwrite values.
* Note that the "prepend" variable is used to make sure we have an empty
* space at either the beginning or the end of the intset. */
//通过_intsetGetEncoded得到升级前的该位置的整数值
//设置原来的整数集的值,如果prepend=1表示新值在头插入,那么原来的数值全部向后偏移
while(length--)
_intsetSet(is,length+prepend,_intsetGetEncoded(is,length,curenc));
/* Set the value at the beginning or the end. */
if (prepend) //在头插入
_intsetSet(is,0,value);
else //在尾插入
_intsetSet(is,intrev32ifbe(is->length),value);
is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
return is;
}
intsetRemove
[cpp] view plaincopyprint?
//删除一个整数
intset *intsetRemove(intset *is, int64_t value, int *success) {
uint8_t valenc = _intsetValueEncoding(value);
uint32_t pos;
if (success) *success = 0;
//value在原集合中
if (valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,&pos)) {
uint32_t len = intrev32ifbe(is->length);
/* We know we can delete */
if (success) *success = 1;
/* Overwrite value with tail and update length */
//如果 pos 不是 is 的最末尾,直接通过memmove内存覆盖的方式删除该整数值
//如果是末尾,直接resize删除
if (pos < (len-1)) intsetMoveTail(is,pos+1,pos);
is = intsetResize(is,len-1);//将空间缩小
is->length = intrev32ifbe(len-1);
}
return is;
} intset添加元素流程图