]> git.saurik.com Git - redis.git/blob - src/t_string.c
special encoding limits redefined, and all the config options for special encodings...
[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 long long bitvalue;
115 int byte, bit, on;
116
117 if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
118 return;
119
120 if (getLongLongFromObjectOrReply(c,c->argv[3],&bitvalue,err) != REDIS_OK)
121 return;
122
123 /* A bit can only be set to be on or off... */
124 if (bitvalue & ~1) {
125 addReplyError(c,err);
126 return;
127 }
128
129 o = lookupKeyWrite(c->db,c->argv[1]);
130 if (o == NULL) {
131 o = createObject(REDIS_STRING,sdsempty());
132 dbAdd(c->db,c->argv[1],o);
133 } else {
134 if (checkType(c,o,REDIS_STRING)) return;
135
136 /* Create a copy when the object is shared or encoded. */
137 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
138 robj *decoded = getDecodedObject(o);
139 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
140 decrRefCount(decoded);
141 dbReplace(c->db,c->argv[1],o);
142 }
143 }
144
145 byte = bitoffset >> 3;
146 bit = 7 - (bitoffset & 0x7);
147 on = bitvalue & 0x1;
148 o->ptr = sdsgrowzero(o->ptr,byte+1);
149 ((char*)o->ptr)[byte] |= on << bit;
150 ((char*)o->ptr)[byte] &= ~((!on) << bit);
151
152 touchWatchedKey(c->db,c->argv[1]);
153 server.dirty++;
154 addReply(c,shared.cone);
155 }
156
157 void getbitCommand(redisClient *c) {
158 robj *o;
159 size_t bitoffset, byte, bitmask;
160 int on = 0;
161 char llbuf[32];
162
163 if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
164 return;
165
166 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
167 checkType(c,o,REDIS_STRING)) return;
168
169 byte = bitoffset >> 3;
170 bitmask = 1 << (7 - (bitoffset & 0x7));
171 if (o->encoding != REDIS_ENCODING_RAW) {
172 if (byte < (size_t)ll2string(llbuf,sizeof(llbuf),(long)o->ptr))
173 on = llbuf[byte] & bitmask;
174 } else {
175 if (byte < sdslen(o->ptr))
176 on = ((sds)o->ptr)[byte] & bitmask;
177 }
178 addReply(c, on ? shared.cone : shared.czero);
179 }
180
181 void setrangeCommand(redisClient *c) {
182 robj *o;
183 long offset;
184 sds value = c->argv[3]->ptr;
185
186 if (getLongFromObjectOrReply(c,c->argv[2],&offset,NULL) != REDIS_OK)
187 return;
188
189 o = lookupKeyWrite(c->db,c->argv[1]);
190 if (o == NULL) {
191 /* Negative offset is always 0 for non-existing keys */
192 if (offset < 0) offset = 0;
193
194 /* Return 0 when setting nothing on a non-existing string */
195 if (sdslen(value) == 0) {
196 addReply(c,shared.czero);
197 return;
198 }
199
200 /* Return when the resulting string exceeds allowed size */
201 if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK)
202 return;
203
204 o = createObject(REDIS_STRING,sdsempty());
205 dbAdd(c->db,c->argv[1],o);
206 } else {
207 int olen;
208
209 /* Key exists, check type */
210 if (checkType(c,o,REDIS_STRING))
211 return;
212
213 /* Find out existing value length */
214 if (o->encoding == REDIS_ENCODING_INT) {
215 char llbuf[32];
216 olen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);
217 } else {
218 olen = sdslen(o->ptr);
219 }
220
221 /* Return existing string length when setting nothing */
222 if (sdslen(value) == 0) {
223 addReplyLongLong(c,olen);
224 return;
225 }
226
227 /* Convert negative indexes. Note that for SETRANGE, the meaning of a
228 * negative index is a little different than for other commands.
229 * Here, an offset of -1 points to the trailing NULL byte of the
230 * string instead of the last character. */
231 if (offset < 0) {
232 offset = olen+1+offset;
233 if (offset < 0) offset = 0;
234 }
235
236 /* Return when the resulting string exceeds allowed size */
237 if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK)
238 return;
239
240 /* Create a copy when the object is shared or encoded. */
241 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
242 robj *decoded = getDecodedObject(o);
243 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
244 decrRefCount(decoded);
245 dbReplace(c->db,c->argv[1],o);
246 }
247 }
248
249 if (sdslen(value) > 0) {
250 o->ptr = sdsgrowzero(o->ptr,offset+sdslen(value));
251 memcpy((char*)o->ptr+offset,value,sdslen(value));
252 touchWatchedKey(c->db,c->argv[1]);
253 server.dirty++;
254 }
255 addReplyLongLong(c,sdslen(o->ptr));
256 }
257
258 void getrangeCommand(redisClient *c) {
259 robj *o;
260 long start, end;
261 char *str, llbuf[32];
262 size_t strlen;
263
264 if (getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != REDIS_OK)
265 return;
266 if (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK)
267 return;
268 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
269 checkType(c,o,REDIS_STRING)) return;
270
271 if (o->encoding == REDIS_ENCODING_INT) {
272 str = llbuf;
273 strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);
274 } else {
275 str = o->ptr;
276 strlen = sdslen(str);
277 }
278
279 /* Convert negative indexes */
280 if (start < 0) start = strlen+start;
281 if (end < 0) end = strlen+end;
282 if (start < 0) start = 0;
283 if (end < 0) end = 0;
284 if ((unsigned)end >= strlen) end = strlen-1;
285
286 /* Precondition: end >= 0 && end < strlen, so the only condition where
287 * nothing can be returned is: start > end. */
288 if (start > end) {
289 addReply(c,shared.nullbulk);
290 } else {
291 addReplyBulkCBuffer(c,(char*)str+start,end-start+1);
292 }
293 }
294
295 void mgetCommand(redisClient *c) {
296 int j;
297
298 addReplyMultiBulkLen(c,c->argc-1);
299 for (j = 1; j < c->argc; j++) {
300 robj *o = lookupKeyRead(c->db,c->argv[j]);
301 if (o == NULL) {
302 addReply(c,shared.nullbulk);
303 } else {
304 if (o->type != REDIS_STRING) {
305 addReply(c,shared.nullbulk);
306 } else {
307 addReplyBulk(c,o);
308 }
309 }
310 }
311 }
312
313 void msetGenericCommand(redisClient *c, int nx) {
314 int j, busykeys = 0;
315
316 if ((c->argc % 2) == 0) {
317 addReplyError(c,"wrong number of arguments for MSET");
318 return;
319 }
320 /* Handle the NX flag. The MSETNX semantic is to return zero and don't
321 * set nothing at all if at least one already key exists. */
322 if (nx) {
323 for (j = 1; j < c->argc; j += 2) {
324 if (lookupKeyWrite(c->db,c->argv[j]) != NULL) {
325 busykeys++;
326 }
327 }
328 }
329 if (busykeys) {
330 addReply(c, shared.czero);
331 return;
332 }
333
334 for (j = 1; j < c->argc; j += 2) {
335 c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);
336 dbReplace(c->db,c->argv[j],c->argv[j+1]);
337 incrRefCount(c->argv[j+1]);
338 removeExpire(c->db,c->argv[j]);
339 touchWatchedKey(c->db,c->argv[j]);
340 }
341 server.dirty += (c->argc-1)/2;
342 addReply(c, nx ? shared.cone : shared.ok);
343 }
344
345 void msetCommand(redisClient *c) {
346 msetGenericCommand(c,0);
347 }
348
349 void msetnxCommand(redisClient *c) {
350 msetGenericCommand(c,1);
351 }
352
353 void incrDecrCommand(redisClient *c, long long incr) {
354 long long value;
355 robj *o;
356
357 o = lookupKeyWrite(c->db,c->argv[1]);
358 if (o != NULL && checkType(c,o,REDIS_STRING)) return;
359 if (getLongLongFromObjectOrReply(c,o,&value,NULL) != REDIS_OK) return;
360
361 value += incr;
362 o = createStringObjectFromLongLong(value);
363 dbReplace(c->db,c->argv[1],o);
364 touchWatchedKey(c->db,c->argv[1]);
365 server.dirty++;
366 addReply(c,shared.colon);
367 addReply(c,o);
368 addReply(c,shared.crlf);
369 }
370
371 void incrCommand(redisClient *c) {
372 incrDecrCommand(c,1);
373 }
374
375 void decrCommand(redisClient *c) {
376 incrDecrCommand(c,-1);
377 }
378
379 void incrbyCommand(redisClient *c) {
380 long long incr;
381
382 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
383 incrDecrCommand(c,incr);
384 }
385
386 void decrbyCommand(redisClient *c) {
387 long long incr;
388
389 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
390 incrDecrCommand(c,-incr);
391 }
392
393 void appendCommand(redisClient *c) {
394 int retval;
395 size_t totlen;
396 robj *o, *append;
397
398 o = lookupKeyWrite(c->db,c->argv[1]);
399 c->argv[2] = tryObjectEncoding(c->argv[2]);
400 if (o == NULL) {
401 /* Create the key */
402 retval = dbAdd(c->db,c->argv[1],c->argv[2]);
403 incrRefCount(c->argv[2]);
404 totlen = stringObjectLen(c->argv[2]);
405 } else {
406 if (o->type != REDIS_STRING) {
407 addReply(c,shared.wrongtypeerr);
408 return;
409 }
410
411 append = getDecodedObject(c->argv[2]);
412 if (o->encoding == REDIS_ENCODING_RAW &&
413 (sdslen(o->ptr) + sdslen(append->ptr)) > 512*1024*1024)
414 {
415 addReplyError(c,"string exceeds maximum allowed size (512MB)");
416 decrRefCount(append);
417 return;
418 }
419
420 /* If the object is shared or encoded, we have to make a copy */
421 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
422 robj *decoded = getDecodedObject(o);
423 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
424 decrRefCount(decoded);
425 dbReplace(c->db,c->argv[1],o);
426 }
427
428 /* Append the value */
429 o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr));
430 decrRefCount(append);
431 totlen = sdslen(o->ptr);
432 }
433 touchWatchedKey(c->db,c->argv[1]);
434 server.dirty++;
435 addReplyLongLong(c,totlen);
436 }
437
438 void strlenCommand(redisClient *c) {
439 robj *o;
440
441 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
442 checkType(c,o,REDIS_STRING)) return;
443
444 if (o->encoding == REDIS_ENCODING_RAW) {
445 addReplyLongLong(c,sdslen(o->ptr));
446 } else if (o->encoding == REDIS_ENCODING_INT) {
447 char llbuf[32];
448 int len = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);
449 addReplyLongLong(c,len);
450 } else {
451 redisPanic("Unknown string encoding");
452 }
453 }
454