matrix_slice/
lib.rs

1//! Implements references to blocks of a matrix.
2//!
3//! Consider a reference to a slice (sometimes also just called slice). Typically, it is created by
4//! unsizing a reference to an array through type coercion, such as sing `&[0, 1, 2]` in a
5//! parameter to function that takes `&[u32]` which turns a reference `&[u32; 3]` to a slice
6//! `&[u32]`. The length of the slice, controlling the number of elements and thus the provenance
7//! of access that the reference allows, is stored in a tag alongside the pointer to its elements
8//! and initialized from the known length of the array. Since this tag is a runtime value we can
9//! manipulate it while upholding the invariants required by the type system.
10//!
11//! This is analogous to that but for blocks of a matrix. A block is a rectangular region of a
12//! matrix where the matrix provides an underlying pitch (or stride) between rows and a total
13//! number of elements and the block the number of rows and columns that are spanned, i.e. are
14//! allowed to be accessed by (mutable) reference.
15//!
16//! ## Treatment of empty blocks
17//!
18//! A block may have zero rows or zero columns. In either case the block is empty and provides no
19//! access to any elements yet will still return an empty slice for some operations that would
20//! otherwise access multiple elements. The memory address of such a block is **not** necessarily
21//! at its expected location but it will be in-bounds of the underlying matrix data.
22//!
23//! Consider the bottom right `2x2` block of a row-major `3x3` matrix.
24//!
25//! ```text
26//! +---+---+---+
27//! | x | x | x |
28//! +---+---+---+
29//! | x | 4 | 5 |
30//! +---+---+---+
31//! | x | 7 | 8 |
32//! +---+---+---+
33//! ```
34//!
35//! This block has a pitch of 3 but only spans 2 columns. If we would naively calculate the address
36//! of its past-the-end element we would get an element below `7` which is out-of-bounds. Hence if
37//! we split this at row 2, into itself and an empty `0x2` block, the latter block's data pointer
38//! would be created with undefined behavior. Instead, we sacrifice the ability to 'locate' such
39//! empty blocks and instead have them point at an arbitrary (empty) in-bounds slice within the
40//! matrix. (Currently, that is the start of the block from which it was created).
41#![no_std]
42use core::{cell::Cell, fmt, marker::PhantomData, ops, ptr::NonNull};
43
44/// Create a block reference from a full matrix represented as an array of rows.
45///
46/// # Examples
47///
48/// ```
49/// let data = &mut [
50///    [0, 1, 2],
51///    [3, 4, 5],
52/// ];
53///
54/// let mut block = matrix_slice::from_array_rows(data);
55///
56/// assert_eq!(block.rows(), 2);
57/// assert_eq!(block.cols(), 3);
58///
59/// assert_eq!(block[(1, 1)], 4);
60/// ```
61pub fn from_array_rows<'a, T, const N: usize>(data: &'a [[T; N]]) -> BlockRef<'a, T> {
62    BlockRef {
63        block: BlockSlice {
64            rows: data.len(),
65            cols: N,
66            pitch: N,
67        },
68        data: NonNull::from_ref(data).cast(),
69        lifetime: PhantomData,
70    }
71}
72
73/// A reference to a block of a matrix with shared access to elements.
74#[derive(Copy, Clone)]
75pub struct BlockRef<'a, T> {
76    data: NonNull<T>,
77    block: BlockSlice,
78    lifetime: PhantomData<&'a [T]>,
79}
80
81// SAFETY: See `&[T]`. The reference can be used to, potentially, get a `&T` for each element in
82// the block and thus the block itself provides the exact same properties as `T`. The `BlockRef` is
83// then `&[T]` itself and thus has properties of a reference to such a type. Refer to the
84// reference: <https://doc.rust-lang.org/stable/std/primitive.reference.html>
85//
86// We have `&T: Sync` iff `T: Sync`
87unsafe impl<T> Sync for BlockRef<'_, T> where T: Sync {}
88// We have `&T: Send` iff `T: Sync`
89unsafe impl<T> Send for BlockRef<'_, T> where T: Sync {}
90
91const _: () = {
92    // We can coerce a block to a shorter lifetime.
93    fn _coerce_block<'a, 'b: 'a, T>(v: BlockRef<'b, T>) -> BlockRef<'a, T> {
94        v
95    }
96
97    // We can coerce a reference to a block to a shorter lifetime.
98    fn _coerce_covariant<'lt, 'a, 'b: 'a, T>(v: &'lt BlockRef<'b, T>) -> &'lt BlockRef<'a, T> {
99        v
100    }
101
102    fn _coerce_covariant_fn<'lt, 'a, 'b: 'a, T>(v: fn(BlockRef<'a, T>)) -> fn(BlockRef<'b, T>) {
103        v
104    }
105
106    fn _coerce_item_covariant<'lt, 'a, 'b: 'a, T>(v: BlockRef<'lt, &'b T>) -> BlockRef<'lt, &'a T> {
107        v
108    }
109};
110
111/// Creates an empty block reference, within a matrix of a dangling slice.
112impl<T> Default for BlockRef<'_, T> {
113    fn default() -> Self {
114        from_array_rows::<T, 0>(&[])
115    }
116}
117
118impl<'data, T> BlockRef<'data, T> {
119    /// Create a new block reference from a raw slice and pitch.
120    ///
121    /// The resulting block refers to the whole matrix.
122    ///
123    /// # Panics
124    ///
125    /// Panics if the length of `data` is not a multiple of `pitch`.
126    pub fn new(data: &'data [T], pitch: usize) -> Self {
127        assert!(data.len().is_multiple_of(pitch));
128
129        BlockRef {
130            block: BlockSlice {
131                rows: data.len() / pitch,
132                cols: pitch,
133                pitch,
134            },
135            data: NonNull::from_ref(data).cast(),
136            lifetime: PhantomData,
137        }
138    }
139
140    /// Number of rows in this block.
141    pub fn rows(&self) -> usize {
142        self.block.rows
143    }
144
145    /// Number of columns in this block.
146    pub fn cols(&self) -> usize {
147        self.block.cols
148    }
149
150    /// Divide into two blocks at the given column.
151    ///
152    /// # Examples
153    ///
154    /// ```
155    /// let data = &[
156    ///     [0, 1, 2],
157    ///     [3, 4, 5],
158    /// ];
159    ///
160    /// let block = matrix_slice::from_array_rows(data);
161    /// let (left, right) = block.split_at_col(2);
162    ///
163    /// assert_eq!(left[(1, 0)], 3);
164    /// assert_eq!(right[(1, 0)], 5);
165    /// ```
166    pub fn split_at_col(self, mid: usize) -> (BlockRef<'data, T>, BlockRef<'data, T>) {
167        self.split_at_col_checked(mid).unwrap()
168    }
169
170    /// Divide into two blocks at the given column.
171    ///
172    /// See [`Self::split_at_col`] but returns `None` if out of bounds.
173    pub fn split_at_col_checked(
174        self,
175        mid: usize,
176    ) -> Option<(BlockRef<'data, T>, BlockRef<'data, T>)> {
177        if let Some((lhs, rhs, offset)) = self.block.split_at_col(mid) {
178            Some((
179                BlockRef {
180                    data: self.data,
181                    block: lhs,
182                    lifetime: self.lifetime,
183                },
184                BlockRef {
185                    data: unsafe { self.data.add(offset) },
186                    block: rhs,
187                    lifetime: self.lifetime,
188                },
189            ))
190        } else {
191            None
192        }
193    }
194
195    /// Divide into two blocks at the given row.
196    ///
197    /// # Examples
198    ///
199    /// ```
200    /// let data = &[
201    ///     [0, 1, 2],
202    ///     [3, 4, 5],
203    /// ];
204    ///
205    /// let block = matrix_slice::from_array_rows(data);
206    /// let (top, bot) = block.split_at_row(1);
207    ///
208    /// assert_eq!(top[(0, 2)], 2);
209    /// assert_eq!(bot[(0, 2)], 5);
210    /// ```
211    pub fn split_at_row(self, mid: usize) -> (BlockRef<'data, T>, BlockRef<'data, T>) {
212        self.split_at_row_checked(mid).unwrap()
213    }
214
215    /// Divide into two blocks at the given row.
216    ///
217    /// See [`Self::split_at_row`] but returns `None` if out of bounds.
218    pub fn split_at_row_checked(
219        self,
220        mid: usize,
221    ) -> Option<(BlockRef<'data, T>, BlockRef<'data, T>)> {
222        if let Some((lhs, rhs, offset)) = self.block.split_at_row(mid) {
223            Some((
224                BlockRef {
225                    data: self.data,
226                    block: lhs,
227                    lifetime: self.lifetime,
228                },
229                BlockRef {
230                    data: unsafe { self.data.add(offset) },
231                    block: rhs,
232                    lifetime: self.lifetime,
233                },
234            ))
235        } else {
236            None
237        }
238    }
239
240    /// Choose a range of rows and contract the block to that.
241    ///
242    /// The argument type is flexible, allowing ranges (`1..3`), half open ranges (`2..` and `..2`)
243    /// among others. See the [`MatrixIndex`] trait, which is sealed though as its details are not
244    /// yet finalized.
245    ///
246    /// # Examples
247    ///
248    /// ```
249    /// let data = &[
250    ///     [0, 1, 2],
251    ///     [3, 4, 5],
252    ///     [6, 7, 8],
253    /// ];
254    ///
255    /// let block = matrix_slice::from_array_rows(data);
256    ///
257    /// let center = block.select_rows(1..2).unwrap();
258    /// assert_eq!(center.rows(), 1);
259    /// assert_eq!(center.cols(), 3);
260    /// assert_eq!(center[(0, 1)], 4);
261    /// ```
262    pub fn select_rows<R>(self, range: R) -> Option<BlockRef<'data, T>>
263    where
264        R: MatrixIndex,
265    {
266        let (start, len) = range.into_start_and_len(self.block.rows)?;
267        let (_, block, offset) = self.block.split_at_row(start)?;
268        assert!(block.rows >= len);
269
270        Some(BlockRef {
271            block: BlockSlice { rows: len, ..block },
272            data: unsafe { self.data.add(offset) },
273            lifetime: self.lifetime,
274        })
275    }
276
277    /// Choose a range of columns and contract the block to that.
278    ///
279    /// The argument type is flexible, allowing ranges (`1..3`), half open ranges (`2..` and `..2`)
280    /// among others. See the [`MatrixIndex`] trait, which is sealed though as its details are not
281    /// yet finalized.
282    pub fn select_cols<R>(self, range: R) -> Option<BlockRef<'data, T>>
283    where
284        R: MatrixIndex,
285    {
286        let (start, len) = range.into_start_and_len(self.block.rows)?;
287        let (_, block, offset) = self.block.split_at_col(start)?;
288        assert!(block.cols >= len);
289
290        Some(BlockRef {
291            block: BlockSlice { cols: len, ..block },
292            data: unsafe { self.data.add(offset) },
293            lifetime: self.lifetime,
294        })
295    }
296
297    /// Choose a sub-block by its range of rows and columns.
298    pub fn select(
299        self,
300        row_range: impl MatrixIndex,
301        col_range: impl MatrixIndex,
302    ) -> Option<BlockRef<'data, T>> {
303        let block = self.select_rows(row_range)?;
304        block.select_cols(col_range)
305    }
306
307    /// Extract a contiguous underlying slice of elements if the block is contiguous.
308    ///
309    /// # Examples
310    ///
311    /// ```
312    /// let data = &[[0u32; 3]; 3];
313    /// let block = matrix_slice::from_array_rows(data);
314    ///
315    /// let (block, _) = block.split_at_row(2);
316    /// assert!(block.into_contiguous_slice().is_some());
317    ///
318    /// let (pre, post) = block.split_at_col(2);
319    /// assert!(pre.into_contiguous_slice().is_none());
320    /// assert!(post.into_contiguous_slice().is_none());
321    ///
322    /// let (same, _) = block.split_at_col(3);
323    /// assert!(same.into_contiguous_slice().is_some());
324    /// ```
325    pub fn into_contiguous_slice(self) -> Option<&'data [T]> {
326        if let Some(items) = self.block.contiguous_span() {
327            Some(unsafe { core::slice::from_raw_parts(self.data.as_ptr().cast(), items) })
328        } else {
329            None
330        }
331    }
332
333    /// Turn this into a slice of the first row, assuming it is at most one row.
334    fn fake_contiguity(mut self) -> &'data [T] {
335        self.block.fake_contiguity();
336        self.into_contiguous_slice().unwrap()
337    }
338
339    /// Extract access as a slice of arrays if the block is contiguous.
340    ///
341    /// The caller must choose `N` matching the number of columns.
342    pub fn into_array_rows_checked<const N: usize>(self) -> Option<&'data [[T; N]]> {
343        if self.block.cols == self.block.pitch && self.block.cols == N {
344            Some(unsafe { core::slice::from_raw_parts(self.data.as_ptr().cast(), self.block.rows) })
345        } else {
346            None
347        }
348    }
349
350    /// Iterate over the rows of this block.
351    pub fn iter_rows(self) -> IterRows<'data, T> {
352        IterRows { block: self }
353    }
354
355    /// Create a reference to this block with a shorter lifetime.
356    pub fn reborrow(&self) -> BlockRef<'_, T> {
357        BlockRef {
358            data: self.data,
359            block: self.block,
360            lifetime: PhantomData,
361        }
362    }
363}
364
365impl<T> ops::Index<(usize, usize)> for BlockRef<'_, T> {
366    type Output = T;
367
368    fn index(&self, index: (usize, usize)) -> &Self::Output {
369        let idx = self.block.in_bounds_index(index.0, index.1);
370        // SAFETY: Index is bounded by `total_span` which itself is a lower estimate of the
371        // provenance of the pointer.
372        unsafe { &*self.data.as_ptr().add(idx) }
373    }
374}
375
376impl<T: fmt::Debug> fmt::Debug for BlockRef<'_, T> {
377    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
378        f.debug_list().entries(self.reborrow().iter_rows()).finish()
379    }
380}
381
382/// Create a mutable block reference from a full matrix represented as an array of rows.
383///
384/// # Examples
385///
386/// ```
387/// let data = &mut [
388///    [0, 1, 2],
389///    [3, 4, 5],
390/// ];
391///
392/// let mut block = matrix_slice::from_array_rows_mut(data);
393///
394/// assert_eq!(block.rows(), 2);
395/// assert_eq!(block.cols(), 3);
396///
397/// block[(1, 1)] = 42;
398///
399/// assert_eq!(data[1][1], 42);
400/// ```
401pub fn from_array_rows_mut<'a, T, const N: usize>(data: &'a mut [[T; N]]) -> BlockMut<'a, T> {
402    BlockMut {
403        block: BlockSlice {
404            rows: data.len(),
405            cols: N,
406            pitch: N,
407        },
408        data: NonNull::from_mut(data).cast(),
409        lifetime: PhantomData,
410    }
411}
412
413/// A reference to a block of a matrix with unique access to elements.
414pub struct BlockMut<'a, T> {
415    data: NonNull<T>,
416    block: BlockSlice,
417    lifetime: PhantomData<&'a mut [T]>,
418}
419
420// SAFETY: See `BlockRef` but with `&mut [T]`.
421//
422// We have `&mut T: Sync` iff `T: Sync`
423unsafe impl<T> Sync for BlockMut<'_, T> where T: Sync {}
424// We have `&mut T: Send` iff `T: Send`
425unsafe impl<T> Send for BlockMut<'_, T> where T: Sync {}
426
427/// ```compile_fail
428/// use matrix_slice::BlockMut;
429///
430/// // This coercion must *not* be possible. The field `lifetime` ensures the right variance.
431/// fn _coerce_item_not_covariant<'lt, 'a, 'b: 'a, T>(
432///     v: BlockMut<'lt, &'b T>,
433/// ) -> BlockMut<'lt, &'a T> {
434///     v
435/// //  ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
436/// }
437///
438/// ```compile_fail
439/// use matrix_slice::BlockMut;
440///
441/// fn _copy_block(v: BlockMut<'_, u32>) -> [BlockMut<'_, u32>; 2] {
442///    [v, v]
443/// }
444const _: () = {
445    // We can coerce a block to a shorter lifetime.
446    fn _coerce_block_mut<'a, 'b: 'a, T>(v: BlockMut<'b, T>) -> BlockMut<'a, T> {
447        v
448    }
449
450    // We can coerce a reference to a block to a shorter lifetime.
451    fn _coerce_covariant<'lt, 'a, 'b: 'a, T>(v: &'lt BlockMut<'b, T>) -> &'lt BlockMut<'a, T> {
452        v
453    }
454
455    fn _coerce_covariant_fn<'lt, 'a, 'b: 'a, T>(v: fn(BlockMut<'a, T>)) -> fn(BlockMut<'b, T>) {
456        v
457    }
458};
459
460/// Creates an empty block reference, within a matrix of a dangling slice.
461impl<T> Default for BlockMut<'_, T> {
462    fn default() -> Self {
463        from_array_rows_mut::<T, 0>(&mut [])
464    }
465}
466
467impl<'data, T> BlockMut<'data, T> {
468    /// Create a new block reference from a raw slice and pitch.
469    ///
470    /// The resulting block refers to the whole matrix.
471    ///
472    /// # Panics
473    ///
474    /// Panics if the length of `data` is not a multiple of `pitch`.
475    pub fn new(data: &'data mut [T], pitch: usize) -> Self {
476        assert!(data.len().is_multiple_of(pitch));
477
478        BlockMut {
479            block: BlockSlice {
480                rows: data.len() / pitch,
481                cols: pitch,
482                pitch,
483            },
484            data: NonNull::from_ref(data).cast(),
485            lifetime: PhantomData,
486        }
487    }
488
489    /// Number of rows in this block.
490    pub fn rows(&self) -> usize {
491        self.block.rows
492    }
493
494    /// Number of columns in this block.
495    pub fn cols(&self) -> usize {
496        self.block.cols
497    }
498
499    /// Divide into two blocks at the given column.
500    ///
501    /// # Examples
502    ///
503    /// ```
504    /// let data = &mut [
505    ///     [0, 1, 2],
506    ///     [3, 4, 5],
507    /// ];
508    ///
509    /// let block = matrix_slice::from_array_rows_mut(data);
510    /// let (left, right) = block.split_at_col(2);
511    ///
512    /// assert_eq!(left[(1, 0)], 3);
513    /// assert_eq!(right[(1, 0)], 5);
514    /// ```
515    pub fn split_at_col(self, mid: usize) -> (BlockMut<'data, T>, BlockMut<'data, T>) {
516        self.split_at_col_checked(mid).unwrap()
517    }
518
519    /// Divide into two blocks at the given column.
520    ///
521    /// See [`Self::split_at_col`] but returns `None` if out of bounds.
522    pub fn split_at_col_checked(
523        self,
524        mid: usize,
525    ) -> Option<(BlockMut<'data, T>, BlockMut<'data, T>)> {
526        if let Some((lhs, rhs, offset)) = self.block.split_at_col(mid) {
527            Some((
528                BlockMut {
529                    data: self.data,
530                    block: lhs,
531                    lifetime: self.lifetime,
532                },
533                BlockMut {
534                    data: unsafe { self.data.add(offset) },
535                    block: rhs,
536                    lifetime: self.lifetime,
537                },
538            ))
539        } else {
540            None
541        }
542    }
543
544    /// Divide into two blocks at the given row.
545    ///
546    /// # Examples
547    ///
548    /// ```
549    /// let data = &mut [
550    ///     [0, 1, 2],
551    ///     [3, 4, 5],
552    /// ];
553    ///
554    /// let block = matrix_slice::from_array_rows_mut(data);
555    /// let (top, bot) = block.split_at_row(1);
556    ///
557    /// assert_eq!(top[(0, 2)], 2);
558    /// assert_eq!(bot[(0, 2)], 5);
559    /// ```
560    pub fn split_at_row(self, mid: usize) -> (BlockMut<'data, T>, BlockMut<'data, T>) {
561        self.split_at_row_checked(mid).unwrap()
562    }
563
564    /// Divide into two blocks at the given row.
565    ///
566    /// See [`Self::split_at_row`] but returns `None` if out of bounds.
567    pub fn split_at_row_checked(
568        self,
569        mid: usize,
570    ) -> Option<(BlockMut<'data, T>, BlockMut<'data, T>)> {
571        if let Some((lhs, rhs, offset)) = self.block.split_at_row(mid) {
572            Some((
573                BlockMut {
574                    data: self.data,
575                    block: lhs,
576                    lifetime: self.lifetime,
577                },
578                BlockMut {
579                    data: unsafe { self.data.add(offset) },
580                    block: rhs,
581                    lifetime: self.lifetime,
582                },
583            ))
584        } else {
585            None
586        }
587    }
588
589    /// Choose a range of rows and contract the block to that.
590    ///
591    /// The argument type is flexible, allowing ranges (`1..3`), half open ranges (`2..` and `..2`)
592    /// among others. See the [`MatrixIndex`] trait, which is sealed though as its details are not
593    /// yet finalized.
594    ///
595    /// # Examples
596    ///
597    /// ```
598    /// let data = &mut [
599    ///     [0, 1, 2],
600    ///     [3, 4, 5],
601    ///     [6, 7, 8],
602    /// ];
603    ///
604    /// let block = matrix_slice::from_array_rows_mut(data);
605    ///
606    /// let center = block.select_rows(1..2).unwrap();
607    /// assert_eq!(center.rows(), 1);
608    /// assert_eq!(center.cols(), 3);
609    /// assert_eq!(center[(0, 1)], 4);
610    /// ```
611    pub fn select_rows<R>(self, range: R) -> Option<BlockMut<'data, T>>
612    where
613        R: MatrixIndex,
614    {
615        let (start, len) = range.into_start_and_len(self.block.rows)?;
616        let (_, block, offset) = self.block.split_at_row(start)?;
617        assert!(block.rows >= len);
618
619        Some(BlockMut {
620            block: BlockSlice { rows: len, ..block },
621            data: unsafe { self.data.add(offset) },
622            lifetime: self.lifetime,
623        })
624    }
625
626    /// Choose a range of columns and contract the block to that.
627    ///
628    /// The argument type is flexible, allowing ranges (`1..3`), half open ranges (`2..` and `..2`)
629    /// among others. See the [`MatrixIndex`] trait, which is sealed though as its details are not
630    /// yet finalized.
631    pub fn select_cols<R>(self, range: R) -> Option<BlockMut<'data, T>>
632    where
633        R: MatrixIndex,
634    {
635        let (start, len) = range.into_start_and_len(self.block.rows)?;
636        let (_, block, offset) = self.block.split_at_col(start)?;
637        assert!(block.cols >= len);
638
639        Some(BlockMut {
640            block: BlockSlice { cols: len, ..block },
641            data: unsafe { self.data.add(offset) },
642            lifetime: self.lifetime,
643        })
644    }
645
646    /// Choose a sub-block by its range of rows and columns.
647    pub fn select(
648        self,
649        row_range: impl MatrixIndex,
650        col_range: impl MatrixIndex,
651    ) -> Option<BlockMut<'data, T>> {
652        let block = self.select_rows(row_range)?;
653        block.select_cols(col_range)
654    }
655
656    /// Extract a contiguous underlying slice of elements if the block is contiguous.
657    ///
658    /// # Examples
659    ///
660    /// ```
661    /// let data = &mut [[0u32; 3]; 3];
662    /// let mut block = matrix_slice::from_array_rows_mut(data);
663    ///
664    /// let (mut part, _) = block.reborrow().split_at_row(2);
665    /// assert!(part.into_contiguous_slice().is_some());
666    ///
667    /// let (pre, post) = block.reborrow().split_at_col(2);
668    /// assert!(pre.into_contiguous_slice().is_none());
669    /// assert!(post.into_contiguous_slice().is_none());
670    ///
671    /// let (same, _) = block.reborrow().split_at_col(3);
672    /// assert!(same.into_contiguous_slice().is_some());
673    /// ```
674    pub fn into_contiguous_slice(self) -> Option<&'data mut [T]> {
675        if let Some(items) = self.block.contiguous_span() {
676            Some(unsafe { core::slice::from_raw_parts_mut(self.data.as_ptr().cast(), items) })
677        } else {
678            None
679        }
680    }
681
682    /// Turn this into a slice of the first row, assuming it is at most one row.
683    fn fake_contiguity(mut self) -> &'data mut [T] {
684        self.block.fake_contiguity();
685        self.into_contiguous_slice().unwrap()
686    }
687
688    /// Extract access as a slice of arrays if the block is contiguous.
689    ///
690    /// The caller must choose `N` matching the number of columns.
691    ///
692    /// # Examples
693    ///
694    /// ```
695    /// let data = &mut [[0u32; 3]; 3];
696    /// let mut block = matrix_slice::from_array_rows_mut(data);
697    ///
698    /// // Turns this back into the same type as `data` had.
699    /// assert!(block.reborrow().into_array_rows_checked::<3>().is_some());
700    ///
701    /// // Using an incorrect number of columns fails.
702    /// assert!(block.reborrow().into_array_rows_checked::<2>().is_none());
703    ///
704    /// // Can still be used after splitting at rows.
705    /// let (_, mut block) = block.split_at_row(2);
706    /// assert!(block.reborrow().into_array_rows_checked::<3>().is_some());
707    /// ```
708    pub fn into_array_rows_checked<const N: usize>(self) -> Option<&'data mut [[T; N]]> {
709        if self.block.cols == self.block.pitch && self.block.cols == N {
710            Some(unsafe {
711                core::slice::from_raw_parts_mut(self.data.as_ptr().cast(), self.block.rows)
712            })
713        } else {
714            None
715        }
716    }
717
718    /// Turn this unique reference into a shared reference.
719    pub fn cast_const(self) -> BlockRef<'data, T> {
720        // SAFETY: shared access can always be re-tagged from unique access.
721        BlockRef {
722            data: self.data,
723            block: self.block,
724            lifetime: PhantomData,
725        }
726    }
727
728    /// Create a unique reference to this block with a shorter lifetime.
729    pub fn reborrow(&mut self) -> BlockMut<'_, T> {
730        // SAFETY: Unique access is created by deriving it from our current pointer so the
731        // provenance is the same, and temporally it can not overlap access through the current
732        // value due to the lifetime enforcing a borrow relationship.
733        BlockMut {
734            data: self.data,
735            block: self.block,
736            lifetime: PhantomData,
737        }
738    }
739
740    /// Iterate over the rows of this block.
741    pub fn iter_rows(self) -> IterRows<'data, T> {
742        self.cast_const().iter_rows()
743    }
744
745    /// Iterate over the rows of this block.
746    pub fn iter_rows_mut(self) -> IterRowsMut<'data, T> {
747        IterRowsMut { block: self }
748    }
749
750    /// Modify the item type to a `Cell`, allowing interior mutability.
751    ///
752    /// This is the equivalent of [`Cell::from_mut`] over elements in this slice.
753    pub fn as_cells(self) -> BlockMut<'data, Cell<T>> {
754        // SAFETY: `Cell<T>` has the same layout as `T`.
755        BlockMut {
756            data: self.data.cast(),
757            block: self.block,
758            lifetime: PhantomData,
759        }
760    }
761}
762
763impl<'data, T> BlockMut<'data, Cell<T>> {
764    /// Modify the item type from a `Cell` to its interior type.
765    ///
766    /// This is the equivalent of [`Cell::get_mut`] over elements in this slice.
767    pub fn as_cell_items(self) -> BlockMut<'data, T> {
768        BlockMut {
769            data: self.data.cast(),
770            block: self.block,
771            lifetime: PhantomData,
772        }
773    }
774}
775
776impl<T> ops::Index<(usize, usize)> for BlockMut<'_, T> {
777    type Output = T;
778
779    fn index(&self, index: (usize, usize)) -> &Self::Output {
780        let idx = self.block.in_bounds_index(index.0, index.1);
781        // SAFETY: Index is bounded by `total_span` which itself is a lower estimate of the
782        // provenance of the pointer.
783        unsafe { &*self.data.as_ptr().add(idx) }
784    }
785}
786
787impl<T> ops::IndexMut<(usize, usize)> for BlockMut<'_, T> {
788    fn index_mut(&mut self, index: (usize, usize)) -> &mut Self::Output {
789        let idx = self.block.in_bounds_index(index.0, index.1);
790        // SAFETY: Index is bounded by `total_span` which itself is a lower estimate of the
791        // provenance of the pointer.
792        unsafe { &mut *self.data.as_ptr().add(idx) }
793    }
794}
795
796/// Represents the provenance of a pointer to a block of a matrix.
797///
798/// FIXME: before exposing this consider `PartialEq, … Ord` implications. These were added to
799/// satisfy the `Pointee` trait requirements but really what does ordering mean? We have chosen the
800/// field `pitch` to be last but that is super arbitrary.
801///
802/// We assume row major order here for the convention of _naming_ things. That is, when we say row
803/// we mean a tightly packed slice of items. This implies that the item pitch is assumed to be `1`.
804/// We have two major possible choices in representation a block-subset of a matrix: store the
805/// dimensions of the block with a matrix row pitch or store the total size of the matrix and two
806/// lengths.
807///
808/// The former of these allows us to represent both `0×N` and `M×0` blocks naturally, while the
809/// latter allows one of them but provides a fast capacity that's pre-calculated. We choose the
810/// former. In either case we need to store three `usize` values. Note that the total span of items
811/// is *not* `rows * pitch` since the last row might be ragged.
812#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
813struct BlockSlice {
814    rows: usize,
815    cols: usize,
816    pitch: usize,
817}
818
819const _: () = {
820    // As per Rust 1.92's `Pointee` trait. Suspicious: `Ord`. See comment on `BlockSlice`.
821    use core::{fmt, hash};
822
823    fn _can_eventually_be_ptr_metadata<
824        // Missing: `Freeze` which is unstable
825        Metadata: fmt::Debug + Copy + Send + Sync + Ord + hash::Hash + Unpin,
826    >() {
827    }
828
829    let _ = _can_eventually_be_ptr_metadata::<BlockSlice>;
830};
831
832impl BlockSlice {
833    /// The number of elements if this block is contiguous (cols equals pitch).
834    fn contiguous_span(&self) -> Option<usize> {
835        if self.cols == self.pitch {
836            Some(self.rows * self.cols)
837        } else {
838            None
839        }
840    }
841
842    /// The number of elements spanned by this block (including those we are not allowed to
843    /// access).
844    fn total_span(&self) -> usize {
845        if let Some(all_but_last) = self.rows.checked_sub(1) {
846            all_but_last * self.pitch + self.cols
847        } else {
848            0
849        }
850    }
851
852    /// The caller must ensure that this block has at most one row.
853    fn fake_contiguity(&mut self) {
854        debug_assert!(self.rows <= 1);
855        debug_assert!(self.cols <= self.pitch);
856
857        self.rows = self.rows.min(1);
858        // SAFETY: Reducing the pitch when we have at most one row does not change the elements we
859        // may refer to. The pitch always exceeds the number of columns.
860        self.pitch = self.cols;
861    }
862
863    fn split_at_row(self, mid: usize) -> Option<(BlockSlice, BlockSlice, usize)> {
864        let n = self.rows.checked_sub(mid)?;
865
866        let lhs = BlockSlice {
867            rows: mid,
868            cols: self.cols,
869            pitch: self.pitch,
870        };
871
872        let rhs = BlockSlice {
873            rows: n,
874            cols: self.cols,
875            pitch: self.pitch,
876        };
877
878        // Careful: If we split a block after its last row (i.e. lhs and self are identical),
879        // the naive offset of rows * pitch may point beyond the total span of elements covered
880        // by ourselves. In this case the rhs does not cover any row so we assign it any
881        // in-bounds offset.
882        let offset = if n > 0 { mid * self.pitch } else { 0 };
883        debug_assert!(offset <= self.total_span());
884
885        Some((lhs, rhs, offset))
886    }
887
888    fn split_at_col(self, mid: usize) -> Option<(BlockSlice, BlockSlice, usize)> {
889        let n = self.cols.checked_sub(mid)?;
890
891        let lhs = BlockSlice {
892            rows: self.rows,
893            cols: mid,
894            pitch: self.pitch,
895        };
896
897        let rhs = BlockSlice {
898            rows: self.rows,
899            cols: n,
900            pitch: self.pitch,
901        };
902
903        // If we have no rows at all then this block does not cover any elements so we must
904        // pick an offset of 0 to guarantee in-bounds access. The good news is that this case
905        // also implies that the other side is empty so its offset does not matter.
906        let offset = if self.rows > 0 { mid } else { 0 };
907        debug_assert!(offset <= self.total_span());
908
909        Some((lhs, rhs, offset))
910    }
911
912    /// Return the absolute position of the element, if in bounds. Otherwise, panic.
913    fn in_bounds_index(&self, row: usize, col: usize) -> usize {
914        assert!(row < self.rows);
915        assert!(col < self.cols);
916        let idx = row * self.pitch + col;
917        debug_assert!(idx < self.total_span());
918        idx
919    }
920}
921
922/// Iterate over the rows of a block in a matrix.
923///
924/// We assume row-major matrices here, a row is a contiguous slice of items.
925pub struct IterRows<'a, T> {
926    block: BlockRef<'a, T>,
927}
928
929impl<'data, T> Iterator for IterRows<'data, T> {
930    type Item = &'data [T];
931
932    fn next(&mut self) -> Option<Self::Item> {
933        if self.block.rows() == 0 {
934            None
935        } else {
936            // FIXME: add `split_off_rows` instead.
937            let (row, rest) = core::mem::take(&mut self.block).split_at_row(1);
938            self.block = rest;
939            // One row as it was created from `split_at_row(1)`.
940            Some(row.fake_contiguity())
941        }
942    }
943}
944
945/// Iterate over mutable rows of a block in a matrix.
946///
947/// We assume row-major matrices here, a row is a contiguous slice of items.
948pub struct IterRowsMut<'a, T> {
949    block: BlockMut<'a, T>,
950}
951
952impl<'data, T> Iterator for IterRowsMut<'data, T> {
953    type Item = &'data mut [T];
954
955    fn next(&mut self) -> Option<Self::Item> {
956        if self.block.rows() == 0 {
957            None
958        } else {
959            // FIXME: add `split_off_rows` instead.
960            let (row, rest) = core::mem::take(&mut self.block).split_at_row(1);
961            self.block = rest;
962            // One row as it was created from `split_at_row(1)`.
963            Some(row.fake_contiguity())
964        }
965    }
966}
967
968pub trait MatrixIndex: sealed::Sealed {}
969
970impl MatrixIndex for ops::Range<usize> {}
971impl MatrixIndex for ops::RangeInclusive<usize> {}
972impl MatrixIndex for ops::RangeFrom<usize> {}
973impl MatrixIndex for ops::RangeTo<usize> {}
974impl MatrixIndex for ops::RangeToInclusive<usize> {}
975impl MatrixIndex for ops::RangeFull {}
976
977mod sealed {
978    use core::ops;
979
980    pub trait Sealed {
981        fn into_start_and_len(self, dim: usize) -> Option<(usize, usize)>;
982    }
983
984    impl Sealed for ops::Range<usize> {
985        fn into_start_and_len(self, dim: usize) -> Option<(usize, usize)> {
986            if self.start <= self.end && self.end <= dim {
987                Some((self.start, self.end - self.start))
988            } else {
989                None
990            }
991        }
992    }
993
994    impl Sealed for ops::RangeInclusive<usize> {
995        fn into_start_and_len(self, dim: usize) -> Option<(usize, usize)> {
996            let start = *self.start();
997            let end = *self.end();
998            if start <= end && end < dim {
999                Some((start, end - start + 1))
1000            } else {
1001                None
1002            }
1003        }
1004    }
1005
1006    impl Sealed for ops::RangeFrom<usize> {
1007        fn into_start_and_len(self, dim: usize) -> Option<(usize, usize)> {
1008            if self.start <= dim {
1009                Some((self.start, dim - self.start))
1010            } else {
1011                None
1012            }
1013        }
1014    }
1015
1016    impl Sealed for ops::RangeTo<usize> {
1017        fn into_start_and_len(self, dim: usize) -> Option<(usize, usize)> {
1018            if self.end <= dim {
1019                Some((0, self.end))
1020            } else {
1021                None
1022            }
1023        }
1024    }
1025
1026    impl Sealed for ops::RangeToInclusive<usize> {
1027        fn into_start_and_len(self, dim: usize) -> Option<(usize, usize)> {
1028            if self.end < dim {
1029                Some((0, self.end + 1))
1030            } else {
1031                None
1032            }
1033        }
1034    }
1035
1036    impl Sealed for ops::RangeFull {
1037        fn into_start_and_len(self, dim: usize) -> Option<(usize, usize)> {
1038            Some((0, dim))
1039        }
1040    }
1041}
1042
1043/// Tests should also be ran under MIRI.
1044#[cfg(test)]
1045mod tests {
1046    // Verify that splitting as in the example works.
1047    #[test]
1048    fn well_defined_split() {
1049        let data = &[[0u32; 3]; 3];
1050        let block = super::from_array_rows(data);
1051        let (_, block) = block.split_at_row(1);
1052        let (_, block) = block.split_at_col(1);
1053
1054        block.split_at_row_checked(2).unwrap();
1055    }
1056    #[test]
1057    fn well_defined_split_mut() {
1058        let data = &mut [[0u32; 3]; 3];
1059        let block = super::from_array_rows_mut(data);
1060        let (_, block) = block.split_at_row(1);
1061        let (_, block) = block.split_at_col(1);
1062
1063        block.split_at_row_checked(2).unwrap();
1064    }
1065}