]> git.saurik.com Git - redis.git/blob - src/t_string.c
d537c8b909fe20942dfa615c18fcf26549157638
[redis.git] / src / t_string.c
1 #include <limits.h>
2 #include "redis.h"
3
4 /*-----------------------------------------------------------------------------
5 * String Commands
6 *----------------------------------------------------------------------------*/
7
8 void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire) {
9 int retval;
10 long seconds = 0; /* initialized to avoid an harmness warning */
11
12 if (expire) {
13 if (getLongFromObjectOrReply(c, expire, &seconds, NULL) != REDIS_OK)
14 return;
15 if (seconds <= 0) {
16 addReplyError(c,"invalid expire time in SETEX");
17 return;
18 }
19 }
20
21 retval = dbAdd(c->db,key,val);
22 if (retval == REDIS_ERR) {
23 if (!nx) {
24 dbReplace(c->db,key,val);
25 incrRefCount(val);
26 } else {
27 addReply(c,shared.czero);
28 return;
29 }
30 } else {
31 incrRefCount(val);
32 }
33 touchWatchedKey(c->db,key);
34 server.dirty++;
35 removeExpire(c->db,key);
36 if (expire) setExpire(c->db,key,time(NULL)+seconds);
37 addReply(c, nx ? shared.cone : shared.ok);
38 }
39
40 void setCommand(redisClient *c) {
41 c->argv[2] = tryObjectEncoding(c->argv[2]);
42 setGenericCommand(c,0,c->argv[1],c->argv[2],NULL);
43 }
44
45 void setnxCommand(redisClient *c) {
46 c->argv[2] = tryObjectEncoding(c->argv[2]);
47 setGenericCommand(c,1,c->argv[1],c->argv[2],NULL);
48 }
49
50 void setexCommand(redisClient *c) {
51 c->argv[3] = tryObjectEncoding(c->argv[3]);
52 setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2]);
53 }
54
55 int getGenericCommand(redisClient *c) {
56 robj *o;
57
58 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL)
59 return REDIS_OK;
60
61 if (o->type != REDIS_STRING) {
62 addReply(c,shared.wrongtypeerr);
63 return REDIS_ERR;
64 } else {
65 addReplyBulk(c,o);
66 return REDIS_OK;
67 }
68 }
69
70 void getCommand(redisClient *c) {
71 getGenericCommand(c);
72 }
73
74 void getsetCommand(redisClient *c) {
75 if (getGenericCommand(c) == REDIS_ERR) return;
76 c->argv[2] = tryObjectEncoding(c->argv[2]);
77 dbReplace(c->db,c->argv[1],c->argv[2]);
78 incrRefCount(c->argv[2]);
79 touchWatchedKey(c->db,c->argv[1]);
80 server.dirty++;
81 removeExpire(c->db,c->argv[1]);
82 }
83
84 static int getBitOffsetFromArgument(redisClient *c, robj *o, size_t *offset) {
85 long long loffset;
86 char *err = "bit offset is not an integer or out of range";
87
88 if (getLongLongFromObjectOrReply(c,o,&loffset,err) != REDIS_OK)
89 return REDIS_ERR;
90
91 /* Limit offset to SIZE_T_MAX or 512MB in bytes */
92 if ((loffset < 0) ||
93 ((unsigned long long)loffset >= (unsigned)SIZE_T_MAX) ||
94 ((unsigned long long)loffset >> 3) >= (512*1024*1024))
95 {
96 addReplyError(c,err);
97 return REDIS_ERR;
98 }
99
100 *offset = (size_t)loffset;
101 return REDIS_OK;
102 }
103
104 void setbitCommand(redisClient *c) {
105 robj *o;
106 char *err = "bit is not an integer or out of range";
107 size_t bitoffset;
108 long long bitvalue;
109 int byte, bit, on;
110
111 if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
112 return;
113
114 if (getLongLongFromObjectOrReply(c,c->argv[3],&bitvalue,err) != REDIS_OK)
115 return;
116
117 /* A bit can only be set to be on or off... */
118 if (bitvalue & ~1) {
119 addReplyError(c,err);
120 return;
121 }
122
123 o = lookupKeyWrite(c->db,c->argv[1]);
124 if (o == NULL) {
125 o = createObject(REDIS_STRING,sdsempty());
126 dbAdd(c->db,c->argv[1],o);
127 } else {
128 if (checkType(c,o,REDIS_STRING)) return;
129
130 /* Create a copy when the object is shared or encoded. */
131 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
132 robj *decoded = getDecodedObject(o);
133 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
134 decrRefCount(decoded);
135 dbReplace(c->db,c->argv[1],o);
136 }
137 }
138
139 byte = bitoffset >> 3;
140 bit = 7 - (bitoffset & 0x7);
141 on = bitvalue & 0x1;
142 o->ptr = sdsgrowzero(o->ptr,byte+1);
143 ((char*)o->ptr)[byte] |= on << bit;
144 ((char*)o->ptr)[byte] &= ~((!on) << bit);
145
146 touchWatchedKey(c->db,c->argv[1]);
147 server.dirty++;
148 addReply(c,shared.cone);
149 }
150
151 void getbitCommand(redisClient *c) {
152 robj *o;
153 size_t bitoffset, byte, bitmask;
154 int on = 0;
155 char llbuf[32];
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 bitmask = 1 << (7 - (bitoffset & 0x7));
165 if (o->encoding != REDIS_ENCODING_RAW) {
166 if (byte < (size_t)ll2string(llbuf,sizeof(llbuf),(long)o->ptr))
167 on = llbuf[byte] & bitmask;
168 } else {
169 if (byte < sdslen(o->ptr))
170 on = ((sds)o->ptr)[byte] & bitmask;
171 }
172 addReply(c, on ? shared.cone : shared.czero);
173 }
174
175 void mgetCommand(redisClient *c) {
176 int j;
177
178 addReplyMultiBulkLen(c,c->argc-1);
179 for (j = 1; j < c->argc; j++) {
180 robj *o = lookupKeyRead(c->db,c->argv[j]);
181 if (o == NULL) {
182 addReply(c,shared.nullbulk);
183 } else {
184 if (o->type != REDIS_STRING) {
185 addReply(c,shared.nullbulk);
186 } else {
187 addReplyBulk(c,o);
188 }
189 }
190 }
191 }
192
193 void msetGenericCommand(redisClient *c, int nx) {
194 int j, busykeys = 0;
195
196 if ((c->argc % 2) == 0) {
197 addReplyError(c,"wrong number of arguments for MSET");
198 return;
199 }
200 /* Handle the NX flag. The MSETNX semantic is to return zero and don't
201 * set nothing at all if at least one already key exists. */
202 if (nx) {
203 for (j = 1; j < c->argc; j += 2) {
204 if (lookupKeyWrite(c->db,c->argv[j]) != NULL) {
205 busykeys++;
206 }
207 }
208 }
209 if (busykeys) {
210 addReply(c, shared.czero);
211 return;
212 }
213
214 for (j = 1; j < c->argc; j += 2) {
215 c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);
216 dbReplace(c->db,c->argv[j],c->argv[j+1]);
217 incrRefCount(c->argv[j+1]);
218 removeExpire(c->db,c->argv[j]);
219 touchWatchedKey(c->db,c->argv[j]);
220 }
221 server.dirty += (c->argc-1)/2;
222 addReply(c, nx ? shared.cone : shared.ok);
223 }
224
225 void msetCommand(redisClient *c) {
226 msetGenericCommand(c,0);
227 }
228
229 void msetnxCommand(redisClient *c) {
230 msetGenericCommand(c,1);
231 }
232
233 void incrDecrCommand(redisClient *c, long long incr) {
234 long long value;
235 robj *o;
236
237 o = lookupKeyWrite(c->db,c->argv[1]);
238 if (o != NULL && checkType(c,o,REDIS_STRING)) return;
239 if (getLongLongFromObjectOrReply(c,o,&value,NULL) != REDIS_OK) return;
240
241 value += incr;
242 o = createStringObjectFromLongLong(value);
243 dbReplace(c->db,c->argv[1],o);
244 touchWatchedKey(c->db,c->argv[1]);
245 server.dirty++;
246 addReply(c,shared.colon);
247 addReply(c,o);
248 addReply(c,shared.crlf);
249 }
250
251 void incrCommand(redisClient *c) {
252 incrDecrCommand(c,1);
253 }
254
255 void decrCommand(redisClient *c) {
256 incrDecrCommand(c,-1);
257 }
258
259 void incrbyCommand(redisClient *c) {
260 long long incr;
261
262 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
263 incrDecrCommand(c,incr);
264 }
265
266 void decrbyCommand(redisClient *c) {
267 long long incr;
268
269 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
270 incrDecrCommand(c,-incr);
271 }
272
273 void appendCommand(redisClient *c) {
274 int retval;
275 size_t totlen;
276 robj *o, *append;
277
278 o = lookupKeyWrite(c->db,c->argv[1]);
279 c->argv[2] = tryObjectEncoding(c->argv[2]);
280 if (o == NULL) {
281 /* Create the key */
282 retval = dbAdd(c->db,c->argv[1],c->argv[2]);
283 incrRefCount(c->argv[2]);
284 totlen = stringObjectLen(c->argv[2]);
285 } else {
286 if (o->type != REDIS_STRING) {
287 addReply(c,shared.wrongtypeerr);
288 return;
289 }
290
291 append = getDecodedObject(c->argv[2]);
292 if (o->encoding == REDIS_ENCODING_RAW &&
293 (sdslen(o->ptr) + sdslen(append->ptr)) > 512*1024*1024)
294 {
295 addReplyError(c,"string exceeds maximum allowed size (512MB)");
296 decrRefCount(append);
297 return;
298 }
299
300 /* If the object is shared or encoded, we have to make a copy */
301 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
302 robj *decoded = getDecodedObject(o);
303 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
304 decrRefCount(decoded);
305 dbReplace(c->db,c->argv[1],o);
306 }
307
308 /* Append the value */
309 o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr));
310 decrRefCount(append);
311 totlen = sdslen(o->ptr);
312 }
313 touchWatchedKey(c->db,c->argv[1]);
314 server.dirty++;
315 addReplyLongLong(c,totlen);
316 }
317
318 void substrCommand(redisClient *c) {
319 robj *o;
320 long start = atoi(c->argv[2]->ptr);
321 long end = atoi(c->argv[3]->ptr);
322 size_t rangelen, strlen;
323 sds range;
324
325 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
326 checkType(c,o,REDIS_STRING)) return;
327
328 o = getDecodedObject(o);
329 strlen = sdslen(o->ptr);
330
331 /* convert negative indexes */
332 if (start < 0) start = strlen+start;
333 if (end < 0) end = strlen+end;
334 if (start < 0) start = 0;
335 if (end < 0) end = 0;
336
337 /* indexes sanity checks */
338 if (start > end || (size_t)start >= strlen) {
339 /* Out of range start or start > end result in null reply */
340 addReply(c,shared.nullbulk);
341 decrRefCount(o);
342 return;
343 }
344 if ((size_t)end >= strlen) end = strlen-1;
345 rangelen = (end-start)+1;
346
347 /* Return the result */
348 addReplySds(c,sdscatprintf(sdsempty(),"$%zu\r\n",rangelen));
349 range = sdsnewlen((char*)o->ptr+start,rangelen);
350 addReplySds(c,range);
351 addReply(c,shared.crlf);
352 decrRefCount(o);
353 }
354
355 void strlenCommand(redisClient *c) {
356 robj *o;
357
358 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
359 checkType(c,o,REDIS_STRING)) return;
360
361 if (o->encoding == REDIS_ENCODING_RAW) {
362 addReplyLongLong(c,sdslen(o->ptr));
363 } else if (o->encoding == REDIS_ENCODING_INT) {
364 char llbuf[32];
365 int len = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);
366 addReplyLongLong(c,len);
367 } else {
368 redisPanic("Unknown string encoding");
369 }
370 }
371