diff --git a/src/config.c b/src/config.c index e1b743dbf..5c4498869 100644 --- a/src/config.c +++ b/src/config.c @@ -332,13 +332,10 @@ badfmt: /* Bad format errors */ void configGetCommand(redisClient *c) { robj *o = getDecodedObject(c->argv[2]); - robj *lenobj = createObject(REDIS_STRING,NULL); + void *replylen = addDeferredMultiBulkLength(c); char *pattern = o->ptr; int matches = 0; - addReply(c,lenobj); - decrRefCount(lenobj); - if (stringmatch(pattern,"dbfilename",0)) { addReplyBulkCString(c,"dbfilename"); addReplyBulkCString(c,server.dbfilename); @@ -410,7 +407,7 @@ void configGetCommand(redisClient *c) { matches++; } decrRefCount(o); - lenobj->ptr = sdscatprintf(sdsempty(),"*%d\r\n",matches*2); + setDeferredMultiBulkLength(c,replylen,matches*2); } void configCommand(redisClient *c) { diff --git a/src/db.c b/src/db.c index 6d287d72c..8c6c6bc82 100644 --- a/src/db.c +++ b/src/db.c @@ -223,11 +223,9 @@ void keysCommand(redisClient *c) { sds pattern = c->argv[1]->ptr; int plen = sdslen(pattern); unsigned long numkeys = 0; - robj *lenobj = createObject(REDIS_STRING,NULL); + void *replylen = addDeferredMultiBulkLength(c); di = dictGetIterator(c->db->dict); - addReply(c,lenobj); - decrRefCount(lenobj); while((de = dictNext(di)) != NULL) { sds key = dictGetEntryKey(de); robj *keyobj; @@ -243,7 +241,7 @@ void keysCommand(redisClient *c) { } } dictReleaseIterator(di); - lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",numkeys); + setDeferredMultiBulkLength(c,replylen,numkeys); } void dbsizeCommand(redisClient *c) { diff --git a/src/networking.c b/src/networking.c index da0cd0a16..464d5e02c 100644 --- a/src/networking.c +++ b/src/networking.c @@ -145,6 +145,34 @@ void addReplyString(redisClient *c, char *s, size_t len) { } } +/* Adds an empty object to the reply list that will contain the multi bulk + * length, which is not known when this function is called. */ +void *addDeferredMultiBulkLength(redisClient *c) { + if (_ensureFileEvent(c) != REDIS_OK) return NULL; + _addReplyObjectToList(c,createObject(REDIS_STRING,NULL)); + return listLast(c->reply); +} + +/* Populate the length object and try glueing it to the next chunk. */ +void setDeferredMultiBulkLength(redisClient *c, void *node, long length) { + listNode *ln = (listNode*)node; + robj *len, *next; + + /* Abort when *node is NULL (see addDeferredMultiBulkLength). */ + if (node == NULL) return; + + len = listNodeValue(ln); + len->ptr = sdscatprintf(sdsempty(),"*%ld\r\n",length); + if (ln->next != NULL) { + next = listNodeValue(ln->next); + /* Only glue when the next node is a reply chunk. */ + if (next->type == REDIS_REPLY_NODE) { + len->ptr = sdscatlen(len->ptr,next->ptr,sdslen(next->ptr)); + listDelNode(c->reply,ln->next); + } + } +} + void addReplyDouble(redisClient *c, double d) { char dbuf[128], sbuf[128]; int dlen, slen; diff --git a/src/redis.h b/src/redis.h index e2f694543..752d56c30 100644 --- a/src/redis.h +++ b/src/redis.h @@ -603,6 +603,8 @@ void resetClient(redisClient *c); void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask); void sendReplyToClientWritev(aeEventLoop *el, int fd, void *privdata, int mask); void addReply(redisClient *c, robj *obj); +void *addDeferredMultiBulkLength(redisClient *c); +void setDeferredMultiBulkLength(redisClient *c, void *node, long length); void addReplySds(redisClient *c, sds s); void processInputBuffer(redisClient *c); void acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask); diff --git a/src/t_hash.c b/src/t_hash.c index b6be284fa..c8be72f2d 100644 --- a/src/t_hash.c +++ b/src/t_hash.c @@ -350,17 +350,15 @@ void hlenCommand(redisClient *c) { } void genericHgetallCommand(redisClient *c, int flags) { - robj *o, *lenobj, *obj; + robj *o, *obj; unsigned long count = 0; hashTypeIterator *hi; + void *replylen = NULL; if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL || checkType(c,o,REDIS_HASH)) return; - lenobj = createObject(REDIS_STRING,NULL); - addReply(c,lenobj); - decrRefCount(lenobj); - + replylen = addDeferredMultiBulkLength(c); hi = hashTypeInitIterator(o); while (hashTypeNext(hi) != REDIS_ERR) { if (flags & REDIS_HASH_KEY) { @@ -377,8 +375,7 @@ void genericHgetallCommand(redisClient *c, int flags) { } } hashTypeReleaseIterator(hi); - - lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",count); + setDeferredMultiBulkLength(c,replylen,count); } void hkeysCommand(redisClient *c) { diff --git a/src/t_set.c b/src/t_set.c index 68e132278..d6041e724 100644 --- a/src/t_set.c +++ b/src/t_set.c @@ -320,7 +320,8 @@ int qsortCompareSetsByCardinality(const void *s1, const void *s2) { void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum, robj *dstkey) { robj **sets = zmalloc(sizeof(robj*)*setnum); setTypeIterator *si; - robj *ele, *lenobj = NULL, *dstset = NULL; + robj *ele, *dstset = NULL; + void *replylen = NULL; unsigned long j, cardinality = 0; for (j = 0; j < setnum; j++) { @@ -356,9 +357,7 @@ void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum, * to the output list and save the pointer to later modify it with the * right length */ if (!dstkey) { - lenobj = createObject(REDIS_STRING,NULL); - addReply(c,lenobj); - decrRefCount(lenobj); + replylen = addDeferredMultiBulkLength(c); } else { /* If we have a target key where to store the resulting set * create this key with an empty set inside */ @@ -400,7 +399,7 @@ void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum, touchWatchedKey(c->db,dstkey); server.dirty++; } else { - lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",cardinality); + setDeferredMultiBulkLength(c,replylen,cardinality); } zfree(sets); } diff --git a/src/t_zset.c b/src/t_zset.c index e93e5c406..d25b1a669 100644 --- a/src/t_zset.c +++ b/src/t_zset.c @@ -866,7 +866,8 @@ void genericZrangebyscoreCommand(redisClient *c, int justcount) { zset *zsetobj = o->ptr; zskiplist *zsl = zsetobj->zsl; zskiplistNode *ln; - robj *ele, *lenobj = NULL; + robj *ele; + void *replylen = NULL; unsigned long rangelen = 0; /* Get the first node with the score >= min, or with @@ -884,11 +885,8 @@ void genericZrangebyscoreCommand(redisClient *c, int justcount) { * are in the list, so we push this object that will represent * the multi-bulk length in the output buffer, and will "fix" * it later */ - if (!justcount) { - lenobj = createObject(REDIS_STRING,NULL); - addReply(c,lenobj); - decrRefCount(lenobj); - } + if (!justcount) + replylen = addDeferredMultiBulkLength(c); while(ln && (maxex ? (ln->score < max) : (ln->score <= max))) { if (offset) { @@ -910,7 +908,7 @@ void genericZrangebyscoreCommand(redisClient *c, int justcount) { if (justcount) { addReplyLongLong(c,(long)rangelen); } else { - lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n", + setDeferredMultiBulkLength(c,replylen, withscores ? (rangelen*2) : rangelen); } }