xref: /redis-3.2.3/src/object.c (revision 21736b41)
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 
createObject(int type,void * ptr)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. */
createRawStringObject(const char * ptr,size_t len)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. */
createEmbeddedStringObject(const char * ptr,size_t len)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
createStringObject(const char * ptr,size_t len)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 
createStringObjectFromLongLong(long long value)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. */
createStringObjectFromLongDouble(long double value,int humanfriendly)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. */
dupStringObject(robj * o)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 
createQuicklistObject(void)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 
createZiplistObject(void)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 
createSetObject(void)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 
createIntsetObject(void)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 
createHashObject(void)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 
createZsetObject(void)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 
createZsetZiplistObject(void)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 
freeStringObject(robj * o)237 void freeStringObject(robj *o) {
238     if (o->encoding == OBJ_ENCODING_RAW) {
239         sdsfree(o->ptr);
240     }
241 }
242 
freeListObject(robj * o)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 
freeSetObject(robj * o)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 
freeZsetObject(robj * o)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 
freeHashObject(robj * o)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 
incrRefCount(robj * o)297 void incrRefCount(robj *o) {
298     o->refcount++;
299 }
300 
decrRefCount(robj * o)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. */
decrRefCountVoid(void * o)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  */
resetRefCount(robj * obj)337 robj *resetRefCount(robj *obj) {
338     obj->refcount = 0;
339     return obj;
340 }
341 
checkType(client * c,robj * o,int type)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 
isObjectRepresentableAsLongLong(robj * o,long long * llval)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 */
tryObjectEncoding(robj * o)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 20 chars is not
384      * representable as a 32 nor 64 bit integer. */
385     len = sdslen(s);
386     if (len <= 20 && 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. */
getDecodedObject(robj * o)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 
compareStringObjectsWithFlags(robj * a,robj * b,int flags)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. */
compareStringObjects(robj * a,robj * b)504 int compareStringObjects(robj *a, robj *b) {
505     return compareStringObjectsWithFlags(a,b,REDIS_COMPARE_BINARY);
506 }
507 
508 /* Wrapper for compareStringObjectsWithFlags() using collation. */
collateStringObjects(robj * a,robj * b)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. */
equalStringObjects(robj * a,robj * b)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 
stringObjectLen(robj * o)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 
getDoubleFromObject(robj * o,double * target)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 
getDoubleFromObjectOrReply(client * c,robj * o,double * target,const char * msg)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 
getLongDoubleFromObject(robj * o,long double * target)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 
getLongDoubleFromObjectOrReply(client * c,robj * o,long double * target,const char * msg)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 
getLongLongFromObject(robj * o,long long * target)617 int getLongLongFromObject(robj *o, long long *target) {
618     long long value;
619 
620     if (o == NULL) {
621         value = 0;
622     } else {
623         serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
624         if (sdsEncodedObject(o)) {
625             if (string2ll(o->ptr,sdslen(o->ptr),&value) == 0) return C_ERR;
626         } else if (o->encoding == OBJ_ENCODING_INT) {
627             value = (long)o->ptr;
628         } else {
629             serverPanic("Unknown string encoding");
630         }
631     }
632     if (target) *target = value;
633     return C_OK;
634 }
635 
getLongLongFromObjectOrReply(client * c,robj * o,long long * target,const char * msg)636 int getLongLongFromObjectOrReply(client *c, robj *o, long long *target, const char *msg) {
637     long long value;
638     if (getLongLongFromObject(o, &value) != C_OK) {
639         if (msg != NULL) {
640             addReplyError(c,(char*)msg);
641         } else {
642             addReplyError(c,"value is not an integer or out of range");
643         }
644         return C_ERR;
645     }
646     *target = value;
647     return C_OK;
648 }
649 
getLongFromObjectOrReply(client * c,robj * o,long * target,const char * msg)650 int getLongFromObjectOrReply(client *c, robj *o, long *target, const char *msg) {
651     long long value;
652 
653     if (getLongLongFromObjectOrReply(c, o, &value, msg) != C_OK) return C_ERR;
654     if (value < LONG_MIN || value > LONG_MAX) {
655         if (msg != NULL) {
656             addReplyError(c,(char*)msg);
657         } else {
658             addReplyError(c,"value is out of range");
659         }
660         return C_ERR;
661     }
662     *target = value;
663     return C_OK;
664 }
665 
strEncoding(int encoding)666 char *strEncoding(int encoding) {
667     switch(encoding) {
668     case OBJ_ENCODING_RAW: return "raw";
669     case OBJ_ENCODING_INT: return "int";
670     case OBJ_ENCODING_HT: return "hashtable";
671     case OBJ_ENCODING_QUICKLIST: return "quicklist";
672     case OBJ_ENCODING_ZIPLIST: return "ziplist";
673     case OBJ_ENCODING_INTSET: return "intset";
674     case OBJ_ENCODING_SKIPLIST: return "skiplist";
675     case OBJ_ENCODING_EMBSTR: return "embstr";
676     default: return "unknown";
677     }
678 }
679 
680 /* Given an object returns the min number of milliseconds the object was never
681  * requested, using an approximated LRU algorithm. */
estimateObjectIdleTime(robj * o)682 unsigned long long estimateObjectIdleTime(robj *o) {
683     unsigned long long lruclock = LRU_CLOCK();
684     if (lruclock >= o->lru) {
685         return (lruclock - o->lru) * LRU_CLOCK_RESOLUTION;
686     } else {
687         return (lruclock + (LRU_CLOCK_MAX - o->lru)) *
688                     LRU_CLOCK_RESOLUTION;
689     }
690 }
691 
692 /* This is a helper function for the OBJECT command. We need to lookup keys
693  * without any modification of LRU or other parameters. */
objectCommandLookup(client * c,robj * key)694 robj *objectCommandLookup(client *c, robj *key) {
695     dictEntry *de;
696 
697     if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL;
698     return (robj*) dictGetVal(de);
699 }
700 
objectCommandLookupOrReply(client * c,robj * key,robj * reply)701 robj *objectCommandLookupOrReply(client *c, robj *key, robj *reply) {
702     robj *o = objectCommandLookup(c,key);
703 
704     if (!o) addReply(c, reply);
705     return o;
706 }
707 
708 /* Object command allows to inspect the internals of an Redis Object.
709  * Usage: OBJECT <refcount|encoding|idletime> <key> */
objectCommand(client * c)710 void objectCommand(client *c) {
711     robj *o;
712 
713     if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) {
714         if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
715                 == NULL) return;
716         addReplyLongLong(c,o->refcount);
717     } else if (!strcasecmp(c->argv[1]->ptr,"encoding") && c->argc == 3) {
718         if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
719                 == NULL) return;
720         addReplyBulkCString(c,strEncoding(o->encoding));
721     } else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) {
722         if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
723                 == NULL) return;
724         addReplyLongLong(c,estimateObjectIdleTime(o)/1000);
725     } else {
726         addReplyError(c,"Syntax error. Try OBJECT (refcount|encoding|idletime)");
727     }
728 }
729 
730