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