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