]> git.saurik.com Git - redis.git/blob - src/t_string.c
faster INCR doing far less allocation in common cases
[redis.git] / src / t_string.c
1 #include "redis.h"
2
3 /*-----------------------------------------------------------------------------
4 * String Commands
5 *----------------------------------------------------------------------------*/
6
7 void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire) {
8 int retval;
9 long seconds = 0; /* initialized to avoid an harmness warning */
10
11 if (expire) {
12 if (getLongFromObjectOrReply(c, expire, &seconds, NULL) != REDIS_OK)
13 return;
14 if (seconds <= 0) {
15 addReplyError(c,"invalid expire time in SETEX");
16 return;
17 }
18 }
19
20 retval = dbAdd(c->db,key,val);
21 if (retval == REDIS_ERR) {
22 if (!nx) {
23 dbReplace(c->db,key,val);
24 incrRefCount(val);
25 } else {
26 addReply(c,shared.czero);
27 return;
28 }
29 } else {
30 incrRefCount(val);
31 }
32 touchWatchedKey(c->db,key);
33 server.dirty++;
34 removeExpire(c->db,key);
35 if (expire) setExpire(c->db,key,time(NULL)+seconds);
36 addReply(c, nx ? shared.cone : shared.ok);
37 }
38
39 void setCommand(redisClient *c) {
40 c->argv[2] = tryObjectEncoding(c->argv[2]);
41 setGenericCommand(c,0,c->argv[1],c->argv[2],NULL);
42 }
43
44 void setnxCommand(redisClient *c) {
45 c->argv[2] = tryObjectEncoding(c->argv[2]);
46 setGenericCommand(c,1,c->argv[1],c->argv[2],NULL);
47 }
48
49 void setexCommand(redisClient *c) {
50 c->argv[3] = tryObjectEncoding(c->argv[3]);
51 setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2]);
52 }
53
54 int getGenericCommand(redisClient *c) {
55 robj *o;
56
57 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL)
58 return REDIS_OK;
59
60 if (o->type != REDIS_STRING) {
61 addReply(c,shared.wrongtypeerr);
62 return REDIS_ERR;
63 } else {
64 addReplyBulk(c,o);
65 return REDIS_OK;
66 }
67 }
68
69 void getCommand(redisClient *c) {
70 getGenericCommand(c);
71 }
72
73 void getsetCommand(redisClient *c) {
74 if (getGenericCommand(c) == REDIS_ERR) return;
75 c->argv[2] = tryObjectEncoding(c->argv[2]);
76 dbReplace(c->db,c->argv[1],c->argv[2]);
77 incrRefCount(c->argv[2]);
78 touchWatchedKey(c->db,c->argv[1]);
79 server.dirty++;
80 removeExpire(c->db,c->argv[1]);
81 }
82
83 void mgetCommand(redisClient *c) {
84 int j;
85
86 addReplyMultiBulkLen(c,c->argc-1);
87 for (j = 1; j < c->argc; j++) {
88 robj *o = lookupKeyRead(c->db,c->argv[j]);
89 if (o == NULL) {
90 addReply(c,shared.nullbulk);
91 } else {
92 if (o->type != REDIS_STRING) {
93 addReply(c,shared.nullbulk);
94 } else {
95 addReplyBulk(c,o);
96 }
97 }
98 }
99 }
100
101 void msetGenericCommand(redisClient *c, int nx) {
102 int j, busykeys = 0;
103
104 if ((c->argc % 2) == 0) {
105 addReplyError(c,"wrong number of arguments for MSET");
106 return;
107 }
108 /* Handle the NX flag. The MSETNX semantic is to return zero and don't
109 * set nothing at all if at least one already key exists. */
110 if (nx) {
111 for (j = 1; j < c->argc; j += 2) {
112 if (lookupKeyWrite(c->db,c->argv[j]) != NULL) {
113 busykeys++;
114 }
115 }
116 }
117 if (busykeys) {
118 addReply(c, shared.czero);
119 return;
120 }
121
122 for (j = 1; j < c->argc; j += 2) {
123 c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);
124 dbReplace(c->db,c->argv[j],c->argv[j+1]);
125 incrRefCount(c->argv[j+1]);
126 removeExpire(c->db,c->argv[j]);
127 touchWatchedKey(c->db,c->argv[j]);
128 }
129 server.dirty += (c->argc-1)/2;
130 addReply(c, nx ? shared.cone : shared.ok);
131 }
132
133 void msetCommand(redisClient *c) {
134 msetGenericCommand(c,0);
135 }
136
137 void msetnxCommand(redisClient *c) {
138 msetGenericCommand(c,1);
139 }
140
141 void incrDecrCommand(redisClient *c, long long incr) {
142 long long value;
143 robj *o;
144
145 o = lookupKeyWrite(c->db,c->argv[1]);
146 if (o != NULL && checkType(c,o,REDIS_STRING)) return;
147
148 /* Fast path if the object is integer encoded and is not shared. */
149 if (o && o->refcount == 1 && o->encoding == REDIS_ENCODING_INT) {
150 long long newval = ((long)o->ptr) + incr;
151
152 if (newval >= LONG_MIN && newval <= LONG_MAX) {
153 o->ptr = (void*) (long) newval;
154 touchWatchedKey(c->db,c->argv[1]);
155 server.dirty++;
156 addReplyLongLong(c,newval);
157 return;
158 }
159 /* ... else take the usual safe path */
160 }
161
162 /* Otherwise we create a new object and replace the old one. */
163 if (getLongLongFromObjectOrReply(c,o,&value,NULL) != REDIS_OK) return;
164 value += incr;
165 o = createStringObjectFromLongLong(value);
166 dbReplace(c->db,c->argv[1],o);
167 touchWatchedKey(c->db,c->argv[1]);
168 server.dirty++;
169 addReply(c,shared.colon);
170 addReply(c,o);
171 addReply(c,shared.crlf);
172 }
173
174 void incrCommand(redisClient *c) {
175 incrDecrCommand(c,1);
176 }
177
178 void decrCommand(redisClient *c) {
179 incrDecrCommand(c,-1);
180 }
181
182 void incrbyCommand(redisClient *c) {
183 long long incr;
184
185 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
186 incrDecrCommand(c,incr);
187 }
188
189 void decrbyCommand(redisClient *c) {
190 long long incr;
191
192 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
193 incrDecrCommand(c,-incr);
194 }
195
196 void appendCommand(redisClient *c) {
197 int retval;
198 size_t totlen;
199 robj *o;
200
201 o = lookupKeyWrite(c->db,c->argv[1]);
202 c->argv[2] = tryObjectEncoding(c->argv[2]);
203 if (o == NULL) {
204 /* Create the key */
205 retval = dbAdd(c->db,c->argv[1],c->argv[2]);
206 incrRefCount(c->argv[2]);
207 totlen = stringObjectLen(c->argv[2]);
208 } else {
209 if (o->type != REDIS_STRING) {
210 addReply(c,shared.wrongtypeerr);
211 return;
212 }
213 /* If the object is specially encoded or shared we have to make
214 * a copy */
215 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
216 robj *decoded = getDecodedObject(o);
217
218 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
219 decrRefCount(decoded);
220 dbReplace(c->db,c->argv[1],o);
221 }
222 /* APPEND! */
223 if (c->argv[2]->encoding == REDIS_ENCODING_RAW) {
224 o->ptr = sdscatlen(o->ptr,
225 c->argv[2]->ptr, sdslen(c->argv[2]->ptr));
226 } else {
227 o->ptr = sdscatprintf(o->ptr, "%ld",
228 (unsigned long) c->argv[2]->ptr);
229 }
230 totlen = sdslen(o->ptr);
231 }
232 touchWatchedKey(c->db,c->argv[1]);
233 server.dirty++;
234 addReplyLongLong(c,totlen);
235 }
236
237 void substrCommand(redisClient *c) {
238 robj *o;
239 long start = atoi(c->argv[2]->ptr);
240 long end = atoi(c->argv[3]->ptr);
241 size_t rangelen, strlen;
242 sds range;
243
244 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
245 checkType(c,o,REDIS_STRING)) return;
246
247 o = getDecodedObject(o);
248 strlen = sdslen(o->ptr);
249
250 /* convert negative indexes */
251 if (start < 0) start = strlen+start;
252 if (end < 0) end = strlen+end;
253 if (start < 0) start = 0;
254 if (end < 0) end = 0;
255
256 /* indexes sanity checks */
257 if (start > end || (size_t)start >= strlen) {
258 /* Out of range start or start > end result in null reply */
259 addReply(c,shared.nullbulk);
260 decrRefCount(o);
261 return;
262 }
263 if ((size_t)end >= strlen) end = strlen-1;
264 rangelen = (end-start)+1;
265
266 /* Return the result */
267 addReplySds(c,sdscatprintf(sdsempty(),"$%zu\r\n",rangelen));
268 range = sdsnewlen((char*)o->ptr+start,rangelen);
269 addReplySds(c,range);
270 addReply(c,shared.crlf);
271 decrRefCount(o);
272 }
273
274 void strlenCommand(redisClient *c) {
275 robj *o;
276
277 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
278 checkType(c,o,REDIS_STRING)) return;
279
280 o = getDecodedObject(o);
281 addReplyLongLong(c,sdslen(o->ptr));
282 decrRefCount(o);
283 }