zoukankan      html  css  js  c++  java
  • Redis源码之String操作

    0.前言

    String操作是Redis操作中最基本的类型,包含get,set,mget,mset,append等等。下面我们会具体分析下一些命令的详细流程,特么简单的命令没有列出。

    1.SET命令
    2.GET命令
    3.SETBIT命令
    4.GETBIT命令
    5.BTICOUNT命令
    6.BTIPOS命令
    7.BITOP命令

    1.SET命令

    set操作set key value [nx, xx, ex, px], 解析完命令参数后,直接调用setCommand进行相应操作

    void setCommand(redisClient *c) {
        int j;
        robj *expire = NULL;
        int unit = UNIT_SECONDS;
        int flags = REDIS_SET_NO_FLAGS;
       
         /*set key value 占了三个参数,因此从第四个参数开始解析set选项nx,xx,ex,px*/
        for (j = 3; j < c->argc; j++) {
            char *a = c->argv[j]->ptr;
            robj *next = (j == c->argc-1) ? NULL : c->argv[j+1];
            
              /*解析NX,XX,EX,PX标记*/
            if ((a[0] == 'n' || a[0] == 'N') &&
                (a[1] == 'x' || a[1] == 'X') && a[2] == '') {
                flags |= REDIS_SET_NX;
            } else if ((a[0] == 'x' || a[0] == 'X') &&
                       (a[1] == 'x' || a[1] == 'X') && a[2] == '') {
                flags |= REDIS_SET_XX;
            } else if ((a[0] == 'e' || a[0] == 'E') &&
                       (a[1] == 'x' || a[1] == 'X') && a[2] == '' && next) {
                unit = UNIT_SECONDS;
                expire = next;
                j++;
            } else if ((a[0] == 'p' || a[0] == 'P') &&
                       (a[1] == 'x' || a[1] == 'X') && a[2] == '' && next) {
                unit = UNIT_MILLISECONDS;
                expire = next;
                j++;
            } else {
                addReply(c,shared.syntaxerr);
                return;
            }
        }
       
         /*尝试编码字符串,为了节省内存使用共享值或者存储为long类型,可参考《数据结构object》一文*/
        c->argv[2] = tryObjectEncoding(c->argv[2]);
    	
    	/*真正执行set操作函数,下面介绍此函数*/
        setGenericCommand(c,flags,c->argv[1],c->argv[2],expire,unit,NULL,NULL);
    }
    
    void setGenericCommand(redisClient *c, int flags, robj *key, robj *val, robj *expire, int unit, robj *ok_reply, robj *abort_reply) {
        long long milliseconds = 0; /* initialized to avoid any harmness warning */
    
        if (expire) {
              /*将object值取出保存到 milliseconds变量中*/
            if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != REDIS_OK)
                return;
            if (milliseconds <= 0) {
                addReplyErrorFormat(c,"invalid expire time in %s",c->cmd->name);
                return;
            }
            if (unit == UNIT_SECONDS) milliseconds *= 1000;
        }
       
         /*通过查找key值,验证是否符合nx和xx标记,不符合条件直接返回nil*/
        if ((flags & REDIS_SET_NX && lookupKeyWrite(c->db,key) != NULL) ||
            (flags & REDIS_SET_XX && lookupKeyWrite(c->db,key) == NULL))
        {
            addReply(c, abort_reply ? abort_reply : shared.nullbulk);
            return;
        }
         /*设置key,value值,下面进行介绍*/
        setKey(c->db,key,val);
        server.dirty++;
         /*如果设置了生存周期,则设置或更新生存周期*/
        if (expire) setExpire(c->db,key,mstime()+milliseconds);
         /*向订阅键值发生变化事件的客户端,发送事件通知*/
        notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"set",key,c->db->id);
         /*向订阅命令事件的客户端,发送事件通知*/
        if (expire) notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,
            "expire",key,c->db->id);
        addReply(c, ok_reply ? ok_reply : shared.ok);
    }
    
    void setKey(redisDb *db, robj *key, robj *val) {
         /*判断是否已经存在key,存在则覆盖,不存在则添加,放入db->dict字典中*/
        if (lookupKeyWrite(db,key) == NULL) {
            dbAdd(db,key,val);
        } else {
            dbOverwrite(db,key,val);
        }
         /*增加引用*/
        incrRefCount(val);
         /*重置生存周期*/
        removeExpire(db,key);
         /*如果客户端watch此key,则设置标记,打断当前正在执行的事务,后面客户端执行exec时,则将执行失败*/
        signalModifiedKey(db,key);
    }
    
    

    2.GET命令

    get操作get key value, 实在是太简单了,直接从db->dict中通过key取出value值,如果没有找到则返回nil。由于Redis带有生存周期key并不会在生命周期到时立即删除,
    get时首先会判断生存时间是否到期,到期则直接删除,并同步给slave,返回给客户端nil。

    3.SETBIT命令

    setbit操作setbit key bitoffset bitval, setbit其实就是对一段内存指定的偏移量上设置一个bit位,直接调用setbitCommand函数进行处理。

    void setbitCommand(redisClient *c) {
        robj *o;
        char *err = "bit is not an integer or out of range";
        size_t bitoffset;
        int byte, bit;
        int byteval, bitval;
        long on;
       
         /*bit偏移量,偏移量小于512M,取值时进行了判断*/
        if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
            return;
       
         /*设置值0或1*/
        if (getLongFromObjectOrReply(c,c->argv[3],&on,err) != REDIS_OK)
            return;
    
        /* 判断值有效性,只能0或1 */
        if (on & ~1) {
            addReplyError(c,err);
            return;
        }
    
        o = lookupKeyWrite(c->db,c->argv[1]);
        if (o == NULL) {
            o = createObject(REDIS_STRING,sdsempty());
            dbAdd(c->db,c->argv[1],o);
        } else {
            if (checkType(c,o,REDIS_STRING)) return;
              /*保证object独占的非共享,判断如果是共享的string,则重新创建新的string覆盖原来的string */
            o = dbUnshareStringValue(c->db,c->argv[1],o);
        }
    
        /*计算偏移位置,右移三位相当于对8取整*/
        byte = bitoffset >> 3;
         /*判断字符串长度是否足够,不够则增加长度并置为0*/
        o->ptr = sdsgrowzero(o->ptr,byte+1);
    
        /*首先取出原来位置一个字节保存的值*/
        byteval = ((uint8_t*)o->ptr)[byte];
         /*bit代表一个字节内从右向左的偏移量*/
        bit = 7 - (bitoffset & 0x7);
         /*取出原来bit位上值,后面返回给客户端*/
        bitval = byteval & (1 << bit);
    
        /*取出此字节上其余7bit位值*/
        byteval &= ~(1 << bit);
         /*更新客户端需要更新的bit位的值*/
        byteval |= ((on & 0x1) << bit);
        ((uint8_t*)o->ptr)[byte] = byteval;
        signalModifiedKey(c->db,c->argv[1]);
        notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"setbit",c->argv[1],c->db->id);
        server.dirty++;
        addReply(c, bitval ? shared.cone : shared.czero);
    }
    

    4.GETBIT命令

    getbit操作getbit key bitoffset, 比较简单,直接从一块内存上指定的偏移量上取出bit位值

    void getbitCommand(redisClient *c) {
        robj *o;
        char llbuf[32];
        size_t bitoffset;
        size_t byte, bit;
        size_t bitval = 0;
    
        if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
            return;
    
        if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
            checkType(c,o,REDIS_STRING)) return;
            
         /*计算bit位偏移位置*/
        byte = bitoffset >> 3;
        bit = 7 - (bitoffset & 0x7);
        if (o->encoding != REDIS_ENCODING_RAW) {
              /*非二进制编码,就是REDIS_ENCODING_INT编码,转码为字符串取值*/
            if (byte < (size_t)ll2string(llbuf,sizeof(llbuf),(long)o->ptr))
                bitval = llbuf[byte] & (1 << bit);
        } else {
              /*二进制编码直接取值*/
            if (byte < sdslen(o->ptr))
                bitval = ((uint8_t*)o->ptr)[byte] & (1 << bit);
        }
    
        addReply(c, bitval ? shared.cone : shared.czero);
    }
    

    5.BITCOUNT命令

    bitcount操作bitcount key [start, end], 统计start到end二进制数据中,start和end的单位是字节,bit为1的个数,使用了几种算法,调用bitcountCommand函数进行处理。

    void bitcountCommand(redisClient *c) {
        robj *o;
        long start, end, strlen;
        unsigned char *p;
        char llbuf[32];
    
        /* 通过key值查找字符串 */
        if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
            checkType(c,o,REDIS_STRING)) return;
    
        /* 获取字符串长度, 如果编码是REDIS_ENCODING_INT, 转换为临时字符串*/
        if (o->encoding == REDIS_ENCODING_INT) {
            p = (unsigned char*) llbuf;
            strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);
        } else {
            p = (unsigned char*) o->ptr;
            strlen = sdslen(o->ptr);
        }
    
        /* 解析用户输入的start和end参数 */
        if (c->argc == 4) {
            if (getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != REDIS_OK)
                return;
            if (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK)
                return;
            /* Convert negative indexes */
            if (start < 0) start = strlen+start;
            if (end < 0) end = strlen+end;
            if (start < 0) start = 0;
            if (end < 0) end = 0;
            if (end >= strlen) end = strlen-1;
        } else if (c->argc == 2) {
            /* The whole string. */
            start = 0;
            end = strlen-1;
        } else {
            /* Syntax error. */
            addReply(c,shared.syntaxerr);
            return;
        }
    
        if (start > end) {
            addReply(c,shared.czero);
        } else {
            long bytes = end-start+1;
             
              /*调用redisPopcount函数计算bit位1的个数, 下面介绍*/
            addReplyLongLong(c,redisPopcount(p+start,bytes));
        }
    }
    

    计算bit为1的个数函数, redis采用了查表法和平行算法, 统计bit位为1的个数算法很多也很有趣,可以参考博客

    size_t redisPopcount(void *s, long count) {
        size_t bits = 0;
        unsigned char *p = s;
        uint32_t *p4;
    	
    	/*bit位值存储表,列出了一个字节的所有值*/
        static const unsigned char bitsinbyte[256] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8};
    
        /* 判断指针的初始位置是否32bit对齐, 如果没有对齐首先计算没有对齐的部分, 保证32bit对齐, 提高后面算法性能 */
        while((unsigned long)p & 3 && count) {
              /*使用查表法统计bit为1的个数*/
            bits += bitsinbyte[*p++];
            count--;
        }
    
        /* 每次对16个字节进行统计 */
        p4 = (uint32_t*)p;
        while(count>=16) {
            uint32_t aux1, aux2, aux3, aux4;
    
            aux1 = *p4++;
            aux2 = *p4++;
            aux3 = *p4++;
            aux4 = *p4++;
            count -= 16;
             
              /*首先计算相邻两个bit位中1的个数*/
            aux1 = aux1 - ((aux1 >> 1) & 0x55555555);
              /*计算相邻四位bit位中1的个数*/
            aux1 = (aux1 & 0x33333333) + ((aux1 >> 2) & 0x33333333);
            aux2 = aux2 - ((aux2 >> 1) & 0x55555555);
            aux2 = (aux2 & 0x33333333) + ((aux2 >> 2) & 0x33333333);
            aux3 = aux3 - ((aux3 >> 1) & 0x55555555);
            aux3 = (aux3 & 0x33333333) + ((aux3 >> 2) & 0x33333333);
            aux4 = aux4 - ((aux4 >> 1) & 0x55555555);
            aux4 = (aux4 & 0x33333333) + ((aux4 >> 2) & 0x33333333);
                     /*
                          *(aux1 + (aux1 >> 4))计算一个字节内1的个数, 和0x0F0F0F0F进行与运算提取出每个字节中计算结果, 和0x01010101相乘相当于对每个字节进行加和,
                          *结果保存在最高一个字节中, 右移24位, 取出计算结果.
                          */
            bits += ((((aux1 + (aux1 >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24) +
                    ((((aux2 + (aux2 >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24) +
                    ((((aux3 + (aux3 >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24) +
                    ((((aux4 + (aux4 >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24);
        }
        /* 计算剩余数据中1的个数 */
        p = (unsigned char*)p4;
        while(count--) bits += bitsinbyte[*p++];
        return bits;
    }
    

    6.BITPOS命令

    bitpos命令bitpos bit [start, end], 查找一段二进制中start到end中0或者1第一次出现的位置,start和end单位是字节,调用bitposCommand函数处理。

    void bitposCommand(redisClient *c) {
        robj *o;
        long bit, start, end, strlen;
        unsigned char *p;
        char llbuf[32];
        int end_given = 0;
    
        /* 获取查找的bit位值,0或者1*/
        if (getLongFromObjectOrReply(c,c->argv[2],&bit,NULL) != REDIS_OK)
            return;
        if (bit != 0 && bit != 1) {
            addReplyError(c, "The bit argument must be 1 or 0.");
            return;
        }
    
        /* 如果key不存在,查找bit为0,则返回0 ,否则返回-1*/
        if ((o = lookupKeyRead(c->db,c->argv[1])) == NULL) {
            addReplyLongLong(c, bit ? -1 : 0);
            return;
        }
        if (checkType(c,o,REDIS_STRING)) return;
    
    	/* 获取字符串长度, 如果编码是REDIS_ENCODING_INT, 转换为临时字符串*/
        if (o->encoding == REDIS_ENCODING_INT) {
            p = (unsigned char*) llbuf;
            strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);
        } else {
            p = (unsigned char*) o->ptr;
            strlen = sdslen(o->ptr);
        }
    
        /* 解析用户输入的start和end参数,参数是字节为单位*/
        if (c->argc == 4 || c->argc == 5) {
            if (getLongFromObjectOrReply(c,c->argv[3],&start,NULL) != REDIS_OK)
                return;
            if (c->argc == 5) {
                if (getLongFromObjectOrReply(c,c->argv[4],&end,NULL) != REDIS_OK)
                    return;
                end_given = 1;
            } else {
                end = strlen-1;
            }
            /* Convert negative indexes */
            if (start < 0) start = strlen+start;
            if (end < 0) end = strlen+end;
            if (start < 0) start = 0;
            if (end < 0) end = 0;
            if (end >= strlen) end = strlen-1;
        } else if (c->argc == 3) {
            /* The whole string. */
            start = 0;
            end = strlen-1;
        } else {
            /* Syntax error. */
            addReply(c,shared.syntaxerr);
            return;
        }
    
        if (start > end) {
            addReplyLongLong(c, -1);
        } else {
            long bytes = end-start+1;
    		/*调用redisBitpos函数计算bit位位置,下面介绍*/
            long pos = redisBitpos(p+start,bytes,bit);
    
            if (end_given && bit == 0 && pos == bytes*8) {
                addReplyLongLong(c,-1);
                return;
            }
    		
    		/*最后结果要加上先前用户给出的start开始长度*/
            if (pos != -1) pos += start*8; /* Adjust for the bytes we skipped. */
            addReplyLongLong(c,pos);
        }
    }
    
    long redisBitpos(void *s, unsigned long count, int bit) {
        unsigned long *l;
        unsigned char *c;
        unsigned long skipval, word = 0, one;
        long pos = 0; /* Position of bit, to return to the caller. */
        unsigned long j;
    	
    	/*
    	 *首先判断字节是否按sizeof(*l)对齐,不对齐则一个一个字节进行比较,直到sizeof(*l)对齐或者是count为0
    	 */
        skipval = bit ? 0 : UCHAR_MAX;
        c = (unsigned char*) s;
        while((unsigned long)c & (sizeof(*l)-1) && count) {
            if (*c != skipval) break;
            c++;
            count--;
            pos += 8;
        }
    
        /*
    	 *如果字节对齐时,count值没有为0,则按照sizeof(*l)长度进行查找,加快查找速度,特别是对大块的连续0或者1
    	 */
        skipval = bit ? 0 : ULONG_MAX;
        l = (unsigned long*) c;
        while (count >= sizeof(*l)) {
            if (*l != skipval) break;
            l++;
            count -= sizeof(*l);
            pos += sizeof(*l)*8;
        }
    	
    	/*查找结束后,将查找最后的到值以大端形式写入word,并不一定找到*/
        c = (unsigned char*)l;
        for (j = 0; j < sizeof(*l); j++) {
            word <<= 8;
            if (count) {
                word |= *c;
                c++;
                count--;
            }
        }
    	
    	/*bit为1时,没有找到直接返回-1*/
        if (bit == 1 && word == 0) return -1;
    
    	/*
    	 *设置one最高位位1,其他位为0, 用于取出word中每一位
    	 */
        one = ULONG_MAX; /* All bits set to 1.*/
        one >>= 1;       /* All bits set to 1 but the MSB. */
        one = ~one;      /* All bits set to 0 but the MSB. */
    	
    	/*
    	 *依次取出word中每一位进行比较,直到找到bit
    	 */
        while(one) {
    		/*这里比较设计的很巧妙,节省了一个变量记录移动位数*/
            if (((one & word) != 0) == bit) return pos;
            pos++;
            one >>= 1;
        }
    	
    	/*
    	 *这里并不会到达,上面已经把所有情况考虑到了
    	 */
    	
        redisPanic("End of redisBitpos() reached.");
        return 0; /* Just to avoid warnings. */
    }
    

    7.BITOP命令

    bitop操作bitop operation destkey key [key ...], operation可以是and, or, xor, not,对后面列出的key二进制数据之间进行operation运算,结果保存在destkey中,操作时间复杂度O(N),数据量过大可能会非常耗时,调用bitopCommand函数处理。

    void bitopCommand(redisClient *c) {
        char *opname = c->argv[1]->ptr;
        robj *o, *targetkey = c->argv[2];
        unsigned long op, j, numkeys;
        robj **objects;      /* Array of source objects. */
        unsigned char **src; /* Array of source strings pointers. */
        unsigned long *len, maxlen = 0; /* Array of length of src strings,
                                           and max len. */
        unsigned long minlen = 0;    /* Min len among the input keys. */
        unsigned char *res = NULL; /* Resulting string. */
    
        /* 解析运算类型,与,或,异或,非*/
        if ((opname[0] == 'a' || opname[0] == 'A') && !strcasecmp(opname,"and"))
            op = BITOP_AND;
        else if((opname[0] == 'o' || opname[0] == 'O') && !strcasecmp(opname,"or"))
            op = BITOP_OR;
        else if((opname[0] == 'x' || opname[0] == 'X') && !strcasecmp(opname,"xor"))
            op = BITOP_XOR;
        else if((opname[0] == 'n' || opname[0] == 'N') && !strcasecmp(opname,"not"))
            op = BITOP_NOT;
        else {
            addReply(c,shared.syntaxerr);
            return;
        }
    
        /* 操作为非时,key只能有一个*/
        if (op == BITOP_NOT && c->argc != 4) {
            addReplyError(c,"BITOP NOT must be called with a single source key.");
            return;
        }
    
        /* 计算key数量,然后为所有key分配一个数组,后面运算时使用 */
        numkeys = c->argc - 3;
        src = zmalloc(sizeof(unsigned char*) * numkeys);
        len = zmalloc(sizeof(long) * numkeys);
        objects = zmalloc(sizeof(robj*) * numkeys);
        for (j = 0; j < numkeys; j++) {
            o = lookupKeyRead(c->db,c->argv[j+3]);
            /* 如果找不到key对应的值,则赋值空字符串*/
            if (o == NULL) {
                objects[j] = NULL;
                src[j] = NULL;
                len[j] = 0;
                minlen = 0;
                continue;
            }
            /* 参与运行的key对应值,必须为字符串,非字符串直接返回错误 */
            if (checkType(c,o,REDIS_STRING)) {
                unsigned long i;
                for (i = 0; i < j; i++) {
                    if (objects[i])
                        decrRefCount(objects[i]);
                }
                zfree(src);
                zfree(len);
                zfree(objects);
                return;
            }
            objects[j] = getDecodedObject(o);
            src[j] = objects[j]->ptr;
            len[j] = sdslen(objects[j]->ptr);
    		/*利用循环直接计算出key对应的字符串最大长度和最小长度*/
            if (len[j] > maxlen) maxlen = len[j];
            if (j == 0 || len[j] < minlen) minlen = len[j];
        }
    
        /* 至少有一个string非空,才进行计算操作 */
        if (maxlen) {
            res = (unsigned char*) sdsnewlen(NULL,maxlen);
            unsigned char output, byte;
            unsigned long i;
    
            /* 如果key少于16个,为什么是16可能是经验值,就执行下面算法,算法多了一次copy操作,但循环次数变少了,一次循环运算4*sizeof(unsigned long)个字节*/
            j = 0;
            if (minlen && numkeys <= 16) {
                unsigned long *lp[16];
                unsigned long *lres = (unsigned long*) res;
    			
    			/*sds字符串本身8字节对齐,不用在进行对齐操作*/
                memcpy(lp,src,sizeof(unsigned long*)*numkeys);
    			/*copy第一个key中value值,以备后面进行运算*/
                memcpy(res,src[0],minlen);
    
                /* 根据不同操作进行相应计算,每次运算4*sizeof(unsigned long)字节 */
                if (op == BITOP_AND) {
                    while(minlen >= sizeof(unsigned long)*4) {
                        for (i = 1; i < numkeys; i++) {
                            lres[0] &= lp[i][0];
                            lres[1] &= lp[i][1];
                            lres[2] &= lp[i][2];
                            lres[3] &= lp[i][3];
                            lp[i]+=4;
                        }
                        lres+=4;
                        j += sizeof(unsigned long)*4;
                        minlen -= sizeof(unsigned long)*4;
                    }
                } else if (op == BITOP_OR) {
                    while(minlen >= sizeof(unsigned long)*4) {
                        for (i = 1; i < numkeys; i++) {
                            lres[0] |= lp[i][0];
                            lres[1] |= lp[i][1];
                            lres[2] |= lp[i][2];
                            lres[3] |= lp[i][3];
                            lp[i]+=4;
                        }
                        lres+=4;
                        j += sizeof(unsigned long)*4;
                        minlen -= sizeof(unsigned long)*4;
                    }
                } else if (op == BITOP_XOR) {
                    while(minlen >= sizeof(unsigned long)*4) {
                        for (i = 1; i < numkeys; i++) {
                            lres[0] ^= lp[i][0];
                            lres[1] ^= lp[i][1];
                            lres[2] ^= lp[i][2];
                            lres[3] ^= lp[i][3];
                            lp[i]+=4;
                        }
                        lres+=4;
                        j += sizeof(unsigned long)*4;
                        minlen -= sizeof(unsigned long)*4;
                    }
                } else if (op == BITOP_NOT) {
                    while(minlen >= sizeof(unsigned long)*4) {
                        lres[0] = ~lres[0];
                        lres[1] = ~lres[1];
                        lres[2] = ~lres[2];
                        lres[3] = ~lres[3];
                        lres+=4;
                        j += sizeof(unsigned long)*4;
                        minlen -= sizeof(unsigned long)*4;
                    }
                }
            }
    
            /* 这里既处理上面算法中剩余的字节,也处理key数量大于16时,使用的是最原始的方式一个字节一个字节进行运算 */
            for (; j < maxlen; j++) {
                output = (len[0] <= j) ? 0 : src[0][j];
                if (op == BITOP_NOT) output = ~output;
                for (i = 1; i < numkeys; i++) {
                    byte = (len[i] <= j) ? 0 : src[i][j];
                    switch(op) {
                    case BITOP_AND: output &= byte; break;
                    case BITOP_OR:  output |= byte; break;
                    case BITOP_XOR: output ^= byte; break;
                    }
                }
                res[j] = output;
            }
        }
        for (j = 0; j < numkeys; j++) {
            if (objects[j])
                decrRefCount(objects[j]);
        }
        zfree(src);
        zfree(len);
        zfree(objects);
    
        /* 如果至少一个非空,则将结果存入targetkey中,否则在targetkey存在情况下执行删除操作 */
        if (maxlen) {
            o = createObject(REDIS_STRING,res);
            setKey(c->db,targetkey,o);
            notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"set",targetkey,c->db->id);
            decrRefCount(o);
        } else if (dbDelete(c->db,targetkey)) {
            signalModifiedKey(c->db,targetkey);
            notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",targetkey,c->db->id);
        }
        server.dirty++;
    	
    	/*返回给客户端字符串最大长度*/
        addReplyLongLong(c,maxlen); 
    }
    
  • 相关阅读:
    软件工程个人作业02
    第三周学习进度条
    《构建之法》阅读笔记01
    第二周学习进度条
    软件工程个人作业01
    大道至简第三章读后感
    动手动脑课后实践
    JAVA数字想加并输出
    大道至简第二章读后感
    大道至简读后感
  • 原文地址:https://www.cnblogs.com/ourroad/p/4883703.html
Copyright © 2011-2022 走看看