diff --git a/src/listpack.c b/src/listpack.c index ca51c217a..648b94b46 100644 --- a/src/listpack.c +++ b/src/listpack.c @@ -1456,6 +1456,45 @@ unsigned char *lpBatchDelete(unsigned char *lp, unsigned char **ps, unsigned lon return lpShrinkToFit(lp); } +/* Trim a listpack, remove 'ltrim' elements from left and 'rtrim' elements from right. */ +unsigned char *lpTrim(unsigned char *lp, unsigned long ltrim, unsigned long rtrim) { + if (ltrim == 0 && rtrim == 0) return lp; + size_t bytes = lpBytes(lp); + unsigned long llen = lpLength(lp); + if (ltrim + rtrim >= llen) { + /* Remove all elements */ + lp[LP_HDR_SIZE] = LP_EOF; + lpSetTotalBytes(lp, LP_HDR_SIZE + 1); + lpSetNumElements(lp, 0); + return lpShrinkToFit(lp); + } + unsigned long rangelen = llen - ltrim - rtrim; + unsigned char *first, *tail; /* first points to the first byte we want to save, + and tail points to the byte after the last byte we want to save */ + first = lpSeek(lp, ltrim); + assert(first != NULL); + tail = lp + bytes - 1; /* When rtrim is zero, tail points to the EOF char */ + if (rtrim) { + if (rangelen < rtrim) { + tail = first; + unsigned long num = rangelen; + while (num--) { + tail = lpSkip(tail); + } + assert(tail[0] != LP_EOF); + lpAssertValidEntry(lp, bytes, tail); + } else { + tail = lpSeek(lp, llen - (int)rtrim); + } + } + size_t rangebytes = tail - first; + memmove(lp + LP_HDR_SIZE, first, rangebytes); + lp[LP_HDR_SIZE + rangebytes] = LP_EOF; + lpSetTotalBytes(lp, LP_HDR_SIZE + rangebytes + 1); + lpSetNumElements(lp, rangelen); + return lpShrinkToFit(lp); +} + /* Merge listpacks 'first' and 'second' by appending 'second' to 'first'. * * NOTE: The larger listpack is reallocated to contain the new merged listpack. diff --git a/src/listpack.h b/src/listpack.h index 06723d287..53a2efef0 100644 --- a/src/listpack.h +++ b/src/listpack.h @@ -54,6 +54,7 @@ unsigned char *lpBatchAppend(unsigned char *lp, listpackEntry *entries, unsigned unsigned char *lpBatchInsert(unsigned char *lp, unsigned char *p, int where, listpackEntry *entries, unsigned int len, unsigned char **newp); unsigned char *lpBatchDelete(unsigned char *lp, unsigned char **ps, unsigned long count); +unsigned char *lpTrim(unsigned char *lp, unsigned long ltrim, unsigned long rtrim); unsigned char *lpMerge(unsigned char **first, unsigned char **second); unsigned char *lpDup(unsigned char *lp); unsigned long lpLength(unsigned char *lp); diff --git a/src/t_list.c b/src/t_list.c index ba982a5e7..5d2d03e19 100644 --- a/src/t_list.c +++ b/src/t_list.c @@ -961,8 +961,7 @@ void ltrimCommand(client *c) { quicklistDelRange(o->ptr,0,ltrim); quicklistDelRange(o->ptr,-rtrim,rtrim); } else if (o->encoding == OBJ_ENCODING_LISTPACK) { - o->ptr = lpDeleteRange(o->ptr,0,ltrim); - o->ptr = lpDeleteRange(o->ptr,-rtrim,rtrim); + o->ptr = lpTrim(o->ptr, ltrim, rtrim); } else { serverPanic("Unknown list encoding"); }