redis整数集合升级扩容(代码学习)

本文探讨了Redis中整数集合在遇到int64_t类型整数时的升级过程,包括类型判断、扩容及填数步骤。在升级过程中,首先检查新增值类型,然后通过`intsetResize`进行扩容,再将数据后移。新增数时,若不改变集合类型则无需升级,但仍需扩容并插入新数。扩容使用`realloc`,移位借助`memmove`以避免覆盖原有内容。实验发现,分配空间时应使用calloc而非malloc。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

redis整数集合有固定的数据类型,比如是整数集合是int32_t类型,新增一个int64_t类型的整数,就需要对整个集合进行升级。

redis的程序值得学习的地方太多。

一、升级

升级分为两步:扩容  +  填数

1.1 新增值的类型判断

static uint8_t _intsetValueEncoding(int64_t v) {
    if (v < INT32_MIN || v > INT32_MAX)
        return INTSET_ENC_INT64;
    else if (v < INT16_MIN || v > INT16_MAX)
        return INTSET_ENC_INT32;
    else
        return INTSET_ENC_INT16;
}

C中int类型是32位的,范围是 INT32_MIN=-2147483648到 INT32_MAX=2147483647 。

1.2 扩容

进行扩容在原有空间上增加1个新增数的位置

is = intsetResize(is,intrev32ifbe(is->length)+1);

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

1.3 填数

数据依次后移

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

static void _intsetSet(intset *is, int pos, int64_t value) {//按照新类型进行填数
    uint32_t encoding = intrev32ifbe(is->encoding);

    if (encoding == INTSET_ENC_INT64) {
        ((int64_t*)is->contents)[pos] = value;
        memrev64ifbe(((int64_t*)is->contents)+pos);
    } 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 int64_t _intsetGetEncoded(intset *is, int pos, uint8_t enc) {//获取当前位置的value
    int64_t v64;
    int32_t v32;
    int16_t v16;

    if (enc == INTSET_ENC_INT64) {
        memcpy(&v64,((int64_t*)is->contents)+pos,sizeof(v64));
        memrev64ifbe(&v64);
        return v64;
    } else if (enc == INTSET_ENC_INT32) {
        memcpy(&v32,((int32_t*)is->contents)+pos,sizeof(v32));
        memrev32ifbe(&v32);//大小端转换函数
        return v32;
    } else {
        memcpy(&v16,((int16_t*)is->contents)+pos,sizeof(v16));
        memrev16ifbe(&v16);
        return v16;
    }
}

附加redis的大小端转换函数

void memrev16(void *p) {
    unsigned char *x = p, t;

    t = x[0];
    x[0] = x[1];
    x[1] = t;
}

/* Toggle the 32 bit unsigned integer pointed by *p from little endian to
 * big endian */
void memrev32(void *p) {
    unsigned char *x = p, t;

    t = x[0];
    x[0] = x[3];
    x[3] = t;
    t = x[1];
    x[1] = x[2];
    x[2] = t;
}

/* Toggle the 64 bit unsigned integer pointed by *p from little endian to
 * big endian */
void memrev64(void *p) {
    unsigned char *x = p, t;

    t = x[0];
    x[0] = x[7];
    x[7] = t;
    t = x[1];
    x[1] = x[6];
    x[6] = t;
    t = x[2];
    x[2] = x[5];
    x[5] = t;
    t = x[3];
    x[3] = x[4];
    x[4] = t;
}

二、新增数

如果只是新增数,不改变集合的数据类型,则不需要升级

2.1 扩容

 同上 is = intsetResize(is,intrev32ifbe(is->length)+1);

2.2 插入新数

找到插入位置,移动其后的所有数。

查找位置

 if (intsetSearch(is,value,&pos)) {
            if (success) *success = 0;
            return is;
        }

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;

    /* The value can never be found when the set is empty */
    if (intrev32ifbe(is->length) == 0) {//集合为空
        if (pos) *pos = 0;
        return 0;
    } else {
        /* Check for the case where we know we cannot find the value,
         * but do know the insert position. */
        //如果value比集合中最后一个值(集合中的最大值)要打,那么插入的位置就应该是最后
        if (value > _intsetGet(is,intrev32ifbe(is->length)-1)) {
            if (pos) *pos = intrev32ifbe(is->length);
            return 0;
        } else if (value < _intsetGet(is,0)) {//value是最小值,那么就插入在最前
            if (pos) *pos = 0;
            return 0;
        }
    }

    while(max >= min) {//二分查找
        mid = (min+max)/2;
        cur = _intsetGet(is,mid);
        if (value > cur) {//查找后半部分
            min = mid+1;
        } else if (value < cur) {//查找前半部分
            max = mid-1;
        } else {
            break;
        }
    }

    if (value == cur) {
        if (pos) *pos = mid;
        return 1;
    } else {
        if (pos) *pos = min;//value不在intset中
        return 0;
    }
}

移动其后的所有数

if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);

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);
}

写个小程序帮助理解

扩容的时候要用realloc函数,否则会覆盖已有内容。

移位的时候使用memmove函数,也是为了避免覆盖已有内容。

#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;

void test()
{   char *str;
    str = (char *) malloc(6);
    strcpy(str, "12345");
    printf("String = %s\n", str);

    str = (char *) realloc(str, sizeof(str)+1);
    memmove(str+4,str+3,strlen(str)-3+1);
    str[3]='a';
    printf("String = %s\n", str);
    printf("%d  %d\n",sizeof(str),strlen(str));
     free(str);
}

int search(int *set,char value, int *pos,int len)
{
    int min =0;
    int max=len;
    int mid;
    int cur;
    while(max>=min){
        printf("min= %d  max= %d  ",min,max);
        mid=(max+min)/2;
        cur=*(set+mid);
        printf("mid =%d  cur= %d  \n",mid,cur);
        if(cur>value){
            max=mid-1;
        }
        else if(cur<value){
            min=mid+1;
        }
        else {
            break;
        }
    }
    if(value==cur){
        if(pos) *pos=mid;
        return 1;
    }
    else{
        if(pos) *pos=min;
        return 0;
    }
}

void printset(int set[],int len){
    int i;
    for(i=0;i<len;i++){
        printf("set[%d]=%d , ",i,*(set+i));
    }
     printf("\n");
}
int main()
{
    test();
    int *set ;
    set= (int*)calloc(6, 4);    
    int i,j=0;
    for (i=0;i<6;i++){
        j+=2;
       set[i] = j;
    }  
    int length=6;
    printset(set,length);
    int pos=0;
    if(!search(set,3,&pos,length)){
       set=(int*)realloc(set,(length+1)*sizeof(int));
       memmove(set+pos+1,set+pos,(length-pos)*sizeof(int));
       set[pos]=3;
       printset(set,length+1);
    }
    return 0;
}

运行结果:在分配空间时,只能用calloc,malloc不可以。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值