From 8c304be359f15151ed8fe3e5ff61b0238881358d Mon Sep 17 00:00:00 2001 From: antirez Date: Tue, 14 Dec 2010 12:10:51 +0100 Subject: [PATCH 1/1] cow friendly HGETALL and variants --- src/debug.c | 4 +-- src/redis.h | 3 ++- src/t_hash.c | 73 ++++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 58 insertions(+), 22 deletions(-) diff --git a/src/debug.c b/src/debug.c index 0144c78c..fff8d727 100644 --- a/src/debug.c +++ b/src/debug.c @@ -152,10 +152,10 @@ void computeDatasetDigest(unsigned char *final) { unsigned char eledigest[20]; memset(eledigest,0,20); - obj = hashTypeCurrent(hi,REDIS_HASH_KEY); + obj = hashTypeCurrentObject(hi,REDIS_HASH_KEY); mixObjectDigest(eledigest,obj); decrRefCount(obj); - obj = hashTypeCurrent(hi,REDIS_HASH_VALUE); + obj = hashTypeCurrentObject(hi,REDIS_HASH_VALUE); mixObjectDigest(eledigest,obj); decrRefCount(obj); xorDigest(digest,eledigest,20); diff --git a/src/redis.h b/src/redis.h index 08a37762..67b76a47 100644 --- a/src/redis.h +++ b/src/redis.h @@ -832,7 +832,8 @@ unsigned long hashTypeLength(robj *o); hashTypeIterator *hashTypeInitIterator(robj *subject); void hashTypeReleaseIterator(hashTypeIterator *hi); int hashTypeNext(hashTypeIterator *hi); -robj *hashTypeCurrent(hashTypeIterator *hi, int what); +int hashTypeCurrent(hashTypeIterator *hi, int what, robj **objval, unsigned char **v, unsigned int *vlen); +robj *hashTypeCurrentObject(hashTypeIterator *hi, int what); robj *hashTypeLookupWriteOrCreate(redisClient *c, robj *key); /* Pub / Sub */ diff --git a/src/t_hash.c b/src/t_hash.c index 21fe9e10..838f29dd 100644 --- a/src/t_hash.c +++ b/src/t_hash.c @@ -185,24 +185,50 @@ int hashTypeNext(hashTypeIterator *hi) { } /* Get key or value object at current iteration position. - * This increases the refcount of the field object by 1. */ -robj *hashTypeCurrent(hashTypeIterator *hi, int what) { - robj *o; + * The returned item differs with the hash object encoding: + * - When encoding is REDIS_ENCODING_HT, the objval pointer is populated + * with the original object. + * - When encoding is REDIS_ENCODING_ZIPMAP, a pointer to the string and + * its length is retunred populating the v and vlen pointers. + * This function is copy on write friendly as accessing objects in read only + * does not require writing to any memory page. + * + * The function returns the encoding of the object, so that the caller + * can underestand if the key or value was returned as object or C string. */ +int hashTypeCurrent(hashTypeIterator *hi, int what, robj **objval, unsigned char **v, unsigned int *vlen) { if (hi->encoding == REDIS_ENCODING_ZIPMAP) { if (what & REDIS_HASH_KEY) { - o = createStringObject((char*)hi->zk,hi->zklen); + *v = hi->zk; + *vlen = hi->zklen; } else { - o = createStringObject((char*)hi->zv,hi->zvlen); + *v = hi->zv; + *vlen = hi->zvlen; } } else { - if (what & REDIS_HASH_KEY) { - o = dictGetEntryKey(hi->de); - } else { - o = dictGetEntryVal(hi->de); - } - incrRefCount(o); + if (what & REDIS_HASH_KEY) + *objval = dictGetEntryKey(hi->de); + else + *objval = dictGetEntryVal(hi->de); + } + return hi->encoding; +} + +/* A non copy-on-write friendly but higher level version of hashTypeCurrent() + * that always returns an object with refcount incremented by one (or a new + * object), so it's up to the caller to decrRefCount() the object if no + * reference is retained. */ +robj *hashTypeCurrentObject(hashTypeIterator *hi, int what) { + robj *obj; + unsigned char *v; + unsigned int vlen; + int encoding = hashTypeCurrent(hi,what,&obj,&v,&vlen); + + if (encoding == REDIS_ENCODING_HT) { + incrRefCount(obj); + return obj; + } else { + return createStringObject((char*)v,vlen); } - return o; } robj *hashTypeLookupWriteOrCreate(redisClient *c, robj *key) { @@ -392,7 +418,7 @@ void hlenCommand(redisClient *c) { } void genericHgetallCommand(redisClient *c, int flags) { - robj *o, *obj; + robj *o; unsigned long count = 0; hashTypeIterator *hi; void *replylen = NULL; @@ -403,16 +429,25 @@ void genericHgetallCommand(redisClient *c, int flags) { replylen = addDeferredMultiBulkLength(c); hi = hashTypeInitIterator(o); while (hashTypeNext(hi) != REDIS_ERR) { + robj *obj; + unsigned char *v; + unsigned int vlen; + int encoding; + if (flags & REDIS_HASH_KEY) { - obj = hashTypeCurrent(hi,REDIS_HASH_KEY); - addReplyBulk(c,obj); - decrRefCount(obj); + encoding = hashTypeCurrent(hi,REDIS_HASH_KEY,&obj,&v,&vlen); + if (encoding == REDIS_ENCODING_HT) + addReplyBulk(c,obj); + else + addReplyBulkCBuffer(c,v,vlen); count++; } if (flags & REDIS_HASH_VALUE) { - obj = hashTypeCurrent(hi,REDIS_HASH_VALUE); - addReplyBulk(c,obj); - decrRefCount(obj); + encoding = hashTypeCurrent(hi,REDIS_HASH_VALUE,&obj,&v,&vlen); + if (encoding == REDIS_ENCODING_HT) + addReplyBulk(c,obj); + else + addReplyBulkCBuffer(c,v,vlen); count++; } } -- 2.45.2