xref: /redis-3.2.3/src/object.c (revision 897adc1c)
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