#![no_std]
use core::{cell::Cell, fmt, marker::PhantomData, ops, ptr::NonNull};
pub fn from_array_rows<'a, T, const N: usize>(data: &'a [[T; N]]) -> BlockRef<'a, T> {
BlockRef {
block: BlockSlice {
rows: data.len(),
cols: N,
pitch: N,
},
data: NonNull::from_ref(data).cast(),
lifetime: PhantomData,
}
}
#[derive(Copy, Clone)]
pub struct BlockRef<'a, T> {
data: NonNull<T>,
block: BlockSlice,
lifetime: PhantomData<&'a [T]>,
}
unsafe impl<T> Sync for BlockRef<'_, T> where T: Sync {}
unsafe impl<T> Send for BlockRef<'_, T> where T: Sync {}
const _: () = {
fn _coerce_block<'a, 'b: 'a, T>(v: BlockRef<'b, T>) -> BlockRef<'a, T> {
v
}
fn _coerce_covariant<'lt, 'a, 'b: 'a, T>(v: &'lt BlockRef<'b, T>) -> &'lt BlockRef<'a, T> {
v
}
fn _coerce_covariant_fn<'lt, 'a, 'b: 'a, T>(v: fn(BlockRef<'a, T>)) -> fn(BlockRef<'b, T>) {
v
}
fn _coerce_item_covariant<'lt, 'a, 'b: 'a, T>(v: BlockRef<'lt, &'b T>) -> BlockRef<'lt, &'a T> {
v
}
};
impl<T> Default for BlockRef<'_, T> {
fn default() -> Self {
from_array_rows::<T, 0>(&[])
}
}
impl<'data, T> BlockRef<'data, T> {
pub fn new(data: &'data [T], pitch: usize) -> Self {
assert!(data.len().is_multiple_of(pitch));
BlockRef {
block: BlockSlice {
rows: data.len() / pitch,
cols: pitch,
pitch,
},
data: NonNull::from_ref(data).cast(),
lifetime: PhantomData,
}
}
pub fn rows(&self) -> usize {
self.block.rows
}
pub fn cols(&self) -> usize {
self.block.cols
}
pub fn split_at_col(self, mid: usize) -> (BlockRef<'data, T>, BlockRef<'data, T>) {
self.split_at_col_checked(mid).unwrap()
}
pub fn split_at_col_checked(
self,
mid: usize,
) -> Option<(BlockRef<'data, T>, BlockRef<'data, T>)> {
if let Some((lhs, rhs, offset)) = self.block.split_at_col(mid) {
Some((
BlockRef {
data: self.data,
block: lhs,
lifetime: self.lifetime,
},
BlockRef {
data: unsafe { self.data.add(offset) },
block: rhs,
lifetime: self.lifetime,
},
))
} else {
None
}
}
pub fn split_at_row(self, mid: usize) -> (BlockRef<'data, T>, BlockRef<'data, T>) {
self.split_at_row_checked(mid).unwrap()
}
pub fn split_at_row_checked(
self,
mid: usize,
) -> Option<(BlockRef<'data, T>, BlockRef<'data, T>)> {
if let Some((lhs, rhs, offset)) = self.block.split_at_row(mid) {
Some((
BlockRef {
data: self.data,
block: lhs,
lifetime: self.lifetime,
},
BlockRef {
data: unsafe { self.data.add(offset) },
block: rhs,
lifetime: self.lifetime,
},
))
} else {
None
}
}
pub fn select_rows<R>(self, range: R) -> Option<BlockRef<'data, T>>
where
R: MatrixIndex,
{
let (start, len) = range.into_start_and_len(self.block.rows)?;
let (_, block, offset) = self.block.split_at_row(start)?;
assert!(block.rows >= len);
Some(BlockRef {
block: BlockSlice { rows: len, ..block },
data: unsafe { self.data.add(offset) },
lifetime: self.lifetime,
})
}
pub fn select_cols<R>(self, range: R) -> Option<BlockRef<'data, T>>
where
R: MatrixIndex,
{
let (start, len) = range.into_start_and_len(self.block.rows)?;
let (_, block, offset) = self.block.split_at_col(start)?;
assert!(block.cols >= len);
Some(BlockRef {
block: BlockSlice { cols: len, ..block },
data: unsafe { self.data.add(offset) },
lifetime: self.lifetime,
})
}
pub fn select(
self,
row_range: impl MatrixIndex,
col_range: impl MatrixIndex,
) -> Option<BlockRef<'data, T>> {
let block = self.select_rows(row_range)?;
block.select_cols(col_range)
}
pub fn into_contiguous_slice(self) -> Option<&'data [T]> {
if let Some(items) = self.block.contiguous_span() {
Some(unsafe { core::slice::from_raw_parts(self.data.as_ptr().cast(), items) })
} else {
None
}
}
fn fake_contiguity(mut self) -> &'data [T] {
self.block.fake_contiguity();
self.into_contiguous_slice().unwrap()
}
pub fn into_array_rows_checked<const N: usize>(self) -> Option<&'data [[T; N]]> {
if self.block.cols == self.block.pitch && self.block.cols == N {
Some(unsafe { core::slice::from_raw_parts(self.data.as_ptr().cast(), self.block.rows) })
} else {
None
}
}
pub fn iter_rows(self) -> IterRows<'data, T> {
IterRows { block: self }
}
pub fn reborrow(&self) -> BlockRef<'_, T> {
BlockRef {
data: self.data,
block: self.block,
lifetime: PhantomData,
}
}
}
impl<T> ops::Index<(usize, usize)> for BlockRef<'_, T> {
type Output = T;
fn index(&self, index: (usize, usize)) -> &Self::Output {
let idx = self.block.in_bounds_index(index.0, index.1);
unsafe { &*self.data.as_ptr().add(idx) }
}
}
impl<T: fmt::Debug> fmt::Debug for BlockRef<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.reborrow().iter_rows()).finish()
}
}
pub fn from_array_rows_mut<'a, T, const N: usize>(data: &'a mut [[T; N]]) -> BlockMut<'a, T> {
BlockMut {
block: BlockSlice {
rows: data.len(),
cols: N,
pitch: N,
},
data: NonNull::from_mut(data).cast(),
lifetime: PhantomData,
}
}
pub struct BlockMut<'a, T> {
data: NonNull<T>,
block: BlockSlice,
lifetime: PhantomData<&'a mut [T]>,
}
unsafe impl<T> Sync for BlockMut<'_, T> where T: Sync {}
unsafe impl<T> Send for BlockMut<'_, T> where T: Sync {}
const _: () = {
fn _coerce_block_mut<'a, 'b: 'a, T>(v: BlockMut<'b, T>) -> BlockMut<'a, T> {
v
}
fn _coerce_covariant<'lt, 'a, 'b: 'a, T>(v: &'lt BlockMut<'b, T>) -> &'lt BlockMut<'a, T> {
v
}
fn _coerce_covariant_fn<'lt, 'a, 'b: 'a, T>(v: fn(BlockMut<'a, T>)) -> fn(BlockMut<'b, T>) {
v
}
};
impl<T> Default for BlockMut<'_, T> {
fn default() -> Self {
from_array_rows_mut::<T, 0>(&mut [])
}
}
impl<'data, T> BlockMut<'data, T> {
pub fn new(data: &'data mut [T], pitch: usize) -> Self {
assert!(data.len().is_multiple_of(pitch));
BlockMut {
block: BlockSlice {
rows: data.len() / pitch,
cols: pitch,
pitch,
},
data: NonNull::from_ref(data).cast(),
lifetime: PhantomData,
}
}
pub fn rows(&self) -> usize {
self.block.rows
}
pub fn cols(&self) -> usize {
self.block.cols
}
pub fn split_at_col(self, mid: usize) -> (BlockMut<'data, T>, BlockMut<'data, T>) {
self.split_at_col_checked(mid).unwrap()
}
pub fn split_at_col_checked(
self,
mid: usize,
) -> Option<(BlockMut<'data, T>, BlockMut<'data, T>)> {
if let Some((lhs, rhs, offset)) = self.block.split_at_col(mid) {
Some((
BlockMut {
data: self.data,
block: lhs,
lifetime: self.lifetime,
},
BlockMut {
data: unsafe { self.data.add(offset) },
block: rhs,
lifetime: self.lifetime,
},
))
} else {
None
}
}
pub fn split_at_row(self, mid: usize) -> (BlockMut<'data, T>, BlockMut<'data, T>) {
self.split_at_row_checked(mid).unwrap()
}
pub fn split_at_row_checked(
self,
mid: usize,
) -> Option<(BlockMut<'data, T>, BlockMut<'data, T>)> {
if let Some((lhs, rhs, offset)) = self.block.split_at_row(mid) {
Some((
BlockMut {
data: self.data,
block: lhs,
lifetime: self.lifetime,
},
BlockMut {
data: unsafe { self.data.add(offset) },
block: rhs,
lifetime: self.lifetime,
},
))
} else {
None
}
}
pub fn select_rows<R>(self, range: R) -> Option<BlockMut<'data, T>>
where
R: MatrixIndex,
{
let (start, len) = range.into_start_and_len(self.block.rows)?;
let (_, block, offset) = self.block.split_at_row(start)?;
assert!(block.rows >= len);
Some(BlockMut {
block: BlockSlice { rows: len, ..block },
data: unsafe { self.data.add(offset) },
lifetime: self.lifetime,
})
}
pub fn select_cols<R>(self, range: R) -> Option<BlockMut<'data, T>>
where
R: MatrixIndex,
{
let (start, len) = range.into_start_and_len(self.block.rows)?;
let (_, block, offset) = self.block.split_at_col(start)?;
assert!(block.cols >= len);
Some(BlockMut {
block: BlockSlice { cols: len, ..block },
data: unsafe { self.data.add(offset) },
lifetime: self.lifetime,
})
}
pub fn select(
self,
row_range: impl MatrixIndex,
col_range: impl MatrixIndex,
) -> Option<BlockMut<'data, T>> {
let block = self.select_rows(row_range)?;
block.select_cols(col_range)
}
pub fn into_contiguous_slice(self) -> Option<&'data mut [T]> {
if let Some(items) = self.block.contiguous_span() {
Some(unsafe { core::slice::from_raw_parts_mut(self.data.as_ptr().cast(), items) })
} else {
None
}
}
fn fake_contiguity(mut self) -> &'data mut [T] {
self.block.fake_contiguity();
self.into_contiguous_slice().unwrap()
}
pub fn into_array_rows_checked<const N: usize>(self) -> Option<&'data mut [[T; N]]> {
if self.block.cols == self.block.pitch && self.block.cols == N {
Some(unsafe {
core::slice::from_raw_parts_mut(self.data.as_ptr().cast(), self.block.rows)
})
} else {
None
}
}
pub fn cast_const(self) -> BlockRef<'data, T> {
BlockRef {
data: self.data,
block: self.block,
lifetime: PhantomData,
}
}
pub fn reborrow(&mut self) -> BlockMut<'_, T> {
BlockMut {
data: self.data,
block: self.block,
lifetime: PhantomData,
}
}
pub fn iter_rows(self) -> IterRows<'data, T> {
self.cast_const().iter_rows()
}
pub fn iter_rows_mut(self) -> IterRowsMut<'data, T> {
IterRowsMut { block: self }
}
pub fn as_cells(self) -> BlockMut<'data, Cell<T>> {
BlockMut {
data: self.data.cast(),
block: self.block,
lifetime: PhantomData,
}
}
}
impl<'data, T> BlockMut<'data, Cell<T>> {
pub fn as_cell_items(self) -> BlockMut<'data, T> {
BlockMut {
data: self.data.cast(),
block: self.block,
lifetime: PhantomData,
}
}
}
impl<T> ops::Index<(usize, usize)> for BlockMut<'_, T> {
type Output = T;
fn index(&self, index: (usize, usize)) -> &Self::Output {
let idx = self.block.in_bounds_index(index.0, index.1);
unsafe { &*self.data.as_ptr().add(idx) }
}
}
impl<T> ops::IndexMut<(usize, usize)> for BlockMut<'_, T> {
fn index_mut(&mut self, index: (usize, usize)) -> &mut Self::Output {
let idx = self.block.in_bounds_index(index.0, index.1);
unsafe { &mut *self.data.as_ptr().add(idx) }
}
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
struct BlockSlice {
rows: usize,
cols: usize,
pitch: usize,
}
const _: () = {
use core::{fmt, hash};
fn _can_eventually_be_ptr_metadata<
Metadata: fmt::Debug + Copy + Send + Sync + Ord + hash::Hash + Unpin,
>() {
}
let _ = _can_eventually_be_ptr_metadata::<BlockSlice>;
};
impl BlockSlice {
fn contiguous_span(&self) -> Option<usize> {
if self.cols == self.pitch {
Some(self.rows * self.cols)
} else {
None
}
}
fn total_span(&self) -> usize {
if let Some(all_but_last) = self.rows.checked_sub(1) {
all_but_last * self.pitch + self.cols
} else {
0
}
}
fn fake_contiguity(&mut self) {
debug_assert!(self.rows <= 1);
debug_assert!(self.cols <= self.pitch);
self.rows = self.rows.min(1);
self.pitch = self.cols;
}
fn split_at_row(self, mid: usize) -> Option<(BlockSlice, BlockSlice, usize)> {
let n = self.rows.checked_sub(mid)?;
let lhs = BlockSlice {
rows: mid,
cols: self.cols,
pitch: self.pitch,
};
let rhs = BlockSlice {
rows: n,
cols: self.cols,
pitch: self.pitch,
};
let offset = if n > 0 { mid * self.pitch } else { 0 };
debug_assert!(offset <= self.total_span());
Some((lhs, rhs, offset))
}
fn split_at_col(self, mid: usize) -> Option<(BlockSlice, BlockSlice, usize)> {
let n = self.cols.checked_sub(mid)?;
let lhs = BlockSlice {
rows: self.rows,
cols: mid,
pitch: self.pitch,
};
let rhs = BlockSlice {
rows: self.rows,
cols: n,
pitch: self.pitch,
};
let offset = if self.rows > 0 { mid } else { 0 };
debug_assert!(offset <= self.total_span());
Some((lhs, rhs, offset))
}
fn in_bounds_index(&self, row: usize, col: usize) -> usize {
assert!(row < self.rows);
assert!(col < self.cols);
let idx = row * self.pitch + col;
debug_assert!(idx < self.total_span());
idx
}
}
pub struct IterRows<'a, T> {
block: BlockRef<'a, T>,
}
impl<'data, T> Iterator for IterRows<'data, T> {
type Item = &'data [T];
fn next(&mut self) -> Option<Self::Item> {
if self.block.rows() == 0 {
None
} else {
let (row, rest) = core::mem::take(&mut self.block).split_at_row(1);
self.block = rest;
Some(row.fake_contiguity())
}
}
}
pub struct IterRowsMut<'a, T> {
block: BlockMut<'a, T>,
}
impl<'data, T> Iterator for IterRowsMut<'data, T> {
type Item = &'data mut [T];
fn next(&mut self) -> Option<Self::Item> {
if self.block.rows() == 0 {
None
} else {
let (row, rest) = core::mem::take(&mut self.block).split_at_row(1);
self.block = rest;
Some(row.fake_contiguity())
}
}
}
pub trait MatrixIndex: sealed::Sealed {}
impl MatrixIndex for ops::Range<usize> {}
impl MatrixIndex for ops::RangeInclusive<usize> {}
impl MatrixIndex for ops::RangeFrom<usize> {}
impl MatrixIndex for ops::RangeTo<usize> {}
impl MatrixIndex for ops::RangeToInclusive<usize> {}
impl MatrixIndex for ops::RangeFull {}
mod sealed {
use core::ops;
pub trait Sealed {
fn into_start_and_len(self, dim: usize) -> Option<(usize, usize)>;
}
impl Sealed for ops::Range<usize> {
fn into_start_and_len(self, dim: usize) -> Option<(usize, usize)> {
if self.start <= self.end && self.end <= dim {
Some((self.start, self.end - self.start))
} else {
None
}
}
}
impl Sealed for ops::RangeInclusive<usize> {
fn into_start_and_len(self, dim: usize) -> Option<(usize, usize)> {
let start = *self.start();
let end = *self.end();
if start <= end && end < dim {
Some((start, end - start + 1))
} else {
None
}
}
}
impl Sealed for ops::RangeFrom<usize> {
fn into_start_and_len(self, dim: usize) -> Option<(usize, usize)> {
if self.start <= dim {
Some((self.start, dim - self.start))
} else {
None
}
}
}
impl Sealed for ops::RangeTo<usize> {
fn into_start_and_len(self, dim: usize) -> Option<(usize, usize)> {
if self.end <= dim {
Some((0, self.end))
} else {
None
}
}
}
impl Sealed for ops::RangeToInclusive<usize> {
fn into_start_and_len(self, dim: usize) -> Option<(usize, usize)> {
if self.end < dim {
Some((0, self.end + 1))
} else {
None
}
}
}
impl Sealed for ops::RangeFull {
fn into_start_and_len(self, dim: usize) -> Option<(usize, usize)> {
Some((0, dim))
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn well_defined_split() {
let data = &[[0u32; 3]; 3];
let block = super::from_array_rows(data);
let (_, block) = block.split_at_row(1);
let (_, block) = block.split_at_col(1);
block.split_at_row_checked(2).unwrap();
}
#[test]
fn well_defined_split_mut() {
let data = &mut [[0u32; 3]; 3];
let block = super::from_array_rows_mut(data);
let (_, block) = block.split_at_row(1);
let (_, block) = block.split_at_col(1);
block.split_at_row_checked(2).unwrap();
}
}