use std::{marker::PhantomData, num::NonZeroUsize, ptr::NonNull};
#[cfg(feature = "rayon")]
use crate::views::rowmajor::MatrixMut;
use crate::views::rowmajor::{Layout, Matrix, Mut, Ref};
#[derive(Debug)]
pub struct Rows<'a, T> {
ptr: NonNull<T>,
remaining: usize,
ncols: usize,
_lifetime: PhantomData<&'a [T]>,
}
impl<'a, T> Rows<'a, T> {
pub(super) fn new(m: Ref<'a, T>) -> Self {
let layout = m.layout();
Self {
ptr: m.as_nonnull(),
remaining: layout.nrows(),
ncols: layout.ncols(),
_lifetime: PhantomData,
}
}
}
unsafe impl<T> Send for Rows<'_, T> where T: Sync {}
unsafe impl<T> Sync for Rows<'_, T> where T: Sync {}
impl<'a, T> Iterator for Rows<'a, T> {
type Item = &'a [T];
fn next(&mut self) -> Option<&'a [T]> {
self.remaining.checked_sub(1).map(|remaining| {
let item =
unsafe { std::slice::from_raw_parts(self.ptr.as_ptr().cast_const(), self.ncols) };
self.remaining = remaining;
self.ptr = unsafe { self.ptr.add(self.ncols) };
item
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.remaining, Some(self.remaining))
}
}
impl<T> ExactSizeIterator for Rows<'_, T> {}
impl<T> std::iter::FusedIterator for Rows<'_, T> {}
#[derive(Debug)]
pub struct RowsMut<'a, T> {
ptr: NonNull<T>,
remaining: usize,
ncols: usize,
_lifetime: PhantomData<&'a mut [T]>,
}
impl<'a, T> RowsMut<'a, T> {
pub(super) fn new(m: Mut<'a, T>) -> Self {
let layout = m.layout();
Self {
ptr: m.as_nonnull(),
remaining: layout.nrows(),
ncols: layout.ncols(),
_lifetime: PhantomData,
}
}
}
unsafe impl<T> Send for RowsMut<'_, T> where T: Send {}
unsafe impl<T> Sync for RowsMut<'_, T> where T: Sync {}
impl<'a, T> Iterator for RowsMut<'a, T> {
type Item = &'a mut [T];
fn next(&mut self) -> Option<&'a mut [T]> {
self.remaining.checked_sub(1).map(|remaining| {
let item = unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.ncols) };
self.remaining = remaining;
self.ptr = unsafe { self.ptr.add(self.ncols) };
item
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.remaining, Some(self.remaining))
}
}
impl<T> ExactSizeIterator for RowsMut<'_, T> {}
impl<T> std::iter::FusedIterator for RowsMut<'_, T> {}
#[derive(Debug)]
pub struct Windows<'a, T> {
ptr: NonNull<T>,
remaining: usize,
batchsize: NonZeroUsize,
ncols: usize,
_lifetime: PhantomData<&'a [T]>,
}
impl<'a, T> Windows<'a, T> {
pub(super) fn new(m: Ref<'a, T>, batchsize: NonZeroUsize) -> Self {
let layout = m.layout();
Self {
ptr: m.as_nonnull(),
remaining: layout.nrows(),
batchsize,
ncols: layout.ncols(),
_lifetime: PhantomData,
}
}
}
unsafe impl<T> Send for Windows<'_, T> where T: Sync {}
unsafe impl<T> Sync for Windows<'_, T> where T: Sync {}
impl<'a, T> Iterator for Windows<'a, T> {
type Item = Ref<'a, T>;
fn next(&mut self) -> Option<Ref<'a, T>> {
if self.remaining == 0 {
None
} else {
let next_remaining = self.remaining.saturating_sub(self.batchsize.get());
let nrows = self.remaining - next_remaining;
let window = unsafe {
Ref {
ptr: self.ptr,
layout: Layout::new_unchecked(nrows, self.ncols),
_lifetime: PhantomData,
}
};
self.ptr = unsafe { self.ptr.add(nrows * self.ncols) };
self.remaining = next_remaining;
Some(window)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.remaining.div_ceil(self.batchsize.get());
(remaining, Some(remaining))
}
}
impl<T> ExactSizeIterator for Windows<'_, T> {}
impl<T> std::iter::FusedIterator for Windows<'_, T> {}
#[cfg(feature = "rayon")]
pub(super) struct ParMut<'a, T> {
ptr: NonNull<T>,
layout: Layout<T>,
_lifetime: PhantomData<&'a mut [T]>,
}
#[cfg(feature = "rayon")]
unsafe impl<T: Send> Send for ParMut<'_, T> {}
#[cfg(feature = "rayon")]
unsafe impl<T: Send> Sync for ParMut<'_, T> {}
#[cfg(feature = "rayon")]
impl<'a, T> ParMut<'a, T> {
pub(super) fn new<M>(matrix: &'a mut M) -> Self
where
M: MatrixMut<Element = T> + ?Sized,
{
let layout = matrix.layout();
let ptr = matrix.as_nonnull_mut();
Self {
ptr,
layout,
_lifetime: PhantomData,
}
}
pub(super) fn nrows(&self) -> usize {
self.layout.nrows()
}
pub(super) unsafe fn row_disjoint_unchecked(&self, row: usize) -> &'a mut [T] {
debug_assert!(row < self.layout.nrows());
let ncols = self.layout.ncols();
unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr().add(row * ncols), ncols) }
}
pub(super) unsafe fn window_disjoint_unchecked(
&self,
rows: std::ops::Range<usize>,
) -> Mut<'a, T> {
debug_assert!(rows.start <= rows.end);
debug_assert!(rows.end <= self.layout.nrows());
let ncols = self.layout.ncols();
let nrows = rows.end - rows.start;
let ptr = unsafe { self.ptr.add(rows.start * ncols) };
Mut {
ptr,
layout: unsafe { Layout::new_unchecked(nrows, ncols) },
_lifetime: PhantomData,
}
}
}
#[cfg(all(test, feature = "rayon"))]
mod tests {
use super::ParMut;
use crate::views::rowmajor::{Matrix, MatrixMut, Owned};
#[test]
fn par_mut_zero_column_views_can_coexist() {
let mut matrix = Owned::from_element(usize::MAX, 0, 0);
let ptr = matrix.as_ptr();
let matrix = ParMut::new(&mut matrix);
let rows = unsafe {
[
matrix.row_disjoint_unchecked(0),
matrix.row_disjoint_unchecked(1),
matrix.row_disjoint_unchecked(usize::MAX - 1),
]
};
let windows = unsafe {
[
matrix.window_disjoint_unchecked(0..2),
matrix.window_disjoint_unchecked(2..usize::MAX),
]
};
assert!(rows.iter().all(|row| row.is_empty() && row.as_ptr() == ptr));
assert!(windows.iter().all(|window| {
window.ncols() == 0 && window.as_slice().is_empty() && window.as_ptr() == ptr
}));
}
#[test]
fn par_mut_disjoint_nonempty_views_can_coexist() {
let mut matrix = Owned::from_fn(6, 2, |rc| rc.row * 100 + rc.col);
{
let matrix = ParMut::new(&mut matrix);
let (row0, row1, mut window2, mut window4) = unsafe {
(
matrix.row_disjoint_unchecked(0),
matrix.row_disjoint_unchecked(1),
matrix.window_disjoint_unchecked(2..4),
matrix.window_disjoint_unchecked(4..6),
)
};
row0[0] = 10;
row1[1] = 11;
let mut window2_rows: Vec<_> = window2.rows_mut().collect();
let mut window4_rows: Vec<_> = window4.rows_mut().collect();
window2_rows[0][0] = 20;
window2_rows[1][1] = 31;
window4_rows[0][0] = 40;
window4_rows[1][1] = 51;
}
assert_eq!(
matrix.as_slice(),
&[10, 1, 100, 11, 20, 201, 300, 31, 40, 401, 500, 51]
);
}
}