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