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