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 /* 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 char buf[32]; 533 534 return ll2string(buf,32,(long)o->ptr); 535 } 536 } 537 538 int getDoubleFromObject(robj *o, double *target) { 539 double value; 540 char *eptr; 541 542 if (o == NULL) { 543 value = 0; 544 } else { 545 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); 546 if (sdsEncodedObject(o)) { 547 errno = 0; 548 value = strtod(o->ptr, &eptr); 549 if (isspace(((char*)o->ptr)[0]) || 550 eptr[0] != '\0' || 551 (errno == ERANGE && 552 (value == HUGE_VAL || value == -HUGE_VAL || value == 0)) || 553 errno == EINVAL || 554 isnan(value)) 555 return REDIS_ERR; 556 } else if (o->encoding == REDIS_ENCODING_INT) { 557 value = (long)o->ptr; 558 } else { 559 redisPanic("Unknown string encoding"); 560 } 561 } 562 *target = value; 563 return REDIS_OK; 564 } 565 566 int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) { 567 double value; 568 if (getDoubleFromObject(o, &value) != REDIS_OK) { 569 if (msg != NULL) { 570 addReplyError(c,(char*)msg); 571 } else { 572 addReplyError(c,"value is not a valid float"); 573 } 574 return REDIS_ERR; 575 } 576 *target = value; 577 return REDIS_OK; 578 } 579 580 int getLongDoubleFromObject(robj *o, long double *target) { 581 long double value; 582 char *eptr; 583 584 if (o == NULL) { 585 value = 0; 586 } else { 587 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); 588 if (sdsEncodedObject(o)) { 589 errno = 0; 590 value = strtold(o->ptr, &eptr); 591 if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' || 592 errno == ERANGE || isnan(value)) 593 return REDIS_ERR; 594 } else if (o->encoding == REDIS_ENCODING_INT) { 595 value = (long)o->ptr; 596 } else { 597 redisPanic("Unknown string encoding"); 598 } 599 } 600 *target = value; 601 return REDIS_OK; 602 } 603 604 int getLongDoubleFromObjectOrReply(redisClient *c, robj *o, long double *target, const char *msg) { 605 long double value; 606 if (getLongDoubleFromObject(o, &value) != REDIS_OK) { 607 if (msg != NULL) { 608 addReplyError(c,(char*)msg); 609 } else { 610 addReplyError(c,"value is not a valid float"); 611 } 612 return REDIS_ERR; 613 } 614 *target = value; 615 return REDIS_OK; 616 } 617 618 int getLongLongFromObject(robj *o, long long *target) { 619 long long value; 620 char *eptr; 621 622 if (o == NULL) { 623 value = 0; 624 } else { 625 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); 626 if (sdsEncodedObject(o)) { 627 errno = 0; 628 value = strtoll(o->ptr, &eptr, 10); 629 if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' || 630 errno == ERANGE) 631 return REDIS_ERR; 632 } else if (o->encoding == REDIS_ENCODING_INT) { 633 value = (long)o->ptr; 634 } else { 635 redisPanic("Unknown string encoding"); 636 } 637 } 638 if (target) *target = value; 639 return REDIS_OK; 640 } 641 642 int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) { 643 long long value; 644 if (getLongLongFromObject(o, &value) != REDIS_OK) { 645 if (msg != NULL) { 646 addReplyError(c,(char*)msg); 647 } else { 648 addReplyError(c,"value is not an integer or out of range"); 649 } 650 return REDIS_ERR; 651 } 652 *target = value; 653 return REDIS_OK; 654 } 655 656 int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) { 657 long long value; 658 659 if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR; 660 if (value < LONG_MIN || value > LONG_MAX) { 661 if (msg != NULL) { 662 addReplyError(c,(char*)msg); 663 } else { 664 addReplyError(c,"value is out of range"); 665 } 666 return REDIS_ERR; 667 } 668 *target = value; 669 return REDIS_OK; 670 } 671 672 char *strEncoding(int encoding) { 673 switch(encoding) { 674 case REDIS_ENCODING_RAW: return "raw"; 675 case REDIS_ENCODING_INT: return "int"; 676 case REDIS_ENCODING_HT: return "hashtable"; 677 case REDIS_ENCODING_QUICKLIST: return "quicklist"; 678 case REDIS_ENCODING_ZIPLIST: return "ziplist"; 679 case REDIS_ENCODING_INTSET: return "intset"; 680 case REDIS_ENCODING_SKIPLIST: return "skiplist"; 681 case REDIS_ENCODING_EMBSTR: return "embstr"; 682 default: return "unknown"; 683 } 684 } 685 686 /* Given an object returns the min number of milliseconds the object was never 687 * requested, using an approximated LRU algorithm. */ 688 unsigned long long estimateObjectIdleTime(robj *o) { 689 unsigned long long lruclock = LRU_CLOCK(); 690 if (lruclock >= o->lru) { 691 return (lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION; 692 } else { 693 return (lruclock + (REDIS_LRU_CLOCK_MAX - o->lru)) * 694 REDIS_LRU_CLOCK_RESOLUTION; 695 } 696 } 697 698 /* This is a helper function for the OBJECT command. We need to lookup keys 699 * without any modification of LRU or other parameters. */ 700 robj *objectCommandLookup(redisClient *c, robj *key) { 701 dictEntry *de; 702 703 if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL; 704 return (robj*) dictGetVal(de); 705 } 706 707 robj *objectCommandLookupOrReply(redisClient *c, robj *key, robj *reply) { 708 robj *o = objectCommandLookup(c,key); 709 710 if (!o) addReply(c, reply); 711 return o; 712 } 713 714 /* Object command allows to inspect the internals of an Redis Object. 715 * Usage: OBJECT <refcount|encoding|idletime> <key> */ 716 void objectCommand(redisClient *c) { 717 robj *o; 718 719 if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) { 720 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) 721 == NULL) return; 722 addReplyLongLong(c,o->refcount); 723 } else if (!strcasecmp(c->argv[1]->ptr,"encoding") && c->argc == 3) { 724 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) 725 == NULL) return; 726 addReplyBulkCString(c,strEncoding(o->encoding)); 727 } else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) { 728 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) 729 == NULL) return; 730 addReplyLongLong(c,estimateObjectIdleTime(o)/1000); 731 } else { 732 addReplyError(c,"Syntax error. Try OBJECT (refcount|encoding|idletime)"); 733 } 734 } 735 736