]> git.saurik.com Git - redis.git/blob - src/t_string.c
Add commands SETBIT/GETBIT
[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 1GB in bytes */
92 if ((loffset < 0) ||
93 ((unsigned long long)loffset >= (unsigned)SIZE_T_MAX) ||
94 ((unsigned long long)loffset >> 3) >= (1024*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 size_t bitoffset;
107 int on;
108
109 if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
110 return;
111
112 on = ((sds)c->argv[3]->ptr)[0] - '0';
113 if (sdslen(c->argv[3]->ptr) != 1 || (on & ~1)) {
114 addReplyError(c,"bit should be 0 or 1");
115 return;
116 }
117
118 o = lookupKeyWrite(c->db,c->argv[1]);
119 if (o == NULL) {
120 sds value = sdssetbit(sdsempty(),bitoffset,on);
121 o = createObject(REDIS_STRING,value);
122 dbAdd(c->db,c->argv[1],o);
123 } else {
124 if (checkType(c,o,REDIS_STRING)) return;
125
126 /* Create a copy when the object is shared or encoded. */
127 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
128 robj *decoded = getDecodedObject(o);
129 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
130 decrRefCount(decoded);
131 dbReplace(c->db,c->argv[1],o);
132 }
133
134 o->ptr = sdssetbit(o->ptr,bitoffset,on);
135 }
136 touchWatchedKey(c->db,c->argv[1]);
137 server.dirty++;
138 addReply(c,shared.cone);
139 }
140
141 void getbitCommand(redisClient *c) {
142 robj *o;
143 size_t bitoffset, byte, bitmask;
144 int on = 0;
145 char llbuf[32];
146
147 if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
148 return;
149
150 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
151 checkType(c,o,REDIS_STRING)) return;
152
153 byte = bitoffset >> 3;
154 bitmask = 1 << (7 - (bitoffset & 0x7));
155 if (o->encoding != REDIS_ENCODING_RAW) {
156 if (byte < (size_t)ll2string(llbuf,sizeof(llbuf),(long)o->ptr))
157 on = llbuf[byte] & bitmask;
158 } else {
159 if (byte < sdslen(o->ptr))
160 on = ((sds)o->ptr)[byte] & bitmask;
161 }
162 addReply(c, on ? shared.cone : shared.czero);
163 }
164
165 void mgetCommand(redisClient *c) {
166 int j;
167
168 addReplyMultiBulkLen(c,c->argc-1);
169 for (j = 1; j < c->argc; j++) {
170 robj *o = lookupKeyRead(c->db,c->argv[j]);
171 if (o == NULL) {
172 addReply(c,shared.nullbulk);
173 } else {
174 if (o->type != REDIS_STRING) {
175 addReply(c,shared.nullbulk);
176 } else {
177 addReplyBulk(c,o);
178 }
179 }
180 }
181 }
182
183 void msetGenericCommand(redisClient *c, int nx) {
184 int j, busykeys = 0;
185
186 if ((c->argc % 2) == 0) {
187 addReplyError(c,"wrong number of arguments for MSET");
188 return;
189 }
190 /* Handle the NX flag. The MSETNX semantic is to return zero and don't
191 * set nothing at all if at least one already key exists. */
192 if (nx) {
193 for (j = 1; j < c->argc; j += 2) {
194 if (lookupKeyWrite(c->db,c->argv[j]) != NULL) {
195 busykeys++;
196 }
197 }
198 }
199 if (busykeys) {
200 addReply(c, shared.czero);
201 return;
202 }
203
204 for (j = 1; j < c->argc; j += 2) {
205 c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);
206 dbReplace(c->db,c->argv[j],c->argv[j+1]);
207 incrRefCount(c->argv[j+1]);
208 removeExpire(c->db,c->argv[j]);
209 touchWatchedKey(c->db,c->argv[j]);
210 }
211 server.dirty += (c->argc-1)/2;
212 addReply(c, nx ? shared.cone : shared.ok);
213 }
214
215 void msetCommand(redisClient *c) {
216 msetGenericCommand(c,0);
217 }
218
219 void msetnxCommand(redisClient *c) {
220 msetGenericCommand(c,1);
221 }
222
223 void incrDecrCommand(redisClient *c, long long incr) {
224 long long value;
225 robj *o;
226
227 o = lookupKeyWrite(c->db,c->argv[1]);
228 if (o != NULL && checkType(c,o,REDIS_STRING)) return;
229 if (getLongLongFromObjectOrReply(c,o,&value,NULL) != REDIS_OK) return;
230
231 value += incr;
232 o = createStringObjectFromLongLong(value);
233 dbReplace(c->db,c->argv[1],o);
234 touchWatchedKey(c->db,c->argv[1]);
235 server.dirty++;
236 addReply(c,shared.colon);
237 addReply(c,o);
238 addReply(c,shared.crlf);
239 }
240
241 void incrCommand(redisClient *c) {
242 incrDecrCommand(c,1);
243 }
244
245 void decrCommand(redisClient *c) {
246 incrDecrCommand(c,-1);
247 }
248
249 void incrbyCommand(redisClient *c) {
250 long long incr;
251
252 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
253 incrDecrCommand(c,incr);
254 }
255
256 void decrbyCommand(redisClient *c) {
257 long long incr;
258
259 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
260 incrDecrCommand(c,-incr);
261 }
262
263 void appendCommand(redisClient *c) {
264 int retval;
265 size_t totlen;
266 robj *o;
267
268 o = lookupKeyWrite(c->db,c->argv[1]);
269 c->argv[2] = tryObjectEncoding(c->argv[2]);
270 if (o == NULL) {
271 /* Create the key */
272 retval = dbAdd(c->db,c->argv[1],c->argv[2]);
273 incrRefCount(c->argv[2]);
274 totlen = stringObjectLen(c->argv[2]);
275 } else {
276 if (o->type != REDIS_STRING) {
277 addReply(c,shared.wrongtypeerr);
278 return;
279 }
280 /* If the object is specially encoded or shared we have to make
281 * a copy */
282 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
283 robj *decoded = getDecodedObject(o);
284
285 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
286 decrRefCount(decoded);
287 dbReplace(c->db,c->argv[1],o);
288 }
289 /* APPEND! */
290 if (c->argv[2]->encoding == REDIS_ENCODING_RAW) {
291 o->ptr = sdscatlen(o->ptr,
292 c->argv[2]->ptr, sdslen(c->argv[2]->ptr));
293 } else {
294 o->ptr = sdscatprintf(o->ptr, "%ld",
295 (unsigned long) c->argv[2]->ptr);
296 }
297 totlen = sdslen(o->ptr);
298 }
299 touchWatchedKey(c->db,c->argv[1]);
300 server.dirty++;
301 addReplyLongLong(c,totlen);
302 }
303
304 void substrCommand(redisClient *c) {
305 robj *o;
306 long start = atoi(c->argv[2]->ptr);
307 long end = atoi(c->argv[3]->ptr);
308 size_t rangelen, strlen;
309 sds range;
310
311 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
312 checkType(c,o,REDIS_STRING)) return;
313
314 o = getDecodedObject(o);
315 strlen = sdslen(o->ptr);
316
317 /* convert negative indexes */
318 if (start < 0) start = strlen+start;
319 if (end < 0) end = strlen+end;
320 if (start < 0) start = 0;
321 if (end < 0) end = 0;
322
323 /* indexes sanity checks */
324 if (start > end || (size_t)start >= strlen) {
325 /* Out of range start or start > end result in null reply */
326 addReply(c,shared.nullbulk);
327 decrRefCount(o);
328 return;
329 }
330 if ((size_t)end >= strlen) end = strlen-1;
331 rangelen = (end-start)+1;
332
333 /* Return the result */
334 addReplySds(c,sdscatprintf(sdsempty(),"$%zu\r\n",rangelen));
335 range = sdsnewlen((char*)o->ptr+start,rangelen);
336 addReplySds(c,range);
337 addReply(c,shared.crlf);
338 decrRefCount(o);
339 }
340
341 void strlenCommand(redisClient *c) {
342 robj *o;
343
344 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
345 checkType(c,o,REDIS_STRING)) return;
346
347 o = getDecodedObject(o);
348 addReplyLongLong(c,sdslen(o->ptr));
349 decrRefCount(o);
350 }