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