]> git.saurik.com Git - redis.git/blob - src/t_string.c
e50692183ec40fd1d62ee4f82fa9333140e82fe8
[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 if (offset < 0) {
199 addReplyError(c,"offset is out of range");
200 return;
201 }
202
203 o = lookupKeyWrite(c->db,c->argv[1]);
204 if (o == NULL) {
205 /* Return 0 when setting nothing on a non-existing string */
206 if (sdslen(value) == 0) {
207 addReply(c,shared.czero);
208 return;
209 }
210
211 /* Return when the resulting string exceeds allowed size */
212 if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK)
213 return;
214
215 o = createObject(REDIS_STRING,sdsempty());
216 dbAdd(c->db,c->argv[1],o);
217 } else {
218 int olen;
219
220 /* Key exists, check type */
221 if (checkType(c,o,REDIS_STRING))
222 return;
223
224 /* Find out existing value length */
225 if (o->encoding == REDIS_ENCODING_INT) {
226 char llbuf[32];
227 olen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);
228 } else {
229 olen = sdslen(o->ptr);
230 }
231
232 /* Return existing string length when setting nothing */
233 if (sdslen(value) == 0) {
234 addReplyLongLong(c,olen);
235 return;
236 }
237
238 /* Return when the resulting string exceeds allowed size */
239 if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK)
240 return;
241
242 /* Create a copy when the object is shared or encoded. */
243 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
244 robj *decoded = getDecodedObject(o);
245 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
246 decrRefCount(decoded);
247 dbReplace(c->db,c->argv[1],o);
248 }
249 }
250
251 if (sdslen(value) > 0) {
252 o->ptr = sdsgrowzero(o->ptr,offset+sdslen(value));
253 memcpy((char*)o->ptr+offset,value,sdslen(value));
254 touchWatchedKey(c->db,c->argv[1]);
255 server.dirty++;
256 }
257 addReplyLongLong(c,sdslen(o->ptr));
258 }
259
260 void getrangeCommand(redisClient *c) {
261 robj *o;
262 long start, end;
263 char *str, llbuf[32];
264 size_t strlen;
265
266 if (getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != REDIS_OK)
267 return;
268 if (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK)
269 return;
270 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
271 checkType(c,o,REDIS_STRING)) return;
272
273 if (o->encoding == REDIS_ENCODING_INT) {
274 str = llbuf;
275 strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);
276 } else {
277 str = o->ptr;
278 strlen = sdslen(str);
279 }
280
281 /* Convert negative indexes */
282 if (start < 0) start = strlen+start;
283 if (end < 0) end = strlen+end;
284 if (start < 0) start = 0;
285 if (end < 0) end = 0;
286 if ((unsigned)end >= strlen) end = strlen-1;
287
288 /* Precondition: end >= 0 && end < strlen, so the only condition where
289 * nothing can be returned is: start > end. */
290 if (start > end) {
291 addReply(c,shared.nullbulk);
292 } else {
293 addReplyBulkCBuffer(c,(char*)str+start,end-start+1);
294 }
295 }
296
297 void mgetCommand(redisClient *c) {
298 int j;
299
300 addReplyMultiBulkLen(c,c->argc-1);
301 for (j = 1; j < c->argc; j++) {
302 robj *o = lookupKeyRead(c->db,c->argv[j]);
303 if (o == NULL) {
304 addReply(c,shared.nullbulk);
305 } else {
306 if (o->type != REDIS_STRING) {
307 addReply(c,shared.nullbulk);
308 } else {
309 addReplyBulk(c,o);
310 }
311 }
312 }
313 }
314
315 void msetGenericCommand(redisClient *c, int nx) {
316 int j, busykeys = 0;
317
318 if ((c->argc % 2) == 0) {
319 addReplyError(c,"wrong number of arguments for MSET");
320 return;
321 }
322 /* Handle the NX flag. The MSETNX semantic is to return zero and don't
323 * set nothing at all if at least one already key exists. */
324 if (nx) {
325 for (j = 1; j < c->argc; j += 2) {
326 if (lookupKeyWrite(c->db,c->argv[j]) != NULL) {
327 busykeys++;
328 }
329 }
330 }
331 if (busykeys) {
332 addReply(c, shared.czero);
333 return;
334 }
335
336 for (j = 1; j < c->argc; j += 2) {
337 c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);
338 dbReplace(c->db,c->argv[j],c->argv[j+1]);
339 incrRefCount(c->argv[j+1]);
340 removeExpire(c->db,c->argv[j]);
341 touchWatchedKey(c->db,c->argv[j]);
342 }
343 server.dirty += (c->argc-1)/2;
344 addReply(c, nx ? shared.cone : shared.ok);
345 }
346
347 void msetCommand(redisClient *c) {
348 msetGenericCommand(c,0);
349 }
350
351 void msetnxCommand(redisClient *c) {
352 msetGenericCommand(c,1);
353 }
354
355 void incrDecrCommand(redisClient *c, long long incr) {
356 long long value;
357 robj *o;
358
359 o = lookupKeyWrite(c->db,c->argv[1]);
360 if (o != NULL && checkType(c,o,REDIS_STRING)) return;
361 if (getLongLongFromObjectOrReply(c,o,&value,NULL) != REDIS_OK) return;
362
363 value += incr;
364 o = createStringObjectFromLongLong(value);
365 dbReplace(c->db,c->argv[1],o);
366 touchWatchedKey(c->db,c->argv[1]);
367 server.dirty++;
368 addReply(c,shared.colon);
369 addReply(c,o);
370 addReply(c,shared.crlf);
371 }
372
373 void incrCommand(redisClient *c) {
374 incrDecrCommand(c,1);
375 }
376
377 void decrCommand(redisClient *c) {
378 incrDecrCommand(c,-1);
379 }
380
381 void incrbyCommand(redisClient *c) {
382 long long incr;
383
384 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
385 incrDecrCommand(c,incr);
386 }
387
388 void decrbyCommand(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 appendCommand(redisClient *c) {
396 size_t totlen;
397 robj *o, *append;
398
399 o = lookupKeyWrite(c->db,c->argv[1]);
400 if (o == NULL) {
401 /* Create the key */
402 c->argv[2] = tryObjectEncoding(c->argv[2]);
403 dbAdd(c->db,c->argv[1],c->argv[2]);
404 incrRefCount(c->argv[2]);
405 totlen = stringObjectLen(c->argv[2]);
406 } else {
407 /* Key exists, check type */
408 if (checkType(c,o,REDIS_STRING))
409 return;
410
411 /* "append" is an argument, so always an sds */
412 append = c->argv[2];
413 totlen = stringObjectLen(o)+sdslen(append->ptr);
414 if (checkStringLength(c,totlen) != REDIS_OK)
415 return;
416
417 /* If the object is shared or encoded, we have to make a copy */
418 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
419 robj *decoded = getDecodedObject(o);
420 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
421 decrRefCount(decoded);
422 dbReplace(c->db,c->argv[1],o);
423 }
424
425 /* Append the value */
426 o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr));
427 totlen = sdslen(o->ptr);
428 }
429 touchWatchedKey(c->db,c->argv[1]);
430 server.dirty++;
431 addReplyLongLong(c,totlen);
432 }
433
434 void strlenCommand(redisClient *c) {
435 robj *o;
436
437 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
438 checkType(c,o,REDIS_STRING)) return;
439
440 if (o->encoding == REDIS_ENCODING_RAW) {
441 addReplyLongLong(c,sdslen(o->ptr));
442 } else if (o->encoding == REDIS_ENCODING_INT) {
443 char llbuf[32];
444 int len = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);
445 addReplyLongLong(c,len);
446 } else {
447 redisPanic("Unknown string encoding");
448 }
449 }
450