/* Check if we need to convert the ziplist */
listTypeTryConversion(subject,value);
if (subject->encoding == REDIS_ENCODING_ZIPLIST &&
- ziplistLen(subject->ptr) > server.list_max_ziplist_entries)
+ ziplistLen(subject->ptr) >= server.list_max_ziplist_entries)
listTypeConvert(subject,REDIS_ENCODING_LIST);
if (subject->encoding == REDIS_ENCODING_ZIPLIST) {
return value;
}
-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);
+static void listTypeInsert(listTypeEntry *entry, robj *value, int where) {
+ robj *subject = entry->li->subject;
+ if (entry->li->encoding == REDIS_ENCODING_ZIPLIST) {
+ value = getDecodedObject(value);
+ if (where == REDIS_TAIL) {
+ unsigned char *next = ziplistNext(subject->ptr,entry->zi);
+
+ /* When we insert after the current element, but the current element
+ * is the tail of the list, we need to do a push. */
if (next == NULL) {
- listTypePush(subject,new_obj,REDIS_TAIL);
+ subject->ptr = ziplistPush(subject->ptr,value->ptr,sdslen(value->ptr),REDIS_TAIL);
} else {
- subject->ptr = ziplistInsert(subject->ptr,next,new_obj->ptr,sdslen(new_obj->ptr));
+ subject->ptr = ziplistInsert(subject->ptr,next,value->ptr,sdslen(value->ptr));
}
} else {
- subject->ptr = ziplistInsert(subject->ptr,old_entry->zi,new_obj->ptr,sdslen(new_obj->ptr));
+ subject->ptr = ziplistInsert(subject->ptr,entry->zi,value->ptr,sdslen(value->ptr));
}
- } else if (subject->encoding == REDIS_ENCODING_LIST) {
- if (where == REDIS_HEAD) {
- listInsertNode(subject->ptr,old_entry->ln,new_obj,1);
+ decrRefCount(value);
+ } else if (entry->li->encoding == REDIS_ENCODING_LIST) {
+ if (where == REDIS_TAIL) {
+ listInsertNode(subject->ptr,entry->ln,value,AL_START_TAIL);
} else {
- listInsertNode(subject->ptr,old_entry->ln,new_obj,0);
+ listInsertNode(subject->ptr,entry->ln,value,AL_START_HEAD);
}
- incrRefCount(new_obj);
+ incrRefCount(value);
} else {
redisPanic("Unknown list encoding");
}
pushGenericCommand(c,REDIS_TAIL);
}
-static void pushxGenericCommand(redisClient *c, int where, robj *old_obj, robj *new_obj) {
+static void pushxGenericCommand(redisClient *c, robj *refval, robj *val, int where) {
robj *subject;
listTypeIterator *iter;
listTypeEntry entry;
+ int inserted = 0;
if ((subject = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,subject,REDIS_LIST)) return;
- if (handleClientsWaitingListPush(c,c->argv[1],new_obj)) {
+ if (handleClientsWaitingListPush(c,c->argv[1],val)) {
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);
- }
+ if (refval != NULL) {
+ /* Note: we expect refval to be string-encoded because it is *not* the
+ * last argument of the multi-bulk LINSERT. */
+ redisAssert(refval->encoding == REDIS_ENCODING_RAW);
+
+ /* We're not sure if this value can be inserted yet, but we cannot
+ * convert the list inside the iterator. We don't want to loop over
+ * the list twice (once to see if the value can be inserted and once
+ * to do the actual insert), so we assume this value can be inserted
+ * and convert the ziplist to a regular list if necessary. */
+ listTypeTryConversion(subject,val);
+
+ /* Seek refval from head to tail */
+ iter = listTypeInitIterator(subject,0,REDIS_TAIL);
while (listTypeNext(iter,&entry)) {
- if (listTypeEqual(&entry,old_obj)) {
- listTypeInsert(subject,&entry,new_obj,where);
+ if (listTypeEqual(&entry,refval)) {
+ listTypeInsert(&entry,val,where);
+ inserted = 1;
break;
}
}
listTypeReleaseIterator(iter);
+
+ if (inserted) {
+ /* Check if the length exceeds the ziplist length threshold. */
+ if (subject->encoding == REDIS_ENCODING_ZIPLIST &&
+ ziplistLen(subject->ptr) > server.list_max_ziplist_entries)
+ listTypeConvert(subject,REDIS_ENCODING_LIST);
+ server.dirty++;
+ }
} else {
- listTypePush(subject,new_obj,where);
+ listTypePush(subject,val,where);
+ server.dirty++;
}
- server.dirty++;
addReplyUlong(c,listTypeLength(subject));
}
static void lpushxCommand(redisClient *c) {
- pushxGenericCommand(c,REDIS_HEAD,NULL,c->argv[2]);
+ pushxGenericCommand(c,NULL,c->argv[2],REDIS_HEAD);
}
static void rpushxCommand(redisClient *c) {
- pushxGenericCommand(c,REDIS_TAIL,NULL,c->argv[2]);
+ pushxGenericCommand(c,NULL,c->argv[2],REDIS_TAIL);
}
static void linsertCommand(redisClient *c) {
if (strcasecmp(c->argv[2]->ptr,"after") == 0) {
- pushxGenericCommand(c,REDIS_HEAD,c->argv[3],c->argv[4]);
+ pushxGenericCommand(c,c->argv[3],c->argv[4],REDIS_TAIL);
} else if (strcasecmp(c->argv[2]->ptr,"before") == 0) {
- pushxGenericCommand(c,REDIS_TAIL,c->argv[3],c->argv[4]);
+ pushxGenericCommand(c,c->argv[3],c->argv[4],REDIS_HEAD);
} else {
addReply(c,shared.syntaxerr);
}