object.c 24.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
/* Redis Object implementation.
 *
 * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   * Neither the name of Redis nor the names of its contributors may be used
 *     to endorse or promote products derived from this software without
 *     specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

31
#include "redis.h"
32
#include "lzf.h"    /* LZF compression library */
33
#include <math.h>
34
#include <ctype.h>
35 36

robj *createObject(int type, void *ptr) {
37
    robj *o = zmalloc(sizeof(*o));
38 39 40 41
    o->type = type;
    o->encoding = REDIS_ENCODING_RAW;
    o->ptr = ptr;
    o->refcount = 1;
42

43
    /* Set the LRU to the current lruclock (minutes resolution). */
44
    o->lru = LRU_CLOCK();
45 46 47
    return o;
}

48 49 50
/* Create a string object with encoding REDIS_ENCODING_RAW, that is a plain
 * string object where o->ptr points to a proper sds string. */
robj *createRawStringObject(char *ptr, size_t len) {
51 52 53
    return createObject(REDIS_STRING,sdsnewlen(ptr,len));
}

54 55 56 57 58 59 60 61 62 63 64
/* Create a string object with encoding REDIS_ENCODING_EMBSTR, that is
 * an object where the sds string is actually an unmodifiable string
 * allocated in the same chunk as the object itself. */
robj *createEmbeddedStringObject(char *ptr, size_t len) {
    robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr)+len+1);
    struct sdshdr *sh = (void*)(o+1);

    o->type = REDIS_STRING;
    o->encoding = REDIS_ENCODING_EMBSTR;
    o->ptr = sh+1;
    o->refcount = 1;
65
    o->lru = LRU_CLOCK();
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88

    sh->len = len;
    sh->free = 0;
    if (ptr) {
        memcpy(sh->buf,ptr,len);
        sh->buf[len] = '\0';
    } else {
        memset(sh->buf,0,len+1);
    }
    return o;
}

/* Create a string object with EMBSTR encoding if it is smaller than
 * REIDS_ENCODING_EMBSTR_SIZE_LIMIT, otherwise the RAW encoding is
 * used. */
#define REDIS_ENCODING_EMBSTR_SIZE_LIMIT 32
robj *createStringObject(char *ptr, size_t len) {
    if (len <= REDIS_ENCODING_EMBSTR_SIZE_LIMIT)
        return createEmbeddedStringObject(ptr,len);
    else
        return createRawStringObject(ptr,len);
}

89 90
robj *createStringObjectFromLongLong(long long value) {
    robj *o;
A
antirez 已提交
91
    if (value >= 0 && value < REDIS_SHARED_INTEGERS) {
92 93 94 95 96 97 98 99 100 101 102 103 104 105
        incrRefCount(shared.integers[value]);
        o = shared.integers[value];
    } else {
        if (value >= LONG_MIN && value <= LONG_MAX) {
            o = createObject(REDIS_STRING, NULL);
            o->encoding = REDIS_ENCODING_INT;
            o->ptr = (void*)((long)value);
        } else {
            o = createObject(REDIS_STRING,sdsfromlonglong(value));
        }
    }
    return o;
}

A
antirez 已提交
106 107 108
/* Note: this function is defined into object.c since here it is where it
 * belongs but it is actually designed to be used just for INCRBYFLOAT */
robj *createStringObjectFromLongDouble(long double value) {
A
antirez 已提交
109
    char buf[256];
A
antirez 已提交
110 111 112
    int len;

    /* We use 17 digits precision since with 128 bit floats that precision
G
guiquanz 已提交
113
     * after rounding is able to represent most small decimal numbers in a way
A
antirez 已提交
114 115 116
     * that is "non surprising" for the user (that is, most small decimal
     * numbers will be represented in a way that when converted back into
     * a string are exactly the same as what the user typed.) */
117 118
    len = snprintf(buf,sizeof(buf),"%.17Lf", value);
    /* Now remove trailing zeroes after the '.' */
A
antirez 已提交
119 120
    if (strchr(buf,'.') != NULL) {
        char *p = buf+len-1;
121 122 123 124 125 126
        while(*p == '0') {
            p--;
            len--;
        }
        if (*p == '.') len--;
    }
A
antirez 已提交
127 128 129
    return createStringObject(buf,len);
}

130 131 132 133 134 135 136 137
/* Duplicate a string object, with the guarantee that the returned object
 * has the same encoding as the original one.
 *
 * This function also guarantees that duplicating a small integere object
 * (or a string object that contains a representation of a small integer)
 * will always result in a fresh object that is unshared (refcount == 1).
 *
 * The resulting object always has refcount set to 1. */
138
robj *dupStringObject(robj *o) {
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
    robj *d;

    redisAssert(o->type == REDIS_STRING);

    switch(o->encoding) {
    case REDIS_ENCODING_RAW:
        return createRawStringObject(o->ptr,sdslen(o->ptr));
    case REDIS_ENCODING_EMBSTR:
        return createEmbeddedStringObject(o->ptr,sdslen(o->ptr));
    case REDIS_ENCODING_INT:
        d = createObject(REDIS_STRING, NULL);
        d->encoding = REDIS_ENCODING_INT;
        d->ptr = o->ptr;
        return d;
    default:
        redisPanic("Wrong encoding.");
        break;
    }
157 158 159 160 161
}

robj *createListObject(void) {
    list *l = listCreate();
    robj *o = createObject(REDIS_LIST,l);
162
    listSetFreeMethod(l,decrRefCountVoid);
163 164 165 166 167 168 169 170 171 172 173 174 175
    o->encoding = REDIS_ENCODING_LINKEDLIST;
    return o;
}

robj *createZiplistObject(void) {
    unsigned char *zl = ziplistNew();
    robj *o = createObject(REDIS_LIST,zl);
    o->encoding = REDIS_ENCODING_ZIPLIST;
    return o;
}

robj *createSetObject(void) {
    dict *d = dictCreate(&setDictType,NULL);
176 177 178 179 180 181 182 183 184 185
    robj *o = createObject(REDIS_SET,d);
    o->encoding = REDIS_ENCODING_HT;
    return o;
}

robj *createIntsetObject(void) {
    intset *is = intsetNew();
    robj *o = createObject(REDIS_SET,is);
    o->encoding = REDIS_ENCODING_INTSET;
    return o;
186 187 188
}

robj *createHashObject(void) {
189 190 191
    unsigned char *zl = ziplistNew();
    robj *o = createObject(REDIS_HASH, zl);
    o->encoding = REDIS_ENCODING_ZIPLIST;
192 193 194 195 196
    return o;
}

robj *createZsetObject(void) {
    zset *zs = zmalloc(sizeof(*zs));
197
    robj *o;
198 199 200

    zs->dict = dictCreate(&zsetDictType,NULL);
    zs->zsl = zslCreate();
201 202 203
    o = createObject(REDIS_ZSET,zs);
    o->encoding = REDIS_ENCODING_SKIPLIST;
    return o;
204 205
}

206 207 208 209 210 211 212
robj *createZsetZiplistObject(void) {
    unsigned char *zl = ziplistNew();
    robj *o = createObject(REDIS_ZSET,zl);
    o->encoding = REDIS_ENCODING_ZIPLIST;
    return o;
}

213
void freeStringObject(robj *o) {
214 215
    if (o->encoding == REDIS_ENCODING_RAW ||
        o->encoding == REDIS_ENCODING_LZF) {
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
        sdsfree(o->ptr);
    }
}

void freeListObject(robj *o) {
    switch (o->encoding) {
    case REDIS_ENCODING_LINKEDLIST:
        listRelease((list*) o->ptr);
        break;
    case REDIS_ENCODING_ZIPLIST:
        zfree(o->ptr);
        break;
    default:
        redisPanic("Unknown list encoding type");
    }
}

void freeSetObject(robj *o) {
234 235 236 237 238 239 240 241 242 243
    switch (o->encoding) {
    case REDIS_ENCODING_HT:
        dictRelease((dict*) o->ptr);
        break;
    case REDIS_ENCODING_INTSET:
        zfree(o->ptr);
        break;
    default:
        redisPanic("Unknown set encoding type");
    }
244 245 246
}

void freeZsetObject(robj *o) {
247 248
    zset *zs;
    switch (o->encoding) {
249
    case REDIS_ENCODING_SKIPLIST:
250 251 252 253 254 255 256 257 258 259 260
        zs = o->ptr;
        dictRelease(zs->dict);
        zslFree(zs->zsl);
        zfree(zs);
        break;
    case REDIS_ENCODING_ZIPLIST:
        zfree(o->ptr);
        break;
    default:
        redisPanic("Unknown sorted set encoding");
    }
261 262 263 264 265 266 267
}

void freeHashObject(robj *o) {
    switch (o->encoding) {
    case REDIS_ENCODING_HT:
        dictRelease((dict*) o->ptr);
        break;
268
    case REDIS_ENCODING_ZIPLIST:
269 270 271 272 273 274 275 276 277 278 279 280
        zfree(o->ptr);
        break;
    default:
        redisPanic("Unknown hash encoding type");
        break;
    }
}

void incrRefCount(robj *o) {
    o->refcount++;
}

281
void decrRefCount(robj *o) {
282
    if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0");
A
antirez 已提交
283
    if (o->refcount == 1) {
284 285 286 287 288 289 290 291
        switch(o->type) {
        case REDIS_STRING: freeStringObject(o); break;
        case REDIS_LIST: freeListObject(o); break;
        case REDIS_SET: freeSetObject(o); break;
        case REDIS_ZSET: freeZsetObject(o); break;
        case REDIS_HASH: freeHashObject(o); break;
        default: redisPanic("Unknown object type"); break;
        }
292
        zfree(o);
A
antirez 已提交
293 294
    } else {
        o->refcount--;
295 296 297
    }
}

298 299 300 301 302 303 304
/* This variant of decrRefCount() gets its argument as void, and is useful
 * as free method in data structures that expect a 'void free_object(void*)'
 * prototype for the free method. */
void decrRefCountVoid(void *o) {
    decrRefCount(o);
}

305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
/* This function set the ref count to zero without freeing the object.
 * It is useful in order to pass a new object to functions incrementing
 * the ref count of the received object. Example:
 *
 *    functionThatWillIncrementRefCount(resetRefCount(CreateObject(...)));
 *
 * Otherwise you need to resort to the less elegant pattern:
 *
 *    *obj = createObject(...);
 *    functionThatWillIncrementRefCount(obj);
 *    decrRefCount(obj);
 */
robj *resetRefCount(robj *obj) {
    obj->refcount = 0;
    return obj;
}

322 323 324 325 326 327 328 329
int checkType(redisClient *c, robj *o, int type) {
    if (o->type != type) {
        addReply(c,shared.wrongtypeerr);
        return 1;
    }
    return 0;
}

330
int isObjectRepresentableAsLongLong(robj *o, long long *llval) {
331
    redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
332 333 334 335 336 337 338 339
    if (o->encoding == REDIS_ENCODING_INT) {
        if (llval) *llval = (long) o->ptr;
        return REDIS_OK;
    } else {
        return string2ll(o->ptr,sdslen(o->ptr),llval) ? REDIS_OK : REDIS_ERR;
    }
}

340
/* Try to encode a string object in order to save space. */
341
#define LZF_COMPR_STATIC_BUF (1024*32)
342 343 344
robj *tryObjectEncoding(robj *o) {
    long value;
    sds s = o->ptr;
345
    size_t len;
346

A
antirez 已提交
347 348 349 350 351 352 353 354 355 356
    /* Make sure this is a string object, the only type we encode
     * in this function. Other types use encoded memory efficient
     * representations but are handled by the commands implementing
     * the type. */
    redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);

    /* We try some specialized encoding only for objects that are
     * RAW or EMBSTR encoded, in other words objects that are still
     * in represented by an actually array of chars. */
    if (!sdsEncodedObject(o)) return o;
357 358

    /* It's not safe to encode shared objects: shared objects can be shared
A
antirez 已提交
359 360
     * everywhere in the "object space" of Redis and may end in places where
     * they are not handled. We handle them only as values in the keyspace. */
361 362
     if (o->refcount > 1) return o;

363 364
    /* Check if we can represent this string as a long integer.
     * Note that we are sure that a string larger than 21 chars is not
A
antirez 已提交
365
     * representable as a 32 nor 64 bit integer. */
366
    len = sdslen(s);
A
antirez 已提交
367 368 369 370 371 372 373 374 375
    if (len <= 21 && string2l(s,len,&value)) {
        /* This object is encodable as a long. Try to use a shared object.
         * Note that we avoid using shared integers when maxmemory is used
         * because every object needs to have a private LRU field for the LRU
         * algorithm to work well. */
        if (server.maxmemory == 0 &&
            value >= 0 &&
            value < REDIS_SHARED_INTEGERS)
        {
376
            decrRefCount(o);
A
antirez 已提交
377 378
            incrRefCount(shared.integers[value]);
            return shared.integers[value];
379
        } else {
A
antirez 已提交
380 381 382
            if (o->encoding == REDIS_ENCODING_RAW) sdsfree(o->ptr);
            o->encoding = REDIS_ENCODING_INT;
            o->ptr = (void*) value;
383 384 385
            return o;
        }
    }
386

A
antirez 已提交
387 388 389 390 391 392 393 394 395 396 397 398 399
    /* If the string is small and is still RAW encoded,
     * try the EMBSTR encoding which is more efficient.
     * In this representation the object and the SDS string are allocated
     * in the same chunk of memory to save space and cache misses. */
    if (len <= REDIS_ENCODING_EMBSTR_SIZE_LIMIT) {
        robj *emb;

        if (o->encoding == REDIS_ENCODING_EMBSTR) return o;
        emb = createEmbeddedStringObject(s,sdslen(s));
        decrRefCount(o);
        return emb;
    }

400
    /* Try LZF compression for objects up to server.mem_compression_max_size
401
     * and greater than REDIS_ENCODING_EMBSTR_SIZE_LIMIT. */
402
    if (server.mem_compression && len <= server.mem_compression_max_size) {
403 404
        /* Allocate four more bytes in our buffer since we need to store
         * the size of the compressed string as header. */
405 406
        unsigned char compr_static[LZF_COMPR_STATIC_BUF];
        unsigned char *compr = compr_static;
407 408 409 410
        size_t comprlen, outlen;

        /* Save want to save at least 25% of memory for this to make sense. */
        outlen = len-4-(len/4);
411 412 413 414 415 416

        /* Use an heap allocated buffer if the output is too big for our
         * static buffer. We use the trick to directly allocating an empty
         * SDS string so we can use it directly to create the object later. */
        if (outlen+4 > LZF_COMPR_STATIC_BUF)
            compr = (unsigned char*) sdsnewlen(NULL,outlen+4);
417 418 419 420 421 422 423 424 425
        comprlen = lzf_compress(s,len,compr+4,outlen);
        if (comprlen != 0) {
            /* Object successfully compressed within the required space. */
            compr[0] = len & 0xff;
            compr[1] = (len >> 8) & 0xff;
            compr[2] = (len >> 16) & 0xff;
            compr[3] = (len >> 24) & 0xff;
            if (o->encoding == REDIS_ENCODING_RAW) sdsfree(o->ptr);
            o->encoding = REDIS_ENCODING_LZF;
426 427 428 429 430 431 432 433
            if (compr == compr_static) {
                /* When compressing to the static buffer we have to
                 * generate the SDS string here. */
                o->ptr = sdsnewlen(compr,comprlen+4);
            } else {
                /* Already an SDS, use it. */
                o->ptr = compr;
            }
434 435
            return o;
        }
436
        if (compr != compr_static) sdsfree((char*)compr);
437 438
    }

A
antirez 已提交
439
    /* We can't encode the object...
440
     *
A
antirez 已提交
441 442 443
     * Do the last try, and at least optimize the SDS string inside
     * the string object to require little space, in case there
     * is more than 10% of free space at the end of the SDS string.
444
     *
A
antirez 已提交
445 446 447 448 449 450 451
     * We do that only for relatively large strings as this branch
     * is only entered if the length of the string is greater than
     * REDIS_ENCODING_EMBSTR_SIZE_LIMIT. */
    if (o->encoding == REDIS_ENCODING_RAW &&
        sdsavail(s) > len/10)
    {
        o->ptr = sdsRemoveFreeSpace(o->ptr);
452
    }
A
antirez 已提交
453 454 455

    /* Return the original object. */
    return o;
456 457 458 459 460 461 462
}

/* Get a decoded version of an encoded object (returned as a new object).
 * If the object is already raw-encoded just increment the ref count. */
robj *getDecodedObject(robj *o) {
    robj *dec;

463
    if (sdsEncodedObject(o)) {
464 465 466 467 468 469 470 471 472
        incrRefCount(o);
        return o;
    }
    if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) {
        char buf[32];

        ll2string(buf,32,(long)o->ptr);
        dec = createStringObject(buf,strlen(buf));
        return dec;
473 474 475 476 477 478 479 480
    } else if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_LZF) {
        int origlen = stringObjectLen(o);
        sds orig = sdsnewlen(NULL,origlen);
        unsigned char *p = o->ptr;

        if (lzf_decompress(p+4,sdslen(o->ptr)-4,orig,origlen) == 0)
            redisPanic("LZF error during object decoding.");
        return createObject(REDIS_STRING,orig);
481 482 483 484 485
    } else {
        redisPanic("Unknown encoding type");
    }
}

486
/* Compare two string objects via strcmp() or strcoll() depending on flags.
487 488 489 490
 * Note that the objects may be integer-encoded. In such a case we
 * use ll2string() to get a string representation of the numbers on the stack
 * and compare the strings, it's much faster than calling getDecodedObject().
 *
491 492 493 494 495 496 497
 * Important note: when REDIS_COMPARE_BINARY is used a binary-safe comparison
 * is used. */

#define REDIS_COMPARE_BINARY (1<<0)
#define REDIS_COMPARE_COLL (1<<1)

int compareStringObjectsWithFlags(robj *a, robj *b, int flags) {
498
    redisAssertWithInfo(NULL,a,a->type == REDIS_STRING && b->type == REDIS_STRING);
499
    char bufa[128], bufb[128], *astr, *bstr;
500
    size_t alen, blen, minlen;
501 502

    if (a == b) return 0;
503 504 505 506
    if (sdsEncodedObject(a)) {
        astr = a->ptr;
        alen = sdslen(astr);
    } else {
507
        alen = ll2string(bufa,sizeof(bufa),(long) a->ptr);
508 509
        astr = bufa;
    }
510 511 512 513
    if (sdsEncodedObject(b)) {
        bstr = b->ptr;
        blen = sdslen(bstr);
    } else {
514
        blen = ll2string(bufb,sizeof(bufb),(long) b->ptr);
515
        bstr = bufb;
516 517 518 519 520 521 522 523 524 525
    }
    if (flags & REDIS_COMPARE_COLL) {
        return strcoll(astr,bstr);
    } else {
        int cmp;

        minlen = (alen < blen) ? alen : blen;
        cmp = memcmp(astr,bstr,minlen);
        if (cmp == 0) return alen-blen;
        return cmp;
526
    }
527 528 529 530 531 532 533 534 535 536
}

/* Wrapper for compareStringObjectsWithFlags() using binary comparison. */
int compareStringObjects(robj *a, robj *b) {
    return compareStringObjectsWithFlags(a,b,REDIS_COMPARE_BINARY);
}

/* Wrapper for compareStringObjectsWithFlags() using collation. */
int collateStringObjects(robj *a, robj *b) {
    return compareStringObjectsWithFlags(a,b,REDIS_COMPARE_COLL);
537 538 539 540 541 542 543
}

/* Equal string objects return 1 if the two objects are the same from the
 * point of view of a string comparison, otherwise 0 is returned. Note that
 * this function is faster then checking for (compareStringObject(a,b) == 0)
 * because it can perform some more optimization. */
int equalStringObjects(robj *a, robj *b) {
544 545 546 547
    if (a->encoding == REDIS_ENCODING_INT &&
        b->encoding == REDIS_ENCODING_INT){
        /* If both strings are integer encoded just check if the stored
         * long is the same. */
548 549 550 551 552 553
        return a->ptr == b->ptr;
    } else {
        return compareStringObjects(a,b) == 0;
    }
}

554 555 556 557 558 559 560
/* Returns the original (uncompressed) size of an LZF encoded object.
 * Only called by stringObjectLen() that should be the main interface. */
size_t stringObjectUncompressedLen(robj *o) {
    unsigned char *p = o->ptr;
    return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
}

561
size_t stringObjectLen(robj *o) {
562
    redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
563
    if (sdsEncodedObject(o)) {
564
        return sdslen(o->ptr);
565 566
    } else if (o->encoding == REDIS_ENCODING_LZF) {
        return stringObjectUncompressedLen(o);
567 568 569 570 571 572 573 574 575 576 577 578 579
    } else {
        char buf[32];
        return ll2string(buf,32,(long)o->ptr);
    }
}

int getDoubleFromObject(robj *o, double *target) {
    double value;
    char *eptr;

    if (o == NULL) {
        value = 0;
    } else {
580
        redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
581
        if (sdsEncodedObject(o)) {
A
antirez 已提交
582
            errno = 0;
583
            value = strtod(o->ptr, &eptr);
584 585 586 587 588 589
            if (isspace(((char*)o->ptr)[0]) ||
                eptr[0] != '\0' ||
                (errno == ERANGE &&
                    (value == HUGE_VAL || value == -HUGE_VAL || value == 0)) ||
                errno == EINVAL ||
                isnan(value))
A
antirez 已提交
590
                return REDIS_ERR;
591 592 593 594 595 596 597 598 599 600 601 602 603 604
        } else if (o->encoding == REDIS_ENCODING_INT) {
            value = (long)o->ptr;
        } else {
            redisPanic("Unknown string encoding");
        }
    }
    *target = value;
    return REDIS_OK;
}

int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) {
    double value;
    if (getDoubleFromObject(o, &value) != REDIS_OK) {
        if (msg != NULL) {
605
            addReplyError(c,(char*)msg);
606
        } else {
A
antirez 已提交
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
            addReplyError(c,"value is not a valid float");
        }
        return REDIS_ERR;
    }
    *target = value;
    return REDIS_OK;
}

int getLongDoubleFromObject(robj *o, long double *target) {
    long double value;
    char *eptr;

    if (o == NULL) {
        value = 0;
    } else {
        redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
623
        if (sdsEncodedObject(o)) {
A
antirez 已提交
624 625
            errno = 0;
            value = strtold(o->ptr, &eptr);
626 627
            if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
                errno == ERANGE || isnan(value))
A
antirez 已提交
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
                return REDIS_ERR;
        } else if (o->encoding == REDIS_ENCODING_INT) {
            value = (long)o->ptr;
        } else {
            redisPanic("Unknown string encoding");
        }
    }
    *target = value;
    return REDIS_OK;
}

int getLongDoubleFromObjectOrReply(redisClient *c, robj *o, long double *target, const char *msg) {
    long double value;
    if (getLongDoubleFromObject(o, &value) != REDIS_OK) {
        if (msg != NULL) {
            addReplyError(c,(char*)msg);
        } else {
            addReplyError(c,"value is not a valid float");
646 647 648 649 650 651 652 653 654 655 656 657 658 659
        }
        return REDIS_ERR;
    }
    *target = value;
    return REDIS_OK;
}

int getLongLongFromObject(robj *o, long long *target) {
    long long value;
    char *eptr;

    if (o == NULL) {
        value = 0;
    } else {
660
        redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
661
        if (sdsEncodedObject(o)) {
662
            errno = 0;
663
            value = strtoll(o->ptr, &eptr, 10);
664 665
            if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
                errno == ERANGE)
666
                return REDIS_ERR;
667 668 669 670 671 672
        } else if (o->encoding == REDIS_ENCODING_INT) {
            value = (long)o->ptr;
        } else {
            redisPanic("Unknown string encoding");
        }
    }
673
    if (target) *target = value;
674 675 676 677 678 679 680
    return REDIS_OK;
}

int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) {
    long long value;
    if (getLongLongFromObject(o, &value) != REDIS_OK) {
        if (msg != NULL) {
681
            addReplyError(c,(char*)msg);
682
        } else {
683
            addReplyError(c,"value is not an integer or out of range");
684 685 686 687 688 689 690 691 692 693 694 695 696
        }
        return REDIS_ERR;
    }
    *target = value;
    return REDIS_OK;
}

int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) {
    long long value;

    if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR;
    if (value < LONG_MIN || value > LONG_MAX) {
        if (msg != NULL) {
697
            addReplyError(c,(char*)msg);
698
        } else {
699
            addReplyError(c,"value is out of range");
700 701 702 703 704 705 706 707 708 709 710 711 712 713
        }
        return REDIS_ERR;
    }
    *target = value;
    return REDIS_OK;
}

char *strEncoding(int encoding) {
    switch(encoding) {
    case REDIS_ENCODING_RAW: return "raw";
    case REDIS_ENCODING_INT: return "int";
    case REDIS_ENCODING_HT: return "hashtable";
    case REDIS_ENCODING_LINKEDLIST: return "linkedlist";
    case REDIS_ENCODING_ZIPLIST: return "ziplist";
714
    case REDIS_ENCODING_INTSET: return "intset";
715
    case REDIS_ENCODING_SKIPLIST: return "skiplist";
716
    case REDIS_ENCODING_EMBSTR: return "embstr";
717
    case REDIS_ENCODING_LZF: return "lzf";
718 719 720
    default: return "unknown";
    }
}
721

722
/* Given an object returns the min number of milliseconds the object was never
723
 * requested, using an approximated LRU algorithm. */
724
unsigned long long estimateObjectIdleTime(robj *o) {
725 726 727
    unsigned long long lruclock = LRU_CLOCK();
    if (lruclock >= o->lru) {
        return (lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION;
728
    } else {
729
        return (lruclock + (REDIS_LRU_CLOCK_MAX - o->lru)) *
730
                    REDIS_LRU_CLOCK_RESOLUTION;
731 732
    }
}
A
antirez 已提交
733

734
/* This is a helper function for the OBJECT command. We need to lookup keys
A
antirez 已提交
735 736 737 738 739
 * without any modification of LRU or other parameters. */
robj *objectCommandLookup(redisClient *c, robj *key) {
    dictEntry *de;

    if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL;
740
    return (robj*) dictGetVal(de);
A
antirez 已提交
741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765
}

robj *objectCommandLookupOrReply(redisClient *c, robj *key, robj *reply) {
    robj *o = objectCommandLookup(c,key);

    if (!o) addReply(c, reply);
    return o;
}

/* Object command allows to inspect the internals of an Redis Object.
 * Usage: OBJECT <verb> ... arguments ... */
void objectCommand(redisClient *c) {
    robj *o;

    if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) {
        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
                == NULL) return;
        addReplyLongLong(c,o->refcount);
    } else if (!strcasecmp(c->argv[1]->ptr,"encoding") && c->argc == 3) {
        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
                == NULL) return;
        addReplyBulkCString(c,strEncoding(o->encoding));
    } else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) {
        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
                == NULL) return;
766
        addReplyLongLong(c,estimateObjectIdleTime(o)/1000);
A
antirez 已提交
767 768 769 770 771
    } else {
        addReplyError(c,"Syntax error. Try OBJECT (refcount|encoding|idletime)");
    }
}