slice_group_by/linear_group/
linear_group_by_key.rs

1use crate::offset_from;
2use std::slice::{from_raw_parts, from_raw_parts_mut};
3use std::{fmt, marker};
4
5macro_rules! group_by_key {
6    (struct $name:ident, $elem:ty, $mkslice:ident) => {
7        impl<'a, T: 'a, P> $name<'a, T, P> {
8            #[inline]
9            pub fn is_empty(&self) -> bool {
10                self.ptr == self.end
11            }
12
13            #[inline]
14            pub fn remainder_len(&self) -> usize {
15                unsafe { offset_from(self.end, self.ptr) }
16            }
17        }
18
19        impl<'a, T: 'a, F, K> std::iter::Iterator for $name<'a, T, F>
20        where
21            F: FnMut(&T) -> K,
22            K: PartialEq,
23        {
24            type Item = $elem;
25
26            fn next(&mut self) -> Option<Self::Item> {
27                if self.is_empty() {
28                    return None;
29                }
30
31                let mut i = 0;
32                let mut ptr = self.ptr;
33
34                // we use an unsafe block to avoid bounds checking here.
35                // this is safe because the only thing we do here is to get
36                // two elements at `ptr` and `ptr + 1`, bounds checking is done by hand.
37
38                // we need to get *two* contiguous elements so we check that:
39                //  - the first element is at the `end - 1` position because
40                //  - the second one will be read from `ptr + 1` that must
41                //    be lower or equal to `end`
42                unsafe {
43                    while ptr != self.end.sub(1) {
44                        let a = &*ptr;
45                        ptr = ptr.add(1);
46                        let b = &*ptr;
47
48                        i += 1;
49
50                        if (self.func)(a) != (self.func)(b) {
51                            let slice = $mkslice(self.ptr, i);
52                            self.ptr = ptr;
53                            return Some(slice);
54                        }
55                    }
56                }
57
58                // `i` is either `0` or the `slice length - 1` because either:
59                //  - we have not entered the loop and so `i` is equal to `0`
60                //    the slice length is necessarily `1` because we ensure it is not empty
61                //  - we have entered the loop and we have not early returned
62                //    so `i` is equal to the slice `length - 1`
63                let slice = unsafe { $mkslice(self.ptr, i + 1) };
64                self.ptr = self.end;
65                Some(slice)
66            }
67
68            fn size_hint(&self) -> (usize, Option<usize>) {
69                if self.is_empty() {
70                    return (0, Some(0));
71                }
72
73                let len = self.remainder_len();
74                (1, Some(len))
75            }
76
77            fn last(mut self) -> Option<Self::Item> {
78                self.next_back()
79            }
80        }
81
82        impl<'a, T: 'a, F, K> std::iter::DoubleEndedIterator for $name<'a, T, F>
83        where
84            F: FnMut(&T) -> K,
85            K: PartialEq,
86        {
87            fn next_back(&mut self) -> Option<Self::Item> {
88                // during the loop we retrieve two elements at `ptr` and `ptr - 1`.
89                if self.is_empty() {
90                    return None;
91                }
92
93                let mut i = 0;
94
95                unsafe {
96                    // we ensure that the first element that will be read
97                    // is not under `end` because `end` is out of bound.
98                    let mut ptr = self.end.sub(1);
99
100                    while ptr != self.ptr {
101                        // we first get `a` that is at the left of `ptr`
102                        // then `b` that is under the `ptr` position.
103                        let a = &*ptr.sub(1);
104                        let b = &*ptr;
105
106                        i += 1;
107
108                        if (self.func)(a) != (self.func)(b) {
109                            // the slice to return starts at the `ptr` position
110                            // and `i` is the length of it.
111                            let slice = $mkslice(ptr, i);
112
113                            // because `end` is always an invalid bound
114                            // we use `ptr` as `end` for the future call to `next`.
115                            self.end = ptr;
116                            return Some(slice);
117                        }
118
119                        ptr = ptr.sub(1);
120                    }
121                }
122
123                let slice = unsafe { $mkslice(self.ptr, i + 1) };
124                self.ptr = self.end;
125                Some(slice)
126            }
127        }
128
129        impl<'a, T: 'a, F, K> std::iter::FusedIterator for $name<'a, T, F>
130        where
131            F: FnMut(&T) -> K,
132            K: PartialEq,
133        {
134        }
135    };
136}
137
138/// An iterator that will return non-overlapping groups of equal elements
139/// in the slice using *linear/sequential search*.
140///
141/// It will give an element to the given function, producing a key and comparing
142/// the keys to determine groups.
143pub struct LinearGroupByKey<'a, T: 'a, F> {
144    ptr: *const T,
145    end: *const T,
146    func: F,
147    _phantom: marker::PhantomData<&'a T>,
148}
149
150impl<'a, T, F> LinearGroupByKey<'a, T, F> {
151    pub fn new(slice: &'a [T], func: F) -> Self {
152        LinearGroupByKey {
153            ptr: slice.as_ptr(),
154            end: unsafe { slice.as_ptr().add(slice.len()) },
155            func,
156            _phantom: marker::PhantomData,
157        }
158    }
159}
160
161impl<'a, T: 'a, F> LinearGroupByKey<'a, T, F> {
162    /// Returns the remainder of the original slice that is going to be
163    /// returned by the iterator.
164    pub fn remainder(&self) -> &[T] {
165        let len = self.remainder_len();
166        unsafe { from_raw_parts(self.ptr, len) }
167    }
168}
169
170impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for LinearGroupByKey<'a, T, P> {
171    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
172        f.debug_struct("LinearGroupByKey")
173            .field("remainder", &self.remainder())
174            .finish()
175    }
176}
177
178group_by_key! { struct LinearGroupByKey, &'a [T], from_raw_parts }
179
180/// An iterator that will return non-overlapping *mutable* groups in the slice
181/// using *linear/sequential search*.
182///
183/// It will give an element to the given function, producing a key and comparing
184/// the keys to determine groups.
185pub struct LinearGroupByKeyMut<'a, T: 'a, F> {
186    ptr: *mut T,
187    end: *mut T,
188    func: F,
189    _phantom: marker::PhantomData<&'a mut T>,
190}
191
192impl<'a, T, F> LinearGroupByKeyMut<'a, T, F> {
193    pub fn new(slice: &'a mut [T], func: F) -> Self {
194        let ptr = slice.as_mut_ptr();
195        let end = unsafe { ptr.add(slice.len()) };
196        LinearGroupByKeyMut {
197            ptr,
198            end,
199            func,
200            _phantom: marker::PhantomData,
201        }
202    }
203}
204
205impl<'a, T: 'a, F> LinearGroupByKeyMut<'a, T, F> {
206    /// Returns the remainder of the original slice that is going to be
207    /// returned by the iterator.
208    pub fn into_remainder(self) -> &'a mut [T] {
209        let len = self.remainder_len();
210        unsafe { from_raw_parts_mut(self.ptr, len) }
211    }
212}
213
214impl<'a, T: 'a + fmt::Debug, F> fmt::Debug for LinearGroupByKeyMut<'a, T, F> {
215    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
216        let len = self.remainder_len();
217        let remainder = unsafe { from_raw_parts(self.ptr, len) };
218
219        f.debug_struct("LinearGroupByKeyMut")
220            .field("remainder", &remainder)
221            .finish()
222    }
223}
224
225group_by_key! { struct LinearGroupByKeyMut, &'a mut [T], from_raw_parts_mut }