update list iteration semantic to work as expected (i.e. "while(lNext(..))")

This commit is contained in:
Pieter Noordhuis 2010-05-31 20:25:31 +02:00
parent 6a8e35ad92
commit be02a7c0d6

130
redis.c
View File

@ -4806,7 +4806,7 @@ static robj *lPop(robj *subject, int where) {
value = createStringObjectFromLongLong(vval); value = createStringObjectFromLongLong(vval);
} }
} }
subject->ptr = ziplistDelete(subject->ptr,&p,ZIPLIST_TAIL); subject->ptr = ziplistDelete(subject->ptr,&p);
} else if (subject->encoding == REDIS_ENCODING_LIST) { } else if (subject->encoding == REDIS_ENCODING_LIST) {
list *list = subject->ptr; list *list = subject->ptr;
listNode *ln; listNode *ln;
@ -4840,15 +4840,24 @@ static unsigned long lLength(robj *subject) {
typedef struct { typedef struct {
robj *subject; robj *subject;
unsigned char encoding; unsigned char encoding;
unsigned char direction; /* Iteration direction */
unsigned char *zi; unsigned char *zi;
listNode *ln; listNode *ln;
} lIterator; } lIterator;
/* Structure for an entry while iterating over a list. */
typedef struct {
lIterator *li;
unsigned char *zi; /* Entry in ziplist */
listNode *ln; /* Entry in linked list */
} lEntry;
/* Initialize an iterator at the specified index. */ /* Initialize an iterator at the specified index. */
static lIterator *lInitIterator(robj *subject, int index) { static lIterator *lInitIterator(robj *subject, int index, unsigned char direction) {
lIterator *li = zmalloc(sizeof(lIterator)); lIterator *li = zmalloc(sizeof(lIterator));
li->subject = subject; li->subject = subject;
li->encoding = subject->encoding; li->encoding = subject->encoding;
li->direction = direction;
if (li->encoding == REDIS_ENCODING_ZIPLIST) { if (li->encoding == REDIS_ENCODING_ZIPLIST) {
li->zi = ziplistIndex(subject->ptr,index); li->zi = ziplistIndex(subject->ptr,index);
} else if (li->encoding == REDIS_ENCODING_LIST) { } else if (li->encoding == REDIS_ENCODING_LIST) {
@ -4864,26 +4873,45 @@ static void lReleaseIterator(lIterator *li) {
zfree(li); zfree(li);
} }
/* Whether the entry pointed at is a valid entry. */ /* Stores pointer to current the entry in the provided entry structure
static int lIsEntry(lIterator *li) { * and advances the position of the iterator. Returns 1 when the current
* entry is in fact an entry, 0 otherwise. */
static int lNext(lIterator *li, lEntry *entry) {
entry->li = li;
if (li->encoding == REDIS_ENCODING_ZIPLIST) { if (li->encoding == REDIS_ENCODING_ZIPLIST) {
return li->zi != NULL; entry->zi = li->zi;
if (entry->zi != NULL) {
if (li->direction == REDIS_TAIL)
li->zi = ziplistNext(li->subject->ptr,li->zi);
else
li->zi = ziplistPrev(li->subject->ptr,li->zi);
return 1;
}
} else if (li->encoding == REDIS_ENCODING_LIST) { } else if (li->encoding == REDIS_ENCODING_LIST) {
return li->ln != NULL; entry->ln = li->ln;
if (entry->ln != NULL) {
if (li->direction == REDIS_TAIL)
li->ln = li->ln->next;
else
li->ln = li->ln->prev;
return 1;
}
} else { } else {
redisPanic("Unknown list encoding"); redisPanic("Unknown list encoding");
} }
return 0;
} }
/* Return entry or NULL at the current position of the iterator. */ /* Return entry or NULL at the current position of the iterator. */
static robj *lGet(lIterator *li) { static robj *lGet(lEntry *entry) {
lIterator *li = entry->li;
robj *value = NULL; robj *value = NULL;
if (li->encoding == REDIS_ENCODING_ZIPLIST) { if (li->encoding == REDIS_ENCODING_ZIPLIST) {
char *v; char *v;
unsigned int vlen; unsigned int vlen;
long long vval; long long vval;
redisAssert(li->zi != NULL); redisAssert(entry->zi != NULL);
if (ziplistGet(li->zi,&v,&vlen,&vval)) { if (ziplistGet(entry->zi,&v,&vlen,&vval)) {
if (v) { if (v) {
value = createStringObject(v,vlen); value = createStringObject(v,vlen);
} else { } else {
@ -4891,8 +4919,8 @@ static robj *lGet(lIterator *li) {
} }
} }
} else if (li->encoding == REDIS_ENCODING_LIST) { } else if (li->encoding == REDIS_ENCODING_LIST) {
redisAssert(li->ln != NULL); redisAssert(entry->ln != NULL);
value = listNodeValue(li->ln); value = listNodeValue(entry->ln);
incrRefCount(value); incrRefCount(value);
} else { } else {
redisPanic("Unknown list encoding"); redisPanic("Unknown list encoding");
@ -4900,50 +4928,39 @@ static robj *lGet(lIterator *li) {
return value; return value;
} }
/* Delete the element pointed to. */
static void lDelete(lIterator *li, int direction) {
if (li->encoding == REDIS_ENCODING_ZIPLIST) {
direction = (direction == REDIS_HEAD) ? ZIPLIST_HEAD : ZIPLIST_TAIL;
li->subject->ptr = ziplistDelete(li->subject->ptr,&li->zi,direction);
} else if (li->encoding == REDIS_ENCODING_LIST) {
listNode *next;
if (direction == REDIS_HEAD)
next = li->ln->prev;
else
next = li->ln->next;
listDelNode(li->subject->ptr,li->ln);
li->ln = next;
} else {
redisPanic("Unknown list encoding");
}
}
/* Compare the given object with the entry at the current position. */ /* Compare the given object with the entry at the current position. */
static int lEqualTo(lIterator *li, robj *o) { static int lEqual(lEntry *entry, robj *o) {
lIterator *li = entry->li;
if (li->encoding == REDIS_ENCODING_ZIPLIST) { if (li->encoding == REDIS_ENCODING_ZIPLIST) {
redisAssert(o->encoding == REDIS_ENCODING_RAW); redisAssert(o->encoding == REDIS_ENCODING_RAW);
return ziplistCompare(li->zi,o->ptr,sdslen(o->ptr)); return ziplistCompare(entry->zi,o->ptr,sdslen(o->ptr));
} else if (li->encoding == REDIS_ENCODING_LIST) { } else if (li->encoding == REDIS_ENCODING_LIST) {
return equalStringObjects(o,listNodeValue(li->ln)); return equalStringObjects(o,listNodeValue(entry->ln));
} else { } else {
redisPanic("Unknown list encoding"); redisPanic("Unknown list encoding");
} }
} }
/* Move to the next or previous entry in the list. */ /* Delete the element pointed to. */
static void lMove(lIterator *li, int where) { static void lDelete(lEntry *entry) {
lIterator *li = entry->li;
if (li->encoding == REDIS_ENCODING_ZIPLIST) { if (li->encoding == REDIS_ENCODING_ZIPLIST) {
redisAssert(li->zi != NULL); unsigned char *p = entry->zi;
if (where == REDIS_HEAD) li->subject->ptr = ziplistDelete(li->subject->ptr,&p);
li->zi = ziplistPrev(li->zi);
/* Update position of the iterator depending on the direction */
if (li->direction == REDIS_TAIL)
li->zi = p;
else else
li->zi = ziplistNext(li->zi); li->zi = ziplistPrev(li->subject->ptr,p);
} else if (li->encoding == REDIS_ENCODING_LIST) { } else if (entry->li->encoding == REDIS_ENCODING_LIST) {
redisAssert(li->ln != NULL); listNode *next;
if (where == REDIS_HEAD) if (li->direction == REDIS_TAIL)
li->ln = li->ln->prev; next = entry->ln->next;
else else
li->ln = li->ln->next; next = entry->ln->prev;
listDelNode(li->subject->ptr,entry->ln);
li->ln = next;
} else { } else {
redisPanic("Unknown list encoding"); redisPanic("Unknown list encoding");
} }
@ -5037,7 +5054,7 @@ static void lsetCommand(redisClient *c) {
if (p == NULL) { if (p == NULL) {
addReply(c,shared.outofrangeerr); addReply(c,shared.outofrangeerr);
} else { } else {
o->ptr = ziplistDelete(o->ptr,&p,ZIPLIST_TAIL); o->ptr = ziplistDelete(o->ptr,&p);
value = getDecodedObject(value); value = getDecodedObject(value);
o->ptr = ziplistInsert(o->ptr,p,value->ptr,sdslen(value->ptr)); o->ptr = ziplistInsert(o->ptr,p,value->ptr,sdslen(value->ptr));
decrRefCount(value); decrRefCount(value);
@ -5089,6 +5106,7 @@ static void lrangeCommand(redisClient *c) {
int end = atoi(c->argv[3]->ptr); int end = atoi(c->argv[3]->ptr);
int llen; int llen;
int rangelen, j; int rangelen, j;
lEntry entry;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL
|| checkType(c,o,REDIS_LIST)) return; || checkType(c,o,REDIS_LIST)) return;
@ -5111,12 +5129,12 @@ static void lrangeCommand(redisClient *c) {
/* Return the result in form of a multi-bulk reply */ /* Return the result in form of a multi-bulk reply */
addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",rangelen)); addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",rangelen));
lIterator *li = lInitIterator(o,start); lIterator *li = lInitIterator(o,start,REDIS_TAIL);
for (j = 0; j < rangelen; j++) { for (j = 0; j < rangelen; j++) {
value = lGet(li); redisAssert(lNext(li,&entry));
redisAssert(value != NULL); value = lGet(&entry);
addReplyBulk(c,value); addReplyBulk(c,value);
lMove(li,REDIS_TAIL); decrRefCount(value);
} }
lReleaseIterator(li); lReleaseIterator(li);
} }
@ -5177,7 +5195,7 @@ static void lremCommand(redisClient *c) {
robj *subject, *obj = c->argv[3]; robj *subject, *obj = c->argv[3];
int toremove = atoi(c->argv[2]->ptr); int toremove = atoi(c->argv[2]->ptr);
int removed = 0; int removed = 0;
int direction; lEntry entry;
subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero); subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero);
if (subject == NULL || checkType(c,subject,REDIS_LIST)) return; if (subject == NULL || checkType(c,subject,REDIS_LIST)) return;
@ -5189,21 +5207,17 @@ static void lremCommand(redisClient *c) {
lIterator *li; lIterator *li;
if (toremove < 0) { if (toremove < 0) {
toremove = -toremove; toremove = -toremove;
direction = REDIS_HEAD; li = lInitIterator(subject,-1,REDIS_HEAD);
li = lInitIterator(subject,-1);
} else { } else {
direction = REDIS_TAIL; li = lInitIterator(subject,0,REDIS_TAIL);
li = lInitIterator(subject,0);
} }
while (lIsEntry(li)) { while (lNext(li,&entry)) {
if (lEqualTo(li,obj)) { if (lEqual(&entry,obj)) {
lDelete(li,direction); lDelete(&entry);
server.dirty++; server.dirty++;
removed++; removed++;
if (toremove && removed == toremove) break; if (toremove && removed == toremove) break;
} else {
lMove(li,direction);
} }
} }
lReleaseIterator(li); lReleaseIterator(li);