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 31 #include "redis.h" 32 #include <math.h> 33 #include <ctype.h> 34 35 #ifdef __CYGWIN__ 36 #define strtold(a,b) ((long double)strtod((a),(b))) 37 #endif 38 39 robj *createObject(int type, void *ptr) { 40 robj *o = zmalloc(sizeof(*o)); 41 o->type = type; 42 o->encoding = REDIS_ENCODING_RAW; 43 o->ptr = ptr; 44 o->refcount = 1; 45 46 /* Set the LRU to the current lruclock (minutes resolution). */ 47 o->lru = LRU_CLOCK(); 48 return o; 49 } 50 51 /* Create a string object with encoding REDIS_ENCODING_RAW, that is a plain 52 * string object where o->ptr points to a proper sds string. */ 53 robj *createRawStringObject(char *ptr, size_t len) { 54 return createObject(REDIS_STRING,sdsnewlen(ptr,len)); 55 } 56 57 /* Create a string object with encoding REDIS_ENCODING_EMBSTR, that is 58 * an object where the sds string is actually an unmodifiable string 59 * allocated in the same chunk as the object itself. */ 60 robj *createEmbeddedStringObject(char *ptr, size_t len) { 61 robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr)+len+1); 62 struct sdshdr *sh = (void*)(o+1); 63 64 o->type = REDIS_STRING; 65 o->encoding = REDIS_ENCODING_EMBSTR; 66 o->ptr = sh+1; 67 o->refcount = 1; 68 o->lru = LRU_CLOCK(); 69 70 sh->len = len; 71 sh->free = 0; 72 if (ptr) { 73 memcpy(sh->buf,ptr,len); 74 sh->buf[len] = '\0'; 75 } else { 76 memset(sh->buf,0,len+1); 77 } 78 return o; 79 } 80 81 /* Create a string object with EMBSTR encoding if it is smaller than 82 * REIDS_ENCODING_EMBSTR_SIZE_LIMIT, otherwise the RAW encoding is 83 * used. 84 * 85 * The current limit of 39 is chosen so that the biggest string object 86 * we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc. */ 87 #define REDIS_ENCODING_EMBSTR_SIZE_LIMIT 39 88 robj *createStringObject(char *ptr, size_t len) { 89 if (len <= REDIS_ENCODING_EMBSTR_SIZE_LIMIT) 90 return createEmbeddedStringObject(ptr,len); 91 else 92 return createRawStringObject(ptr,len); 93 } 94 95 robj *createStringObjectFromLongLong(long long value) { 96 robj *o; 97 if (value >= 0 && value < REDIS_SHARED_INTEGERS) { 98 incrRefCount(shared.integers[value]); 99 o = shared.integers[value]; 100 } else { 101 if (value >= LONG_MIN && value <= LONG_MAX) { 102 o = createObject(REDIS_STRING, NULL); 103 o->encoding = REDIS_ENCODING_INT; 104 o->ptr = (void*)((long)value); 105 } else { 106 o = createObject(REDIS_STRING,sdsfromlonglong(value)); 107 } 108 } 109 return o; 110 } 111 112 /* Note: this function is defined into object.c since here it is where it 113 * belongs but it is actually designed to be used just for INCRBYFLOAT */ 114 robj *createStringObjectFromLongDouble(long double value) { 115 char buf[256]; 116 int len; 117 118 /* We use 17 digits precision since with 128 bit floats that precision 119 * after rounding is able to represent most small decimal numbers in a way 120 * that is "non surprising" for the user (that is, most small decimal 121 * numbers will be represented in a way that when converted back into 122 * a string are exactly the same as what the user typed.) */ 123 len = snprintf(buf,sizeof(buf),"%.17Lf", value); 124 /* Now remove trailing zeroes after the '.' */ 125 if (strchr(buf,'.') != NULL) { 126 char *p = buf+len-1; 127 while(*p == '0') { 128 p--; 129 len--; 130 } 131 if (*p == '.') len--; 132 } 133 return createStringObject(buf,len); 134 } 135 136 /* Duplicate a string object, with the guarantee that the returned object 137 * has the same encoding as the original one. 138 * 139 * This function also guarantees that duplicating a small integere object 140 * (or a string object that contains a representation of a small integer) 141 * will always result in a fresh object that is unshared (refcount == 1). 142 * 143 * The resulting object always has refcount set to 1. */ 144 robj *dupStringObject(robj *o) { 145 robj *d; 146 147 redisAssert(o->type == REDIS_STRING); 148 149 switch(o->encoding) { 150 case REDIS_ENCODING_RAW: 151 return createRawStringObject(o->ptr,sdslen(o->ptr)); 152 case REDIS_ENCODING_EMBSTR: 153 return createEmbeddedStringObject(o->ptr,sdslen(o->ptr)); 154 case REDIS_ENCODING_INT: 155 d = createObject(REDIS_STRING, NULL); 156 d->encoding = REDIS_ENCODING_INT; 157 d->ptr = o->ptr; 158 return d; 159 default: 160 redisPanic("Wrong encoding."); 161 break; 162 } 163 } 164 165 robj *createListObject(void) { 166 list *l = listCreate(); 167 robj *o = createObject(REDIS_LIST,l); 168 listSetFreeMethod(l,decrRefCountVoid); 169 o->encoding = REDIS_ENCODING_LINKEDLIST; 170 return o; 171 } 172 173 robj *createZiplistObject(void) { 174 unsigned char *zl = ziplistNew(); 175 robj *o = createObject(REDIS_LIST,zl); 176 o->encoding = REDIS_ENCODING_ZIPLIST; 177 return o; 178 } 179 180 robj *createSetObject(void) { 181 dict *d = dictCreate(&setDictType,NULL); 182 robj *o = createObject(REDIS_SET,d); 183 o->encoding = REDIS_ENCODING_HT; 184 return o; 185 } 186 187 robj *createIntsetObject(void) { 188 intset *is = intsetNew(); 189 robj *o = createObject(REDIS_SET,is); 190 o->encoding = REDIS_ENCODING_INTSET; 191 return o; 192 } 193 194 robj *createHashObject(void) { 195 unsigned char *zl = ziplistNew(); 196 robj *o = createObject(REDIS_HASH, zl); 197 o->encoding = REDIS_ENCODING_ZIPLIST; 198 return o; 199 } 200 201 robj *createZsetObject(void) { 202 zset *zs = zmalloc(sizeof(*zs)); 203 robj *o; 204 205 zs->dict = dictCreate(&zsetDictType,NULL); 206 zs->zsl = zslCreate(); 207 o = createObject(REDIS_ZSET,zs); 208 o->encoding = REDIS_ENCODING_SKIPLIST; 209 return o; 210 } 211 212 robj *createZsetZiplistObject(void) { 213 unsigned char *zl = ziplistNew(); 214 robj *o = createObject(REDIS_ZSET,zl); 215 o->encoding = REDIS_ENCODING_ZIPLIST; 216 return o; 217 } 218 219 void freeStringObject(robj *o) { 220 if (o->encoding == REDIS_ENCODING_RAW) { 221 sdsfree(o->ptr); 222 } 223 } 224 225 void freeListObject(robj *o) { 226 switch (o->encoding) { 227 case REDIS_ENCODING_LINKEDLIST: 228 listRelease((list*) o->ptr); 229 break; 230 case REDIS_ENCODING_ZIPLIST: 231 zfree(o->ptr); 232 break; 233 default: 234 redisPanic("Unknown list encoding type"); 235 } 236 } 237 238 void freeSetObject(robj *o) { 239 switch (o->encoding) { 240 case REDIS_ENCODING_HT: 241 dictRelease((dict*) o->ptr); 242 break; 243 case REDIS_ENCODING_INTSET: 244 zfree(o->ptr); 245 break; 246 default: 247 redisPanic("Unknown set encoding type"); 248 } 249 } 250 251 void freeZsetObject(robj *o) { 252 zset *zs; 253 switch (o->encoding) { 254 case REDIS_ENCODING_SKIPLIST: 255 zs = o->ptr; 256 dictRelease(zs->dict); 257 zslFree(zs->zsl); 258 zfree(zs); 259 break; 260 case REDIS_ENCODING_ZIPLIST: 261 zfree(o->ptr); 262 break; 263 default: 264 redisPanic("Unknown sorted set encoding"); 265 } 266 } 267 268 void freeHashObject(robj *o) { 269 switch (o->encoding) { 270 case REDIS_ENCODING_HT: 271 dictRelease((dict*) o->ptr); 272 break; 273 case REDIS_ENCODING_ZIPLIST: 274 zfree(o->ptr); 275 break; 276 default: 277 redisPanic("Unknown hash encoding type"); 278 break; 279 } 280 } 281 282 void incrRefCount(robj *o) { 283 o->refcount++; 284 } 285 286 void decrRefCount(robj *o) { 287 if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0"); 288 if (o->refcount == 1) { 289 switch(o->type) { 290 case REDIS_STRING: freeStringObject(o); break; 291 case REDIS_LIST: freeListObject(o); break; 292 case REDIS_SET: freeSetObject(o); break; 293 case REDIS_ZSET: freeZsetObject(o); break; 294 case REDIS_HASH: freeHashObject(o); break; 295 default: redisPanic("Unknown object type"); break; 296 } 297 zfree(o); 298 } else { 299 o->refcount--; 300 } 301 } 302 303 /* This variant of decrRefCount() gets its argument as void, and is useful 304 * as free method in data structures that expect a 'void free_object(void*)' 305 * prototype for the free method. */ 306 void decrRefCountVoid(void *o) { 307 decrRefCount(o); 308 } 309 310 /* This function set the ref count to zero without freeing the object. 311 * It is useful in order to pass a new object to functions incrementing 312 * the ref count of the received object. Example: 313 * 314 * functionThatWillIncrementRefCount(resetRefCount(CreateObject(...))); 315 * 316 * Otherwise you need to resort to the less elegant pattern: 317 * 318 * *obj = createObject(...); 319 * functionThatWillIncrementRefCount(obj); 320 * decrRefCount(obj); 321 */ 322 robj *resetRefCount(robj *obj) { 323 obj->refcount = 0; 324 return obj; 325 } 326 327 int checkType(redisClient *c, robj *o, int type) { 328 if (o->type != type) { 329 addReply(c,shared.wrongtypeerr); 330 return 1; 331 } 332 return 0; 333 } 334 335 int isObjectRepresentableAsLongLong(robj *o, long long *llval) { 336 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); 337 if (o->encoding == REDIS_ENCODING_INT) { 338 if (llval) *llval = (long) o->ptr; 339 return REDIS_OK; 340 } else { 341 return string2ll(o->ptr,sdslen(o->ptr),llval) ? REDIS_OK : REDIS_ERR; 342 } 343 } 344 345 /* Try to encode a string object in order to save space */ 346 robj *tryObjectEncoding(robj *o) { 347 long value; 348 sds s = o->ptr; 349 size_t len; 350 351 /* Make sure this is a string object, the only type we encode 352 * in this function. Other types use encoded memory efficient 353 * representations but are handled by the commands implementing 354 * the type. */ 355 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); 356 357 /* We try some specialized encoding only for objects that are 358 * RAW or EMBSTR encoded, in other words objects that are still 359 * in represented by an actually array of chars. */ 360 if (!sdsEncodedObject(o)) return o; 361 362 /* It's not safe to encode shared objects: shared objects can be shared 363 * everywhere in the "object space" of Redis and may end in places where 364 * they are not handled. We handle them only as values in the keyspace. */ 365 if (o->refcount > 1) return o; 366 367 /* Check if we can represent this string as a long integer. 368 * Note that we are sure that a string larger than 21 chars is not 369 * representable as a 32 nor 64 bit integer. */ 370 len = sdslen(s); 371 if (len <= 21 && string2l(s,len,&value)) { 372 /* This object is encodable as a long. Try to use a shared object. 373 * Note that we avoid using shared integers when maxmemory is used 374 * because every object needs to have a private LRU field for the LRU 375 * algorithm to work well. */ 376 if ((server.maxmemory == 0 || 377 (server.maxmemory_policy != REDIS_MAXMEMORY_VOLATILE_LRU && 378 server.maxmemory_policy != REDIS_MAXMEMORY_ALLKEYS_LRU)) && 379 value >= 0 && 380 value < REDIS_SHARED_INTEGERS) 381 { 382 decrRefCount(o); 383 incrRefCount(shared.integers[value]); 384 return shared.integers[value]; 385 } else { 386 if (o->encoding == REDIS_ENCODING_RAW) sdsfree(o->ptr); 387 o->encoding = REDIS_ENCODING_INT; 388 o->ptr = (void*) value; 389 return o; 390 } 391 } 392 393 /* If the string is small and is still RAW encoded, 394 * try the EMBSTR encoding which is more efficient. 395 * In this representation the object and the SDS string are allocated 396 * in the same chunk of memory to save space and cache misses. */ 397 if (len <= REDIS_ENCODING_EMBSTR_SIZE_LIMIT) { 398 robj *emb; 399 400 if (o->encoding == REDIS_ENCODING_EMBSTR) return o; 401 emb = createEmbeddedStringObject(s,sdslen(s)); 402 decrRefCount(o); 403 return emb; 404 } 405 406 /* We can't encode the object... 407 * 408 * Do the last try, and at least optimize the SDS string inside 409 * the string object to require little space, in case there 410 * is more than 10% of free space at the end of the SDS string. 411 * 412 * We do that only for relatively large strings as this branch 413 * is only entered if the length of the string is greater than 414 * REDIS_ENCODING_EMBSTR_SIZE_LIMIT. */ 415 if (o->encoding == REDIS_ENCODING_RAW && 416 sdsavail(s) > len/10) 417 { 418 o->ptr = sdsRemoveFreeSpace(o->ptr); 419 } 420 421 /* Return the original object. */ 422 return o; 423 } 424 425 /* Get a decoded version of an encoded object (returned as a new object). 426 * If the object is already raw-encoded just increment the ref count. */ 427 robj *getDecodedObject(robj *o) { 428 robj *dec; 429 430 if (sdsEncodedObject(o)) { 431 incrRefCount(o); 432 return o; 433 } 434 if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) { 435 char buf[32]; 436 437 ll2string(buf,32,(long)o->ptr); 438 dec = createStringObject(buf,strlen(buf)); 439 return dec; 440 } else { 441 redisPanic("Unknown encoding type"); 442 } 443 } 444 445 /* Compare two string objects via strcmp() or strcoll() depending on flags. 446 * Note that the objects may be integer-encoded. In such a case we 447 * use ll2string() to get a string representation of the numbers on the stack 448 * and compare the strings, it's much faster than calling getDecodedObject(). 449 * 450 * Important note: when REDIS_COMPARE_BINARY is used a binary-safe comparison 451 * is used. */ 452 453 #define REDIS_COMPARE_BINARY (1<<0) 454 #define REDIS_COMPARE_COLL (1<<1) 455 456 int compareStringObjectsWithFlags(robj *a, robj *b, int flags) { 457 redisAssertWithInfo(NULL,a,a->type == REDIS_STRING && b->type == REDIS_STRING); 458 char bufa[128], bufb[128], *astr, *bstr; 459 size_t alen, blen, minlen; 460 461 if (a == b) return 0; 462 if (sdsEncodedObject(a)) { 463 astr = a->ptr; 464 alen = sdslen(astr); 465 } else { 466 alen = ll2string(bufa,sizeof(bufa),(long) a->ptr); 467 astr = bufa; 468 } 469 if (sdsEncodedObject(b)) { 470 bstr = b->ptr; 471 blen = sdslen(bstr); 472 } else { 473 blen = ll2string(bufb,sizeof(bufb),(long) b->ptr); 474 bstr = bufb; 475 } 476 if (flags & REDIS_COMPARE_COLL) { 477 return strcoll(astr,bstr); 478 } else { 479 int cmp; 480 481 minlen = (alen < blen) ? alen : blen; 482 cmp = memcmp(astr,bstr,minlen); 483 if (cmp == 0) return alen-blen; 484 return cmp; 485 } 486 } 487 488 /* Wrapper for compareStringObjectsWithFlags() using binary comparison. */ 489 int compareStringObjects(robj *a, robj *b) { 490 return compareStringObjectsWithFlags(a,b,REDIS_COMPARE_BINARY); 491 } 492 493 /* Wrapper for compareStringObjectsWithFlags() using collation. */ 494 int collateStringObjects(robj *a, robj *b) { 495 return compareStringObjectsWithFlags(a,b,REDIS_COMPARE_COLL); 496 } 497 498 /* Equal string objects return 1 if the two objects are the same from the 499 * point of view of a string comparison, otherwise 0 is returned. Note that 500 * this function is faster then checking for (compareStringObject(a,b) == 0) 501 * because it can perform some more optimization. */ 502 int equalStringObjects(robj *a, robj *b) { 503 if (a->encoding == REDIS_ENCODING_INT && 504 b->encoding == REDIS_ENCODING_INT){ 505 /* If both strings are integer encoded just check if the stored 506 * long is the same. */ 507 return a->ptr == b->ptr; 508 } else { 509 return compareStringObjects(a,b) == 0; 510 } 511 } 512 513 size_t stringObjectLen(robj *o) { 514 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); 515 if (sdsEncodedObject(o)) { 516 return sdslen(o->ptr); 517 } else { 518 char buf[32]; 519 520 return ll2string(buf,32,(long)o->ptr); 521 } 522 } 523 524 int getDoubleFromObject(robj *o, double *target) { 525 double value; 526 char *eptr; 527 528 if (o == NULL) { 529 value = 0; 530 } else { 531 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); 532 if (sdsEncodedObject(o)) { 533 errno = 0; 534 value = strtod(o->ptr, &eptr); 535 if (isspace(((char*)o->ptr)[0]) || 536 eptr[0] != '\0' || 537 (errno == ERANGE && 538 (value == HUGE_VAL || value == -HUGE_VAL || value == 0)) || 539 errno == EINVAL || 540 isnan(value)) 541 return REDIS_ERR; 542 } else if (o->encoding == REDIS_ENCODING_INT) { 543 value = (long)o->ptr; 544 } else { 545 redisPanic("Unknown string encoding"); 546 } 547 } 548 *target = value; 549 return REDIS_OK; 550 } 551 552 int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) { 553 double value; 554 if (getDoubleFromObject(o, &value) != REDIS_OK) { 555 if (msg != NULL) { 556 addReplyError(c,(char*)msg); 557 } else { 558 addReplyError(c,"value is not a valid float"); 559 } 560 return REDIS_ERR; 561 } 562 *target = value; 563 return REDIS_OK; 564 } 565 566 int getLongDoubleFromObject(robj *o, long double *target) { 567 long double value; 568 char *eptr; 569 570 if (o == NULL) { 571 value = 0; 572 } else { 573 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); 574 if (sdsEncodedObject(o)) { 575 errno = 0; 576 value = strtold(o->ptr, &eptr); 577 if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' || 578 errno == ERANGE || isnan(value)) 579 return REDIS_ERR; 580 } else if (o->encoding == REDIS_ENCODING_INT) { 581 value = (long)o->ptr; 582 } else { 583 redisPanic("Unknown string encoding"); 584 } 585 } 586 *target = value; 587 return REDIS_OK; 588 } 589 590 int getLongDoubleFromObjectOrReply(redisClient *c, robj *o, long double *target, const char *msg) { 591 long double value; 592 if (getLongDoubleFromObject(o, &value) != REDIS_OK) { 593 if (msg != NULL) { 594 addReplyError(c,(char*)msg); 595 } else { 596 addReplyError(c,"value is not a valid float"); 597 } 598 return REDIS_ERR; 599 } 600 *target = value; 601 return REDIS_OK; 602 } 603 604 int getLongLongFromObject(robj *o, long long *target) { 605 long long value; 606 char *eptr; 607 608 if (o == NULL) { 609 value = 0; 610 } else { 611 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); 612 if (sdsEncodedObject(o)) { 613 errno = 0; 614 value = strtoll(o->ptr, &eptr, 10); 615 if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' || 616 errno == ERANGE) 617 return REDIS_ERR; 618 } else if (o->encoding == REDIS_ENCODING_INT) { 619 value = (long)o->ptr; 620 } else { 621 redisPanic("Unknown string encoding"); 622 } 623 } 624 if (target) *target = value; 625 return REDIS_OK; 626 } 627 628 int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) { 629 long long value; 630 if (getLongLongFromObject(o, &value) != REDIS_OK) { 631 if (msg != NULL) { 632 addReplyError(c,(char*)msg); 633 } else { 634 addReplyError(c,"value is not an integer or out of range"); 635 } 636 return REDIS_ERR; 637 } 638 *target = value; 639 return REDIS_OK; 640 } 641 642 int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) { 643 long long value; 644 645 if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR; 646 if (value < LONG_MIN || value > LONG_MAX) { 647 if (msg != NULL) { 648 addReplyError(c,(char*)msg); 649 } else { 650 addReplyError(c,"value is out of range"); 651 } 652 return REDIS_ERR; 653 } 654 *target = value; 655 return REDIS_OK; 656 } 657 658 char *strEncoding(int encoding) { 659 switch(encoding) { 660 case REDIS_ENCODING_RAW: return "raw"; 661 case REDIS_ENCODING_INT: return "int"; 662 case REDIS_ENCODING_HT: return "hashtable"; 663 case REDIS_ENCODING_LINKEDLIST: return "linkedlist"; 664 case REDIS_ENCODING_ZIPLIST: return "ziplist"; 665 case REDIS_ENCODING_INTSET: return "intset"; 666 case REDIS_ENCODING_SKIPLIST: return "skiplist"; 667 case REDIS_ENCODING_EMBSTR: return "embstr"; 668 default: return "unknown"; 669 } 670 } 671 672 /* Given an object returns the min number of milliseconds the object was never 673 * requested, using an approximated LRU algorithm. */ 674 unsigned long long estimateObjectIdleTime(robj *o) { 675 unsigned long long lruclock = LRU_CLOCK(); 676 if (lruclock >= o->lru) { 677 return (lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION; 678 } else { 679 return (lruclock + (REDIS_LRU_CLOCK_MAX - o->lru)) * 680 REDIS_LRU_CLOCK_RESOLUTION; 681 } 682 } 683 684 /* This is a helper function for the OBJECT command. We need to lookup keys 685 * without any modification of LRU or other parameters. */ 686 robj *objectCommandLookup(redisClient *c, robj *key) { 687 dictEntry *de; 688 689 if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL; 690 return (robj*) dictGetVal(de); 691 } 692 693 robj *objectCommandLookupOrReply(redisClient *c, robj *key, robj *reply) { 694 robj *o = objectCommandLookup(c,key); 695 696 if (!o) addReply(c, reply); 697 return o; 698 } 699 700 /* Object command allows to inspect the internals of an Redis Object. 701 * Usage: OBJECT <refcount|encoding|idletime> <key> */ 702 void objectCommand(redisClient *c) { 703 robj *o; 704 705 if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) { 706 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) 707 == NULL) return; 708 addReplyLongLong(c,o->refcount); 709 } else if (!strcasecmp(c->argv[1]->ptr,"encoding") && c->argc == 3) { 710 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) 711 == NULL) return; 712 addReplyBulkCString(c,strEncoding(o->encoding)); 713 } else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) { 714 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) 715 == NULL) return; 716 addReplyLongLong(c,estimateObjectIdleTime(o)/1000); 717 } else { 718 addReplyError(c,"Syntax error. Try OBJECT (refcount|encoding|idletime)"); 719 } 720 } 721 722