nightly_quirks/
slice_partition_dedup.rs

1pub trait SlicePartitionDedup<T> {
2    fn nq_partition_dedup_by<F>(&mut self, same_bucket: F) -> (&mut [T], &mut [T])
3    where
4        F: FnMut(&mut T, &mut T) -> bool;
5
6    fn nq_partition_dedup(&mut self) -> (&mut [T], &mut [T])
7    where
8        T: PartialEq;
9}
10
11impl<T> SlicePartitionDedup<T> for [T] {
12    #[inline]
13    fn nq_partition_dedup_by<F>(&mut self, mut same_bucket: F) -> (&mut [T], &mut [T])
14    where
15        F: FnMut(&mut T, &mut T) -> bool,
16    {
17        // Although we have a mutable reference to `self`, we cannot make
18        // *arbitrary* changes. The `same_bucket` calls could panic, so we
19        // must ensure that the slice is in a valid state at all times.
20        //
21        // The way that we handle this is by using swaps; we iterate
22        // over all the elements, swapping as we go so that at the end
23        // the elements we wish to keep are in the front, and those we
24        // wish to reject are at the back. We can then split the slice.
25        // This operation is still `O(n)`.
26        //
27        // Example: We start in this state, where `r` represents "next
28        // read" and `w` represents "next_write".
29        //
30        //           r
31        //     +---+---+---+---+---+---+
32        //     | 0 | 1 | 1 | 2 | 3 | 3 |
33        //     +---+---+---+---+---+---+
34        //           w
35        //
36        // Comparing self[r] against self[w-1], this is not a duplicate, so
37        // we swap self[r] and self[w] (no effect as r==w) and then increment both
38        // r and w, leaving us with:
39        //
40        //               r
41        //     +---+---+---+---+---+---+
42        //     | 0 | 1 | 1 | 2 | 3 | 3 |
43        //     +---+---+---+---+---+---+
44        //               w
45        //
46        // Comparing self[r] against self[w-1], this value is a duplicate,
47        // so we increment `r` but leave everything else unchanged:
48        //
49        //                   r
50        //     +---+---+---+---+---+---+
51        //     | 0 | 1 | 1 | 2 | 3 | 3 |
52        //     +---+---+---+---+---+---+
53        //               w
54        //
55        // Comparing self[r] against self[w-1], this is not a duplicate,
56        // so swap self[r] and self[w] and advance r and w:
57        //
58        //                       r
59        //     +---+---+---+---+---+---+
60        //     | 0 | 1 | 2 | 1 | 3 | 3 |
61        //     +---+---+---+---+---+---+
62        //                   w
63        //
64        // Not a duplicate, repeat:
65        //
66        //                           r
67        //     +---+---+---+---+---+---+
68        //     | 0 | 1 | 2 | 3 | 1 | 3 |
69        //     +---+---+---+---+---+---+
70        //                       w
71        //
72        // Duplicate, advance r. End of slice. Split at w.
73
74        let len = self.len();
75        if len <= 1 {
76            return (self, &mut []);
77        }
78
79        let ptr = self.as_mut_ptr();
80        let mut next_read: usize = 1;
81        let mut next_write: usize = 1;
82
83        // SAFETY: the `while` condition guarantees `next_read` and `next_write`
84        // are less than `len`, thus are inside `self`. `prev_ptr_write` points to
85        // one element before `ptr_write`, but `next_write` starts at 1, so
86        // `prev_ptr_write` is never less than 0 and is inside the slice.
87        // This fulfils the requirements for dereferencing `ptr_read`, `prev_ptr_write`
88        // and `ptr_write`, and for using `ptr.add(next_read)`, `ptr.add(next_write - 1)`
89        // and `prev_ptr_write.offset(1)`.
90        //
91        // `next_write` is also incremented at most once per loop at most meaning
92        // no element is skipped when it may need to be swapped.
93        //
94        // `ptr_read` and `prev_ptr_write` never point to the same element. This
95        // is required for `&mut *ptr_read`, `&mut *prev_ptr_write` to be safe.
96        // The explanation is simply that `next_read >= next_write` is always true,
97        // thus `next_read > next_write - 1` is too.
98        unsafe {
99            // Avoid bounds checks by using raw pointers.
100            while next_read < len {
101                let ptr_read = ptr.add(next_read);
102                let prev_ptr_write = ptr.add(next_write - 1);
103                if !same_bucket(&mut *ptr_read, &mut *prev_ptr_write) {
104                    if next_read != next_write {
105                        let ptr_write = prev_ptr_write.add(1);
106                        std::mem::swap(&mut *ptr_read, &mut *ptr_write);
107                    }
108                    next_write += 1;
109                }
110                next_read += 1;
111            }
112        }
113
114        self.split_at_mut(next_write)
115    }
116
117    #[inline]
118    fn nq_partition_dedup(&mut self) -> (&mut [T], &mut [T])
119    where
120        T: PartialEq,
121    {
122        self.nq_partition_dedup_by(|a, b| a == b)
123    }
124}