一觉醒来才发现,刚刚的一篇文章,貌似忘了写这个东西,补上,顺便给自己增增速,
bitfield命令起始同之前的bitops的操作差不多,只不过它是提供了针对同一个值的不同段,一次性做多个操作,最终返回一个数组作为结果,其命令形式如下:
BITFIELD key [GET type offset] [SET type offset value] [INCRBY type offset increment] [OVERFLOW WRAP|SAT|FAIL]
由上面的命令可知,BITFIELD命令供支持三种自命令,并设置了溢出处理方式:
1. GET type offset,在指定的偏移量,拿出指定类型的数据
2. SET type offset value, 在指定的偏移量,设置指定类型的数据
3. INCRBY type offset increment,在指定的偏移量,为指定类型增加increment
4. OVERFLOW WRAP|SAT|FAIL, 处理溢出的方式:回环(默认),满足,失败;
具体操作示例如下:
理解了其命令之后,下面来看看代码:
bitfield是将每一个子命令都保存到一个对应的结构当中去,结构如下:
-
/* BITFIELD key subcommmand-1 arg ... subcommand-2 arg ... subcommand-N ...
-
*
-
* Supported subcommands:
-
*
-
* GET <type> <offset>
-
* SET <type> <offset> <value>
-
* INCRBY <type> <offset> <increment>
-
* OVERFLOW [WRAP|SAT|FAIL]
-
*/
-
-
struct bitfieldOp {
-
uint64_t offset; /* Bitfield offset. */
-
int64_t i64; /* Increment amount (INCRBY) or SET value */
-
int opcode; /* Operation id. */
-
int owtype; /* Overflow type to use. */
-
int bits; /* Integer bitfield bits width. */
-
int sign; /* True if signed, otherwise unsigned op. */
-
};
执行逻辑也就明了,先解析出子命令,然后在key对应的value上进行相应操作,遇到溢出要按照指示处理,主要代码如下:
-
void bitfieldCommand(client *c) {
-
robj *o;
-
size_t bitoffset;
-
int j, numops = 0, changes = 0;
-
struct bitfieldOp *ops = NULL; /* Array of ops to execute at end. */
-
int owtype = BFOVERFLOW_WRAP; /* Overflow type. */
-
int readonly = 1;
-
size_t higest_write_offset = 0;
-
-
for (j = 2; j < c->argc; j++) {
-
int remargs = c->argc-j-1; /* Remaining args other than current. */
-
char *subcmd = c->argv[j]->ptr; /* Current command name. */
-
int opcode; /* Current operation code. */
-
long long i64 = 0; /* Signed SET value. */
-
int sign = 0; /* Signed or unsigned type? */
-
int bits = 0; /* Bitfield width in bits. */
-
-
if (!strcasecmp(subcmd,"get") && remargs >= 2)
-
opcode = BITFIELDOP_GET;
-
else if (!strcasecmp(subcmd,"set") && remargs >= 3)
-
opcode = BITFIELDOP_SET;
-
else if (!strcasecmp(subcmd,"incrby") && remargs >= 3)
-
opcode = BITFIELDOP_INCRBY;
-
else if (!strcasecmp(subcmd,"overflow") && remargs >= 1) {
-
char *owtypename = c->argv[j+1]->ptr;
-
j++;
-
if (!strcasecmp(owtypename,"wrap"))
-
owtype = BFOVERFLOW_WRAP;
-
else if (!strcasecmp(owtypename,"sat"))
-
owtype = BFOVERFLOW_SAT;
-
else if (!strcasecmp(owtypename,"fail"))
-
owtype = BFOVERFLOW_FAIL;
-
else {
-
addReplyError(c,"Invalid OVERFLOW type specified");
-
zfree(ops);
-
return;
-
}
-
continue;
-
} else {
-
addReply(c,shared.syntaxerr);
-
zfree(ops);
-
return;
-
}
-
-
/* Get the type and offset arguments, common to all the ops. */
-
if (getBitfieldTypeFromArgument(c,c->argv[j+1],&sign,&bits) != C_OK) {
-
zfree(ops);
-
return;
-
}
-
-
if (getBitOffsetFromArgument(c,c->argv[j+2],&bitoffset,1,bits) != C_OK){
-
zfree(ops);
-
return;
-
}
-
-
if (opcode != BITFIELDOP_GET) {
-
readonly = 0;
-
if (higest_write_offset < bitoffset + bits - 1)
-
higest_write_offset = bitoffset + bits - 1;
-
/* INCRBY and SET require another argument. */
-
if (getLongLongFromObjectOrReply(c,c->argv[j+3],&i64,NULL) != C_OK){
-
zfree(ops);
-
return;
-
}
-
}
-
-
/* Populate the array of operations we'll process. */
-
ops = zrealloc(ops,sizeof(*ops)*(numops+1));
-
ops[numops].offset = bitoffset;
-
ops[numops].i64 = i64;
-
ops[numops].opcode = opcode;
-
ops[numops].owtype = owtype;
-
ops[numops].bits = bits;
-
ops[numops].sign = sign;
-
numops++;
-
-
j += 3 - (opcode == BITFIELDOP_GET);
-
}
-
-
if (readonly) {
-
/* Lookup for read is ok if key doesn't exit, but errors
-
* if it's not a string. */
-
o = lookupKeyRead(c->db,c->argv[1]);
-
if (o != NULL && checkType(c,o,OBJ_STRING)) return;
-
} else {
-
/* Lookup by making room up to the farest bit reached by
-
* this operation. */
-
if ((o = lookupStringForBitCommand(c,
-
higest_write_offset)) == NULL) return;
-
}
-
-
addReplyMultiBulkLen(c,numops);
-
-
/* Actually process the operations. */
-
for (j = 0; j < numops; j++) {
-
struct bitfieldOp *thisop = ops+j;
-
-
/* Execute the operation. */
-
if (thisop->opcode == BITFIELDOP_SET ||
-
thisop->opcode == BITFIELDOP_INCRBY)
-
{
-
/* SET and INCRBY: We handle both with the same code path
-
* for simplicity. SET return value is the previous value so
-
* we need fetch & store as well. */
-
-
/* We need two different but very similar code paths for signed
-
* and unsigned operations, since the set of functions to get/set
-
* the integers and the used variables types are different. */
-
if (thisop->sign) {
-
int64_t oldval, newval, wrapped, retval;
-
int overflow;
-
-
oldval = getSignedBitfield(o->ptr,thisop->offset,
-
thisop->bits);
-
-
if (thisop->opcode == BITFIELDOP_INCRBY) {
-
newval = oldval + thisop->i64;
-
overflow = checkSignedBitfieldOverflow(oldval,
-
thisop->i64,thisop->bits,thisop->owtype,&wrapped);
-
if (overflow) newval = wrapped;
-
retval = newval;
-
} else {
-
newval = thisop->i64;
-
overflow = checkSignedBitfieldOverflow(newval,
-
0,thisop->bits,thisop->owtype,&wrapped);
-
if (overflow) newval = wrapped;
-
retval = oldval;
-
}
-
-
/* On overflow of type is "FAIL", don't write and return
-
* NULL to signal the condition. */
-
if (!(overflow && thisop->owtype == BFOVERFLOW_FAIL)) {
-
addReplyLongLong(c,retval);
-
setSignedBitfield(o->ptr,thisop->offset,
-
thisop->bits,newval);
-
} else {
-
addReply(c,shared.nullbulk);
-
}
-
} else {
-
uint64_t oldval, newval, wrapped, retval;
-
int overflow;
-
-
oldval = getUnsignedBitfield(o->ptr,thisop->offset,
-
thisop->bits);
-
-
if (thisop->opcode == BITFIELDOP_INCRBY) {
-
newval = oldval + thisop->i64;
-
overflow = checkUnsignedBitfieldOverflow(oldval,
-
thisop->i64,thisop->bits,thisop->owtype,&wrapped);
-
if (overflow) newval = wrapped;
-
retval = newval;
-
} else {
-
newval = thisop->i64;
-
overflow = checkUnsignedBitfieldOverflow(newval,
-
0,thisop->bits,thisop->owtype,&wrapped);
-
if (overflow) newval = wrapped;
-
retval = oldval;
-
}
-
/* On overflow of type is "FAIL", don't write and return
-
* NULL to signal the condition. */
-
if (!(overflow && thisop->owtype == BFOVERFLOW_FAIL)) {
-
addReplyLongLong(c,retval);
-
setUnsignedBitfield(o->ptr,thisop->offset,
-
thisop->bits,newval);
-
} else {
-
addReply(c,shared.nullbulk);
-
}
-
}
-
changes++;
-
} else {
-
/* GET */
-
unsigned char buf[9];
-
long strlen = 0;
-
unsigned char *src = NULL;
-
char llbuf[LONG_STR_SIZE];
-
-
if (o != NULL)
-
src = getObjectReadOnlyString(o,&strlen,llbuf);
-
-
/* For GET we use a trick: before executing the operation
-
* copy up to 9 bytes to a local buffer, so that we can easily
-
* execute up to 64 bit operations that are at actual string
-
* object boundaries. */
-
memset(buf,0,9);
-
int i;
-
size_t byte = thisop->offset >> 3;
-
for (i = 0; i < 9; i++) {
-
if (src == NULL || i+byte >= (size_t)strlen) break;
-
buf[i] = src[i+byte];
-
}
-
-
/* Now operate on the copied buffer which is guaranteed
-
* to be zero-padded. */
-
if (thisop->sign) {
-
int64_t val = getSignedBitfield(buf,thisop->offset-(byte*8),
-
thisop->bits);
-
addReplyLongLong(c,val);
-
} else {
-
uint64_t val = getUnsignedBitfield(buf,thisop->offset-(byte*8),
-
thisop->bits);
-
addReplyLongLong(c,val);
-
}
-
}
-
}
-
-
if (changes) {
-
signalModifiedKey(c->db,c->argv[1]);
-
notifyKeyspaceEvent(NOTIFY_STRING,"setbit",c->argv[1],c->db->id);
-
server.dirty += changes;
-
}
-
zfree(ops);
-
}
阅读(2814) | 评论(0) | 转发(0) |