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. */
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);
{"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},
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;
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]