xref: /redis-3.2.3/src/t_string.c (revision c9d0c362)
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 
425