工作生活

「Redis源码解读」—数据结构(四)整数集合

2019-06-30  本文已影响0人  wh4763

知识点

typedef struct intset {
    //encoding 指定了编码方式,有 INTSET_ENC_INT16、INTSET_ENC_INT32、INTSET_ENC_INT64 三种。三者表示的范围是 16位整数、32位整数 以及 64位整数。
    uint32_t encoding; 
    //length 存储了整数集合的元素个数     
    uint32_t length;        
    //整数集合中的元素按照从小到大的顺序在 contents 中排列起来
    int8_t contents[];     
} intset;

编码升级

创建集合

创建一个整数集合 intsetNew,实现在 intset.c 中:

intset *intsetNew(void) {
    intset *is = zmalloc(sizeof(intset));
    is->encoding = intrev32ifbe(INTSET_ENC_INT16);
    is->length = 0;
    return is;
}

元素设置

static void _intsetSet(intset *is, int pos, int64_t value) {
    uint32_t encoding = intrev32ifbe(is->encoding);          /* a */
 
    if (encoding == INTSET_ENC_INT64) {
        ((int64_t*)is->contents)[pos] = value;               /* b */
        memrev64ifbe(((int64_t*)is->contents)+pos);          /* c */
    } else if (encoding == INTSET_ENC_INT32) {
        ((int32_t*)is->contents)[pos] = value;
        memrev32ifbe(((int32_t*)is->contents)+pos);
    } else {
        ((int16_t*)is->contents)[pos] = value;
        memrev16ifbe(((int16_t*)is->contents)+pos);
    }
}

元素查找

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;
    if (intrev32ifbe(is->length) == 0) {
        //整数集合为空,返回0表示查找失败
        if (pos) *pos = 0;                                        
        return 0;
    } else {                                                      
        //value 的值比整数集合中的最大值还大,或者比最小值还小,则返回0表示查找失败
        if (value > _intsetGet(is,intrev32ifbe(is->length)-1)) {
            if (pos) *pos = intrev32ifbe(is->length);
            return 0;
        } else if (value < _intsetGet(is,0)) {
            if (pos) *pos = 0;
            return 0;
        }
    }
    while(max >= min) {                                          
        //执行二分查找,将找到的值存在 cur 中
        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;
        }
    }
    //如果找到则返回1,表示查找成功,并且将 pos 设置为 mid 并返回;如果没找到则返回一个需要插入的位置
    if (value == cur) {                                           
        if (pos) *pos = mid;
        return 1;
    } else {
        if (pos) *pos = min;
        return 0;
    }
}

内存分配

static intset *intsetResize(intset *is, uint32_t len) {
    uint32_t size = len*intrev32ifbe(is->encoding);
    is = zrealloc(is,sizeof(intset)+size);
    return is;
}

编码升级

static intset *intsetUpgradeAndAdd(intset *is, int64_t value) {
    uint8_t curenc = intrev32ifbe(is->encoding);
    uint8_t newenc = _intsetValueEncoding(value);                             
    int length = intrev32ifbe(is->length);
    // curenc 记录升级前的编码,newenc 记录升级后的编码
    int prepend = value < 0 ? 1 : 0;                                       
    is->encoding = intrev32ifbe(newenc);
    //将整数集合 is 的编码设置成新的编码后,进行内存重分配
    is = intsetResize(is,intrev32ifbe(is->length)+1);                      
    while(length--)
        //获取原先内存中的数据,设置到新内存中
        _intsetSet(is,length+prepend,_intsetGetEncoded(is,length,curenc));   
    if (prepend)
        _intsetSet(is,0,value);
    else
        // 当插入的值 value 为负数的时候,为了保证集合的有序性,需要插入到 contents 的头部;反之,插入到尾部;当 value 为负数时 prepend 为1,这样就可以保证在内存拷贝的时候将第 0 个位置留空
        _intsetSet(is,intrev32ifbe(is->length),value);                       
    is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
    return is;
}

元素插入

intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {
    uint8_t valenc = _intsetValueEncoding(value);
    uint32_t pos;
    if (success) *success = 1;
    // 插入的数值 value 的内存编码大于现有集合的编码,直接调用 intsetUpgradeAndAdd 进行编码升级
    if (valenc > intrev32ifbe(is->encoding)) {                               
        return intsetUpgradeAndAdd(is,value);
    } else {
        if (intsetSearch(is,value,&pos)) {                                
            //集合元素是不重复的,如果 intsetSearch 能够找到,则将 success 置为0,表示此次插入失败
            if (success) *success = 0;                                  
            return is;
        }
        //如果 intsetSearch 找不到,将 intset 进行内存重分配,即 长度 加 1
        is = intsetResize(is,intrev32ifbe(is->length)+1);                    
        //pos 为 intsetSearch 过程中找到的 value 将要插入的位置,我们将 pos 以后的内存向后移动1个单位
        if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);    
    }
    _intsetSet(is,pos,value);                                                 
    //调用 _intsetSet 将 value 的值设置到 pos 的位置上,然后给成员变量 length 加 1
    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;
    //当整数集合中存在 value 这个元素时才能执行删除操作
    if (valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,&pos)) { 
        uint32_t len = intrev32ifbe(is->length);
        if (success) *success = 1;
        //如果能通过 intsetSearch 找到元素,那么它的位置就在 pos 上
        if (pos < (len-1)) intsetMoveTail(is,pos+1,pos);            
        //intsetResize 重新分配内存,并且将集合长度减1           
        is = intsetResize(is,len-1);                                            
        is->length = intrev32ifbe(len-1); 
    }
    return is;
}
上一篇 下一篇

猜你喜欢

热点阅读