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}