]> git.saurik.com Git - redis.git/blob - src/t_string.c
New commands: BITOP and BITCOUNT.
[redis.git] / src / t_string.c
1 #include "redis.h"
2 #include <math.h> /* isnan(), isinf() */
3
4 /*-----------------------------------------------------------------------------
5 * String Commands
6 *----------------------------------------------------------------------------*/
7
8 static int checkStringLength(redisClient *c, long long size) {
9 if (size > 512*1024*1024) {
10 addReplyError(c,"string exceeds maximum allowed size (512MB)");
11 return REDIS_ERR;
12 }
13 return REDIS_OK;
14 }
15
16 void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire, int unit) {
17 long long milliseconds = 0; /* initialized to avoid an harmness warning */
18
19 if (expire) {
20 if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != REDIS_OK)
21 return;
22 if (milliseconds <= 0) {
23 addReplyError(c,"invalid expire time in SETEX");
24 return;
25 }
26 if (unit == UNIT_SECONDS) milliseconds *= 1000;
27 }
28
29 if (lookupKeyWrite(c->db,key) != NULL && nx) {
30 addReply(c,shared.czero);
31 return;
32 }
33 setKey(c->db,key,val);
34 server.dirty++;
35 if (expire) setExpire(c->db,key,mstime()+milliseconds);
36 addReply(c, nx ? shared.cone : shared.ok);
37 }
38
39 void setCommand(redisClient *c) {
40 c->argv[2] = tryObjectEncoding(c->argv[2]);
41 setGenericCommand(c,0,c->argv[1],c->argv[2],NULL,0);
42 }
43
44 void setnxCommand(redisClient *c) {
45 c->argv[2] = tryObjectEncoding(c->argv[2]);
46 setGenericCommand(c,1,c->argv[1],c->argv[2],NULL,0);
47 }
48
49 void setexCommand(redisClient *c) {
50 c->argv[3] = tryObjectEncoding(c->argv[3]);
51 setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2],UNIT_SECONDS);
52 }
53
54 void psetexCommand(redisClient *c) {
55 c->argv[3] = tryObjectEncoding(c->argv[3]);
56 setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2],UNIT_MILLISECONDS);
57 }
58
59 int getGenericCommand(redisClient *c) {
60 robj *o;
61
62 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL)
63 return REDIS_OK;
64
65 if (o->type != REDIS_STRING) {
66 addReply(c,shared.wrongtypeerr);
67 return REDIS_ERR;
68 } else {
69 addReplyBulk(c,o);
70 return REDIS_OK;
71 }
72 }
73
74 void getCommand(redisClient *c) {
75 getGenericCommand(c);
76 }
77
78 void getsetCommand(redisClient *c) {
79 if (getGenericCommand(c) == REDIS_ERR) return;
80 c->argv[2] = tryObjectEncoding(c->argv[2]);
81 setKey(c->db,c->argv[1],c->argv[2]);
82 server.dirty++;
83 }
84
85 static int getBitOffsetFromArgument(redisClient *c, robj *o, size_t *offset) {
86 long long loffset;
87 char *err = "bit offset is not an integer or out of range";
88
89 if (getLongLongFromObjectOrReply(c,o,&loffset,err) != REDIS_OK)
90 return REDIS_ERR;
91
92 /* Limit offset to 512MB in bytes */
93 if ((loffset < 0) || ((unsigned long long)loffset >> 3) >= (512*1024*1024))
94 {
95 addReplyError(c,err);
96 return REDIS_ERR;
97 }
98
99 *offset = (size_t)loffset;
100 return REDIS_OK;
101 }
102
103 void setbitCommand(redisClient *c) {
104 robj *o;
105 char *err = "bit is not an integer or out of range";
106 size_t bitoffset;
107 int byte, bit;
108 int byteval, bitval;
109 long on;
110
111 if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
112 return;
113
114 if (getLongFromObjectOrReply(c,c->argv[3],&on,err) != REDIS_OK)
115 return;
116
117 /* Bits can only be set or cleared... */
118 if (on & ~1) {
119 addReplyError(c,err);
120 return;
121 }
122
123 o = lookupKeyWrite(c->db,c->argv[1]);
124 if (o == NULL) {
125 o = createObject(REDIS_STRING,sdsempty());
126 dbAdd(c->db,c->argv[1],o);
127 } else {
128 if (checkType(c,o,REDIS_STRING)) return;
129
130 /* Create a copy when the object is shared or encoded. */
131 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
132 robj *decoded = getDecodedObject(o);
133 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
134 decrRefCount(decoded);
135 dbOverwrite(c->db,c->argv[1],o);
136 }
137 }
138
139 /* Grow sds value to the right length if necessary */
140 byte = bitoffset >> 3;
141 o->ptr = sdsgrowzero(o->ptr,byte+1);
142
143 /* Get current values */
144 byteval = ((char*)o->ptr)[byte];
145 bit = 7 - (bitoffset & 0x7);
146 bitval = byteval & (1 << bit);
147
148 /* Update byte with new bit value and return original value */
149 byteval &= ~(1 << bit);
150 byteval |= ((on & 0x1) << bit);
151 ((char*)o->ptr)[byte] = byteval;
152 signalModifiedKey(c->db,c->argv[1]);
153 server.dirty++;
154 addReply(c, bitval ? shared.cone : shared.czero);
155 }
156
157 void getbitCommand(redisClient *c) {
158 robj *o;
159 char llbuf[32];
160 size_t bitoffset;
161 size_t byte, bit;
162 size_t bitval = 0;
163
164 if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
165 return;
166
167 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
168 checkType(c,o,REDIS_STRING)) return;
169
170 byte = bitoffset >> 3;
171 bit = 7 - (bitoffset & 0x7);
172 if (o->encoding != REDIS_ENCODING_RAW) {
173 if (byte < (size_t)ll2string(llbuf,sizeof(llbuf),(long)o->ptr))
174 bitval = llbuf[byte] & (1 << bit);
175 } else {
176 if (byte < sdslen(o->ptr))
177 bitval = ((char*)o->ptr)[byte] & (1 << bit);
178 }
179
180 addReply(c, bitval ? shared.cone : shared.czero);
181 }
182
183 void setrangeCommand(redisClient *c) {
184 robj *o;
185 long offset;
186 sds value = c->argv[3]->ptr;
187
188 if (getLongFromObjectOrReply(c,c->argv[2],&offset,NULL) != REDIS_OK)
189 return;
190
191 if (offset < 0) {
192 addReplyError(c,"offset is out of range");
193 return;
194 }
195
196 o = lookupKeyWrite(c->db,c->argv[1]);
197 if (o == NULL) {
198 /* Return 0 when setting nothing on a non-existing string */
199 if (sdslen(value) == 0) {
200 addReply(c,shared.czero);
201 return;
202 }
203
204 /* Return when the resulting string exceeds allowed size */
205 if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK)
206 return;
207
208 o = createObject(REDIS_STRING,sdsempty());
209 dbAdd(c->db,c->argv[1],o);
210 } else {
211 size_t olen;
212
213 /* Key exists, check type */
214 if (checkType(c,o,REDIS_STRING))
215 return;
216
217 /* Return existing string length when setting nothing */
218 olen = stringObjectLen(o);
219 if (sdslen(value) == 0) {
220 addReplyLongLong(c,olen);
221 return;
222 }
223
224 /* Return when the resulting string exceeds allowed size */
225 if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK)
226 return;
227
228 /* Create a copy when the object is shared or encoded. */
229 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
230 robj *decoded = getDecodedObject(o);
231 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
232 decrRefCount(decoded);
233 dbOverwrite(c->db,c->argv[1],o);
234 }
235 }
236
237 if (sdslen(value) > 0) {
238 o->ptr = sdsgrowzero(o->ptr,offset+sdslen(value));
239 memcpy((char*)o->ptr+offset,value,sdslen(value));
240 signalModifiedKey(c->db,c->argv[1]);
241 server.dirty++;
242 }
243 addReplyLongLong(c,sdslen(o->ptr));
244 }
245
246 void getrangeCommand(redisClient *c) {
247 robj *o;
248 long start, end;
249 char *str, llbuf[32];
250 size_t strlen;
251
252 if (getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != REDIS_OK)
253 return;
254 if (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK)
255 return;
256 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptybulk)) == NULL ||
257 checkType(c,o,REDIS_STRING)) return;
258
259 if (o->encoding == REDIS_ENCODING_INT) {
260 str = llbuf;
261 strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);
262 } else {
263 str = o->ptr;
264 strlen = sdslen(str);
265 }
266
267 /* Convert negative indexes */
268 if (start < 0) start = strlen+start;
269 if (end < 0) end = strlen+end;
270 if (start < 0) start = 0;
271 if (end < 0) end = 0;
272 if ((unsigned)end >= strlen) end = strlen-1;
273
274 /* Precondition: end >= 0 && end < strlen, so the only condition where
275 * nothing can be returned is: start > end. */
276 if (start > end) {
277 addReply(c,shared.emptybulk);
278 } else {
279 addReplyBulkCBuffer(c,(char*)str+start,end-start+1);
280 }
281 }
282
283 void mgetCommand(redisClient *c) {
284 int j;
285
286 addReplyMultiBulkLen(c,c->argc-1);
287 for (j = 1; j < c->argc; j++) {
288 robj *o = lookupKeyRead(c->db,c->argv[j]);
289 if (o == NULL) {
290 addReply(c,shared.nullbulk);
291 } else {
292 if (o->type != REDIS_STRING) {
293 addReply(c,shared.nullbulk);
294 } else {
295 addReplyBulk(c,o);
296 }
297 }
298 }
299 }
300
301 void msetGenericCommand(redisClient *c, int nx) {
302 int j, busykeys = 0;
303
304 if ((c->argc % 2) == 0) {
305 addReplyError(c,"wrong number of arguments for MSET");
306 return;
307 }
308 /* Handle the NX flag. The MSETNX semantic is to return zero and don't
309 * set nothing at all if at least one already key exists. */
310 if (nx) {
311 for (j = 1; j < c->argc; j += 2) {
312 if (lookupKeyWrite(c->db,c->argv[j]) != NULL) {
313 busykeys++;
314 }
315 }
316 if (busykeys) {
317 addReply(c, shared.czero);
318 return;
319 }
320 }
321
322 for (j = 1; j < c->argc; j += 2) {
323 c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);
324 setKey(c->db,c->argv[j],c->argv[j+1]);
325 }
326 server.dirty += (c->argc-1)/2;
327 addReply(c, nx ? shared.cone : shared.ok);
328 }
329
330 void msetCommand(redisClient *c) {
331 msetGenericCommand(c,0);
332 }
333
334 void msetnxCommand(redisClient *c) {
335 msetGenericCommand(c,1);
336 }
337
338 void incrDecrCommand(redisClient *c, long long incr) {
339 long long value, oldvalue;
340 robj *o, *new;
341
342 o = lookupKeyWrite(c->db,c->argv[1]);
343 if (o != NULL && checkType(c,o,REDIS_STRING)) return;
344 if (getLongLongFromObjectOrReply(c,o,&value,NULL) != REDIS_OK) return;
345
346 oldvalue = value;
347 if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) ||
348 (incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) {
349 addReplyError(c,"increment or decrement would overflow");
350 return;
351 }
352 value += incr;
353 new = createStringObjectFromLongLong(value);
354 if (o)
355 dbOverwrite(c->db,c->argv[1],new);
356 else
357 dbAdd(c->db,c->argv[1],new);
358 signalModifiedKey(c->db,c->argv[1]);
359 server.dirty++;
360 addReply(c,shared.colon);
361 addReply(c,new);
362 addReply(c,shared.crlf);
363 }
364
365 void incrCommand(redisClient *c) {
366 incrDecrCommand(c,1);
367 }
368
369 void decrCommand(redisClient *c) {
370 incrDecrCommand(c,-1);
371 }
372
373 void incrbyCommand(redisClient *c) {
374 long long incr;
375
376 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
377 incrDecrCommand(c,incr);
378 }
379
380 void decrbyCommand(redisClient *c) {
381 long long incr;
382
383 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
384 incrDecrCommand(c,-incr);
385 }
386
387 void incrbyfloatCommand(redisClient *c) {
388 long double incr, value;
389 robj *o, *new, *aux;
390
391 o = lookupKeyWrite(c->db,c->argv[1]);
392 if (o != NULL && checkType(c,o,REDIS_STRING)) return;
393 if (getLongDoubleFromObjectOrReply(c,o,&value,NULL) != REDIS_OK ||
394 getLongDoubleFromObjectOrReply(c,c->argv[2],&incr,NULL) != REDIS_OK)
395 return;
396
397 value += incr;
398 if (isnan(value) || isinf(value)) {
399 addReplyError(c,"increment would produce NaN or Infinity");
400 return;
401 }
402 new = createStringObjectFromLongDouble(value);
403 if (o)
404 dbOverwrite(c->db,c->argv[1],new);
405 else
406 dbAdd(c->db,c->argv[1],new);
407 signalModifiedKey(c->db,c->argv[1]);
408 server.dirty++;
409 addReplyBulk(c,new);
410
411 /* Always replicate INCRBYFLOAT as a SET command with the final value
412 * in order to make sure that differences in float pricision or formatting
413 * will not create differences in replicas or after an AOF restart. */
414 aux = createStringObject("SET",3);
415 rewriteClientCommandArgument(c,0,aux);
416 decrRefCount(aux);
417 rewriteClientCommandArgument(c,2,new);
418 }
419
420 void appendCommand(redisClient *c) {
421 size_t totlen;
422 robj *o, *append;
423
424 o = lookupKeyWrite(c->db,c->argv[1]);
425 if (o == NULL) {
426 /* Create the key */
427 c->argv[2] = tryObjectEncoding(c->argv[2]);
428 dbAdd(c->db,c->argv[1],c->argv[2]);
429 incrRefCount(c->argv[2]);
430 totlen = stringObjectLen(c->argv[2]);
431 } else {
432 /* Key exists, check type */
433 if (checkType(c,o,REDIS_STRING))
434 return;
435
436 /* "append" is an argument, so always an sds */
437 append = c->argv[2];
438 totlen = stringObjectLen(o)+sdslen(append->ptr);
439 if (checkStringLength(c,totlen) != REDIS_OK)
440 return;
441
442 /* If the object is shared or encoded, we have to make a copy */
443 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
444 robj *decoded = getDecodedObject(o);
445 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
446 decrRefCount(decoded);
447 dbOverwrite(c->db,c->argv[1],o);
448 }
449
450 /* Append the value */
451 o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr));
452 totlen = sdslen(o->ptr);
453 }
454 signalModifiedKey(c->db,c->argv[1]);
455 server.dirty++;
456 addReplyLongLong(c,totlen);
457 }
458
459 void strlenCommand(redisClient *c) {
460 robj *o;
461 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
462 checkType(c,o,REDIS_STRING)) return;
463 addReplyLongLong(c,stringObjectLen(o));
464 }
465
466 #define BITOP_AND 0
467 #define BITOP_OR 1
468 #define BITOP_XOR 2
469 #define BITOP_NOT 3
470 /* BITOP op_name target_key src_key1 src_key2 src_key3 ... src_keyN */
471 void bitopCommand(redisClient *c) {
472 char *opname = c->argv[1]->ptr;
473 robj *o, *targetkey = c->argv[2];
474 long op, j, numkeys;
475 unsigned char **src; /* Array of source strings pointers. */
476 long *len, maxlen = 0; /* Array of length of src strings, and max len. */
477 unsigned char *res = NULL; /* Resulting string. */
478
479 /* Parse the operation name. */
480 if ((opname[0] == 'a' || opname[0] == 'A') && !strcasecmp(opname,"and"))
481 op = BITOP_AND;
482 else if((opname[0] == 'o' || opname[0] == 'O') && !strcasecmp(opname,"or"))
483 op = BITOP_OR;
484 else if((opname[0] == 'x' || opname[0] == 'X') && !strcasecmp(opname,"xor"))
485 op = BITOP_XOR;
486 else if((opname[0] == 'n' || opname[0] == 'N') && !strcasecmp(opname,"not"))
487 op = BITOP_NOT;
488 else {
489 addReply(c,shared.syntaxerr);
490 return;
491 }
492
493 /* Sanity check: NOT accepts only a single key argument. */
494 if (op == BITOP_NOT && c->argc != 4) {
495 addReplyError(c,"BITOP NOT must be called with a single source key.");
496 return;
497 }
498
499 /* Lookup keys, and store pointers to the string objects into an array. */
500 numkeys = c->argc - 3;
501 src = zmalloc(sizeof(unsigned char*) * numkeys);
502 len = zmalloc(sizeof(long) * numkeys);
503 for (j = 0; j < numkeys; j++) {
504 o = lookupKeyRead(c->db,c->argv[j+3]);
505 /* Handle non-existing keys as empty strings. */
506 if (o == NULL) {
507 src[j] = NULL;
508 len[j] = 0;
509 continue;
510 }
511 /* Return an error if one of the keys is not a string. */
512 if (checkType(c,o,REDIS_STRING)) {
513 zfree(src);
514 zfree(len);
515 return;
516 }
517 src[j] = o->ptr;
518 len[j] = sdslen(o->ptr);
519 if (len[j] > maxlen) maxlen = len[j];
520 }
521
522 /* Compute the bit operation, if at least one string is not empty. */
523 if (maxlen) {
524 res = (unsigned char*) sdsnewlen(NULL,maxlen);
525 unsigned char output, byte;
526 long i;
527
528 for (j = 0; j < maxlen; j++) {
529 output = (len[0] <= j) ? 0 : src[0][j];
530 if (op == BITOP_NOT) output = ~output;
531 for (i = 1; i < numkeys; i++) {
532 byte = (len[i] <= j) ? 0 : src[i][j];
533 switch(op) {
534 case BITOP_AND: output &= byte; break;
535 case BITOP_OR: output |= byte; break;
536 case BITOP_XOR: output ^= byte; break;
537 }
538 }
539 res[j] = output;
540 }
541 }
542 zfree(src);
543 zfree(len);
544
545 /* Store the computed value into the target key */
546 if (maxlen) {
547 o = createObject(REDIS_STRING,res);
548 setKey(c->db,targetkey,o);
549 decrRefCount(o);
550 } else if (dbDelete(c->db,targetkey)) {
551 signalModifiedKey(c->db,targetkey);
552 }
553 server.dirty++;
554 addReplyLongLong(c,maxlen); /* Return the output string length in bytes. */
555 }
556
557 /* BITCOUNT key [start end] */
558 void bitcountCommand(redisClient *c) {
559 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};
560 robj *o;
561 long start, end;
562 unsigned char *p;
563 char llbuf[32];
564 size_t strlen;
565
566 /* Lookup, check for type, and return 0 for non existing keys. */
567 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
568 checkType(c,o,REDIS_STRING)) return;
569
570 /* Set the 'p' pointer to the string, that can be just a stack allocated
571 * array if our string was integer encoded. */
572 if (o->encoding == REDIS_ENCODING_INT) {
573 p = (unsigned char*) llbuf;
574 strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);
575 } else {
576 p = (unsigned char*) o->ptr;
577 strlen = sdslen(o->ptr);
578 }
579
580 /* Parse start/end range if any. */
581 if (c->argc == 4) {
582 if (getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != REDIS_OK)
583 return;
584 if (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK)
585 return;
586 /* Convert negative indexes */
587 if (start < 0) start = strlen+start;
588 if (end < 0) end = strlen+end;
589 if (start < 0) start = 0;
590 if (end < 0) end = 0;
591 if ((unsigned)end >= strlen) end = strlen-1;
592 } else if (c->argc == 2) {
593 /* The whole string. */
594 start = 0;
595 end = strlen-1;
596 } else {
597 /* Syntax error. */
598 addReply(c,shared.syntaxerr);
599 return;
600 }
601
602 /* Precondition: end >= 0 && end < strlen, so the only condition where
603 * zero can be returned is: start > end. */
604 if (start > end) {
605 addReply(c,shared.czero);
606 } else {
607 long bits = 0, bytes = end-start+1;
608
609 /* We can finally count bits. */
610 p += start;
611 while(bytes--) bits += bitsinbyte[*p++];
612 addReplyLongLong(c,bits);
613 }
614 }