]> git.saurik.com Git - redis.git/blame - src/t_string.c
ziplist.c: added comments about the new 24 bit encoding.
[redis.git] / src / t_string.c
CommitLineData
e2641e09 1#include "redis.h"
5574b53e 2#include <math.h> /* isnan(), isinf() */
e2641e09 3
4/*-----------------------------------------------------------------------------
5 * String Commands
6 *----------------------------------------------------------------------------*/
7
9f9e1cea
PN
8static int checkStringLength(redisClient *c, long long size) {
9 if (size > 512*1024*1024) {
10 addReplyError(c,"string exceeds maximum allowed size (512MB)");
11 return REDIS_ERR;
12 }
13 return REDIS_OK;
14}
15
12d293ca 16void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire, int unit) {
17 long long milliseconds = 0; /* initialized to avoid an harmness warning */
e2641e09 18
19 if (expire) {
12d293ca 20 if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != REDIS_OK)
e2641e09 21 return;
12d293ca 22 if (milliseconds <= 0) {
3ab20376 23 addReplyError(c,"invalid expire time in SETEX");
e2641e09 24 return;
25 }
12d293ca 26 if (unit == UNIT_SECONDS) milliseconds *= 1000;
e2641e09 27 }
28
f85cd526 29 if (lookupKeyWrite(c->db,key) != NULL && nx) {
30 addReply(c,shared.czero);
31 return;
e2641e09 32 }
f85cd526 33 setKey(c->db,key,val);
e2641e09 34 server.dirty++;
12d293ca 35 if (expire) setExpire(c->db,key,mstime()+milliseconds);
e2641e09 36 addReply(c, nx ? shared.cone : shared.ok);
37}
38
39void setCommand(redisClient *c) {
75b41de8 40 c->argv[2] = tryObjectEncoding(c->argv[2]);
12d293ca 41 setGenericCommand(c,0,c->argv[1],c->argv[2],NULL,0);
e2641e09 42}
43
44void setnxCommand(redisClient *c) {
75b41de8 45 c->argv[2] = tryObjectEncoding(c->argv[2]);
12d293ca 46 setGenericCommand(c,1,c->argv[1],c->argv[2],NULL,0);
e2641e09 47}
48
49void setexCommand(redisClient *c) {
75b41de8 50 c->argv[3] = tryObjectEncoding(c->argv[3]);
12d293ca 51 setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2],UNIT_SECONDS);
52}
53
54void psetexCommand(redisClient *c) {
55 c->argv[3] = tryObjectEncoding(c->argv[3]);
56 setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2],UNIT_MILLISECONDS);
e2641e09 57}
58
59int getGenericCommand(redisClient *c) {
60 robj *o;
61
62 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL)
63 return REDIS_OK;
64
65 if (o->type != REDIS_STRING) {
66 addReply(c,shared.wrongtypeerr);
67 return REDIS_ERR;
68 } else {
69 addReplyBulk(c,o);
70 return REDIS_OK;
71 }
72}
73
74void getCommand(redisClient *c) {
75 getGenericCommand(c);
76}
77
78void getsetCommand(redisClient *c) {
79 if (getGenericCommand(c) == REDIS_ERR) return;
75b41de8 80 c->argv[2] = tryObjectEncoding(c->argv[2]);
f85cd526 81 setKey(c->db,c->argv[1],c->argv[2]);
e2641e09 82 server.dirty++;
3c1bf495
PN
83}
84
85static int getBitOffsetFromArgument(redisClient *c, robj *o, size_t *offset) {
86 long long loffset;
87 char *err = "bit offset is not an integer or out of range";
88
89 if (getLongLongFromObjectOrReply(c,o,&loffset,err) != REDIS_OK)
90 return REDIS_ERR;
91
648e9654 92 /* Limit offset to 512MB in bytes */
93 if ((loffset < 0) || ((unsigned long long)loffset >> 3) >= (512*1024*1024))
3c1bf495
PN
94 {
95 addReplyError(c,err);
96 return REDIS_ERR;
97 }
98
99 *offset = (size_t)loffset;
100 return REDIS_OK;
101}
102
103void setbitCommand(redisClient *c) {
104 robj *o;
eae33c1c 105 char *err = "bit is not an integer or out of range";
3c1bf495 106 size_t bitoffset;
30407e1f
PN
107 int byte, bit;
108 int byteval, bitval;
109 long on;
3c1bf495
PN
110
111 if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
112 return;
113
30407e1f 114 if (getLongFromObjectOrReply(c,c->argv[3],&on,err) != REDIS_OK)
eae33c1c
PN
115 return;
116
30407e1f
PN
117 /* Bits can only be set or cleared... */
118 if (on & ~1) {
eae33c1c 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);
f85cd526 135 dbOverwrite(c->db,c->argv[1],o);
3c1bf495 136 }
3c1bf495 137 }
eae33c1c 138
30407e1f 139 /* Grow sds value to the right length if necessary */
eae33c1c 140 byte = bitoffset >> 3;
cc209063 141 o->ptr = sdsgrowzero(o->ptr,byte+1);
eae33c1c 142
30407e1f
PN
143 /* Get current values */
144 byteval = ((char*)o->ptr)[byte];
145 bit = 7 - (bitoffset & 0x7);
146 bitval = byteval & (1 << bit);
147
148 /* Update byte with new bit value and return original value */
149 byteval &= ~(1 << bit);
150 byteval |= ((on & 0x1) << bit);
151 ((char*)o->ptr)[byte] = byteval;
cea8c5cd 152 signalModifiedKey(c->db,c->argv[1]);
3c1bf495 153 server.dirty++;
30407e1f 154 addReply(c, bitval ? shared.cone : shared.czero);
3c1bf495
PN
155}
156
157void getbitCommand(redisClient *c) {
158 robj *o;
3c1bf495 159 char llbuf[32];
30407e1f
PN
160 size_t bitoffset;
161 size_t byte, bit;
162 size_t bitval = 0;
3c1bf495
PN
163
164 if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
165 return;
166
167 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
168 checkType(c,o,REDIS_STRING)) return;
169
170 byte = bitoffset >> 3;
30407e1f 171 bit = 7 - (bitoffset & 0x7);
3c1bf495
PN
172 if (o->encoding != REDIS_ENCODING_RAW) {
173 if (byte < (size_t)ll2string(llbuf,sizeof(llbuf),(long)o->ptr))
30407e1f 174 bitval = llbuf[byte] & (1 << bit);
3c1bf495
PN
175 } else {
176 if (byte < sdslen(o->ptr))
30407e1f 177 bitval = ((char*)o->ptr)[byte] & (1 << bit);
3c1bf495 178 }
30407e1f
PN
179
180 addReply(c, bitval ? shared.cone : shared.czero);
e2641e09 181}
182
9f9e1cea
PN
183void setrangeCommand(redisClient *c) {
184 robj *o;
185 long offset;
186 sds value = c->argv[3]->ptr;
187
188 if (getLongFromObjectOrReply(c,c->argv[2],&offset,NULL) != REDIS_OK)
189 return;
190
8f8eeffe
PN
191 if (offset < 0) {
192 addReplyError(c,"offset is out of range");
193 return;
194 }
195
9f9e1cea
PN
196 o = lookupKeyWrite(c->db,c->argv[1]);
197 if (o == NULL) {
9f9e1cea
PN
198 /* Return 0 when setting nothing on a non-existing string */
199 if (sdslen(value) == 0) {
200 addReply(c,shared.czero);
201 return;
202 }
203
204 /* Return when the resulting string exceeds allowed size */
205 if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK)
206 return;
207
208 o = createObject(REDIS_STRING,sdsempty());
209 dbAdd(c->db,c->argv[1],o);
210 } else {
ad1b4f4f 211 size_t olen;
9f9e1cea
PN
212
213 /* Key exists, check type */
214 if (checkType(c,o,REDIS_STRING))
215 return;
216
9f9e1cea 217 /* Return existing string length when setting nothing */
ad1b4f4f 218 olen = stringObjectLen(o);
9f9e1cea
PN
219 if (sdslen(value) == 0) {
220 addReplyLongLong(c,olen);
221 return;
222 }
223
9f9e1cea
PN
224 /* Return when the resulting string exceeds allowed size */
225 if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK)
226 return;
227
228 /* Create a copy when the object is shared or encoded. */
229 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
230 robj *decoded = getDecodedObject(o);
231 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
232 decrRefCount(decoded);
f85cd526 233 dbOverwrite(c->db,c->argv[1],o);
9f9e1cea
PN
234 }
235 }
236
237 if (sdslen(value) > 0) {
238 o->ptr = sdsgrowzero(o->ptr,offset+sdslen(value));
239 memcpy((char*)o->ptr+offset,value,sdslen(value));
cea8c5cd 240 signalModifiedKey(c->db,c->argv[1]);
9f9e1cea
PN
241 server.dirty++;
242 }
243 addReplyLongLong(c,sdslen(o->ptr));
244}
245
ef11bccc
PN
246void getrangeCommand(redisClient *c) {
247 robj *o;
248 long start, end;
249 char *str, llbuf[32];
250 size_t strlen;
251
252 if (getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != REDIS_OK)
253 return;
254 if (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK)
255 return;
0b537972 256 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptybulk)) == NULL ||
ef11bccc
PN
257 checkType(c,o,REDIS_STRING)) return;
258
259 if (o->encoding == REDIS_ENCODING_INT) {
260 str = llbuf;
261 strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);
262 } else {
263 str = o->ptr;
264 strlen = sdslen(str);
265 }
266
267 /* Convert negative indexes */
268 if (start < 0) start = strlen+start;
269 if (end < 0) end = strlen+end;
270 if (start < 0) start = 0;
271 if (end < 0) end = 0;
272 if ((unsigned)end >= strlen) end = strlen-1;
273
274 /* Precondition: end >= 0 && end < strlen, so the only condition where
275 * nothing can be returned is: start > end. */
276 if (start > end) {
0b537972 277 addReply(c,shared.emptybulk);
ef11bccc
PN
278 } else {
279 addReplyBulkCBuffer(c,(char*)str+start,end-start+1);
280 }
281}
282
e2641e09 283void mgetCommand(redisClient *c) {
284 int j;
285
0537e7bf 286 addReplyMultiBulkLen(c,c->argc-1);
e2641e09 287 for (j = 1; j < c->argc; j++) {
288 robj *o = lookupKeyRead(c->db,c->argv[j]);
289 if (o == NULL) {
290 addReply(c,shared.nullbulk);
291 } else {
292 if (o->type != REDIS_STRING) {
293 addReply(c,shared.nullbulk);
294 } else {
295 addReplyBulk(c,o);
296 }
297 }
298 }
299}
300
301void msetGenericCommand(redisClient *c, int nx) {
302 int j, busykeys = 0;
303
304 if ((c->argc % 2) == 0) {
3ab20376 305 addReplyError(c,"wrong number of arguments for MSET");
e2641e09 306 return;
307 }
308 /* Handle the NX flag. The MSETNX semantic is to return zero and don't
309 * set nothing at all if at least one already key exists. */
310 if (nx) {
311 for (j = 1; j < c->argc; j += 2) {
312 if (lookupKeyWrite(c->db,c->argv[j]) != NULL) {
313 busykeys++;
314 }
315 }
f85cd526 316 if (busykeys) {
317 addReply(c, shared.czero);
318 return;
319 }
e2641e09 320 }
321
322 for (j = 1; j < c->argc; j += 2) {
323 c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);
f85cd526 324 setKey(c->db,c->argv[j],c->argv[j+1]);
e2641e09 325 }
326 server.dirty += (c->argc-1)/2;
327 addReply(c, nx ? shared.cone : shared.ok);
328}
329
330void msetCommand(redisClient *c) {
331 msetGenericCommand(c,0);
332}
333
334void msetnxCommand(redisClient *c) {
335 msetGenericCommand(c,1);
336}
337
338void incrDecrCommand(redisClient *c, long long incr) {
9d7165e8 339 long long value, oldvalue;
f85cd526 340 robj *o, *new;
e2641e09 341
342 o = lookupKeyWrite(c->db,c->argv[1]);
343 if (o != NULL && checkType(c,o,REDIS_STRING)) return;
344 if (getLongLongFromObjectOrReply(c,o,&value,NULL) != REDIS_OK) return;
345
9d7165e8 346 oldvalue = value;
7c96b467 347 if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) ||
348 (incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) {
9d7165e8 349 addReplyError(c,"increment or decrement would overflow");
350 return;
351 }
7c96b467 352 value += incr;
f85cd526 353 new = createStringObjectFromLongLong(value);
354 if (o)
355 dbOverwrite(c->db,c->argv[1],new);
356 else
357 dbAdd(c->db,c->argv[1],new);
cea8c5cd 358 signalModifiedKey(c->db,c->argv[1]);
e2641e09 359 server.dirty++;
360 addReply(c,shared.colon);
f85cd526 361 addReply(c,new);
e2641e09 362 addReply(c,shared.crlf);
363}
364
365void incrCommand(redisClient *c) {
366 incrDecrCommand(c,1);
367}
368
369void decrCommand(redisClient *c) {
370 incrDecrCommand(c,-1);
371}
372
373void incrbyCommand(redisClient *c) {
374 long long incr;
375
376 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
377 incrDecrCommand(c,incr);
378}
379
380void decrbyCommand(redisClient *c) {
381 long long incr;
382
383 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
384 incrDecrCommand(c,-incr);
385}
386
5574b53e 387void incrbyfloatCommand(redisClient *c) {
388 long double incr, value;
5244d6e5 389 robj *o, *new, *aux;
5574b53e 390
391 o = lookupKeyWrite(c->db,c->argv[1]);
392 if (o != NULL && checkType(c,o,REDIS_STRING)) return;
393 if (getLongDoubleFromObjectOrReply(c,o,&value,NULL) != REDIS_OK ||
394 getLongDoubleFromObjectOrReply(c,c->argv[2],&incr,NULL) != REDIS_OK)
395 return;
396
397 value += incr;
398 if (isnan(value) || isinf(value)) {
399 addReplyError(c,"increment would produce NaN or Infinity");
400 return;
401 }
402 new = createStringObjectFromLongDouble(value);
403 if (o)
404 dbOverwrite(c->db,c->argv[1],new);
405 else
406 dbAdd(c->db,c->argv[1],new);
407 signalModifiedKey(c->db,c->argv[1]);
408 server.dirty++;
409 addReplyBulk(c,new);
5244d6e5 410
411 /* Always replicate INCRBYFLOAT as a SET command with the final value
412 * in order to make sure that differences in float pricision or formatting
413 * will not create differences in replicas or after an AOF restart. */
414 aux = createStringObject("SET",3);
415 rewriteClientCommandArgument(c,0,aux);
416 decrRefCount(aux);
417 rewriteClientCommandArgument(c,2,new);
5574b53e 418}
419
e2641e09 420void appendCommand(redisClient *c) {
e2641e09 421 size_t totlen;
076f88d6 422 robj *o, *append;
e2641e09 423
424 o = lookupKeyWrite(c->db,c->argv[1]);
425 if (o == NULL) {
426 /* Create the key */
1333f98d
PN
427 c->argv[2] = tryObjectEncoding(c->argv[2]);
428 dbAdd(c->db,c->argv[1],c->argv[2]);
e2641e09 429 incrRefCount(c->argv[2]);
430 totlen = stringObjectLen(c->argv[2]);
431 } else {
1333f98d
PN
432 /* Key exists, check type */
433 if (checkType(c,o,REDIS_STRING))
e2641e09 434 return;
076f88d6 435
1333f98d
PN
436 /* "append" is an argument, so always an sds */
437 append = c->argv[2];
438 totlen = stringObjectLen(o)+sdslen(append->ptr);
439 if (checkStringLength(c,totlen) != REDIS_OK)
076f88d6 440 return;
076f88d6
PN
441
442 /* If the object is shared or encoded, we have to make a copy */
e2641e09 443 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
444 robj *decoded = getDecodedObject(o);
e2641e09 445 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
446 decrRefCount(decoded);
f85cd526 447 dbOverwrite(c->db,c->argv[1],o);
e2641e09 448 }
076f88d6
PN
449
450 /* Append the value */
451 o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr));
e2641e09 452 totlen = sdslen(o->ptr);
453 }
cea8c5cd 454 signalModifiedKey(c->db,c->argv[1]);
e2641e09 455 server.dirty++;
b70d3555 456 addReplyLongLong(c,totlen);
e2641e09 457}
458
80091bba 459void strlenCommand(redisClient *c) {
460 robj *o;
80091bba 461 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
462 checkType(c,o,REDIS_STRING)) return;
ad1b4f4f 463 addReplyLongLong(c,stringObjectLen(o));
80091bba 464}
7ecd4644 465