其存储结构如下所示:
点击(此处)折叠或打开
-
typedef struct intset {
-
uint32_t encoding; // 编码规则,具体见下面三个宏定义
-
uint32_t length; // 所存元素个数
-
int8_t contents[]; // 元素存储位置
-
} intset;
-
#define INTSET_ENC_INT16 (sizeof(int16_t))
-
#define INTSET_ENC_INT32 (sizeof(int32_t))
- #define INTSET_ENC_INT64 (sizeof(int64_t))
整个intset里面比较好玩的就是插入元素了。由于有三种不同的编码方式,当插入的新元素所占的空间较大,大到目前intset自身的编码无法承载这一数据的时候,intset就会根据新插入的值得大小,将原有的inset的encoding变为需要的,其具体步骤如下:
- 根据插入的元素类型,适当扩展intset所占的空间
- 在维持低层数组存储数据不变的基础上,按照从后到前的顺序(避免覆盖)拷贝到新的位置上
- 将新添加的元素插入到intset当中
点击(此处)折叠或打开
-
/* Insert an integer in the intset */
-
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);
-
if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);
-
}
-
-
_intsetSet(is,pos,value);
-
is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
-
return is;
- }
点击(此处)折叠或打开
-
/* Upgrades the intset to a larger encoding and inserts the given integer. */
-
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;
-
-
/* First set new encoding and resize */
-
is->encoding = intrev32ifbe(newenc);
-
is = intsetResize(is,intrev32ifbe(is->length)+1);
-
-
/* 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.
-
* 通俗的将就是对新插入的元素,我们应该插在头部还是插在尾部 */
-
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;
- }
删除操作的话,逻辑就是能否找到,若找到的话,就直接将后面的元素移动过来,直接覆盖即可
点击(此处)折叠或打开
-
/* Delete integer from intset */
-
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);
-
-
/* We know we can delete */
-
if (success) *success = 1;
-
-
/* Overwrite value with tail and update length */
-
if (pos < (len-1)) intsetMoveTail(is,pos+1,pos);
-
is = intsetResize(is,len-1);
-
is->length = intrev32ifbe(len-1);
-
}
-
return is;
- }