]> git.saurik.com Git - redis.git/blob - src/t_string.c
Make SETBIT return original bit value
[redis.git] / src / t_string.c
1 #include <limits.h>
2 #include "redis.h"
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) {
17 int retval;
18 long seconds = 0; /* initialized to avoid an harmness warning */
19
20 if (expire) {
21 if (getLongFromObjectOrReply(c, expire, &seconds, NULL) != REDIS_OK)
22 return;
23 if (seconds <= 0) {
24 addReplyError(c,"invalid expire time in SETEX");
25 return;
26 }
27 }
28
29 retval = dbAdd(c->db,key,val);
30 if (retval == REDIS_ERR) {
31 if (!nx) {
32 dbReplace(c->db,key,val);
33 incrRefCount(val);
34 } else {
35 addReply(c,shared.czero);
36 return;
37 }
38 } else {
39 incrRefCount(val);
40 }
41 touchWatchedKey(c->db,key);
42 server.dirty++;
43 removeExpire(c->db,key);
44 if (expire) setExpire(c->db,key,time(NULL)+seconds);
45 addReply(c, nx ? shared.cone : shared.ok);
46 }
47
48 void setCommand(redisClient *c) {
49 c->argv[2] = tryObjectEncoding(c->argv[2]);
50 setGenericCommand(c,0,c->argv[1],c->argv[2],NULL);
51 }
52
53 void setnxCommand(redisClient *c) {
54 c->argv[2] = tryObjectEncoding(c->argv[2]);
55 setGenericCommand(c,1,c->argv[1],c->argv[2],NULL);
56 }
57
58 void setexCommand(redisClient *c) {
59 c->argv[3] = tryObjectEncoding(c->argv[3]);
60 setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2]);
61 }
62
63 int getGenericCommand(redisClient *c) {
64 robj *o;
65
66 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL)
67 return REDIS_OK;
68
69 if (o->type != REDIS_STRING) {
70 addReply(c,shared.wrongtypeerr);
71 return REDIS_ERR;
72 } else {
73 addReplyBulk(c,o);
74 return REDIS_OK;
75 }
76 }
77
78 void getCommand(redisClient *c) {
79 getGenericCommand(c);
80 }
81
82 void getsetCommand(redisClient *c) {
83 if (getGenericCommand(c) == REDIS_ERR) return;
84 c->argv[2] = tryObjectEncoding(c->argv[2]);
85 dbReplace(c->db,c->argv[1],c->argv[2]);
86 incrRefCount(c->argv[2]);
87 touchWatchedKey(c->db,c->argv[1]);
88 server.dirty++;
89 removeExpire(c->db,c->argv[1]);
90 }
91
92 static int getBitOffsetFromArgument(redisClient *c, robj *o, size_t *offset) {
93 long long loffset;
94 char *err = "bit offset is not an integer or out of range";
95
96 if (getLongLongFromObjectOrReply(c,o,&loffset,err) != REDIS_OK)
97 return REDIS_ERR;
98
99 /* Limit offset to 512MB in bytes */
100 if ((loffset < 0) || ((unsigned long long)loffset >> 3) >= (512*1024*1024))
101 {
102 addReplyError(c,err);
103 return REDIS_ERR;
104 }
105
106 *offset = (size_t)loffset;
107 return REDIS_OK;
108 }
109
110 void setbitCommand(redisClient *c) {
111 robj *o;
112 char *err = "bit is not an integer or out of range";
113 size_t bitoffset;
114 int byte, bit;
115 int byteval, bitval;
116 long on;
117
118 if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
119 return;
120
121 if (getLongFromObjectOrReply(c,c->argv[3],&on,err) != REDIS_OK)
122 return;
123
124 /* Bits can only be set or cleared... */
125 if (on & ~1) {
126 addReplyError(c,err);
127 return;
128 }
129
130 o = lookupKeyWrite(c->db,c->argv[1]);
131 if (o == NULL) {
132 o = createObject(REDIS_STRING,sdsempty());
133 dbAdd(c->db,c->argv[1],o);
134 } else {
135 if (checkType(c,o,REDIS_STRING)) return;
136
137 /* Create a copy when the object is shared or encoded. */
138 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
139 robj *decoded = getDecodedObject(o);
140 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
141 decrRefCount(decoded);
142 dbReplace(c->db,c->argv[1],o);
143 }
144 }
145
146 /* Grow sds value to the right length if necessary */
147 byte = bitoffset >> 3;
148 o->ptr = sdsgrowzero(o->ptr,byte+1);
149
150 /* Get current values */
151 byteval = ((char*)o->ptr)[byte];
152 bit = 7 - (bitoffset & 0x7);
153 bitval = byteval & (1 << bit);
154
155 /* Update byte with new bit value and return original value */
156 byteval &= ~(1 << bit);
157 byteval |= ((on & 0x1) << bit);
158 ((char*)o->ptr)[byte] = byteval;
159 touchWatchedKey(c->db,c->argv[1]);
160 server.dirty++;
161 addReply(c, bitval ? shared.cone : shared.czero);
162 }
163
164 void getbitCommand(redisClient *c) {
165 robj *o;
166 char llbuf[32];
167 size_t bitoffset;
168 size_t byte, bit;
169 size_t bitval = 0;
170
171 if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
172 return;
173
174 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
175 checkType(c,o,REDIS_STRING)) return;
176
177 byte = bitoffset >> 3;
178 bit = 7 - (bitoffset & 0x7);
179 if (o->encoding != REDIS_ENCODING_RAW) {
180 if (byte < (size_t)ll2string(llbuf,sizeof(llbuf),(long)o->ptr))
181 bitval = llbuf[byte] & (1 << bit);
182 } else {
183 if (byte < sdslen(o->ptr))
184 bitval = ((char*)o->ptr)[byte] & (1 << bit);
185 }
186
187 addReply(c, bitval ? shared.cone : shared.czero);
188 }
189
190 void setrangeCommand(redisClient *c) {
191 robj *o;
192 long offset;
193 sds value = c->argv[3]->ptr;
194
195 if (getLongFromObjectOrReply(c,c->argv[2],&offset,NULL) != REDIS_OK)
196 return;
197
198 o = lookupKeyWrite(c->db,c->argv[1]);
199 if (o == NULL) {
200 /* Negative offset is always 0 for non-existing keys */
201 if (offset < 0) offset = 0;
202
203 /* Return 0 when setting nothing on a non-existing string */
204 if (sdslen(value) == 0) {
205 addReply(c,shared.czero);
206 return;
207 }
208
209 /* Return when the resulting string exceeds allowed size */
210 if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK)
211 return;
212
213 o = createObject(REDIS_STRING,sdsempty());
214 dbAdd(c->db,c->argv[1],o);
215 } else {
216 int olen;
217
218 /* Key exists, check type */
219 if (checkType(c,o,REDIS_STRING))
220 return;
221
222 /* Find out existing value length */
223 if (o->encoding == REDIS_ENCODING_INT) {
224 char llbuf[32];
225 olen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);
226 } else {
227 olen = sdslen(o->ptr);
228 }
229
230 /* Return existing string length when setting nothing */
231 if (sdslen(value) == 0) {
232 addReplyLongLong(c,olen);
233 return;
234 }
235
236 /* Convert negative indexes. Note that for SETRANGE, the meaning of a
237 * negative index is a little different than for other commands.
238 * Here, an offset of -1 points to the trailing NULL byte of the
239 * string instead of the last character. */
240 if (offset < 0) {
241 offset = olen+1+offset;
242 if (offset < 0) offset = 0;
243 }
244
245 /* Return when the resulting string exceeds allowed size */
246 if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK)
247 return;
248
249 /* Create a copy when the object is shared or encoded. */
250 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
251 robj *decoded = getDecodedObject(o);
252 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
253 decrRefCount(decoded);
254 dbReplace(c->db,c->argv[1],o);
255 }
256 }
257
258 if (sdslen(value) > 0) {
259 o->ptr = sdsgrowzero(o->ptr,offset+sdslen(value));
260 memcpy((char*)o->ptr+offset,value,sdslen(value));
261 touchWatchedKey(c->db,c->argv[1]);
262 server.dirty++;
263 }
264 addReplyLongLong(c,sdslen(o->ptr));
265 }
266
267 void getrangeCommand(redisClient *c) {
268 robj *o;
269 long start, end;
270 char *str, llbuf[32];
271 size_t strlen;
272
273 if (getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != REDIS_OK)
274 return;
275 if (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK)
276 return;
277 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
278 checkType(c,o,REDIS_STRING)) return;
279
280 if (o->encoding == REDIS_ENCODING_INT) {
281 str = llbuf;
282 strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);
283 } else {
284 str = o->ptr;
285 strlen = sdslen(str);
286 }
287
288 /* Convert negative indexes */
289 if (start < 0) start = strlen+start;
290 if (end < 0) end = strlen+end;
291 if (start < 0) start = 0;
292 if (end < 0) end = 0;
293 if ((unsigned)end >= strlen) end = strlen-1;
294
295 /* Precondition: end >= 0 && end < strlen, so the only condition where
296 * nothing can be returned is: start > end. */
297 if (start > end) {
298 addReply(c,shared.nullbulk);
299 } else {
300 addReplyBulkCBuffer(c,(char*)str+start,end-start+1);
301 }
302 }
303
304 void mgetCommand(redisClient *c) {
305 int j;
306
307 addReplyMultiBulkLen(c,c->argc-1);
308 for (j = 1; j < c->argc; j++) {
309 robj *o = lookupKeyRead(c->db,c->argv[j]);
310 if (o == NULL) {
311 addReply(c,shared.nullbulk);
312 } else {
313 if (o->type != REDIS_STRING) {
314 addReply(c,shared.nullbulk);
315 } else {
316 addReplyBulk(c,o);
317 }
318 }
319 }
320 }
321
322 void msetGenericCommand(redisClient *c, int nx) {
323 int j, busykeys = 0;
324
325 if ((c->argc % 2) == 0) {
326 addReplyError(c,"wrong number of arguments for MSET");
327 return;
328 }
329 /* Handle the NX flag. The MSETNX semantic is to return zero and don't
330 * set nothing at all if at least one already key exists. */
331 if (nx) {
332 for (j = 1; j < c->argc; j += 2) {
333 if (lookupKeyWrite(c->db,c->argv[j]) != NULL) {
334 busykeys++;
335 }
336 }
337 }
338 if (busykeys) {
339 addReply(c, shared.czero);
340 return;
341 }
342
343 for (j = 1; j < c->argc; j += 2) {
344 c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);
345 dbReplace(c->db,c->argv[j],c->argv[j+1]);
346 incrRefCount(c->argv[j+1]);
347 removeExpire(c->db,c->argv[j]);
348 touchWatchedKey(c->db,c->argv[j]);
349 }
350 server.dirty += (c->argc-1)/2;
351 addReply(c, nx ? shared.cone : shared.ok);
352 }
353
354 void msetCommand(redisClient *c) {
355 msetGenericCommand(c,0);
356 }
357
358 void msetnxCommand(redisClient *c) {
359 msetGenericCommand(c,1);
360 }
361
362 void incrDecrCommand(redisClient *c, long long incr) {
363 long long value;
364 robj *o;
365
366 o = lookupKeyWrite(c->db,c->argv[1]);
367 if (o != NULL && checkType(c,o,REDIS_STRING)) return;
368 if (getLongLongFromObjectOrReply(c,o,&value,NULL) != REDIS_OK) return;
369
370 value += incr;
371 o = createStringObjectFromLongLong(value);
372 dbReplace(c->db,c->argv[1],o);
373 touchWatchedKey(c->db,c->argv[1]);
374 server.dirty++;
375 addReply(c,shared.colon);
376 addReply(c,o);
377 addReply(c,shared.crlf);
378 }
379
380 void incrCommand(redisClient *c) {
381 incrDecrCommand(c,1);
382 }
383
384 void decrCommand(redisClient *c) {
385 incrDecrCommand(c,-1);
386 }
387
388 void incrbyCommand(redisClient *c) {
389 long long incr;
390
391 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
392 incrDecrCommand(c,incr);
393 }
394
395 void decrbyCommand(redisClient *c) {
396 long long incr;
397
398 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
399 incrDecrCommand(c,-incr);
400 }
401
402 void appendCommand(redisClient *c) {
403 int retval;
404 size_t totlen;
405 robj *o, *append;
406
407 o = lookupKeyWrite(c->db,c->argv[1]);
408 c->argv[2] = tryObjectEncoding(c->argv[2]);
409 if (o == NULL) {
410 /* Create the key */
411 retval = dbAdd(c->db,c->argv[1],c->argv[2]);
412 incrRefCount(c->argv[2]);
413 totlen = stringObjectLen(c->argv[2]);
414 } else {
415 if (o->type != REDIS_STRING) {
416 addReply(c,shared.wrongtypeerr);
417 return;
418 }
419
420 append = getDecodedObject(c->argv[2]);
421 if (o->encoding == REDIS_ENCODING_RAW &&
422 (sdslen(o->ptr) + sdslen(append->ptr)) > 512*1024*1024)
423 {
424 addReplyError(c,"string exceeds maximum allowed size (512MB)");
425 decrRefCount(append);
426 return;
427 }
428
429 /* If the object is shared or encoded, we have to make a copy */
430 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
431 robj *decoded = getDecodedObject(o);
432 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
433 decrRefCount(decoded);
434 dbReplace(c->db,c->argv[1],o);
435 }
436
437 /* Append the value */
438 o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr));
439 decrRefCount(append);
440 totlen = sdslen(o->ptr);
441 }
442 touchWatchedKey(c->db,c->argv[1]);
443 server.dirty++;
444 addReplyLongLong(c,totlen);
445 }
446
447 void strlenCommand(redisClient *c) {
448 robj *o;
449
450 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
451 checkType(c,o,REDIS_STRING)) return;
452
453 if (o->encoding == REDIS_ENCODING_RAW) {
454 addReplyLongLong(c,sdslen(o->ptr));
455 } else if (o->encoding == REDIS_ENCODING_INT) {
456 char llbuf[32];
457 int len = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);
458 addReplyLongLong(c,len);
459 } else {
460 redisPanic("Unknown string encoding");
461 }
462 }
463