]> git.saurik.com Git - redis.git/blame - src/object.c
Issue 804 Add Default-Start and Default-Stop LSB tags for RedHat startup and update...
[redis.git] / src / object.c
CommitLineData
d288ee65 1/* Redis Object implementation.
2 *
3 * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * * Neither the name of Redis nor the names of its contributors may be used
15 * to endorse or promote products derived from this software without
16 * specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
e2641e09 31#include "redis.h"
673e1fb7 32#include <math.h>
d93f9a86 33#include <ctype.h>
e2641e09 34
35robj *createObject(int type, void *ptr) {
a9b18e54 36 robj *o = zmalloc(sizeof(*o));
e2641e09 37 o->type = type;
38 o->encoding = REDIS_ENCODING_RAW;
39 o->ptr = ptr;
40 o->refcount = 1;
a9b18e54 41
6c52d5ce 42 /* Set the LRU to the current lruclock (minutes resolution). */
ef59a8bc 43 o->lru = server.lruclock;
e2641e09 44 return o;
45}
46
47robj *createStringObject(char *ptr, size_t len) {
48 return createObject(REDIS_STRING,sdsnewlen(ptr,len));
49}
50
51robj *createStringObjectFromLongLong(long long value) {
52 robj *o;
c9d0c362 53 if (value >= 0 && value < REDIS_SHARED_INTEGERS) {
e2641e09 54 incrRefCount(shared.integers[value]);
55 o = shared.integers[value];
56 } else {
57 if (value >= LONG_MIN && value <= LONG_MAX) {
58 o = createObject(REDIS_STRING, NULL);
59 o->encoding = REDIS_ENCODING_INT;
60 o->ptr = (void*)((long)value);
61 } else {
62 o = createObject(REDIS_STRING,sdsfromlonglong(value));
63 }
64 }
65 return o;
66}
67
5574b53e 68/* Note: this function is defined into object.c since here it is where it
69 * belongs but it is actually designed to be used just for INCRBYFLOAT */
70robj *createStringObjectFromLongDouble(long double value) {
b54cdfb2 71 char buf[256];
5574b53e 72 int len;
73
74 /* We use 17 digits precision since with 128 bit floats that precision
75 * after rouding is able to represent most small decimal numbers in a way
76 * that is "non surprising" for the user (that is, most small decimal
77 * numbers will be represented in a way that when converted back into
78 * a string are exactly the same as what the user typed.) */
7b22c44c 79 len = snprintf(buf,sizeof(buf),"%.17Lf", value);
80 /* Now remove trailing zeroes after the '.' */
b54cdfb2 81 if (strchr(buf,'.') != NULL) {
82 char *p = buf+len-1;
7b22c44c 83 while(*p == '0') {
84 p--;
85 len--;
86 }
87 if (*p == '.') len--;
88 }
5574b53e 89 return createStringObject(buf,len);
90}
91
e2641e09 92robj *dupStringObject(robj *o) {
eab0e26e 93 redisAssertWithInfo(NULL,o,o->encoding == REDIS_ENCODING_RAW);
e2641e09 94 return createStringObject(o->ptr,sdslen(o->ptr));
95}
96
97robj *createListObject(void) {
98 list *l = listCreate();
99 robj *o = createObject(REDIS_LIST,l);
100 listSetFreeMethod(l,decrRefCount);
101 o->encoding = REDIS_ENCODING_LINKEDLIST;
102 return o;
103}
104
105robj *createZiplistObject(void) {
106 unsigned char *zl = ziplistNew();
107 robj *o = createObject(REDIS_LIST,zl);
108 o->encoding = REDIS_ENCODING_ZIPLIST;
109 return o;
110}
111
112robj *createSetObject(void) {
113 dict *d = dictCreate(&setDictType,NULL);
96ffb2fe
PN
114 robj *o = createObject(REDIS_SET,d);
115 o->encoding = REDIS_ENCODING_HT;
116 return o;
117}
118
119robj *createIntsetObject(void) {
120 intset *is = intsetNew();
121 robj *o = createObject(REDIS_SET,is);
122 o->encoding = REDIS_ENCODING_INTSET;
123 return o;
e2641e09 124}
125
126robj *createHashObject(void) {
ebd85e9a
PN
127 unsigned char *zl = ziplistNew();
128 robj *o = createObject(REDIS_HASH, zl);
129 o->encoding = REDIS_ENCODING_ZIPLIST;
e2641e09 130 return o;
131}
132
133robj *createZsetObject(void) {
134 zset *zs = zmalloc(sizeof(*zs));
0b7f6d09 135 robj *o;
e2641e09 136
137 zs->dict = dictCreate(&zsetDictType,NULL);
138 zs->zsl = zslCreate();
0b7f6d09 139 o = createObject(REDIS_ZSET,zs);
140 o->encoding = REDIS_ENCODING_SKIPLIST;
141 return o;
e2641e09 142}
143
9e7cee0e
PN
144robj *createZsetZiplistObject(void) {
145 unsigned char *zl = ziplistNew();
146 robj *o = createObject(REDIS_ZSET,zl);
147 o->encoding = REDIS_ENCODING_ZIPLIST;
148 return o;
149}
150
e2641e09 151void freeStringObject(robj *o) {
152 if (o->encoding == REDIS_ENCODING_RAW) {
153 sdsfree(o->ptr);
154 }
155}
156
157void freeListObject(robj *o) {
158 switch (o->encoding) {
159 case REDIS_ENCODING_LINKEDLIST:
160 listRelease((list*) o->ptr);
161 break;
162 case REDIS_ENCODING_ZIPLIST:
163 zfree(o->ptr);
164 break;
165 default:
166 redisPanic("Unknown list encoding type");
167 }
168}
169
170void freeSetObject(robj *o) {
96ffb2fe
PN
171 switch (o->encoding) {
172 case REDIS_ENCODING_HT:
173 dictRelease((dict*) o->ptr);
174 break;
175 case REDIS_ENCODING_INTSET:
176 zfree(o->ptr);
177 break;
178 default:
179 redisPanic("Unknown set encoding type");
180 }
e2641e09 181}
182
183void freeZsetObject(robj *o) {
0f23eb3b
PN
184 zset *zs;
185 switch (o->encoding) {
100ed062 186 case REDIS_ENCODING_SKIPLIST:
0f23eb3b
PN
187 zs = o->ptr;
188 dictRelease(zs->dict);
189 zslFree(zs->zsl);
190 zfree(zs);
191 break;
192 case REDIS_ENCODING_ZIPLIST:
193 zfree(o->ptr);
194 break;
195 default:
196 redisPanic("Unknown sorted set encoding");
197 }
e2641e09 198}
199
200void freeHashObject(robj *o) {
201 switch (o->encoding) {
202 case REDIS_ENCODING_HT:
203 dictRelease((dict*) o->ptr);
204 break;
ebd85e9a 205 case REDIS_ENCODING_ZIPLIST:
e2641e09 206 zfree(o->ptr);
207 break;
208 default:
209 redisPanic("Unknown hash encoding type");
210 break;
211 }
212}
213
214void incrRefCount(robj *o) {
215 o->refcount++;
216}
217
218void decrRefCount(void *obj) {
219 robj *o = obj;
220
e2641e09 221 if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0");
936c4ab6 222 if (o->refcount == 1) {
e2641e09 223 switch(o->type) {
224 case REDIS_STRING: freeStringObject(o); break;
225 case REDIS_LIST: freeListObject(o); break;
226 case REDIS_SET: freeSetObject(o); break;
227 case REDIS_ZSET: freeZsetObject(o); break;
228 case REDIS_HASH: freeHashObject(o); break;
229 default: redisPanic("Unknown object type"); break;
230 }
a9b18e54 231 zfree(o);
936c4ab6 232 } else {
233 o->refcount--;
e2641e09 234 }
235}
236
4dd444bb 237/* This function set the ref count to zero without freeing the object.
238 * It is useful in order to pass a new object to functions incrementing
239 * the ref count of the received object. Example:
240 *
241 * functionThatWillIncrementRefCount(resetRefCount(CreateObject(...)));
242 *
243 * Otherwise you need to resort to the less elegant pattern:
244 *
245 * *obj = createObject(...);
246 * functionThatWillIncrementRefCount(obj);
247 * decrRefCount(obj);
248 */
249robj *resetRefCount(robj *obj) {
250 obj->refcount = 0;
251 return obj;
252}
253
e2641e09 254int checkType(redisClient *c, robj *o, int type) {
255 if (o->type != type) {
256 addReply(c,shared.wrongtypeerr);
257 return 1;
258 }
259 return 0;
260}
261
5d081931 262int isObjectRepresentableAsLongLong(robj *o, long long *llval) {
eab0e26e 263 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
5d081931
PN
264 if (o->encoding == REDIS_ENCODING_INT) {
265 if (llval) *llval = (long) o->ptr;
266 return REDIS_OK;
267 } else {
268 return string2ll(o->ptr,sdslen(o->ptr),llval) ? REDIS_OK : REDIS_ERR;
269 }
270}
271
e2641e09 272/* Try to encode a string object in order to save space */
273robj *tryObjectEncoding(robj *o) {
274 long value;
275 sds s = o->ptr;
276
277 if (o->encoding != REDIS_ENCODING_RAW)
278 return o; /* Already encoded */
279
280 /* It's not safe to encode shared objects: shared objects can be shared
281 * everywhere in the "object space" of Redis. Encoded objects can only
282 * appear as "values" (and not, for instance, as keys) */
283 if (o->refcount > 1) return o;
284
285 /* Currently we try to encode only strings */
eab0e26e 286 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
e2641e09 287
288 /* Check if we can represent this string as a long integer */
5d081931 289 if (!string2l(s,sdslen(s),&value)) return o;
e2641e09 290
0e5441d8 291 /* Ok, this object can be encoded...
292 *
efd412f9 293 * Can I use a shared object? Only if the object is inside a given range
13a49af4 294 *
295 * Note that we also avoid using shared integers when maxmemory is used
cea8c5cd 296 * because every object needs to have a private LRU field for the LRU
13a49af4 297 * algorithm to work well. */
c9d0c362 298 if (server.maxmemory == 0 && value >= 0 && value < REDIS_SHARED_INTEGERS) {
e2641e09 299 decrRefCount(o);
300 incrRefCount(shared.integers[value]);
301 return shared.integers[value];
302 } else {
303 o->encoding = REDIS_ENCODING_INT;
304 sdsfree(o->ptr);
305 o->ptr = (void*) value;
306 return o;
307 }
308}
309
310/* Get a decoded version of an encoded object (returned as a new object).
311 * If the object is already raw-encoded just increment the ref count. */
312robj *getDecodedObject(robj *o) {
313 robj *dec;
314
315 if (o->encoding == REDIS_ENCODING_RAW) {
316 incrRefCount(o);
317 return o;
318 }
319 if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) {
320 char buf[32];
321
322 ll2string(buf,32,(long)o->ptr);
323 dec = createStringObject(buf,strlen(buf));
324 return dec;
325 } else {
326 redisPanic("Unknown encoding type");
327 }
328}
329
330/* Compare two string objects via strcmp() or alike.
331 * Note that the objects may be integer-encoded. In such a case we
332 * use ll2string() to get a string representation of the numbers on the stack
333 * and compare the strings, it's much faster than calling getDecodedObject().
334 *
335 * Important note: if objects are not integer encoded, but binary-safe strings,
336 * sdscmp() from sds.c will apply memcmp() so this function ca be considered
337 * binary safe. */
338int compareStringObjects(robj *a, robj *b) {
eab0e26e 339 redisAssertWithInfo(NULL,a,a->type == REDIS_STRING && b->type == REDIS_STRING);
e2641e09 340 char bufa[128], bufb[128], *astr, *bstr;
341 int bothsds = 1;
342
343 if (a == b) return 0;
344 if (a->encoding != REDIS_ENCODING_RAW) {
345 ll2string(bufa,sizeof(bufa),(long) a->ptr);
346 astr = bufa;
347 bothsds = 0;
348 } else {
349 astr = a->ptr;
350 }
351 if (b->encoding != REDIS_ENCODING_RAW) {
352 ll2string(bufb,sizeof(bufb),(long) b->ptr);
353 bstr = bufb;
354 bothsds = 0;
355 } else {
356 bstr = b->ptr;
357 }
358 return bothsds ? sdscmp(astr,bstr) : strcmp(astr,bstr);
359}
360
361/* Equal string objects return 1 if the two objects are the same from the
362 * point of view of a string comparison, otherwise 0 is returned. Note that
363 * this function is faster then checking for (compareStringObject(a,b) == 0)
364 * because it can perform some more optimization. */
365int equalStringObjects(robj *a, robj *b) {
366 if (a->encoding != REDIS_ENCODING_RAW && b->encoding != REDIS_ENCODING_RAW){
367 return a->ptr == b->ptr;
368 } else {
369 return compareStringObjects(a,b) == 0;
370 }
371}
372
373size_t stringObjectLen(robj *o) {
eab0e26e 374 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
e2641e09 375 if (o->encoding == REDIS_ENCODING_RAW) {
376 return sdslen(o->ptr);
377 } else {
378 char buf[32];
379
380 return ll2string(buf,32,(long)o->ptr);
381 }
382}
383
384int getDoubleFromObject(robj *o, double *target) {
385 double value;
386 char *eptr;
387
388 if (o == NULL) {
389 value = 0;
390 } else {
eab0e26e 391 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
e2641e09 392 if (o->encoding == REDIS_ENCODING_RAW) {
5574b53e 393 errno = 0;
e2641e09 394 value = strtod(o->ptr, &eptr);
d93f9a86 395 if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
396 errno == ERANGE || isnan(value))
5574b53e 397 return REDIS_ERR;
e2641e09 398 } else if (o->encoding == REDIS_ENCODING_INT) {
399 value = (long)o->ptr;
400 } else {
401 redisPanic("Unknown string encoding");
402 }
403 }
e2641e09 404 *target = value;
405 return REDIS_OK;
406}
407
408int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) {
409 double value;
410 if (getDoubleFromObject(o, &value) != REDIS_OK) {
411 if (msg != NULL) {
3ab20376 412 addReplyError(c,(char*)msg);
e2641e09 413 } else {
5574b53e 414 addReplyError(c,"value is not a valid float");
415 }
416 return REDIS_ERR;
417 }
5574b53e 418 *target = value;
419 return REDIS_OK;
420}
421
422int getLongDoubleFromObject(robj *o, long double *target) {
423 long double value;
424 char *eptr;
425
426 if (o == NULL) {
427 value = 0;
428 } else {
429 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
430 if (o->encoding == REDIS_ENCODING_RAW) {
431 errno = 0;
432 value = strtold(o->ptr, &eptr);
d93f9a86 433 if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
434 errno == ERANGE || isnan(value))
5574b53e 435 return REDIS_ERR;
436 } else if (o->encoding == REDIS_ENCODING_INT) {
437 value = (long)o->ptr;
438 } else {
439 redisPanic("Unknown string encoding");
440 }
441 }
442 *target = value;
443 return REDIS_OK;
444}
445
446int getLongDoubleFromObjectOrReply(redisClient *c, robj *o, long double *target, const char *msg) {
447 long double value;
448 if (getLongDoubleFromObject(o, &value) != REDIS_OK) {
449 if (msg != NULL) {
450 addReplyError(c,(char*)msg);
451 } else {
452 addReplyError(c,"value is not a valid float");
e2641e09 453 }
454 return REDIS_ERR;
455 }
e2641e09 456 *target = value;
457 return REDIS_OK;
458}
459
460int getLongLongFromObject(robj *o, long long *target) {
461 long long value;
462 char *eptr;
463
464 if (o == NULL) {
465 value = 0;
466 } else {
eab0e26e 467 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
e2641e09 468 if (o->encoding == REDIS_ENCODING_RAW) {
d93f9a86 469 errno = 0;
e2641e09 470 value = strtoll(o->ptr, &eptr, 10);
d93f9a86 471 if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
472 errno == ERANGE)
c91abdcd 473 return REDIS_ERR;
e2641e09 474 } else if (o->encoding == REDIS_ENCODING_INT) {
475 value = (long)o->ptr;
476 } else {
477 redisPanic("Unknown string encoding");
478 }
479 }
96ffb2fe 480 if (target) *target = value;
e2641e09 481 return REDIS_OK;
482}
483
484int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) {
485 long long value;
486 if (getLongLongFromObject(o, &value) != REDIS_OK) {
487 if (msg != NULL) {
3ab20376 488 addReplyError(c,(char*)msg);
e2641e09 489 } else {
3ab20376 490 addReplyError(c,"value is not an integer or out of range");
e2641e09 491 }
492 return REDIS_ERR;
493 }
e2641e09 494 *target = value;
495 return REDIS_OK;
496}
497
498int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) {
499 long long value;
500
501 if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR;
502 if (value < LONG_MIN || value > LONG_MAX) {
503 if (msg != NULL) {
3ab20376 504 addReplyError(c,(char*)msg);
e2641e09 505 } else {
3ab20376 506 addReplyError(c,"value is out of range");
e2641e09 507 }
508 return REDIS_ERR;
509 }
e2641e09 510 *target = value;
511 return REDIS_OK;
512}
513
514char *strEncoding(int encoding) {
515 switch(encoding) {
516 case REDIS_ENCODING_RAW: return "raw";
517 case REDIS_ENCODING_INT: return "int";
518 case REDIS_ENCODING_HT: return "hashtable";
e2641e09 519 case REDIS_ENCODING_LINKEDLIST: return "linkedlist";
520 case REDIS_ENCODING_ZIPLIST: return "ziplist";
96ffb2fe 521 case REDIS_ENCODING_INTSET: return "intset";
0b7f6d09 522 case REDIS_ENCODING_SKIPLIST: return "skiplist";
e2641e09 523 default: return "unknown";
524 }
525}
ef59a8bc 526
527/* Given an object returns the min number of seconds the object was never
528 * requested, using an approximated LRU algorithm. */
529unsigned long estimateObjectIdleTime(robj *o) {
530 if (server.lruclock >= o->lru) {
165346ca 531 return (server.lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION;
ef59a8bc 532 } else {
165346ca 533 return ((REDIS_LRU_CLOCK_MAX - o->lru) + server.lruclock) *
534 REDIS_LRU_CLOCK_RESOLUTION;
ef59a8bc 535 }
536}
ece74202 537
538/* This is an helper function for the DEBUG command. We need to lookup keys
539 * without any modification of LRU or other parameters. */
540robj *objectCommandLookup(redisClient *c, robj *key) {
541 dictEntry *de;
542
543 if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL;
c0ba9ebe 544 return (robj*) dictGetVal(de);
ece74202 545}
546
547robj *objectCommandLookupOrReply(redisClient *c, robj *key, robj *reply) {
548 robj *o = objectCommandLookup(c,key);
549
550 if (!o) addReply(c, reply);
551 return o;
552}
553
554/* Object command allows to inspect the internals of an Redis Object.
555 * Usage: OBJECT <verb> ... arguments ... */
556void objectCommand(redisClient *c) {
557 robj *o;
558
559 if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) {
560 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
561 == NULL) return;
562 addReplyLongLong(c,o->refcount);
563 } else if (!strcasecmp(c->argv[1]->ptr,"encoding") && c->argc == 3) {
564 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
565 == NULL) return;
566 addReplyBulkCString(c,strEncoding(o->encoding));
567 } else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) {
568 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
569 == NULL) return;
570 addReplyLongLong(c,estimateObjectIdleTime(o));
571 } else {
572 addReplyError(c,"Syntax error. Try OBJECT (refcount|encoding|idletime)");
573 }
574}
575