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}