Skip to main content

diskann_utils/views/rowmajor/
iter.rs

1/*
2 * Copyright (c) Microsoft Corporation.
3 * Licensed under the MIT license.
4 */
5
6use std::{marker::PhantomData, num::NonZeroUsize, ptr::NonNull};
7
8#[cfg(feature = "rayon")]
9use crate::views::rowmajor::MatrixMut;
10use crate::views::rowmajor::{Layout, Matrix, Mut, Ref};
11
12//------//
13// Rows //
14//------//
15
16/// An iterator over rows in a matrix. See: [`Matrix::rows`].
17#[derive(Debug)]
18pub struct Rows<'a, T> {
19    ptr: NonNull<T>,
20    remaining: usize,
21    ncols: usize,
22    _lifetime: PhantomData<&'a [T]>,
23}
24
25impl<'a, T> Rows<'a, T> {
26    pub(super) fn new(m: Ref<'a, T>) -> Self {
27        let layout = m.layout();
28        Self {
29            ptr: m.as_nonnull(),
30            remaining: layout.nrows(),
31            ncols: layout.ncols(),
32            _lifetime: PhantomData,
33        }
34    }
35}
36
37// SAFETY: `Rows<'_, T>` owns a shared slice borrow, so sending it requires `T: Sync`.
38unsafe impl<T> Send for Rows<'_, T> where T: Sync {}
39// SAFETY: Shared access to `Rows<'_, T>` exposes only shared access to `T`.
40unsafe impl<T> Sync for Rows<'_, T> where T: Sync {}
41
42impl<'a, T> Iterator for Rows<'a, T> {
43    type Item = &'a [T];
44    fn next(&mut self) -> Option<&'a [T]> {
45        self.remaining.checked_sub(1).map(|remaining| {
46            // SAFETY: Construction from a valid `Ref` guarantees that each remaining row
47            // contains `ncols` initialized elements beginning at `self.ptr`.
48            let item =
49                unsafe { std::slice::from_raw_parts(self.ptr.as_ptr().cast_const(), self.ncols) };
50            self.remaining = remaining;
51
52            // SAFETY: Advancing by one row remains within or one past the original matrix
53            // span. The validated parent layout guarantees that the offset is representable.
54            self.ptr = unsafe { self.ptr.add(self.ncols) };
55            item
56        })
57    }
58
59    fn size_hint(&self) -> (usize, Option<usize>) {
60        (self.remaining, Some(self.remaining))
61    }
62}
63
64impl<T> ExactSizeIterator for Rows<'_, T> {}
65impl<T> std::iter::FusedIterator for Rows<'_, T> {}
66
67//---------//
68// RowsMut //
69//---------//
70
71/// An iterator over mutable rows in a matrix. See: [`crate::views::rowmajor::MatrixMut::rows_mut`].
72#[derive(Debug)]
73pub struct RowsMut<'a, T> {
74    ptr: NonNull<T>,
75    remaining: usize,
76    ncols: usize,
77    _lifetime: PhantomData<&'a mut [T]>,
78}
79
80impl<'a, T> RowsMut<'a, T> {
81    pub(super) fn new(m: Mut<'a, T>) -> Self {
82        let layout = m.layout();
83        Self {
84            ptr: m.as_nonnull(),
85            remaining: layout.nrows(),
86            ncols: layout.ncols(),
87            _lifetime: PhantomData,
88        }
89    }
90}
91
92// SAFETY: `RowsMut<'_, T>` owns an exclusive slice borrow, so sending it requires `T: Send`.
93unsafe impl<T> Send for RowsMut<'_, T> where T: Send {}
94// SAFETY: Shared access to `RowsMut<'_, T>` exposes only shared access to `T`.
95unsafe impl<T> Sync for RowsMut<'_, T> where T: Sync {}
96
97impl<'a, T> Iterator for RowsMut<'a, T> {
98    type Item = &'a mut [T];
99    fn next(&mut self) -> Option<&'a mut [T]> {
100        self.remaining.checked_sub(1).map(|remaining| {
101            // SAFETY: Construction from a valid `Mut` guarantees that each remaining row
102            // contains `ncols` initialized elements beginning at `self.ptr`. Advancing the
103            // pointer after every yield makes nonempty returned rows disjoint; zero-length
104            // rows do not access memory and may share an address.
105            let item = unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.ncols) };
106            self.remaining = remaining;
107
108            // SAFETY: Advancing by one row remains within or one past the original matrix
109            // span. The validated parent layout guarantees that the offset is representable.
110            self.ptr = unsafe { self.ptr.add(self.ncols) };
111            item
112        })
113    }
114
115    fn size_hint(&self) -> (usize, Option<usize>) {
116        (self.remaining, Some(self.remaining))
117    }
118}
119
120impl<T> ExactSizeIterator for RowsMut<'_, T> {}
121impl<T> std::iter::FusedIterator for RowsMut<'_, T> {}
122
123//---------//
124// Windows //
125//---------//
126
127/// An iterator over rows in a matrix. See: [`Matrix::window_iter`].
128#[derive(Debug)]
129pub struct Windows<'a, T> {
130    ptr: NonNull<T>,
131    remaining: usize,
132    batchsize: NonZeroUsize,
133    ncols: usize,
134    _lifetime: PhantomData<&'a [T]>,
135}
136
137impl<'a, T> Windows<'a, T> {
138    pub(super) fn new(m: Ref<'a, T>, batchsize: NonZeroUsize) -> Self {
139        let layout = m.layout();
140        Self {
141            ptr: m.as_nonnull(),
142            remaining: layout.nrows(),
143            batchsize,
144            ncols: layout.ncols(),
145            _lifetime: PhantomData,
146        }
147    }
148}
149
150// SAFETY: `Windows<'_, T>` owns a shared slice borrow, so sending it requires `T: Sync`.
151unsafe impl<T> Send for Windows<'_, T> where T: Sync {}
152// SAFETY: Shared access to `Windows<'_, T>` exposes only shared access to `T`.
153unsafe impl<T> Sync for Windows<'_, T> where T: Sync {}
154
155impl<'a, T> Iterator for Windows<'a, T> {
156    type Item = Ref<'a, T>;
157    fn next(&mut self) -> Option<Ref<'a, T>> {
158        if self.remaining == 0 {
159            None
160        } else {
161            let next_remaining = self.remaining.saturating_sub(self.batchsize.get());
162            let nrows = self.remaining - next_remaining;
163
164            // SAFETY: `self.ptr` starts the remaining suffix of a valid `Ref`, and `nrows`
165            // does not exceed that suffix. Keeping the parent's column count therefore
166            // produces a valid subview and a layout no larger than the parent layout.
167            let window = unsafe {
168                Ref {
169                    ptr: self.ptr,
170                    layout: Layout::new_unchecked(nrows, self.ncols),
171                    _lifetime: PhantomData,
172                }
173            };
174
175            // SAFETY: Advancing by the yielded window remains within or one past the
176            // original matrix span. The validated parent layout guarantees that the
177            // multiplication and pointer offset are representable.
178            self.ptr = unsafe { self.ptr.add(nrows * self.ncols) };
179            self.remaining = next_remaining;
180            Some(window)
181        }
182    }
183
184    fn size_hint(&self) -> (usize, Option<usize>) {
185        let remaining = self.remaining.div_ceil(self.batchsize.get());
186        (remaining, Some(remaining))
187    }
188}
189
190impl<T> ExactSizeIterator for Windows<'_, T> {}
191impl<T> std::iter::FusedIterator for Windows<'_, T> {}
192
193//--------//
194// ParMut //
195//--------//
196
197#[cfg(feature = "rayon")]
198/// Carries an exclusive matrix borrow across Rayon workers.
199pub(super) struct ParMut<'a, T> {
200    ptr: NonNull<T>,
201    layout: Layout<T>,
202    _lifetime: PhantomData<&'a mut [T]>,
203}
204
205// SAFETY: `ParMut` owns an exclusive slice borrow, so sending it requires `T: Send`.
206#[cfg(feature = "rayon")]
207unsafe impl<T: Send> Send for ParMut<'_, T> {}
208// SAFETY: The only methods that produce mutable views are unsafe and require callers to
209// ensure disjointness. Sending those views between workers requires `T: Send`.
210#[cfg(feature = "rayon")]
211unsafe impl<T: Send> Sync for ParMut<'_, T> {}
212
213#[cfg(feature = "rayon")]
214impl<'a, T> ParMut<'a, T> {
215    pub(super) fn new<M>(matrix: &'a mut M) -> Self
216    where
217        M: MatrixMut<Element = T> + ?Sized,
218    {
219        let layout = matrix.layout();
220        let ptr = matrix.as_nonnull_mut();
221        Self {
222            ptr,
223            layout,
224            _lifetime: PhantomData,
225        }
226    }
227
228    pub(super) fn nrows(&self) -> usize {
229        self.layout.nrows()
230    }
231
232    /// # Safety
233    ///
234    /// * `row < self.nrows()`.
235    /// * No other live reference derived from this `ParMut` may overlap any element of
236    ///   row `row`. Zero-column rows never overlap, even when their addresses match.
237    pub(super) unsafe fn row_disjoint_unchecked(&self, row: usize) -> &'a mut [T] {
238        debug_assert!(row < self.layout.nrows());
239        let ncols = self.layout.ncols();
240
241        // SAFETY: The caller guarantees that `row` is in-bounds and does not overlap any
242        // other live view. The validated parent layout makes the offset representable and
243        // places the row within the initialized matrix span.
244        unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr().add(row * ncols), ncols) }
245    }
246
247    /// # Safety
248    ///
249    /// * `rows.start <= rows.end <= self.nrows()`.
250    /// * No other live reference derived from this `ParMut` may overlap any element in
251    ///   `rows`. Zero-column windows never overlap.
252    pub(super) unsafe fn window_disjoint_unchecked(
253        &self,
254        rows: std::ops::Range<usize>,
255    ) -> Mut<'a, T> {
256        debug_assert!(rows.start <= rows.end);
257        debug_assert!(rows.end <= self.layout.nrows());
258
259        let ncols = self.layout.ncols();
260        let nrows = rows.end - rows.start;
261
262        // SAFETY: The caller guarantees an ordered, in-bounds range. The validated parent
263        // layout makes the offset representable and places it within or one past the matrix.
264        let ptr = unsafe { self.ptr.add(rows.start * ncols) };
265
266        Mut {
267            ptr,
268            // SAFETY: This window has no more rows than the validated parent and keeps its
269            // column count, so its element count and byte span cannot exceed the parent.
270            layout: unsafe { Layout::new_unchecked(nrows, ncols) },
271            _lifetime: PhantomData,
272        }
273    }
274}
275
276#[cfg(all(test, feature = "rayon"))]
277mod tests {
278    use super::ParMut;
279    use crate::views::rowmajor::{Matrix, MatrixMut, Owned};
280
281    #[test]
282    fn par_mut_zero_column_views_can_coexist() {
283        let mut matrix = Owned::from_element(usize::MAX, 0, 0);
284        let ptr = matrix.as_ptr();
285        let matrix = ParMut::new(&mut matrix);
286
287        // SAFETY: Empty views do not overlap any elements, even when their addresses match.
288        let rows = unsafe {
289            [
290                matrix.row_disjoint_unchecked(0),
291                matrix.row_disjoint_unchecked(1),
292                matrix.row_disjoint_unchecked(usize::MAX - 1),
293            ]
294        };
295        // SAFETY: Empty windows do not overlap any elements, including the live row views
296        // whose logical rows fall within these windows.
297        let windows = unsafe {
298            [
299                matrix.window_disjoint_unchecked(0..2),
300                matrix.window_disjoint_unchecked(2..usize::MAX),
301            ]
302        };
303
304        assert!(rows.iter().all(|row| row.is_empty() && row.as_ptr() == ptr));
305        assert!(windows.iter().all(|window| {
306            window.ncols() == 0 && window.as_slice().is_empty() && window.as_ptr() == ptr
307        }));
308    }
309
310    #[test]
311    fn par_mut_disjoint_nonempty_views_can_coexist() {
312        let mut matrix = Owned::from_fn(6, 2, |rc| rc.row * 100 + rc.col);
313        {
314            let matrix = ParMut::new(&mut matrix);
315
316            // SAFETY: These rows and windows are in-bounds and pairwise disjoint.
317            let (row0, row1, mut window2, mut window4) = unsafe {
318                (
319                    matrix.row_disjoint_unchecked(0),
320                    matrix.row_disjoint_unchecked(1),
321                    matrix.window_disjoint_unchecked(2..4),
322                    matrix.window_disjoint_unchecked(4..6),
323                )
324            };
325            row0[0] = 10;
326            row1[1] = 11;
327
328            let mut window2_rows: Vec<_> = window2.rows_mut().collect();
329            let mut window4_rows: Vec<_> = window4.rows_mut().collect();
330            window2_rows[0][0] = 20;
331            window2_rows[1][1] = 31;
332            window4_rows[0][0] = 40;
333            window4_rows[1][1] = 51;
334        }
335
336        assert_eq!(
337            matrix.as_slice(),
338            &[10, 1, 100, 11, 20, 201, 300, 31, 40, 401, 500, 51]
339        );
340    }
341}