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