Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

LSPLICE #589

Open
wants to merge 1 commit into from

2 participants

@gsilk

This is an implementation of the LSPLICE command described in #550. I actually wrote this on June 14th (shortly after the issue was opened) but it wasn't until now that I got approval from my company to submit a pull request. Cheers,

-gsilk

Gabriel Silk LSPLICE. dc790e9
@toutouastro

@gsilk why you had to get approval from your company ?

@gsilk

@toutouastro Good question -- my company has an approval process for two reasons:

1) We want to make sure that code we contribute back to open source projects is well-written and well-tested
2) We want to make sure that we're contributing code that is relevant to the project and general community, rather than specific to the company itself

@toutouastro

@gsilk I was thinking the company own the code you write out of work hours !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jul 18, 2012
  1. LSPLICE.

    Gabriel Silk authored
This page is out of date. Refresh to see the latest.
View
5 src/help.h
@@ -314,6 +314,11 @@ struct commandHelp {
"Set the value of an element in a list by its index",
2,
"1.0.0" },
+ { "LSPLICE",
+ "key start stop",
+ "Remove a range of elements from a list",
+ 2,
+ "2.6.0" },
{ "LTRIM",
"key start stop",
"Trim a list to the specified range",
View
1  src/redis.c
@@ -139,6 +139,7 @@ struct redisCommand redisCommandTable[] = {
{"lindex",lindexCommand,3,"r",0,NULL,1,1,1,0,0},
{"lset",lsetCommand,4,"wm",0,NULL,1,1,1,0,0},
{"lrange",lrangeCommand,4,"r",0,NULL,1,1,1,0,0},
+ {"lsplice",lspliceCommand,4,"w",0,NULL,1,1,1,0,0},
{"ltrim",ltrimCommand,4,"w",0,NULL,1,1,1,0,0},
{"lrem",lremCommand,4,"w",0,NULL,1,1,1,0,0},
{"rpoplpush",rpoplpushCommand,3,"wm",0,NULL,1,2,1,0,0},
View
1  src/redis.h
@@ -1162,6 +1162,7 @@ void rpopCommand(redisClient *c);
void llenCommand(redisClient *c);
void lindexCommand(redisClient *c);
void lrangeCommand(redisClient *c);
+void lspliceCommand(redisClient *c);
void ltrimCommand(redisClient *c);
void typeCommand(redisClient *c);
void lsetCommand(redisClient *c);
View
58 src/t_list.c
@@ -553,6 +553,64 @@ void lrangeCommand(redisClient *c) {
}
}
+void lspliceCommand(redisClient *c) {
+ robj *o;
+ long start, end, llen, rangelen;
+ listNode *ln, *tmpln;
+ list *list;
+
+ if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != REDIS_OK) ||
+ (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != REDIS_OK)) return;
+
+ if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.ok)) == NULL ||
+ checkType(c,o,REDIS_LIST)) return;
+ llen = listTypeLength(o);
+
+ /* check for invalid indices */
+ if (start < -llen || end < -llen) {
+ addReply(c,shared.outofrangeerr);
+ return;
+ }
+
+ /* convert negative indices */
+ if (start < 0) start = llen+start;
+ if (end < 0) end = llen+end;
+
+ /* Invariant: start >= 0, so this test will be true when end < 0.
+ * The range is empty when start > end or start >= length. */
+ if (start > end || start >= llen) {
+ addReply(c,shared.ok);
+ return;
+ }
+ if (end >= llen) end = llen-1;
+ rangelen = (end-start)+1;
+
+ /* Remove list elements to perform the splice */
+ if (o->encoding == REDIS_ENCODING_ZIPLIST) {
+ o->ptr = ziplistDeleteRange(o->ptr,start,rangelen);
+ } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) {
+ list = o->ptr;
+
+ /* If we are nearest to the end of the list, reach the element
+ * starting from tail and going backward, as it is faster. */
+ if (start > llen/2) start -= llen;
+ ln = listIndex(list,start);
+
+ while(rangelen--) {
+ tmpln = ln->next;
+ listDelNode(list,ln);
+ ln = tmpln;
+ }
+ } else {
+ redisPanic("List encoding is not LINKEDLIST nor ZIPLIST!");
+ }
+
+ if (listTypeLength(o) == 0) dbDelete(c->db,c->argv[1]);
+ signalModifiedKey(c->db,c->argv[1]);
+ server.dirty++;
+ addReply(c,shared.ok);
+}
+
void ltrimCommand(redisClient *c) {
robj *o;
long start, end, llen, j, ltrim, rtrim;
View
40 tests/unit/type/list-2.tcl
@@ -41,4 +41,44 @@ start_server {
}
}
}
+
+ foreach {type large} [array get largevalue] {
+ tags {"slow"} {
+ test "LSPLICE stress testing - $type" {
+ set mylist {}
+ set startlen 32
+ r del mylist
+
+ # Start with the large value to ensure the
+ # right encoding is used.
+ r rpush mylist $large
+ lappend mylist $large
+
+ for {set i 0} {$i < $startlen} {incr i} {
+ set str [randomInt 9223372036854775807]
+ r rpush mylist $str
+ lappend mylist $str
+ }
+
+ for {set i 0} {$i < 1000} {incr i} {
+ set min [expr {int(rand()*$startlen)}]
+ set max [expr {$min+int(rand()*$startlen)}]
+ set lhs {}
+ if {$min > 0} {
+ set lhs [r lrange mylist 0 [expr $min-1]]
+ }
+ set rhs [r lrange mylist [expr $max+1] [r llen mylist]]
+ set expected [concat $lhs $rhs]
+ r lsplice mylist $min $max
+ assert_equal [r lrange mylist 0 [r llen mylist]] $expected
+
+ for {set j [r llen mylist]} {$j < $startlen} {incr j} {
+ set str [randomInt 9223372036854775807]
+ r rpush mylist $str
+ lappend mylist $str
+ }
+ }
+ }
+ }
+ }
}
View
56 tests/unit/type/list.tcl
@@ -694,6 +694,62 @@ start_server {
}
foreach {type large} [array get largevalue] {
+ proc splice_list {type min max} {
+ upvar 1 large large
+ r del mylist
+ create_$type mylist "1 2 3 4 $large"
+ r lsplice mylist $min $max
+ r lrange mylist 0 -1
+ }
+
+ test "LSPLICE start index equals end index - $type" {
+ assert_equal "2 3 4 $large" [splice_list $type 0 0]
+ assert_equal "1 3 4 $large" [splice_list $type 1 1]
+ assert_equal "1 2 4 $large" [splice_list $type 2 2]
+ assert_equal "1 2 3 $large" [splice_list $type 3 3]
+ assert_equal "1 2 3 4" [splice_list $type 4 4]
+ assert_equal "1 2 3 4" [splice_list $type -1 -1]
+ assert_equal "1 2 3 $large" [splice_list $type -2 -2]
+ assert_equal "1 2 4 $large" [splice_list $type -3 -3]
+ assert_equal "1 3 4 $large" [splice_list $type -4 -4]
+ assert_equal "2 3 4 $large" [splice_list $type -5 -5]
+ }
+
+ test "LSPLICE start index is greater than end index - $type" {
+ assert_equal "1 2 3 4 $large" [splice_list $type 3 2]
+ assert_equal "1 2 3 4 $large" [splice_list $type -1 2]
+ assert_equal "1 2 3 4 $large" [splice_list $type -1 0]
+ }
+
+ test "LSPLICE out of range negative start index - $type" {
+ assert_error ERR*range* {splice_list $type -1024 1}
+ }
+
+ test "LSPLICE out of range negative end index - $type" {
+ assert_error "ERR index out of range" {splice_list $type -1024 -1024}
+ }
+
+ test "LSPLICE remove everything - $type" {
+ assert_equal "" [splice_list $type 0 -1]
+ assert_equal "" [splice_list $type 0 4]
+ assert_equal "" [splice_list $type 0 1024]
+ assert_error "ERR index out of range" {splice_list $type -1024 1024}
+ }
+
+ test "LSPLICE basics - $type" {
+ assert_equal "3 4 $large" [splice_list $type 0 1]
+ assert_equal "4 $large" [splice_list $type 0 2]
+ assert_equal "$large" [splice_list $type 0 3]
+ assert_equal "1 4 $large" [splice_list $type 1 2]
+ assert_equal "1 $large" [splice_list $type 1 3]
+ assert_equal "1" [splice_list $type 1 4]
+ assert_equal "1 2 $large" [splice_list $type 2 3]
+ assert_equal "1 2" [splice_list $type 2 4]
+ assert_equal "1 2 3" [splice_list $type 3 4]
+ }
+ }
+
+ foreach {type large} [array get largevalue] {
test "LSET - $type" {
create_$type mylist "99 98 $large 96 95"
r lset mylist 1 foo
Something went wrong with that request. Please try again.