整数集合是redis集合的实现方式之一
主要好处是灵活、节约内存空间
整数集合类定义
集合种存储的数据宽度是根据encoding来定义的,如果等于INTSET_ENC_INT16数据宽度为16位,INTSET_ENC_INT32是32位,INTSET_ENC_INT64是32位
typedef struct intset {
//编码方式
//主要有3种编码格式:INTSET_ENC_INT16、INTSET_ENC_INT32、INTSET_ENC_INT64
uint32_t encoding;
//集合中的数量
uint32_t length;
//保存的值
int8_t contents[];
} intset;
整数集合结构图
如下图所示:
encoding = INSET_ENC_INT16标示contents中存储的数据宽度为16位,length=5 标示当前集合中有5个元素
contents存储具体的数据
整数集合的特点
- 整数集合不会预分配空间,每次添加数据,都会重新计算encoding,重新分配空间
- 整数集合不会降级
代码分析
可以从以下代码看出,不论encoding是否变化,只要插入元素就会重新分配内存
//向整数集合中插入数据
intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {
//计算新增值的encoding
uint8_t valenc = _intsetValueEncoding(value);
uint32_t pos;
if (success) *success = 1;
//如果新增的值的encoding大于当前整数集合的encoding
if (valenc > intrev32ifbe(is->encoding)) {
return intsetUpgradeAndAdd(is,value);
} else {
//返回当前值应该插入的位置,如果已存在直接返回
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;
}
//升级encoding并插入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;
//设置新的encoding
is->encoding = intrev32ifbe(newenc);
//重新分配空间
is = intsetResize(is,intrev32ifbe(is->length)+1);
//因为是upgrade,所以新增的数据只可能在两端,如果prepend=1则预留第一个位置,否则预留最后一个位置
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);
return is;
}
//重新分配整数集合的大小
static intset *intsetResize(intset *is, uint32_t len) {
//计算元素大小
uint32_t size = len*intrev32ifbe(is->encoding);
is = zrealloc(is,sizeof(intset)+size);
return is;
}
void *zrealloc(void *ptr, size_t size) {
#ifndef HAVE_MALLOC_SIZE
void *realptr;
#endif
size_t oldsize;
void *newptr;
if (size == 0 && ptr != NULL) {
zfree(ptr);
return NULL;
}
if (ptr == NULL) return zmalloc(size);
#ifdef HAVE_MALLOC_SIZE
oldsize = zmalloc_size(ptr);
newptr = realloc(ptr,size);
if (!newptr) zmalloc_oom_handler(size);
update_zmalloc_stat_free(oldsize);
update_zmalloc_stat_alloc(zmalloc_size(newptr));
return newptr;
#else
realptr = (char*)ptr-PREFIX_SIZE;
oldsize = *((size_t*)realptr);
newptr = realloc(realptr,size+PREFIX_SIZE);
if (!newptr) zmalloc_oom_handler(size);
*((size_t*)newptr) = size;
update_zmalloc_stat_free(oldsize+PREFIX_SIZE);
update_zmalloc_stat_alloc(size+PREFIX_SIZE);
return (char*)newptr+PREFIX_SIZE;
#endif
}