]> git.saurik.com Git - redis.git/blame - src/t_string.c
Don't decode object on STRLEN when not necessary
[redis.git] / src / t_string.c
CommitLineData
3c1bf495 1#include <limits.h>
e2641e09 2#include "redis.h"
3
4/*-----------------------------------------------------------------------------
5 * String Commands
6 *----------------------------------------------------------------------------*/
7
8void 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) {
3ab20376 16 addReplyError(c,"invalid expire time in SETEX");
e2641e09 17 return;
18 }
19 }
20
e2641e09 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 }
5b4bff9c 33 touchWatchedKey(c->db,key);
e2641e09 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
40void setCommand(redisClient *c) {
75b41de8 41 c->argv[2] = tryObjectEncoding(c->argv[2]);
e2641e09 42 setGenericCommand(c,0,c->argv[1],c->argv[2],NULL);
43}
44
45void setnxCommand(redisClient *c) {
75b41de8 46 c->argv[2] = tryObjectEncoding(c->argv[2]);
e2641e09 47 setGenericCommand(c,1,c->argv[1],c->argv[2],NULL);
48}
49
50void setexCommand(redisClient *c) {
75b41de8 51 c->argv[3] = tryObjectEncoding(c->argv[3]);
e2641e09 52 setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2]);
53}
54
55int 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
70void getCommand(redisClient *c) {
71 getGenericCommand(c);
72}
73
74void getsetCommand(redisClient *c) {
75 if (getGenericCommand(c) == REDIS_ERR) return;
75b41de8 76 c->argv[2] = tryObjectEncoding(c->argv[2]);
e2641e09 77 dbReplace(c->db,c->argv[1],c->argv[2]);
78 incrRefCount(c->argv[2]);
5b4bff9c 79 touchWatchedKey(c->db,c->argv[1]);
e2641e09 80 server.dirty++;
81 removeExpire(c->db,c->argv[1]);
3c1bf495
PN
82}
83
84static 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
586500c0 91 /* Limit offset to SIZE_T_MAX or 512MB in bytes */
3c1bf495
PN
92 if ((loffset < 0) ||
93 ((unsigned long long)loffset >= (unsigned)SIZE_T_MAX) ||
076f88d6 94 ((unsigned long long)loffset >> 3) >= (512*1024*1024))
3c1bf495
PN
95 {
96 addReplyError(c,err);
97 return REDIS_ERR;
98 }
99
100 *offset = (size_t)loffset;
101 return REDIS_OK;
102}
103
104void setbitCommand(redisClient *c) {
105 robj *o;
eae33c1c 106 char *err = "bit is not an integer or out of range";
3c1bf495 107 size_t bitoffset;
eae33c1c
PN
108 long long bitvalue;
109 int byte, bit, on;
3c1bf495
PN
110
111 if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
112 return;
113
eae33c1c
PN
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);
3c1bf495
PN
120 return;
121 }
122
123 o = lookupKeyWrite(c->db,c->argv[1]);
124 if (o == NULL) {
eae33c1c 125 o = createObject(REDIS_STRING,sdsempty());
3c1bf495
PN
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 }
3c1bf495 137 }
eae33c1c
PN
138
139 byte = bitoffset >> 3;
140 bit = 7 - (bitoffset & 0x7);
141 on = bitvalue & 0x1;
cc209063 142 o->ptr = sdsgrowzero(o->ptr,byte+1);
eae33c1c
PN
143 ((char*)o->ptr)[byte] |= on << bit;
144 ((char*)o->ptr)[byte] &= ~((!on) << bit);
145
3c1bf495
PN
146 touchWatchedKey(c->db,c->argv[1]);
147 server.dirty++;
148 addReply(c,shared.cone);
149}
150
151void 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);
e2641e09 173}
174
175void mgetCommand(redisClient *c) {
176 int j;
177
0537e7bf 178 addReplyMultiBulkLen(c,c->argc-1);
e2641e09 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
193void msetGenericCommand(redisClient *c, int nx) {
194 int j, busykeys = 0;
195
196 if ((c->argc % 2) == 0) {
3ab20376 197 addReplyError(c,"wrong number of arguments for MSET");
e2641e09 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]);
5b4bff9c 219 touchWatchedKey(c->db,c->argv[j]);
e2641e09 220 }
221 server.dirty += (c->argc-1)/2;
222 addReply(c, nx ? shared.cone : shared.ok);
223}
224
225void msetCommand(redisClient *c) {
226 msetGenericCommand(c,0);
227}
228
229void msetnxCommand(redisClient *c) {
230 msetGenericCommand(c,1);
231}
232
233void 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);
5b4bff9c 244 touchWatchedKey(c->db,c->argv[1]);
e2641e09 245 server.dirty++;
246 addReply(c,shared.colon);
247 addReply(c,o);
248 addReply(c,shared.crlf);
249}
250
251void incrCommand(redisClient *c) {
252 incrDecrCommand(c,1);
253}
254
255void decrCommand(redisClient *c) {
256 incrDecrCommand(c,-1);
257}
258
259void 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
266void 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
273void appendCommand(redisClient *c) {
274 int retval;
275 size_t totlen;
076f88d6 276 robj *o, *append;
e2641e09 277
278 o = lookupKeyWrite(c->db,c->argv[1]);
75b41de8 279 c->argv[2] = tryObjectEncoding(c->argv[2]);
e2641e09 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 }
076f88d6
PN
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 */
e2641e09 301 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
302 robj *decoded = getDecodedObject(o);
e2641e09 303 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
304 decrRefCount(decoded);
305 dbReplace(c->db,c->argv[1],o);
306 }
076f88d6
PN
307
308 /* Append the value */
309 o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr));
310 decrRefCount(append);
e2641e09 311 totlen = sdslen(o->ptr);
312 }
5b4bff9c 313 touchWatchedKey(c->db,c->argv[1]);
e2641e09 314 server.dirty++;
b70d3555 315 addReplyLongLong(c,totlen);
e2641e09 316}
317
318void 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
80091bba 355void 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;
e2641e09 360
7ecd4644
PN
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 }
80091bba 370}
7ecd4644 371