2012-11-08 18:25:23 +01:00
|
|
|
/* 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.
|
|
|
|
*/
|
|
|
|
|
2010-06-22 00:07:48 +02:00
|
|
|
#include "redis.h"
|
2010-07-29 22:13:31 +02:00
|
|
|
#include <math.h>
|
2011-11-14 15:34:44 +01:00
|
|
|
#include <ctype.h>
|
2010-06-22 00:07:48 +02:00
|
|
|
|
2014-05-12 11:09:07 -04:00
|
|
|
#ifdef __CYGWIN__
|
|
|
|
#define strtold(a,b) ((long double)strtod((a),(b)))
|
|
|
|
#endif
|
|
|
|
|
2010-06-22 00:07:48 +02:00
|
|
|
robj *createObject(int type, void *ptr) {
|
2010-11-04 10:09:30 +01:00
|
|
|
robj *o = zmalloc(sizeof(*o));
|
2010-06-22 00:07:48 +02:00
|
|
|
o->type = type;
|
|
|
|
o->encoding = REDIS_ENCODING_RAW;
|
|
|
|
o->ptr = ptr;
|
|
|
|
o->refcount = 1;
|
2010-11-04 10:09:30 +01:00
|
|
|
|
2012-04-02 11:56:03 +02:00
|
|
|
/* Set the LRU to the current lruclock (minutes resolution). */
|
2014-03-24 14:39:26 -04:00
|
|
|
o->lru = LRU_CLOCK();
|
2010-06-22 00:07:48 +02:00
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
2012-06-05 21:50:10 +02:00
|
|
|
/* 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) {
|
2010-06-22 00:07:48 +02:00
|
|
|
return createObject(REDIS_STRING,sdsnewlen(ptr,len));
|
|
|
|
}
|
|
|
|
|
2012-06-05 21:50:10 +02:00
|
|
|
/* 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;
|
2014-03-24 14:39:26 -04:00
|
|
|
o->lru = LRU_CLOCK();
|
2012-06-05 21:50:10 +02:00
|
|
|
|
|
|
|
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
|
2014-05-07 17:05:09 +02:00
|
|
|
* used.
|
|
|
|
*
|
|
|
|
* The current limit of 39 is chosen so that the biggest string object
|
|
|
|
* we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc. */
|
|
|
|
#define REDIS_ENCODING_EMBSTR_SIZE_LIMIT 39
|
2012-06-05 21:50:10 +02:00
|
|
|
robj *createStringObject(char *ptr, size_t len) {
|
|
|
|
if (len <= REDIS_ENCODING_EMBSTR_SIZE_LIMIT)
|
|
|
|
return createEmbeddedStringObject(ptr,len);
|
|
|
|
else
|
|
|
|
return createRawStringObject(ptr,len);
|
|
|
|
}
|
|
|
|
|
2010-06-22 00:07:48 +02:00
|
|
|
robj *createStringObjectFromLongLong(long long value) {
|
|
|
|
robj *o;
|
2011-06-25 12:22:03 +02:00
|
|
|
if (value >= 0 && value < REDIS_SHARED_INTEGERS) {
|
2010-06-22 00:07:48 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2011-11-12 19:27:35 +01:00
|
|
|
/* 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) {
|
2012-03-22 18:16:41 +01:00
|
|
|
char buf[256];
|
2011-11-12 19:27:35 +01:00
|
|
|
int len;
|
|
|
|
|
|
|
|
/* We use 17 digits precision since with 128 bit floats that precision
|
2013-01-17 01:00:20 +08:00
|
|
|
* after rounding is able to represent most small decimal numbers in a way
|
2011-11-12 19:27:35 +01:00
|
|
|
* 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.) */
|
2012-03-22 18:14:32 +01:00
|
|
|
len = snprintf(buf,sizeof(buf),"%.17Lf", value);
|
|
|
|
/* Now remove trailing zeroes after the '.' */
|
2012-03-22 18:16:41 +01:00
|
|
|
if (strchr(buf,'.') != NULL) {
|
|
|
|
char *p = buf+len-1;
|
2012-03-22 18:14:32 +01:00
|
|
|
while(*p == '0') {
|
|
|
|
p--;
|
|
|
|
len--;
|
|
|
|
}
|
|
|
|
if (*p == '.') len--;
|
|
|
|
}
|
2011-11-12 19:27:35 +01:00
|
|
|
return createStringObject(buf,len);
|
|
|
|
}
|
|
|
|
|
2012-06-05 21:50:10 +02:00
|
|
|
/* 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. */
|
2010-06-22 00:07:48 +02:00
|
|
|
robj *dupStringObject(robj *o) {
|
2012-06-05 21:50:10 +02:00
|
|
|
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;
|
|
|
|
}
|
2010-06-22 00:07:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
robj *createListObject(void) {
|
|
|
|
list *l = listCreate();
|
|
|
|
robj *o = createObject(REDIS_LIST,l);
|
2013-01-25 11:16:00 +01:00
|
|
|
listSetFreeMethod(l,decrRefCountVoid);
|
2010-06-22 00:07:48 +02:00
|
|
|
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);
|
2010-07-02 19:57:12 +02:00
|
|
|
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;
|
2010-06-22 00:07:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
robj *createHashObject(void) {
|
2012-01-02 22:14:10 -08:00
|
|
|
unsigned char *zl = ziplistNew();
|
|
|
|
robj *o = createObject(REDIS_HASH, zl);
|
|
|
|
o->encoding = REDIS_ENCODING_ZIPLIST;
|
2010-06-22 00:07:48 +02:00
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
|
|
|
robj *createZsetObject(void) {
|
|
|
|
zset *zs = zmalloc(sizeof(*zs));
|
2011-04-06 15:36:10 +02:00
|
|
|
robj *o;
|
2010-06-22 00:07:48 +02:00
|
|
|
|
|
|
|
zs->dict = dictCreate(&zsetDictType,NULL);
|
|
|
|
zs->zsl = zslCreate();
|
2011-04-06 15:36:10 +02:00
|
|
|
o = createObject(REDIS_ZSET,zs);
|
|
|
|
o->encoding = REDIS_ENCODING_SKIPLIST;
|
|
|
|
return o;
|
2010-06-22 00:07:48 +02:00
|
|
|
}
|
|
|
|
|
2011-03-08 16:08:52 +01:00
|
|
|
robj *createZsetZiplistObject(void) {
|
|
|
|
unsigned char *zl = ziplistNew();
|
|
|
|
robj *o = createObject(REDIS_ZSET,zl);
|
|
|
|
o->encoding = REDIS_ENCODING_ZIPLIST;
|
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
2010-06-22 00:07:48 +02:00
|
|
|
void freeStringObject(robj *o) {
|
|
|
|
if (o->encoding == REDIS_ENCODING_RAW) {
|
|
|
|
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) {
|
2010-07-02 19:57:12 +02:00
|
|
|
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");
|
|
|
|
}
|
2010-06-22 00:07:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void freeZsetObject(robj *o) {
|
2011-03-08 23:56:59 +01:00
|
|
|
zset *zs;
|
|
|
|
switch (o->encoding) {
|
2011-04-06 16:17:07 +02:00
|
|
|
case REDIS_ENCODING_SKIPLIST:
|
2011-03-08 23:56:59 +01:00
|
|
|
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");
|
|
|
|
}
|
2010-06-22 00:07:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void freeHashObject(robj *o) {
|
|
|
|
switch (o->encoding) {
|
|
|
|
case REDIS_ENCODING_HT:
|
|
|
|
dictRelease((dict*) o->ptr);
|
|
|
|
break;
|
2012-01-02 22:14:10 -08:00
|
|
|
case REDIS_ENCODING_ZIPLIST:
|
2010-06-22 00:07:48 +02:00
|
|
|
zfree(o->ptr);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
redisPanic("Unknown hash encoding type");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void incrRefCount(robj *o) {
|
|
|
|
o->refcount++;
|
|
|
|
}
|
|
|
|
|
2013-01-24 11:27:10 +01:00
|
|
|
void decrRefCount(robj *o) {
|
2010-06-22 00:07:48 +02:00
|
|
|
if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0");
|
2011-06-02 17:41:42 +02:00
|
|
|
if (o->refcount == 1) {
|
2010-06-22 00:07:48 +02:00
|
|
|
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;
|
|
|
|
}
|
2010-11-04 10:09:30 +01:00
|
|
|
zfree(o);
|
2011-06-02 17:41:42 +02:00
|
|
|
} else {
|
|
|
|
o->refcount--;
|
2010-06-22 00:07:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-24 11:27:10 +01:00
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
2011-07-13 15:38:03 +02:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2010-06-22 00:07:48 +02:00
|
|
|
int checkType(redisClient *c, robj *o, int type) {
|
|
|
|
if (o->type != type) {
|
|
|
|
addReply(c,shared.wrongtypeerr);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-04-27 13:24:52 +02:00
|
|
|
int isObjectRepresentableAsLongLong(robj *o, long long *llval) {
|
2011-10-04 18:43:03 +02:00
|
|
|
redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
|
2011-04-27 13:24:52 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-22 00:07:48 +02:00
|
|
|
/* Try to encode a string object in order to save space */
|
|
|
|
robj *tryObjectEncoding(robj *o) {
|
|
|
|
long value;
|
|
|
|
sds s = o->ptr;
|
2013-08-27 11:56:47 +02:00
|
|
|
size_t len;
|
2010-06-22 00:07:48 +02:00
|
|
|
|
2014-04-04 10:28:34 +02:00
|
|
|
/* 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;
|
2010-06-22 00:07:48 +02:00
|
|
|
|
|
|
|
/* It's not safe to encode shared objects: shared objects can be shared
|
2014-04-04 10:28:34 +02:00
|
|
|
* 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. */
|
2010-06-22 00:07:48 +02:00
|
|
|
if (o->refcount > 1) return o;
|
|
|
|
|
2013-08-27 11:56:47 +02:00
|
|
|
/* 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
|
2014-04-04 10:28:34 +02:00
|
|
|
* representable as a 32 nor 64 bit integer. */
|
2013-08-27 11:56:47 +02:00
|
|
|
len = sdslen(s);
|
2014-04-04 10:28:34 +02:00
|
|
|
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. */
|
2014-07-18 10:09:51 +02:00
|
|
|
if ((server.maxmemory == 0 ||
|
|
|
|
(server.maxmemory_policy != REDIS_MAXMEMORY_VOLATILE_LRU &&
|
|
|
|
server.maxmemory_policy != REDIS_MAXMEMORY_ALLKEYS_LRU)) &&
|
2014-04-04 10:28:34 +02:00
|
|
|
value >= 0 &&
|
|
|
|
value < REDIS_SHARED_INTEGERS)
|
|
|
|
{
|
2012-06-05 21:50:10 +02:00
|
|
|
decrRefCount(o);
|
2014-04-04 10:28:34 +02:00
|
|
|
incrRefCount(shared.integers[value]);
|
|
|
|
return shared.integers[value];
|
2012-06-05 21:50:10 +02:00
|
|
|
} else {
|
2014-04-04 10:28:34 +02:00
|
|
|
if (o->encoding == REDIS_ENCODING_RAW) sdsfree(o->ptr);
|
|
|
|
o->encoding = REDIS_ENCODING_INT;
|
|
|
|
o->ptr = (void*) value;
|
2012-06-05 21:50:10 +02:00
|
|
|
return o;
|
|
|
|
}
|
|
|
|
}
|
2010-06-22 00:07:48 +02:00
|
|
|
|
2014-04-04 10:28:34 +02:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We can't encode the object...
|
2010-07-22 13:08:02 +02:00
|
|
|
*
|
2014-04-04 10:28:34 +02:00
|
|
|
* 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.
|
2010-10-15 18:04:05 +02:00
|
|
|
*
|
2014-04-04 10:28:34 +02:00
|
|
|
* 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);
|
2010-06-22 00:07:48 +02:00
|
|
|
}
|
2014-04-04 10:28:34 +02:00
|
|
|
|
|
|
|
/* Return the original object. */
|
|
|
|
return o;
|
2010-06-22 00:07:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
2012-06-05 21:50:10 +02:00
|
|
|
if (sdsEncodedObject(o)) {
|
2010-06-22 00:07:48 +02:00
|
|
|
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;
|
|
|
|
} else {
|
|
|
|
redisPanic("Unknown encoding type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Fixed compareStringObject() and introduced collateStringObject().
compareStringObject was not always giving the same result when comparing
two exact strings, but encoded as integers or as sds strings, since it
switched to strcmp() when at least one of the strings were not sds
encoded.
For instance the two strings "123" and "123\x00456", where the first
string was integer encoded, would result into the old implementation of
compareStringObject() to return 0 as if the strings were equal, while
instead the second string is "greater" than the first in a binary
comparison.
The same compasion, but with "123" encoded as sds string, would instead
return a value < 0, as it is correct. It is not impossible that the
above caused some obscure bug, since the comparison was not always
deterministic, and compareStringObject() is used in the implementation
of skiplists, hash tables, and so forth.
At the same time, collateStringObject() was introduced by this commit, so
that can be used by SORT command to return sorted strings usign
collation instead of binary comparison. See next commit.
2013-07-12 11:56:52 +02:00
|
|
|
/* Compare two string objects via strcmp() or strcoll() depending on flags.
|
2010-06-22 00:07:48 +02:00
|
|
|
* 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().
|
|
|
|
*
|
Fixed compareStringObject() and introduced collateStringObject().
compareStringObject was not always giving the same result when comparing
two exact strings, but encoded as integers or as sds strings, since it
switched to strcmp() when at least one of the strings were not sds
encoded.
For instance the two strings "123" and "123\x00456", where the first
string was integer encoded, would result into the old implementation of
compareStringObject() to return 0 as if the strings were equal, while
instead the second string is "greater" than the first in a binary
comparison.
The same compasion, but with "123" encoded as sds string, would instead
return a value < 0, as it is correct. It is not impossible that the
above caused some obscure bug, since the comparison was not always
deterministic, and compareStringObject() is used in the implementation
of skiplists, hash tables, and so forth.
At the same time, collateStringObject() was introduced by this commit, so
that can be used by SORT command to return sorted strings usign
collation instead of binary comparison. See next commit.
2013-07-12 11:56:52 +02:00
|
|
|
* 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) {
|
2011-10-04 18:43:03 +02:00
|
|
|
redisAssertWithInfo(NULL,a,a->type == REDIS_STRING && b->type == REDIS_STRING);
|
2010-06-22 00:07:48 +02:00
|
|
|
char bufa[128], bufb[128], *astr, *bstr;
|
Fixed compareStringObject() and introduced collateStringObject().
compareStringObject was not always giving the same result when comparing
two exact strings, but encoded as integers or as sds strings, since it
switched to strcmp() when at least one of the strings were not sds
encoded.
For instance the two strings "123" and "123\x00456", where the first
string was integer encoded, would result into the old implementation of
compareStringObject() to return 0 as if the strings were equal, while
instead the second string is "greater" than the first in a binary
comparison.
The same compasion, but with "123" encoded as sds string, would instead
return a value < 0, as it is correct. It is not impossible that the
above caused some obscure bug, since the comparison was not always
deterministic, and compareStringObject() is used in the implementation
of skiplists, hash tables, and so forth.
At the same time, collateStringObject() was introduced by this commit, so
that can be used by SORT command to return sorted strings usign
collation instead of binary comparison. See next commit.
2013-07-12 11:56:52 +02:00
|
|
|
size_t alen, blen, minlen;
|
2010-06-22 00:07:48 +02:00
|
|
|
|
|
|
|
if (a == b) return 0;
|
2012-06-05 21:50:10 +02:00
|
|
|
if (sdsEncodedObject(a)) {
|
|
|
|
astr = a->ptr;
|
|
|
|
alen = sdslen(astr);
|
|
|
|
} else {
|
Fixed compareStringObject() and introduced collateStringObject().
compareStringObject was not always giving the same result when comparing
two exact strings, but encoded as integers or as sds strings, since it
switched to strcmp() when at least one of the strings were not sds
encoded.
For instance the two strings "123" and "123\x00456", where the first
string was integer encoded, would result into the old implementation of
compareStringObject() to return 0 as if the strings were equal, while
instead the second string is "greater" than the first in a binary
comparison.
The same compasion, but with "123" encoded as sds string, would instead
return a value < 0, as it is correct. It is not impossible that the
above caused some obscure bug, since the comparison was not always
deterministic, and compareStringObject() is used in the implementation
of skiplists, hash tables, and so forth.
At the same time, collateStringObject() was introduced by this commit, so
that can be used by SORT command to return sorted strings usign
collation instead of binary comparison. See next commit.
2013-07-12 11:56:52 +02:00
|
|
|
alen = ll2string(bufa,sizeof(bufa),(long) a->ptr);
|
2010-06-22 00:07:48 +02:00
|
|
|
astr = bufa;
|
|
|
|
}
|
2012-06-05 21:50:10 +02:00
|
|
|
if (sdsEncodedObject(b)) {
|
|
|
|
bstr = b->ptr;
|
|
|
|
blen = sdslen(bstr);
|
|
|
|
} else {
|
Fixed compareStringObject() and introduced collateStringObject().
compareStringObject was not always giving the same result when comparing
two exact strings, but encoded as integers or as sds strings, since it
switched to strcmp() when at least one of the strings were not sds
encoded.
For instance the two strings "123" and "123\x00456", where the first
string was integer encoded, would result into the old implementation of
compareStringObject() to return 0 as if the strings were equal, while
instead the second string is "greater" than the first in a binary
comparison.
The same compasion, but with "123" encoded as sds string, would instead
return a value < 0, as it is correct. It is not impossible that the
above caused some obscure bug, since the comparison was not always
deterministic, and compareStringObject() is used in the implementation
of skiplists, hash tables, and so forth.
At the same time, collateStringObject() was introduced by this commit, so
that can be used by SORT command to return sorted strings usign
collation instead of binary comparison. See next commit.
2013-07-12 11:56:52 +02:00
|
|
|
blen = ll2string(bufb,sizeof(bufb),(long) b->ptr);
|
2010-06-22 00:07:48 +02:00
|
|
|
bstr = bufb;
|
Fixed compareStringObject() and introduced collateStringObject().
compareStringObject was not always giving the same result when comparing
two exact strings, but encoded as integers or as sds strings, since it
switched to strcmp() when at least one of the strings were not sds
encoded.
For instance the two strings "123" and "123\x00456", where the first
string was integer encoded, would result into the old implementation of
compareStringObject() to return 0 as if the strings were equal, while
instead the second string is "greater" than the first in a binary
comparison.
The same compasion, but with "123" encoded as sds string, would instead
return a value < 0, as it is correct. It is not impossible that the
above caused some obscure bug, since the comparison was not always
deterministic, and compareStringObject() is used in the implementation
of skiplists, hash tables, and so forth.
At the same time, collateStringObject() was introduced by this commit, so
that can be used by SORT command to return sorted strings usign
collation instead of binary comparison. See next commit.
2013-07-12 11:56:52 +02:00
|
|
|
}
|
|
|
|
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;
|
2010-06-22 00:07:48 +02:00
|
|
|
}
|
Fixed compareStringObject() and introduced collateStringObject().
compareStringObject was not always giving the same result when comparing
two exact strings, but encoded as integers or as sds strings, since it
switched to strcmp() when at least one of the strings were not sds
encoded.
For instance the two strings "123" and "123\x00456", where the first
string was integer encoded, would result into the old implementation of
compareStringObject() to return 0 as if the strings were equal, while
instead the second string is "greater" than the first in a binary
comparison.
The same compasion, but with "123" encoded as sds string, would instead
return a value < 0, as it is correct. It is not impossible that the
above caused some obscure bug, since the comparison was not always
deterministic, and compareStringObject() is used in the implementation
of skiplists, hash tables, and so forth.
At the same time, collateStringObject() was introduced by this commit, so
that can be used by SORT command to return sorted strings usign
collation instead of binary comparison. See next commit.
2013-07-12 11:56:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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);
|
2010-06-22 00:07:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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) {
|
2012-06-05 21:50:10 +02:00
|
|
|
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. */
|
2010-06-22 00:07:48 +02:00
|
|
|
return a->ptr == b->ptr;
|
|
|
|
} else {
|
|
|
|
return compareStringObjects(a,b) == 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t stringObjectLen(robj *o) {
|
2011-10-04 18:43:03 +02:00
|
|
|
redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
|
2012-06-05 21:50:10 +02:00
|
|
|
if (sdsEncodedObject(o)) {
|
2010-06-22 00:07:48 +02:00
|
|
|
return sdslen(o->ptr);
|
|
|
|
} 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 {
|
2011-10-04 18:43:03 +02:00
|
|
|
redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
|
2012-06-05 21:50:10 +02:00
|
|
|
if (sdsEncodedObject(o)) {
|
2011-11-12 19:27:35 +01:00
|
|
|
errno = 0;
|
2010-06-22 00:07:48 +02:00
|
|
|
value = strtod(o->ptr, &eptr);
|
2013-07-16 15:05:13 +02:00
|
|
|
if (isspace(((char*)o->ptr)[0]) ||
|
|
|
|
eptr[0] != '\0' ||
|
|
|
|
(errno == ERANGE &&
|
|
|
|
(value == HUGE_VAL || value == -HUGE_VAL || value == 0)) ||
|
|
|
|
errno == EINVAL ||
|
|
|
|
isnan(value))
|
2011-11-12 19:27:35 +01:00
|
|
|
return REDIS_ERR;
|
2010-06-22 00:07:48 +02:00
|
|
|
} 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) {
|
2010-09-02 19:52:24 +02:00
|
|
|
addReplyError(c,(char*)msg);
|
2010-06-22 00:07:48 +02:00
|
|
|
} else {
|
2011-11-12 19:27:35 +01:00
|
|
|
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);
|
2012-06-05 21:50:10 +02:00
|
|
|
if (sdsEncodedObject(o)) {
|
2011-11-12 19:27:35 +01:00
|
|
|
errno = 0;
|
|
|
|
value = strtold(o->ptr, &eptr);
|
2011-11-14 15:34:44 +01:00
|
|
|
if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
|
|
|
|
errno == ERANGE || isnan(value))
|
2011-11-12 19:27:35 +01:00
|
|
|
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");
|
2010-06-22 00:07:48 +02:00
|
|
|
}
|
|
|
|
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 {
|
2011-10-04 18:43:03 +02:00
|
|
|
redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
|
2012-06-05 21:50:10 +02:00
|
|
|
if (sdsEncodedObject(o)) {
|
2011-11-14 15:34:44 +01:00
|
|
|
errno = 0;
|
2010-06-22 00:07:48 +02:00
|
|
|
value = strtoll(o->ptr, &eptr, 10);
|
2011-11-14 15:34:44 +01:00
|
|
|
if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
|
|
|
|
errno == ERANGE)
|
2010-08-23 17:06:38 +02:00
|
|
|
return REDIS_ERR;
|
2010-06-22 00:07:48 +02:00
|
|
|
} else if (o->encoding == REDIS_ENCODING_INT) {
|
|
|
|
value = (long)o->ptr;
|
|
|
|
} else {
|
|
|
|
redisPanic("Unknown string encoding");
|
|
|
|
}
|
|
|
|
}
|
2010-07-02 19:57:12 +02:00
|
|
|
if (target) *target = value;
|
2010-06-22 00:07:48 +02:00
|
|
|
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) {
|
2010-09-02 19:52:24 +02:00
|
|
|
addReplyError(c,(char*)msg);
|
2010-06-22 00:07:48 +02:00
|
|
|
} else {
|
2010-09-02 19:52:24 +02:00
|
|
|
addReplyError(c,"value is not an integer or out of range");
|
2010-06-22 00:07:48 +02:00
|
|
|
}
|
|
|
|
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) {
|
2010-09-02 19:52:24 +02:00
|
|
|
addReplyError(c,(char*)msg);
|
2010-06-22 00:07:48 +02:00
|
|
|
} else {
|
2010-09-02 19:52:24 +02:00
|
|
|
addReplyError(c,"value is out of range");
|
2010-06-22 00:07:48 +02:00
|
|
|
}
|
|
|
|
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";
|
2010-07-02 19:57:12 +02:00
|
|
|
case REDIS_ENCODING_INTSET: return "intset";
|
2011-04-06 15:36:10 +02:00
|
|
|
case REDIS_ENCODING_SKIPLIST: return "skiplist";
|
2012-06-05 21:50:10 +02:00
|
|
|
case REDIS_ENCODING_EMBSTR: return "embstr";
|
2010-06-22 00:07:48 +02:00
|
|
|
default: return "unknown";
|
|
|
|
}
|
|
|
|
}
|
2010-10-14 13:52:58 +02:00
|
|
|
|
2014-03-20 11:55:18 +01:00
|
|
|
/* Given an object returns the min number of milliseconds the object was never
|
2010-10-14 13:52:58 +02:00
|
|
|
* requested, using an approximated LRU algorithm. */
|
2014-03-20 11:33:25 +01:00
|
|
|
unsigned long long estimateObjectIdleTime(robj *o) {
|
2014-03-20 11:47:12 +01:00
|
|
|
unsigned long long lruclock = LRU_CLOCK();
|
|
|
|
if (lruclock >= o->lru) {
|
|
|
|
return (lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION;
|
2010-10-14 13:52:58 +02:00
|
|
|
} else {
|
2014-03-20 11:47:12 +01:00
|
|
|
return (lruclock + (REDIS_LRU_CLOCK_MAX - o->lru)) *
|
2010-10-14 21:22:21 +02:00
|
|
|
REDIS_LRU_CLOCK_RESOLUTION;
|
2010-10-14 13:52:58 +02:00
|
|
|
}
|
|
|
|
}
|
2011-04-06 12:19:45 +02:00
|
|
|
|
2014-03-30 18:32:17 +02:00
|
|
|
/* This is a helper function for the OBJECT command. We need to lookup keys
|
2011-04-06 12:19:45 +02:00
|
|
|
* 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;
|
2011-11-08 17:07:55 +01:00
|
|
|
return (robj*) dictGetVal(de);
|
2011-04-06 12:19:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
2014-07-11 13:03:26 -07:00
|
|
|
* Usage: OBJECT <refcount|encoding|idletime> <key> */
|
2011-04-06 12:19:45 +02:00
|
|
|
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;
|
2014-03-20 11:55:18 +01:00
|
|
|
addReplyLongLong(c,estimateObjectIdleTime(o)/1000);
|
2011-04-06 12:19:45 +02:00
|
|
|
} else {
|
|
|
|
addReplyError(c,"Syntax error. Try OBJECT (refcount|encoding|idletime)");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|