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