]>
git.saurik.com Git - redis.git/blob - src/bitop.c
2b26265d7be4860a7b5b2b776571fb1c4b54947e
3 /* -----------------------------------------------------------------------------
4 * Helpers and low level bit functions.
5 * -------------------------------------------------------------------------- */
7 /* This helper function used by GETBIT / SETBIT parses the bit offset arguemnt
8 * making sure an error is returned if it is negative or if it overflows
9 * Redis 512 MB limit for the string value. */
10 static int getBitOffsetFromArgument(redisClient
*c
, robj
*o
, size_t *offset
) {
12 char *err
= "bit offset is not an integer or out of range";
14 if (getLongLongFromObjectOrReply(c
,o
,&loffset
,err
) != REDIS_OK
)
17 /* Limit offset to 512MB in bytes */
18 if ((loffset
< 0) || ((unsigned long long)loffset
>> 3) >= (512*1024*1024))
24 *offset
= (size_t)loffset
;
28 /* Count number of bits set in the binary array pointed by 's' and long
29 * 'count' bytes. The implementation of this function is required to
30 * work with a input string length up to 512 MB. */
31 long popcount(void *s
, long count
) {
34 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};
36 /* We can finally count bits. */
37 while(count
--) bits
+= bitsinbyte
[*p
++];
41 /* -----------------------------------------------------------------------------
42 * Bits related string commands: GETBIT, SETBIT, BITCOUNT, BITOP.
43 * -------------------------------------------------------------------------- */
50 /* SETBIT key offset bitvalue */
51 void setbitCommand(redisClient
*c
) {
53 char *err
= "bit is not an integer or out of range";
59 if (getBitOffsetFromArgument(c
,c
->argv
[2],&bitoffset
) != REDIS_OK
)
62 if (getLongFromObjectOrReply(c
,c
->argv
[3],&on
,err
) != REDIS_OK
)
65 /* Bits can only be set or cleared... */
71 o
= lookupKeyWrite(c
->db
,c
->argv
[1]);
73 o
= createObject(REDIS_STRING
,sdsempty());
74 dbAdd(c
->db
,c
->argv
[1],o
);
76 if (checkType(c
,o
,REDIS_STRING
)) return;
78 /* Create a copy when the object is shared or encoded. */
79 if (o
->refcount
!= 1 || o
->encoding
!= REDIS_ENCODING_RAW
) {
80 robj
*decoded
= getDecodedObject(o
);
81 o
= createStringObject(decoded
->ptr
, sdslen(decoded
->ptr
));
82 decrRefCount(decoded
);
83 dbOverwrite(c
->db
,c
->argv
[1],o
);
87 /* Grow sds value to the right length if necessary */
88 byte
= bitoffset
>> 3;
89 o
->ptr
= sdsgrowzero(o
->ptr
,byte
+1);
91 /* Get current values */
92 byteval
= ((char*)o
->ptr
)[byte
];
93 bit
= 7 - (bitoffset
& 0x7);
94 bitval
= byteval
& (1 << bit
);
96 /* Update byte with new bit value and return original value */
97 byteval
&= ~(1 << bit
);
98 byteval
|= ((on
& 0x1) << bit
);
99 ((char*)o
->ptr
)[byte
] = byteval
;
100 signalModifiedKey(c
->db
,c
->argv
[1]);
102 addReply(c
, bitval
? shared
.cone
: shared
.czero
);
105 /* GETBIT key offset */
106 void getbitCommand(redisClient
*c
) {
113 if (getBitOffsetFromArgument(c
,c
->argv
[2],&bitoffset
) != REDIS_OK
)
116 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
117 checkType(c
,o
,REDIS_STRING
)) return;
119 byte
= bitoffset
>> 3;
120 bit
= 7 - (bitoffset
& 0x7);
121 if (o
->encoding
!= REDIS_ENCODING_RAW
) {
122 if (byte
< (size_t)ll2string(llbuf
,sizeof(llbuf
),(long)o
->ptr
))
123 bitval
= llbuf
[byte
] & (1 << bit
);
125 if (byte
< sdslen(o
->ptr
))
126 bitval
= ((char*)o
->ptr
)[byte
] & (1 << bit
);
129 addReply(c
, bitval
? shared
.cone
: shared
.czero
);
132 /* BITOP op_name target_key src_key1 src_key2 src_key3 ... src_keyN */
133 void bitopCommand(redisClient
*c
) {
134 char *opname
= c
->argv
[1]->ptr
;
135 robj
*o
, *targetkey
= c
->argv
[2];
137 unsigned char **src
; /* Array of source strings pointers. */
138 long *len
, maxlen
= 0; /* Array of length of src strings, and max len. */
139 unsigned char *res
= NULL
; /* Resulting string. */
141 /* Parse the operation name. */
142 if ((opname
[0] == 'a' || opname
[0] == 'A') && !strcasecmp(opname
,"and"))
144 else if((opname
[0] == 'o' || opname
[0] == 'O') && !strcasecmp(opname
,"or"))
146 else if((opname
[0] == 'x' || opname
[0] == 'X') && !strcasecmp(opname
,"xor"))
148 else if((opname
[0] == 'n' || opname
[0] == 'N') && !strcasecmp(opname
,"not"))
151 addReply(c
,shared
.syntaxerr
);
155 /* Sanity check: NOT accepts only a single key argument. */
156 if (op
== BITOP_NOT
&& c
->argc
!= 4) {
157 addReplyError(c
,"BITOP NOT must be called with a single source key.");
161 /* Lookup keys, and store pointers to the string objects into an array. */
162 numkeys
= c
->argc
- 3;
163 src
= zmalloc(sizeof(unsigned char*) * numkeys
);
164 len
= zmalloc(sizeof(long) * numkeys
);
165 for (j
= 0; j
< numkeys
; j
++) {
166 o
= lookupKeyRead(c
->db
,c
->argv
[j
+3]);
167 /* Handle non-existing keys as empty strings. */
173 /* Return an error if one of the keys is not a string. */
174 if (checkType(c
,o
,REDIS_STRING
)) {
180 len
[j
] = sdslen(o
->ptr
);
181 if (len
[j
] > maxlen
) maxlen
= len
[j
];
184 /* Compute the bit operation, if at least one string is not empty. */
186 res
= (unsigned char*) sdsnewlen(NULL
,maxlen
);
187 unsigned char output
, byte
;
190 for (j
= 0; j
< maxlen
; j
++) {
191 output
= (len
[0] <= j
) ? 0 : src
[0][j
];
192 if (op
== BITOP_NOT
) output
= ~output
;
193 for (i
= 1; i
< numkeys
; i
++) {
194 byte
= (len
[i
] <= j
) ? 0 : src
[i
][j
];
196 case BITOP_AND
: output
&= byte
; break;
197 case BITOP_OR
: output
|= byte
; break;
198 case BITOP_XOR
: output
^= byte
; break;
207 /* Store the computed value into the target key */
209 o
= createObject(REDIS_STRING
,res
);
210 setKey(c
->db
,targetkey
,o
);
212 } else if (dbDelete(c
->db
,targetkey
)) {
213 signalModifiedKey(c
->db
,targetkey
);
216 addReplyLongLong(c
,maxlen
); /* Return the output string length in bytes. */
219 /* BITCOUNT key [start end] */
220 void bitcountCommand(redisClient
*c
) {
227 /* Lookup, check for type, and return 0 for non existing keys. */
228 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
229 checkType(c
,o
,REDIS_STRING
)) return;
231 /* Set the 'p' pointer to the string, that can be just a stack allocated
232 * array if our string was integer encoded. */
233 if (o
->encoding
== REDIS_ENCODING_INT
) {
234 p
= (unsigned char*) llbuf
;
235 strlen
= ll2string(llbuf
,sizeof(llbuf
),(long)o
->ptr
);
237 p
= (unsigned char*) o
->ptr
;
238 strlen
= sdslen(o
->ptr
);
241 /* Parse start/end range if any. */
243 if (getLongFromObjectOrReply(c
,c
->argv
[2],&start
,NULL
) != REDIS_OK
)
245 if (getLongFromObjectOrReply(c
,c
->argv
[3],&end
,NULL
) != REDIS_OK
)
247 /* Convert negative indexes */
248 if (start
< 0) start
= strlen
+start
;
249 if (end
< 0) end
= strlen
+end
;
250 if (start
< 0) start
= 0;
251 if (end
< 0) end
= 0;
252 if ((unsigned)end
>= strlen
) end
= strlen
-1;
253 } else if (c
->argc
== 2) {
254 /* The whole string. */
259 addReply(c
,shared
.syntaxerr
);
263 /* Precondition: end >= 0 && end < strlen, so the only condition where
264 * zero can be returned is: start > end. */
266 addReply(c
,shared
.czero
);
268 long bytes
= end
-start
+1;
270 addReplyLongLong(c
,popcount(p
+start
,bytes
));