]>
git.saurik.com Git - redis.git/blob - src/bitops.c
053db02b5cf51451ef0010cc8fb8a0ec6b54170b
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
) {
35 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};
37 /* Count bits four bytes at a time */
42 /* Unsigned int is always >= 4 bytes if compiler is GCC */
43 bits
+= __builtin_popcount(aux
);
45 bits
+= bitsinbyte
[aux
&0xff] +
46 bitsinbyte
[(aux
>>8)&0xff] +
47 bitsinbyte
[(aux
>>16)&0xff] +
48 bitsinbyte
[(aux
>>24)&0xff];
51 /* Count the remaining bytes */
52 p
= (unsigned char*)p4
;
53 while(count
--) bits
+= bitsinbyte
[*p
++];
57 /* -----------------------------------------------------------------------------
58 * Bits related string commands: GETBIT, SETBIT, BITCOUNT, BITOP.
59 * -------------------------------------------------------------------------- */
66 /* SETBIT key offset bitvalue */
67 void setbitCommand(redisClient
*c
) {
69 char *err
= "bit is not an integer or out of range";
75 if (getBitOffsetFromArgument(c
,c
->argv
[2],&bitoffset
) != REDIS_OK
)
78 if (getLongFromObjectOrReply(c
,c
->argv
[3],&on
,err
) != REDIS_OK
)
81 /* Bits can only be set or cleared... */
87 o
= lookupKeyWrite(c
->db
,c
->argv
[1]);
89 o
= createObject(REDIS_STRING
,sdsempty());
90 dbAdd(c
->db
,c
->argv
[1],o
);
92 if (checkType(c
,o
,REDIS_STRING
)) return;
94 /* Create a copy when the object is shared or encoded. */
95 if (o
->refcount
!= 1 || o
->encoding
!= REDIS_ENCODING_RAW
) {
96 robj
*decoded
= getDecodedObject(o
);
97 o
= createStringObject(decoded
->ptr
, sdslen(decoded
->ptr
));
98 decrRefCount(decoded
);
99 dbOverwrite(c
->db
,c
->argv
[1],o
);
103 /* Grow sds value to the right length if necessary */
104 byte
= bitoffset
>> 3;
105 o
->ptr
= sdsgrowzero(o
->ptr
,byte
+1);
107 /* Get current values */
108 byteval
= ((char*)o
->ptr
)[byte
];
109 bit
= 7 - (bitoffset
& 0x7);
110 bitval
= byteval
& (1 << bit
);
112 /* Update byte with new bit value and return original value */
113 byteval
&= ~(1 << bit
);
114 byteval
|= ((on
& 0x1) << bit
);
115 ((char*)o
->ptr
)[byte
] = byteval
;
116 signalModifiedKey(c
->db
,c
->argv
[1]);
118 addReply(c
, bitval
? shared
.cone
: shared
.czero
);
121 /* GETBIT key offset */
122 void getbitCommand(redisClient
*c
) {
129 if (getBitOffsetFromArgument(c
,c
->argv
[2],&bitoffset
) != REDIS_OK
)
132 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
133 checkType(c
,o
,REDIS_STRING
)) return;
135 byte
= bitoffset
>> 3;
136 bit
= 7 - (bitoffset
& 0x7);
137 if (o
->encoding
!= REDIS_ENCODING_RAW
) {
138 if (byte
< (size_t)ll2string(llbuf
,sizeof(llbuf
),(long)o
->ptr
))
139 bitval
= llbuf
[byte
] & (1 << bit
);
141 if (byte
< sdslen(o
->ptr
))
142 bitval
= ((char*)o
->ptr
)[byte
] & (1 << bit
);
145 addReply(c
, bitval
? shared
.cone
: shared
.czero
);
148 /* BITOP op_name target_key src_key1 src_key2 src_key3 ... src_keyN */
149 void bitopCommand(redisClient
*c
) {
150 char *opname
= c
->argv
[1]->ptr
;
151 robj
*o
, *targetkey
= c
->argv
[2];
153 unsigned char **src
; /* Array of source strings pointers. */
154 long *len
, maxlen
= 0; /* Array of length of src strings, and max len. */
155 unsigned char *res
= NULL
; /* Resulting string. */
157 /* Parse the operation name. */
158 if ((opname
[0] == 'a' || opname
[0] == 'A') && !strcasecmp(opname
,"and"))
160 else if((opname
[0] == 'o' || opname
[0] == 'O') && !strcasecmp(opname
,"or"))
162 else if((opname
[0] == 'x' || opname
[0] == 'X') && !strcasecmp(opname
,"xor"))
164 else if((opname
[0] == 'n' || opname
[0] == 'N') && !strcasecmp(opname
,"not"))
167 addReply(c
,shared
.syntaxerr
);
171 /* Sanity check: NOT accepts only a single key argument. */
172 if (op
== BITOP_NOT
&& c
->argc
!= 4) {
173 addReplyError(c
,"BITOP NOT must be called with a single source key.");
177 /* Lookup keys, and store pointers to the string objects into an array. */
178 numkeys
= c
->argc
- 3;
179 src
= zmalloc(sizeof(unsigned char*) * numkeys
);
180 len
= zmalloc(sizeof(long) * numkeys
);
181 for (j
= 0; j
< numkeys
; j
++) {
182 o
= lookupKeyRead(c
->db
,c
->argv
[j
+3]);
183 /* Handle non-existing keys as empty strings. */
189 /* Return an error if one of the keys is not a string. */
190 if (checkType(c
,o
,REDIS_STRING
)) {
196 len
[j
] = sdslen(o
->ptr
);
197 if (len
[j
] > maxlen
) maxlen
= len
[j
];
200 /* Compute the bit operation, if at least one string is not empty. */
202 res
= (unsigned char*) sdsnewlen(NULL
,maxlen
);
203 unsigned char output
, byte
;
206 for (j
= 0; j
< maxlen
; j
++) {
207 output
= (len
[0] <= j
) ? 0 : src
[0][j
];
208 if (op
== BITOP_NOT
) output
= ~output
;
209 for (i
= 1; i
< numkeys
; i
++) {
210 byte
= (len
[i
] <= j
) ? 0 : src
[i
][j
];
212 case BITOP_AND
: output
&= byte
; break;
213 case BITOP_OR
: output
|= byte
; break;
214 case BITOP_XOR
: output
^= byte
; break;
223 /* Store the computed value into the target key */
225 o
= createObject(REDIS_STRING
,res
);
226 setKey(c
->db
,targetkey
,o
);
228 } else if (dbDelete(c
->db
,targetkey
)) {
229 signalModifiedKey(c
->db
,targetkey
);
232 addReplyLongLong(c
,maxlen
); /* Return the output string length in bytes. */
235 /* BITCOUNT key [start end] */
236 void bitcountCommand(redisClient
*c
) {
243 /* Lookup, check for type, and return 0 for non existing keys. */
244 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
245 checkType(c
,o
,REDIS_STRING
)) return;
247 /* Set the 'p' pointer to the string, that can be just a stack allocated
248 * array if our string was integer encoded. */
249 if (o
->encoding
== REDIS_ENCODING_INT
) {
250 p
= (unsigned char*) llbuf
;
251 strlen
= ll2string(llbuf
,sizeof(llbuf
),(long)o
->ptr
);
253 p
= (unsigned char*) o
->ptr
;
254 strlen
= sdslen(o
->ptr
);
257 /* Parse start/end range if any. */
259 if (getLongFromObjectOrReply(c
,c
->argv
[2],&start
,NULL
) != REDIS_OK
)
261 if (getLongFromObjectOrReply(c
,c
->argv
[3],&end
,NULL
) != REDIS_OK
)
263 /* Convert negative indexes */
264 if (start
< 0) start
= strlen
+start
;
265 if (end
< 0) end
= strlen
+end
;
266 if (start
< 0) start
= 0;
267 if (end
< 0) end
= 0;
268 if ((unsigned)end
>= strlen
) end
= strlen
-1;
269 } else if (c
->argc
== 2) {
270 /* The whole string. */
275 addReply(c
,shared
.syntaxerr
);
279 /* Precondition: end >= 0 && end < strlen, so the only condition where
280 * zero can be returned is: start > end. */
282 addReply(c
,shared
.czero
);
284 long bytes
= end
-start
+1;
286 addReplyLongLong(c
,popcount(p
+start
,bytes
));