Skip to content

Commit

Permalink
squashed merge from robey/twitter3: LINSERT BEFORE|AFTER, LPUSHX, RPUSHX
Browse files Browse the repository at this point in the history
  • Loading branch information
Robey Pointer authored and pietern committed Jun 11, 2010
1 parent 306974f commit dedff27
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 5 deletions.
35 changes: 32 additions & 3 deletions adlist.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,35 @@ list *listAddNodeTail(list *list, void *value)
return list;
}

list *listInsertNode(list *list, listNode *old_node, void *value, int after) {
listNode *node;

if ((node = zmalloc(sizeof(*node))) == NULL)
return NULL;
node->value = value;
if (after) {
node->prev = old_node;
node->next = old_node->next;
if (list->tail == old_node) {
list->tail = node;
}
} else {
node->next = old_node;
node->prev = old_node->prev;
if (list->head == old_node) {
list->head = node;
}
}
if (node->prev != NULL) {
node->prev->next = node;
}
if (node->next != NULL) {
node->next->prev = node;
}
list->len++;
return list;
}

/* Remove the specified node from the specified list.
* It's up to the caller to free the private value of the node.
*
Expand Down Expand Up @@ -183,9 +212,9 @@ void listRewindTail(list *list, listIter *li) {
* or NULL if there are no more elements, so the classical usage patter
* is:
*
* iter = listGetItarotr(list,<direction>);
* while ((node = listNextIterator(iter)) != NULL) {
* DoSomethingWith(listNodeValue(node));
* iter = listGetIterator(list,<direction>);
* while ((node = listNext(iter)) != NULL) {
* doSomethingWith(listNodeValue(node));
* }
*
* */
Expand Down
1 change: 1 addition & 0 deletions adlist.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ list *listCreate(void);
void listRelease(list *list);
list *listAddNodeHead(list *list, void *value);
list *listAddNodeTail(list *list, void *value);
list *listInsertNode(list *list, listNode *old_node, void *value, int after);
void listDelNode(list *list, listNode *node);
listIter *listGetIterator(list *list, int direction);
listNode *listNext(listIter *iter);
Expand Down
84 changes: 83 additions & 1 deletion redis.c
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ typedef struct redisObject {
unsigned lru:22; /* lru time (relative to server.lruclock) */
int refcount;
void *ptr;
/* VM fields, this are only allocated if VM is active, otherwise the
/* VM fields are only allocated if VM is active, otherwise the
* object allocation function will just allocate
* sizeof(redisObjct) minus sizeof(redisObjectVM), so using
* Redis without VM active will not have any overhead. */
Expand Down Expand Up @@ -692,6 +692,9 @@ static void renameCommand(redisClient *c);
static void renamenxCommand(redisClient *c);
static void lpushCommand(redisClient *c);
static void rpushCommand(redisClient *c);
static void lpushxCommand(redisClient *c);
static void rpushxCommand(redisClient *c);
static void linsertCommand(redisClient *c);
static void lpopCommand(redisClient *c);
static void rpopCommand(redisClient *c);
static void llenCommand(redisClient *c);
Expand Down Expand Up @@ -792,6 +795,9 @@ static struct redisCommand readonlyCommandTable[] = {
{"mget",mgetCommand,-2,REDIS_CMD_INLINE,NULL,1,-1,1},
{"rpush",rpushCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
{"lpush",lpushCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
{"rpushx",rpushxCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
{"lpushx",lpushxCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
{"linsert",linsertCommand,5,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
{"rpop",rpopCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
{"lpop",lpopCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
{"brpop",brpopCommand,-3,REDIS_CMD_INLINE,NULL,1,1,1},
Expand Down Expand Up @@ -5174,6 +5180,82 @@ static void rpushCommand(redisClient *c) {
pushGenericCommand(c,REDIS_TAIL);
}

static void listTypeInsert(robj *subject, listTypeEntry *old_entry, robj *new_obj, int where) {
listTypeTryConversion(subject,new_obj);
if (subject->encoding == REDIS_ENCODING_ZIPLIST) {
if (where == REDIS_HEAD) {
unsigned char *next = ziplistNext(subject->ptr,old_entry->zi);
if (next == NULL) {
listTypePush(subject,new_obj,REDIS_TAIL);
} else {
subject->ptr = ziplistInsert(subject->ptr,next,new_obj->ptr,sdslen(new_obj->ptr));
}
} else {
subject->ptr = ziplistInsert(subject->ptr,old_entry->zi,new_obj->ptr,sdslen(new_obj->ptr));
}
} else if (subject->encoding == REDIS_ENCODING_LIST) {
if (where == REDIS_HEAD) {
listInsertNode(subject->ptr,old_entry->ln,new_obj,1);
} else {
listInsertNode(subject->ptr,old_entry->ln,new_obj,0);
}
incrRefCount(new_obj);
} else {
redisPanic("Unknown list encoding");
}
}

static void pushxGenericCommand(redisClient *c, int where, robj *old_obj, robj *new_obj) {
robj *subject;
listTypeIterator *iter;
listTypeEntry entry;

if ((subject = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,subject,REDIS_LIST)) return;
if (handleClientsWaitingListPush(c,c->argv[1],new_obj)) {
addReply(c,shared.cone);
return;
}

if (old_obj != NULL) {
if (where == REDIS_HEAD) {
iter = listTypeInitIterator(subject,0,REDIS_TAIL);
} else {
iter = listTypeInitIterator(subject,-1,REDIS_HEAD);
}
while (listTypeNext(iter,&entry)) {
if (listTypeEqual(&entry,old_obj)) {
listTypeInsert(subject,&entry,new_obj,where);
break;
}
}
listTypeReleaseIterator(iter);
} else {
listTypePush(subject,new_obj,where);
}

server.dirty++;
addReplyUlong(c,listTypeLength(subject));
}

static void lpushxCommand(redisClient *c) {
pushxGenericCommand(c,REDIS_HEAD,NULL,c->argv[2]);
}

static void rpushxCommand(redisClient *c) {
pushxGenericCommand(c,REDIS_TAIL,NULL,c->argv[2]);
}

static void linsertCommand(redisClient *c) {
if (strcasecmp(c->argv[2]->ptr,"after") == 0) {
pushxGenericCommand(c,REDIS_HEAD,c->argv[3],c->argv[4]);
} else if (strcasecmp(c->argv[2]->ptr,"before") == 0) {
pushxGenericCommand(c,REDIS_TAIL,c->argv[3],c->argv[4]);
} else {
addReply(c,shared.syntaxerr);
}
}

static void llenCommand(redisClient *c) {
robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.czero);
if (o == NULL || checkType(c,o,REDIS_LIST)) return;
Expand Down
2 changes: 1 addition & 1 deletion tests/support/redis.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ array set ::redis::multibulkarg {}

# Flag commands requiring last argument as a bulk write operation
foreach redis_bulk_cmd {
set setnx rpush lpush lset lrem sadd srem sismember echo getset smove zadd zrem zscore zincrby append zrank zrevrank hget hdel hexists setex
set setnx rpush lpush rpushx lpushx linsert lset lrem sadd srem sismember echo getset smove zadd zrem zscore zincrby append zrank zrevrank hget hdel hexists setex
} {
set ::redis::bulkarg($redis_bulk_cmd) {}
}
Expand Down
48 changes: 48 additions & 0 deletions tests/unit/type/list.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,54 @@ start_server {
assert_equal $large_value [r lindex mylist2 2]
}

test {LPUSHX, RPUSHX, LPUSHXAFTER, RPUSHXAFTER - ziplist} {
r del xlist
assert_equal 0 [r lpushx xlist a]
assert_equal 0 [r rpushx xlist a]
assert_equal 1 [r rpush xlist b]
assert_equal 2 [r rpush xlist c]
assert_equal 3 [r rpushx xlist d]
assert_equal 4 [r lpushx xlist a]
assert_encoding ziplist xlist
assert_equal {a b c d} [r lrange xlist 0 10]

assert_equal 5 [r linsert xlist before c zz]
assert_equal {a b zz c d} [r lrange xlist 0 10]
assert_equal 6 [r linsert xlist after c yy]
assert_equal {a b zz c yy d} [r lrange xlist 0 10]
assert_equal 7 [r linsert xlist after d dd]
assert_equal 7 [r linsert xlist after bad ddd]
assert_equal {a b zz c yy d dd} [r lrange xlist 0 10]
assert_equal 8 [r linsert xlist before a aa]
assert_equal 8 [r linsert xlist before bad aaa]
assert_equal {aa a b zz c yy d dd} [r lrange xlist 0 10]
}

test {LPUSHX, RPUSHX, LPUSHXAFTER, RPUSHXAFTER - regular list} {
set large_value "aaaaaaaaaaaaaaaaa"

r del xlist
assert_equal 0 [r lpushx xlist a]
assert_equal 0 [r rpushx xlist a]
assert_equal 1 [r rpush xlist $large_value]
assert_equal 2 [r rpush xlist c]
assert_equal 3 [r rpushx xlist d]
assert_equal 4 [r lpushx xlist a]
assert_encoding list xlist
assert_equal {a aaaaaaaaaaaaaaaaa c d} [r lrange xlist 0 10]

assert_equal 5 [r linsert xlist before c zz]
assert_equal {a aaaaaaaaaaaaaaaaa zz c d} [r lrange xlist 0 10]
assert_equal 6 [r linsert xlist after c yy]
assert_equal {a aaaaaaaaaaaaaaaaa zz c yy d} [r lrange xlist 0 10]
assert_equal 7 [r linsert xlist after d dd]
assert_equal 7 [r linsert xlist after bad ddd]
assert_equal {a aaaaaaaaaaaaaaaaa zz c yy d dd} [r lrange xlist 0 10]
assert_equal 8 [r linsert xlist before a aa]
assert_equal 8 [r linsert xlist before bad aaa]
assert_equal {aa a aaaaaaaaaaaaaaaaa zz c yy d dd} [r lrange xlist 0 10]
}

test {DEL a list - ziplist} {
assert_equal 1 [r del myziplist2]
assert_equal 0 [r exists myziplist2]
Expand Down

0 comments on commit dedff27

Please sign in to comment.