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}