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