about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJed Davis <jld@panix.com>2012-09-01 12:11:54 -0700
committerJed Davis <jld@panix.com>2012-09-10 00:42:58 -0700
commit4ea45669b8d5b7017acd5555163e2a15e85da46c (patch)
treed2ff08bd9671d4db87fdd2ad0db74b3691d852a5
parente5cb6cc1237caeaa998a632c0dcf0bb067e6afef (diff)
Add vec::dedup for in-place consecutive duplicate element removal.
-rw-r--r--src/libcore/vec.rs81
1 files changed, 81 insertions, 0 deletions
diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs
index 746544a6afd..5572f9628e8 100644
--- a/src/libcore/vec.rs
+++ b/src/libcore/vec.rs
@@ -43,6 +43,7 @@ export grow;
 export grow_fn;
 export grow_set;
 export truncate;
+export dedup;
 export map;
 export mapi;
 export map2;
@@ -625,6 +626,41 @@ fn truncate<T>(&v: ~[const T], newlen: uint) {
     }
 }
 
+/**
+ * Remove consecutive repeated elements from a vector; if the vector is
+ * sorted, this removes all duplicates.
+ */
+fn dedup<T: Eq>(&v: ~[const T]) unsafe {
+    if v.len() < 1 { return; }
+    let mut last_written = 0, next_to_read = 1;
+    do as_const_buf(v) |p, ln| {
+        // We have a mutable reference to v, so we can make arbitrary changes.
+        // (cf. push and pop)
+        let p = p as *mut T;
+        // last_written < next_to_read <= ln
+        while next_to_read < ln {
+            // last_written < next_to_read < ln
+            if *ptr::mut_offset(p, next_to_read) ==
+                *ptr::mut_offset(p, last_written) {
+                let _dropped <- *ptr::mut_offset(p, next_to_read);
+            } else {
+                last_written += 1;
+                // last_written <= next_to_read < ln
+                if next_to_read != last_written {
+                    *ptr::mut_offset(p, last_written) <-
+                        *ptr::mut_offset(p, next_to_read);
+                }
+            }
+            // last_written <= next_to_read < ln
+            next_to_read += 1;
+            // last_written < next_to_read <= ln
+        }
+    }
+    // last_written < next_to_read == ln
+    unsafe::set_len(v, last_written + 1);
+}
+
+
 // Appending
 #[inline(always)]
 pure fn append<T: Copy>(+lhs: ~[T], rhs: &[const T]) -> ~[T] {
@@ -2219,6 +2255,51 @@ mod tests {
     }
 
     #[test]
+    fn test_dedup() {
+        fn case(-a: ~[uint], -b: ~[uint]) {
+            let mut v = a;
+            dedup(v);
+            assert(v == b);
+        }
+        case(~[], ~[]);
+        case(~[1], ~[1]);
+        case(~[1,1], ~[1]);
+        case(~[1,2,3], ~[1,2,3]);
+        case(~[1,1,2,3], ~[1,2,3]);
+        case(~[1,2,2,3], ~[1,2,3]);
+        case(~[1,2,3,3], ~[1,2,3]);
+        case(~[1,1,2,2,2,3,3], ~[1,2,3]);
+    }
+
+    #[test]
+    fn test_dedup_unique() {
+        let mut v0 = ~[~1, ~1, ~2, ~3];
+        dedup(v0);
+        let mut v1 = ~[~1, ~2, ~2, ~3];
+        dedup(v1);
+        let mut v2 = ~[~1, ~2, ~3, ~3];
+        dedup(v2);
+        /*
+         * If the ~pointers were leaked or otherwise misused, valgrind and/or
+         * rustrt should raise errors.
+         */
+    }
+
+    #[test]
+    fn test_dedup_shared() {
+        let mut v0 = ~[@1, @1, @2, @3];
+        dedup(v0);
+        let mut v1 = ~[@1, @2, @2, @3];
+        dedup(v1);
+        let mut v2 = ~[@1, @2, @3, @3];
+        dedup(v2);
+        /*
+         * If the @pointers were leaked or otherwise misused, valgrind and/or
+         * rustrt should raise errors.
+         */
+    }
+
+    #[test]
     fn test_map() {
         // Test on-stack map.
         let mut v = ~[1u, 2u, 3u];