From 0e31eaa27f995fead02b861a7df403771ce772d2 Mon Sep 17 00:00:00 2001 From: antirez Date: Thu, 27 Feb 2014 12:53:03 +0100 Subject: [PATCH] More consistent BITPOS behavior with bit=0 and ranges. With the new behavior it is possible to specify just the start in the range (the end will be assumed to be the first byte), or it is possible to specify both start and end. This is useful to change the behavior of the command when looking for zeros inside a string. 1) If the user specifies both start and end, and no 0 is found inside the range, the command returns -1. 2) If instead no range is specified, or just the start is given, even if in the actual string no 0 bit is found, the command returns the first bit on the right after the end of the string. So for example if the string stored at key foo is "\xff\xff": BITPOS foo (returns 16) BITPOS foo 0 -1 (returns -1) BITPOS foo 0 (returns 16) The idea is that when no end is given the user is just looking for the first bit that is zero and can be set to 1 with SETBIT, as it is "available". Instead when a specific range is given, we just look for a zero within the boundaries of the range. --- src/bitops.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/bitops.c b/src/bitops.c index 7715a1ff..a87ed041 100644 --- a/src/bitops.c +++ b/src/bitops.c @@ -505,12 +505,13 @@ void bitcountCommand(redisClient *c) { } } -/* BITPOS key bit [start end] */ +/* BITPOS key bit [start [end]] */ void bitposCommand(redisClient *c) { robj *o; long bit, start, end, strlen; unsigned char *p; char llbuf[32]; + int end_given = 0; /* Parse the bit argument to understand what we are looking for, set * or clear bits. */ @@ -541,11 +542,16 @@ void bitposCommand(redisClient *c) { } /* Parse start/end range if any. */ - if (c->argc == 5) { + if (c->argc == 4 || c->argc == 5) { if (getLongFromObjectOrReply(c,c->argv[3],&start,NULL) != REDIS_OK) return; - if (getLongFromObjectOrReply(c,c->argv[4],&end,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; @@ -570,14 +576,14 @@ void bitposCommand(redisClient *c) { long bytes = end-start+1; long pos = redisBitpos(p+start,bytes,bit); - /* If we are looking for clear bits, and our range does not includes - * the end of the string, but terminates before, we can't consider the - * right of the range as zero padded. + /* If we are looking for clear bits, and the user specified an exact + * range with start-end, we can't consider the right of the range as + * zero padded (as we do when no explicit end is given). * - * so if redisBitpos() returns the first bit outside the string, + * So if redisBitpos() returns the first bit outside the range, * we return -1 to the caller, to mean, in the specified range there * is not a single "0" bit. */ - if (end != strlen-1 && bit == 0 && pos == bytes*8) { + if (end_given && bit == 0 && pos == bytes*8) { addReplyLongLong(c,-1); return; }