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 = getLRUClock(); 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 = getLRUClock(); 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 if (o->encoding == REDIS_ENCODING_INT) 345 return o; /* Already encoded */ 346 347 /* It's not safe to encode shared objects: shared objects can be shared 348 * everywhere in the "object space" of Redis. Encoded objects can only 349 * appear as "values" (and not, for instance, as keys) */ 350 if (o->refcount > 1) return o; 351 352 /* Currently we try to encode only strings */ 353 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); 354 355 /* Check if we can represent this string as a long integer. 356 * Note that we are sure that a string larger than 21 chars is not 357 * representable as a 64 bit integer. */ 358 len = sdslen(s); 359 if (len > 21 || !string2l(s,len,&value)) { 360 /* Integer encoding not possible. Check if we can use EMBSTR. */ 361 if (sdslen(s) <= REDIS_ENCODING_EMBSTR_SIZE_LIMIT) { 362 robj *emb = createEmbeddedStringObject(s,sdslen(s)); 363 decrRefCount(o); 364 return emb; 365 } else { 366 /* We can't encode the object... 367 * 368 * Do the last try, and at least optimize the SDS string inside 369 * the string object to require little space, in case there 370 * is more than 10% of free space at the end of the SDS string. 371 * 372 * We do that only for relatively large strings as this branch 373 * is only entered if the length of the string is greater than 374 * REDIS_ENCODING_EMBSTR_SIZE_LIMIT. */ 375 if (o->encoding == REDIS_ENCODING_RAW && 376 sdsavail(s) > len/10) 377 { 378 o->ptr = sdsRemoveFreeSpace(o->ptr); 379 } 380 /* Return the original object. */ 381 return o; 382 } 383 } 384 385 /* Ok, this object can be encoded... 386 * 387 * Can I use a shared object? Only if the object is inside a given range 388 * 389 * Note that we also avoid using shared integers when maxmemory is used 390 * because every object needs to have a private LRU field for the LRU 391 * algorithm to work well. */ 392 if (server.maxmemory == 0 && value >= 0 && value < REDIS_SHARED_INTEGERS) { 393 decrRefCount(o); 394 incrRefCount(shared.integers[value]); 395 return shared.integers[value]; 396 } else { 397 if (o->encoding == REDIS_ENCODING_RAW) sdsfree(o->ptr); 398 o->encoding = REDIS_ENCODING_INT; 399 o->ptr = (void*) value; 400 return o; 401 } 402 } 403 404 /* Get a decoded version of an encoded object (returned as a new object). 405 * If the object is already raw-encoded just increment the ref count. */ 406 robj *getDecodedObject(robj *o) { 407 robj *dec; 408 409 if (sdsEncodedObject(o)) { 410 incrRefCount(o); 411 return o; 412 } 413 if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) { 414 char buf[32]; 415 416 ll2string(buf,32,(long)o->ptr); 417 dec = createStringObject(buf,strlen(buf)); 418 return dec; 419 } else { 420 redisPanic("Unknown encoding type"); 421 } 422 } 423 424 /* Compare two string objects via strcmp() or strcoll() depending on flags. 425 * Note that the objects may be integer-encoded. In such a case we 426 * use ll2string() to get a string representation of the numbers on the stack 427 * and compare the strings, it's much faster than calling getDecodedObject(). 428 * 429 * Important note: when REDIS_COMPARE_BINARY is used a binary-safe comparison 430 * is used. */ 431 432 #define REDIS_COMPARE_BINARY (1<<0) 433 #define REDIS_COMPARE_COLL (1<<1) 434 435 int compareStringObjectsWithFlags(robj *a, robj *b, int flags) { 436 redisAssertWithInfo(NULL,a,a->type == REDIS_STRING && b->type == REDIS_STRING); 437 char bufa[128], bufb[128], *astr, *bstr; 438 size_t alen, blen, minlen; 439 440 if (a == b) return 0; 441 if (sdsEncodedObject(a)) { 442 astr = a->ptr; 443 alen = sdslen(astr); 444 } else { 445 alen = ll2string(bufa,sizeof(bufa),(long) a->ptr); 446 astr = bufa; 447 } 448 if (sdsEncodedObject(b)) { 449 bstr = b->ptr; 450 blen = sdslen(bstr); 451 } else { 452 blen = ll2string(bufb,sizeof(bufb),(long) b->ptr); 453 bstr = bufb; 454 } 455 if (flags & REDIS_COMPARE_COLL) { 456 return strcoll(astr,bstr); 457 } else { 458 int cmp; 459 460 minlen = (alen < blen) ? alen : blen; 461 cmp = memcmp(astr,bstr,minlen); 462 if (cmp == 0) return alen-blen; 463 return cmp; 464 } 465 } 466 467 /* Wrapper for compareStringObjectsWithFlags() using binary comparison. */ 468 int compareStringObjects(robj *a, robj *b) { 469 return compareStringObjectsWithFlags(a,b,REDIS_COMPARE_BINARY); 470 } 471 472 /* Wrapper for compareStringObjectsWithFlags() using collation. */ 473 int collateStringObjects(robj *a, robj *b) { 474 return compareStringObjectsWithFlags(a,b,REDIS_COMPARE_COLL); 475 } 476 477 /* Equal string objects return 1 if the two objects are the same from the 478 * point of view of a string comparison, otherwise 0 is returned. Note that 479 * this function is faster then checking for (compareStringObject(a,b) == 0) 480 * because it can perform some more optimization. */ 481 int equalStringObjects(robj *a, robj *b) { 482 if (a->encoding == REDIS_ENCODING_INT && 483 b->encoding == REDIS_ENCODING_INT){ 484 /* If both strings are integer encoded just check if the stored 485 * long is the same. */ 486 return a->ptr == b->ptr; 487 } else { 488 return compareStringObjects(a,b) == 0; 489 } 490 } 491 492 size_t stringObjectLen(robj *o) { 493 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); 494 if (sdsEncodedObject(o)) { 495 return sdslen(o->ptr); 496 } else { 497 char buf[32]; 498 499 return ll2string(buf,32,(long)o->ptr); 500 } 501 } 502 503 int getDoubleFromObject(robj *o, double *target) { 504 double value; 505 char *eptr; 506 507 if (o == NULL) { 508 value = 0; 509 } else { 510 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); 511 if (sdsEncodedObject(o)) { 512 errno = 0; 513 value = strtod(o->ptr, &eptr); 514 if (isspace(((char*)o->ptr)[0]) || 515 eptr[0] != '\0' || 516 (errno == ERANGE && 517 (value == HUGE_VAL || value == -HUGE_VAL || value == 0)) || 518 errno == EINVAL || 519 isnan(value)) 520 return REDIS_ERR; 521 } else if (o->encoding == REDIS_ENCODING_INT) { 522 value = (long)o->ptr; 523 } else { 524 redisPanic("Unknown string encoding"); 525 } 526 } 527 *target = value; 528 return REDIS_OK; 529 } 530 531 int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) { 532 double value; 533 if (getDoubleFromObject(o, &value) != REDIS_OK) { 534 if (msg != NULL) { 535 addReplyError(c,(char*)msg); 536 } else { 537 addReplyError(c,"value is not a valid float"); 538 } 539 return REDIS_ERR; 540 } 541 *target = value; 542 return REDIS_OK; 543 } 544 545 int getLongDoubleFromObject(robj *o, long double *target) { 546 long double value; 547 char *eptr; 548 549 if (o == NULL) { 550 value = 0; 551 } else { 552 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); 553 if (sdsEncodedObject(o)) { 554 errno = 0; 555 value = strtold(o->ptr, &eptr); 556 if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' || 557 errno == ERANGE || isnan(value)) 558 return REDIS_ERR; 559 } else if (o->encoding == REDIS_ENCODING_INT) { 560 value = (long)o->ptr; 561 } else { 562 redisPanic("Unknown string encoding"); 563 } 564 } 565 *target = value; 566 return REDIS_OK; 567 } 568 569 int getLongDoubleFromObjectOrReply(redisClient *c, robj *o, long double *target, const char *msg) { 570 long double value; 571 if (getLongDoubleFromObject(o, &value) != REDIS_OK) { 572 if (msg != NULL) { 573 addReplyError(c,(char*)msg); 574 } else { 575 addReplyError(c,"value is not a valid float"); 576 } 577 return REDIS_ERR; 578 } 579 *target = value; 580 return REDIS_OK; 581 } 582 583 int getLongLongFromObject(robj *o, long long *target) { 584 long long value; 585 char *eptr; 586 587 if (o == NULL) { 588 value = 0; 589 } else { 590 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); 591 if (sdsEncodedObject(o)) { 592 errno = 0; 593 value = strtoll(o->ptr, &eptr, 10); 594 if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' || 595 errno == ERANGE) 596 return REDIS_ERR; 597 } else if (o->encoding == REDIS_ENCODING_INT) { 598 value = (long)o->ptr; 599 } else { 600 redisPanic("Unknown string encoding"); 601 } 602 } 603 if (target) *target = value; 604 return REDIS_OK; 605 } 606 607 int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) { 608 long long value; 609 if (getLongLongFromObject(o, &value) != REDIS_OK) { 610 if (msg != NULL) { 611 addReplyError(c,(char*)msg); 612 } else { 613 addReplyError(c,"value is not an integer or out of range"); 614 } 615 return REDIS_ERR; 616 } 617 *target = value; 618 return REDIS_OK; 619 } 620 621 int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) { 622 long long value; 623 624 if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR; 625 if (value < LONG_MIN || value > LONG_MAX) { 626 if (msg != NULL) { 627 addReplyError(c,(char*)msg); 628 } else { 629 addReplyError(c,"value is out of range"); 630 } 631 return REDIS_ERR; 632 } 633 *target = value; 634 return REDIS_OK; 635 } 636 637 char *strEncoding(int encoding) { 638 switch(encoding) { 639 case REDIS_ENCODING_RAW: return "raw"; 640 case REDIS_ENCODING_INT: return "int"; 641 case REDIS_ENCODING_HT: return "hashtable"; 642 case REDIS_ENCODING_LINKEDLIST: return "linkedlist"; 643 case REDIS_ENCODING_ZIPLIST: return "ziplist"; 644 case REDIS_ENCODING_INTSET: return "intset"; 645 case REDIS_ENCODING_SKIPLIST: return "skiplist"; 646 case REDIS_ENCODING_EMBSTR: return "embstr"; 647 default: return "unknown"; 648 } 649 } 650 651 /* Given an object returns the min number of milliseconds the object was never 652 * requested, using an approximated LRU algorithm. */ 653 unsigned long long estimateObjectIdleTime(robj *o) { 654 unsigned long long lruclock = LRU_CLOCK(); 655 if (lruclock >= o->lru) { 656 return (lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION; 657 } else { 658 return (lruclock + (REDIS_LRU_CLOCK_MAX - o->lru)) * 659 REDIS_LRU_CLOCK_RESOLUTION; 660 } 661 } 662 663 /* This is a helper function for the DEBUG command. We need to lookup keys 664 * without any modification of LRU or other parameters. */ 665 robj *objectCommandLookup(redisClient *c, robj *key) { 666 dictEntry *de; 667 668 if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL; 669 return (robj*) dictGetVal(de); 670 } 671 672 robj *objectCommandLookupOrReply(redisClient *c, robj *key, robj *reply) { 673 robj *o = objectCommandLookup(c,key); 674 675 if (!o) addReply(c, reply); 676 return o; 677 } 678 679 /* Object command allows to inspect the internals of an Redis Object. 680 * Usage: OBJECT <verb> ... arguments ... */ 681 void objectCommand(redisClient *c) { 682 robj *o; 683 684 if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) { 685 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) 686 == NULL) return; 687 addReplyLongLong(c,o->refcount); 688 } else if (!strcasecmp(c->argv[1]->ptr,"encoding") && c->argc == 3) { 689 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) 690 == NULL) return; 691 addReplyBulkCString(c,strEncoding(o->encoding)); 692 } else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) { 693 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) 694 == NULL) return; 695 addReplyLongLong(c,estimateObjectIdleTime(o)/1000); 696 } else { 697 addReplyError(c,"Syntax error. Try OBJECT (refcount|encoding|idletime)"); 698 } 699 } 700 701