Commit dedff272 authored by Robey Pointer's avatar Robey Pointer Committed by Pieter Noordhuis
Browse files

squashed merge from robey/twitter3: LINSERT BEFORE|AFTER, LPUSHX, RPUSHX

parent 306974f5
...@@ -123,6 +123,35 @@ list *listAddNodeTail(list *list, void *value) ...@@ -123,6 +123,35 @@ list *listAddNodeTail(list *list, void *value)
return list; 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. /* Remove the specified node from the specified list.
* It's up to the caller to free the private value of the node. * It's up to the caller to free the private value of the node.
* *
...@@ -183,9 +212,9 @@ void listRewindTail(list *list, listIter *li) { ...@@ -183,9 +212,9 @@ void listRewindTail(list *list, listIter *li) {
* or NULL if there are no more elements, so the classical usage patter * or NULL if there are no more elements, so the classical usage patter
* is: * is:
* *
* iter = listGetItarotr(list,<direction>); * iter = listGetIterator(list,<direction>);
* while ((node = listNextIterator(iter)) != NULL) { * while ((node = listNext(iter)) != NULL) {
* DoSomethingWith(listNodeValue(node)); * doSomethingWith(listNodeValue(node));
* } * }
* *
* */ * */
......
...@@ -74,6 +74,7 @@ list *listCreate(void); ...@@ -74,6 +74,7 @@ list *listCreate(void);
void listRelease(list *list); void listRelease(list *list);
list *listAddNodeHead(list *list, void *value); list *listAddNodeHead(list *list, void *value);
list *listAddNodeTail(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); void listDelNode(list *list, listNode *node);
listIter *listGetIterator(list *list, int direction); listIter *listGetIterator(list *list, int direction);
listNode *listNext(listIter *iter); listNode *listNext(listIter *iter);
......
...@@ -261,7 +261,7 @@ typedef struct redisObject { ...@@ -261,7 +261,7 @@ typedef struct redisObject {
unsigned lru:22; /* lru time (relative to server.lruclock) */ unsigned lru:22; /* lru time (relative to server.lruclock) */
int refcount; int refcount;
void *ptr; 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 * object allocation function will just allocate
* sizeof(redisObjct) minus sizeof(redisObjectVM), so using * sizeof(redisObjct) minus sizeof(redisObjectVM), so using
* Redis without VM active will not have any overhead. */ * Redis without VM active will not have any overhead. */
...@@ -692,6 +692,9 @@ static void renameCommand(redisClient *c); ...@@ -692,6 +692,9 @@ static void renameCommand(redisClient *c);
static void renamenxCommand(redisClient *c); static void renamenxCommand(redisClient *c);
static void lpushCommand(redisClient *c); static void lpushCommand(redisClient *c);
static void rpushCommand(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 lpopCommand(redisClient *c);
static void rpopCommand(redisClient *c); static void rpopCommand(redisClient *c);
static void llenCommand(redisClient *c); static void llenCommand(redisClient *c);
...@@ -792,6 +795,9 @@ static struct redisCommand readonlyCommandTable[] = { ...@@ -792,6 +795,9 @@ static struct redisCommand readonlyCommandTable[] = {
{"mget",mgetCommand,-2,REDIS_CMD_INLINE,NULL,1,-1,1}, {"mget",mgetCommand,-2,REDIS_CMD_INLINE,NULL,1,-1,1},
{"rpush",rpushCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,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}, {"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}, {"rpop",rpopCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
{"lpop",lpopCommand,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}, {"brpop",brpopCommand,-3,REDIS_CMD_INLINE,NULL,1,1,1},
...@@ -5174,6 +5180,82 @@ static void rpushCommand(redisClient *c) { ...@@ -5174,6 +5180,82 @@ static void rpushCommand(redisClient *c) {
pushGenericCommand(c,REDIS_TAIL); 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) { static void llenCommand(redisClient *c) {
robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.czero); robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.czero);
if (o == NULL || checkType(c,o,REDIS_LIST)) return; if (o == NULL || checkType(c,o,REDIS_LIST)) return;
......
...@@ -40,7 +40,7 @@ array set ::redis::multibulkarg {} ...@@ -40,7 +40,7 @@ array set ::redis::multibulkarg {}
# Flag commands requiring last argument as a bulk write operation # Flag commands requiring last argument as a bulk write operation
foreach redis_bulk_cmd { 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) {} set ::redis::bulkarg($redis_bulk_cmd) {}
} }
......
...@@ -52,6 +52,54 @@ start_server { ...@@ -52,6 +52,54 @@ start_server {
assert_equal $large_value [r lindex mylist2 2] 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} { test {DEL a list - ziplist} {
assert_equal 1 [r del myziplist2] assert_equal 1 [r del myziplist2]
assert_equal 0 [r exists myziplist2] assert_equal 0 [r exists myziplist2]
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment