X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/98a9abb66d2471e9ea4e657566fbd61a7f28b02b..52d46855d90bfddb3e03d957c48101d123e4dfe7:/src/object.c diff --git a/src/object.c b/src/object.c index a132665e..fd7786ed 100644 --- a/src/object.c +++ b/src/object.c @@ -1,5 +1,4 @@ #include "redis.h" -#include #include robj *createObject(int type, void *ptr) { @@ -21,7 +20,6 @@ robj *createObject(int type, void *ptr) { /* The following is only needed if VM is active, but since the conditional * is probably more costly than initializing the field it's better to * have every field properly initialized anyway. */ - o->storage = REDIS_DS_MEMORY; return o; } @@ -31,9 +29,7 @@ robj *createStringObject(char *ptr, size_t len) { robj *createStringObjectFromLongLong(long long value) { robj *o; - if (value >= 0 && value < REDIS_SHARED_INTEGERS && - !server.ds_enabled && - pthread_equal(pthread_self(),server.mainthread)) { + if (value >= 0 && value < REDIS_SHARED_INTEGERS) { incrRefCount(shared.integers[value]); o = shared.integers[value]; } else { @@ -49,7 +45,7 @@ robj *createStringObjectFromLongLong(long long value) { } robj *dupStringObject(robj *o) { - redisAssert(o->encoding == REDIS_ENCODING_RAW); + redisAssertWithInfo(NULL,o,o->encoding == REDIS_ENCODING_RAW); return createStringObject(o->ptr,sdslen(o->ptr)); } @@ -94,10 +90,20 @@ robj *createHashObject(void) { robj *createZsetObject(void) { zset *zs = zmalloc(sizeof(*zs)); + robj *o; zs->dict = dictCreate(&zsetDictType,NULL); zs->zsl = zslCreate(); - return createObject(REDIS_ZSET,zs); + o = createObject(REDIS_ZSET,zs); + o->encoding = REDIS_ENCODING_SKIPLIST; + return o; +} + +robj *createZsetZiplistObject(void) { + unsigned char *zl = ziplistNew(); + robj *o = createObject(REDIS_ZSET,zl); + o->encoding = REDIS_ENCODING_ZIPLIST; + return o; } void freeStringObject(robj *o) { @@ -133,11 +139,20 @@ void freeSetObject(robj *o) { } void freeZsetObject(robj *o) { - zset *zs = o->ptr; - - dictRelease(zs->dict); - zslFree(zs->zsl); - zfree(zs); + zset *zs; + switch (o->encoding) { + case REDIS_ENCODING_SKIPLIST: + 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"); + } } void freeHashObject(robj *o) { @@ -162,10 +177,7 @@ void decrRefCount(void *obj) { robj *o = obj; if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0"); - if (--(o->refcount) == 0) { - /* DS_SAVING objects should always have a reference in the - * IO Job structure. So we should never reach this state. */ - redisAssert(o->storage != REDIS_DS_SAVING); + if (o->refcount == 1) { switch(o->type) { case REDIS_STRING: freeStringObject(o); break; case REDIS_LIST: freeListObject(o); break; @@ -174,11 +186,29 @@ void decrRefCount(void *obj) { case REDIS_HASH: freeHashObject(o); break; default: redisPanic("Unknown object type"); break; } - o->ptr = NULL; /* defensive programming. We'll see NULL in traces. */ zfree(o); + } else { + o->refcount--; } } +/* 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; +} + int checkType(redisClient *c, robj *o, int type) { if (o->type != type) { addReply(c,shared.wrongtypeerr); @@ -187,6 +217,16 @@ int checkType(redisClient *c, robj *o, int type) { return 0; } +int isObjectRepresentableAsLongLong(robj *o, long long *llval) { + redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); + 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; + } +} + /* Try to encode a string object in order to save space */ robj *tryObjectEncoding(robj *o) { long value; @@ -201,10 +241,10 @@ robj *tryObjectEncoding(robj *o) { if (o->refcount > 1) return o; /* Currently we try to encode only strings */ - redisAssert(o->type == REDIS_STRING); + redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); /* Check if we can represent this string as a long integer */ - if (isStringRepresentableAsLong(s,&value) == REDIS_ERR) return o; + if (!string2l(s,sdslen(s),&value)) return o; /* Ok, this object can be encoded... * @@ -215,10 +255,7 @@ robj *tryObjectEncoding(robj *o) { * Note that we also 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.ds_enabled && - server.maxmemory == 0 && value >= 0 && value < REDIS_SHARED_INTEGERS && - pthread_equal(pthread_self(),server.mainthread)) - { + if (server.maxmemory == 0 && value >= 0 && value < REDIS_SHARED_INTEGERS) { decrRefCount(o); incrRefCount(shared.integers[value]); return shared.integers[value]; @@ -259,7 +296,7 @@ robj *getDecodedObject(robj *o) { * sdscmp() from sds.c will apply memcmp() so this function ca be considered * binary safe. */ int compareStringObjects(robj *a, robj *b) { - redisAssert(a->type == REDIS_STRING && b->type == REDIS_STRING); + redisAssertWithInfo(NULL,a,a->type == REDIS_STRING && b->type == REDIS_STRING); char bufa[128], bufb[128], *astr, *bstr; int bothsds = 1; @@ -294,7 +331,7 @@ int equalStringObjects(robj *a, robj *b) { } size_t stringObjectLen(robj *o) { - redisAssert(o->type == REDIS_STRING); + redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); if (o->encoding == REDIS_ENCODING_RAW) { return sdslen(o->ptr); } else { @@ -311,7 +348,7 @@ int getDoubleFromObject(robj *o, double *target) { if (o == NULL) { value = 0; } else { - redisAssert(o->type == REDIS_STRING); + redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); if (o->encoding == REDIS_ENCODING_RAW) { value = strtod(o->ptr, &eptr); if (eptr[0] != '\0' || isnan(value)) return REDIS_ERR; @@ -348,7 +385,7 @@ int getLongLongFromObject(robj *o, long long *target) { if (o == NULL) { value = 0; } else { - redisAssert(o->type == REDIS_STRING); + redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); if (o->encoding == REDIS_ENCODING_RAW) { value = strtoll(o->ptr, &eptr, 10); if (eptr[0] != '\0') return REDIS_ERR; @@ -406,6 +443,7 @@ char *strEncoding(int encoding) { case REDIS_ENCODING_LINKEDLIST: return "linkedlist"; case REDIS_ENCODING_ZIPLIST: return "ziplist"; case REDIS_ENCODING_INTSET: return "intset"; + case REDIS_ENCODING_SKIPLIST: return "skiplist"; default: return "unknown"; } } @@ -420,3 +458,42 @@ unsigned long estimateObjectIdleTime(robj *o) { REDIS_LRU_CLOCK_RESOLUTION; } } + +/* This is an helper function for the DEBUG command. We need to lookup keys + * 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; + return (robj*) dictGetVal(de); +} + +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 ... 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; + addReplyLongLong(c,estimateObjectIdleTime(o)); + } else { + addReplyError(c,"Syntax error. Try OBJECT (refcount|encoding|idletime)"); + } +} +