use alloc::{vec, vec::Vec};
use core::{
fmt::{self, Debug, Formatter},
iter,
ops::{Deref, DerefMut},
};
use super::Dims;
use inner::Inner;
pub trait AsSlice2<T> {
fn as_slice2(&self) -> Slice2<'_, T>;
}
pub trait AsMutSlice2<T> {
fn as_mut_slice2(&mut self) -> MutSlice2<'_, T>;
}
#[derive(Clone, Eq, PartialEq)]
#[repr(transparent)]
pub struct Buf2<T>(Inner<T, Vec<T>>);
#[derive(Copy, Clone, Eq, PartialEq)]
#[repr(transparent)]
pub struct Slice2<'a, T>(Inner<T, &'a [T]>);
#[repr(transparent)]
pub struct MutSlice2<'a, T>(Inner<T, &'a mut [T]>);
impl<T> Buf2<T> {
pub fn new_from<I>((w, h): Dims, init: I) -> Self
where
I: IntoIterator<Item = T>,
{
let ww = isize::try_from(w).ok();
let hh = isize::try_from(h).ok();
let len = ww.and_then(|w| hh.and_then(|h| w.checked_mul(h)));
let Some(len) = len else {
panic!(
"w * h cannot exceed isize::MAX ({w} * {h} > {})",
isize::MAX
);
};
let data: Vec<_> = init.into_iter().take(len as usize).collect();
assert_eq!(
data.len(),
len as usize,
"insufficient items in iterator ({} < {len}",
data.len()
);
Self(Inner::new((w, h), w, data))
}
pub fn new((w, h): Dims) -> Self
where
T: Default + Clone,
{
let data = vec![T::default(); (w * h) as usize];
Self(Inner::new((w, h), w, data))
}
pub fn new_with<F>((w, h): Dims, mut init_fn: F) -> Self
where
F: FnMut(u32, u32) -> T,
{
let (mut x, mut y) = (0, 0);
Self::new_from(
(w, h),
iter::from_fn(|| {
let res = init_fn(x, y);
x += 1;
if x == w {
(x, y) = (0, y + 1);
}
Some(res)
}),
)
}
pub fn data(&self) -> &[T] {
self.0.data()
}
pub fn data_mut(&mut self) -> &mut [T] {
self.0.data_mut()
}
pub fn reshape(&mut self, dims: Dims) {
self.0.reshape(dims);
}
}
impl<'a, T> Slice2<'a, T> {
pub fn new(dims: Dims, stride: u32, data: &'a [T]) -> Self {
Self(Inner::new(dims, stride, data))
}
}
impl<'a, T> MutSlice2<'a, T> {
pub fn new(dims: Dims, stride: u32, data: &'a mut [T]) -> Self {
Self(Inner::new(dims, stride, data))
}
}
impl<T> AsSlice2<T> for Buf2<T> {
#[inline]
fn as_slice2(&self) -> Slice2<'_, T> {
self.0.as_slice2()
}
}
impl<T> AsSlice2<T> for &Buf2<T> {
#[inline]
fn as_slice2(&self) -> Slice2<'_, T> {
self.0.as_slice2()
}
}
impl<T> AsSlice2<T> for Slice2<'_, T> {
#[inline]
fn as_slice2(&self) -> Slice2<'_, T> {
self.0.as_slice2()
}
}
impl<T> AsSlice2<T> for MutSlice2<'_, T> {
#[inline]
fn as_slice2(&self) -> Slice2<'_, T> {
self.0.as_slice2()
}
}
impl<T> AsMutSlice2<T> for Buf2<T> {
#[inline]
fn as_mut_slice2(&mut self) -> MutSlice2<'_, T> {
self.0.as_mut_slice2()
}
}
impl<T> AsMutSlice2<T> for &mut Buf2<T> {
#[inline]
fn as_mut_slice2(&mut self) -> MutSlice2<'_, T> {
self.0.as_mut_slice2()
}
}
impl<T> AsMutSlice2<T> for MutSlice2<'_, T> {
#[inline]
fn as_mut_slice2(&mut self) -> MutSlice2<'_, T> {
self.0.as_mut_slice2()
}
}
impl<T> Debug for Buf2<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.0.debug_fmt(f, "Buf2")
}
}
impl<T> Debug for Slice2<'_, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.0.debug_fmt(f, "Slice2")
}
}
impl<T> Debug for MutSlice2<'_, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.0.debug_fmt(f, "MutSlice2")
}
}
impl<T> Deref for Buf2<T> {
type Target = Inner<T, Vec<T>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'a, T> Deref for Slice2<'a, T> {
type Target = Inner<T, &'a [T]>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'a, T> Deref for MutSlice2<'a, T> {
type Target = Inner<T, &'a mut [T]>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for Buf2<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<'a, T> DerefMut for MutSlice2<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
pub mod inner {
use core::{
fmt::Formatter,
iter::zip,
marker::PhantomData,
ops::{Deref, DerefMut, Index, IndexMut, Range},
};
use crate::{
math::point::Point2u,
util::{Dims, rect::Rect},
};
use super::{AsSlice2, MutSlice2, Slice2};
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct Inner<T, D> {
dims: Dims,
stride: u32,
data: D,
_pd: PhantomData<T>,
}
impl<T, D> Inner<T, D> {
#[inline]
pub fn width(&self) -> u32 {
self.dims.0
}
#[inline]
pub fn height(&self) -> u32 {
self.dims.1
}
#[inline]
pub fn dims(&self) -> Dims {
self.dims
}
#[inline]
pub fn stride(&self) -> u32 {
self.stride
}
pub fn is_contiguous(&self) -> bool {
let (w, h) = self.dims;
self.stride == w || h <= 1 || w == 0
}
pub fn is_empty(&self) -> bool {
self.dims.0 == 0 || self.dims.1 == 0
}
#[inline]
fn to_index(&self, x: u32, y: u32) -> usize {
(y * self.stride + x) as usize
}
#[inline]
fn to_index_strict(&self, x: u32, y: u32) -> usize {
self.to_index_checked(x, y).unwrap_or_else(|| {
let (w, h) = self.dims;
panic!(
"position (x={x}, y={y}) out of bounds (0..{w}, 0..{h})",
)
})
}
#[inline]
fn to_index_checked(&self, x: u32, y: u32) -> Option<usize> {
let (w, h) = self.dims;
(x < w && y < h).then(|| self.to_index(x, y))
}
fn resolve_bounds(&self, rect: &Rect<u32>) -> (Dims, Range<usize>) {
let (w, h) = self.dims;
let l = rect.left.unwrap_or(0);
let t = rect.top.unwrap_or(0);
let r = rect.right.unwrap_or(w);
let b = rect.bottom.unwrap_or(h);
assert!(l <= r, "range left ({l}) > right ({r})");
assert!(t <= b, "range top ({l}) > bottom ({r})");
assert!(r <= w, "range right ({r}) > width ({w})");
assert!(b <= h, "range bottom ({b}) > height ({h})");
let start = self.to_index(l, t);
let end = if b == t {
self.to_index(r, t)
} else {
self.to_index(r, b - 1)
};
((r - l, b - t), start..end)
}
pub(super) fn debug_fmt(
&self,
f: &mut Formatter,
name: &str,
) -> core::fmt::Result {
f.debug_struct(name)
.field("dims", &self.dims)
.field("stride", &self.stride)
.finish()
}
pub(super) fn reshape(&mut self, dims: Dims) {
assert!(self.is_contiguous());
assert_eq!(dims.0 * dims.1, self.dims.0 * self.dims.1);
self.dims = dims;
}
}
impl<T, D: Deref<Target = [T]>> Inner<T, D> {
#[rustfmt::skip]
pub(super) fn new(dims @ (w, h): Dims, stride: u32, data: D) -> Self {
assert!(w <= stride, "width ({w}) > stride ({stride})");
let len = data.len();
assert!(
h <= 1 || stride as usize <= len,
"stride ({stride}) > data length ({len})"
);
assert!(h as usize <= len, "height ({h}) > data length ({len})");
if h > 0 {
let size = (h - 1) * stride + w;
assert!(
size as usize <= len,
"required size ({size}) > data length ({len})"
);
}
Self { dims, stride, data, _pd: PhantomData }
}
pub(super) fn data(&self) -> &[T] {
&self.data
}
#[inline]
pub fn as_slice2(&self) -> Slice2<'_, T> {
let Self { dims, stride, ref data, _pd } = *self;
Slice2(Inner { dims, stride, data, _pd })
}
pub fn slice(&self, rect: impl Into<Rect>) -> Slice2<'_, T> {
let (dims, rg) = self.resolve_bounds(&rect.into());
Slice2::new(dims, self.stride, &self.data[rg])
}
pub fn get(&self, pos: impl Into<Point2u>) -> Option<&T> {
let [x, y] = pos.into().0;
self.to_index_checked(x, y).map(|i| &self.data[i])
}
pub fn rows(&self) -> impl Iterator<Item = &[T]> {
self.data
.chunks(self.stride as usize)
.map(|row| &row[..self.dims.0 as usize])
}
pub fn iter(&self) -> impl Iterator<Item = &'_ T> {
self.rows().flatten()
}
}
impl<T, D: DerefMut<Target = [T]>> Inner<T, D> {
#[inline]
pub fn as_mut_slice2(&mut self) -> MutSlice2<'_, T> {
#[rustfmt::skip]
let Self { dims, stride, ref mut data, _pd, } = *self;
MutSlice2(Inner { dims, stride, data, _pd })
}
pub(super) fn data_mut(&mut self) -> &mut [T] {
&mut self.data
}
pub fn rows_mut(&mut self) -> impl Iterator<Item = &mut [T]> {
self.data
.chunks_mut(self.stride as usize)
.map(|row| &mut row[..self.dims.0 as usize])
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &'_ mut T> {
self.rows_mut().flatten()
}
pub fn fill(&mut self, val: T)
where
T: Clone,
{
if self.is_contiguous() {
self.data.fill(val);
} else {
self.rows_mut()
.for_each(|row| row.fill(val.clone()));
}
}
pub fn fill_with<F>(&mut self, mut fill_fn: F)
where
F: FnMut(u32, u32) -> T,
{
for (row, y) in zip(self.rows_mut(), 0..) {
for (item, x) in zip(row, 0..) {
*item = fill_fn(x, y);
}
}
}
#[doc(alias = "blit")]
pub fn copy_from(&mut self, other: impl AsSlice2<T>)
where
T: Copy,
{
let other = other.as_slice2();
assert_eq!(
self.dims, other.dims,
"dimension mismatch (self: {:?}, other: {:?})",
self.dims, other.dims
);
for (dest, src) in self.rows_mut().zip(other.rows()) {
dest.copy_from_slice(src);
}
}
pub fn get_mut(&mut self, pos: impl Into<Point2u>) -> Option<&mut T> {
let [x, y] = pos.into().0;
self.to_index_checked(x, y)
.map(|i| &mut self.data[i])
}
pub fn slice_mut(&mut self, rect: impl Into<Rect>) -> MutSlice2<'_, T> {
let (dims, rg) = self.resolve_bounds(&rect.into());
MutSlice2(Inner::new(dims, self.stride, &mut self.data[rg]))
}
}
impl<T, D: Deref<Target = [T]>> Index<usize> for Inner<T, D> {
type Output = [T];
#[inline]
fn index(&self, i: usize) -> &[T] {
let idx = self.to_index_strict(0, i as u32);
let w = self.dims.0 as usize;
&self.data[idx..][..w]
}
}
impl<T, D> IndexMut<usize> for Inner<T, D>
where
Self: Index<usize, Output = [T]>,
D: DerefMut<Target = [T]>,
{
#[inline]
fn index_mut(&mut self, row: usize) -> &mut [T] {
let idx = self.to_index_strict(0, row as u32);
let w = self.dims.0 as usize;
&mut self.data[idx..][..w]
}
}
impl<T, D, Pos> Index<Pos> for Inner<T, D>
where
D: Deref<Target = [T]>,
Pos: Into<Point2u>,
{
type Output = T;
#[inline]
fn index(&self, pos: Pos) -> &T {
let [x, y] = pos.into().0;
&self.data[self.to_index_strict(x, y)]
}
}
impl<T, D, Pos> IndexMut<Pos> for Inner<T, D>
where
D: DerefMut<Target = [T]>,
Pos: Into<Point2u>,
{
#[inline]
fn index_mut(&mut self, pos: Pos) -> &mut T {
let [x, y] = pos.into().0;
let idx = self.to_index_strict(x, y);
&mut self.data[idx]
}
}
}
#[cfg(test)]
mod tests {
use crate::math::pt2;
use super::*;
#[test]
fn buf_new_from() {
let buf = Buf2::new_from((3, 2), 1..);
assert_eq!(buf.data(), &[1, 2, 3, 4, 5, 6]);
}
#[test]
fn buf_new() {
let buf: Buf2<i32> = Buf2::new((3, 2));
assert_eq!(buf.data(), &[0, 0, 0, 0, 0, 0]);
}
#[test]
fn buf_new_with() {
let buf = Buf2::new_with((3, 2), |x, y| x + y);
assert_eq!(buf.data(), &[0, 1, 2, 1, 2, 3]);
}
#[test]
fn buf_extents() {
let buf: Buf2<()> = Buf2::new((4, 5));
assert_eq!(buf.width(), 4);
assert_eq!(buf.height(), 5);
assert_eq!(buf.stride(), 4);
}
#[test]
fn buf_index_and_get() {
let buf = Buf2::new_with((4, 5), |x, y| x * 10 + y);
assert_eq!(buf[2usize], [2, 12, 22, 32]);
assert_eq!(buf[[0, 0]], 0);
assert_eq!(buf[[1, 0]], 10);
assert_eq!(buf[[3, 4]], 34);
assert_eq!(buf.get([2, 3]), Some(&23));
assert_eq!(buf.get([4, 4]), None);
assert_eq!(buf.get([3, 5]), None);
}
#[test]
fn buf_index_mut_and_get_mut() {
let mut buf = Buf2::new_with((4, 5), |x, y| x * 10 + y);
buf[2usize][1] = 123;
assert_eq!(buf[2usize], [2, 123, 22, 32]);
buf[[2, 3]] = 234;
assert_eq!(buf[[2, 3]], 234);
*buf.get_mut([3, 4]).unwrap() = 345;
assert_eq!(buf.get_mut([3, 4]), Some(&mut 345));
assert_eq!(buf.get_mut([4, 4]), None);
assert_eq!(buf.get_mut([3, 5]), None);
}
#[test]
#[should_panic = "position (x=4, y=0) out of bounds (0..4, 0..5)"]
fn buf_index_x_out_of_bounds_should_panic() {
let buf = Buf2::new((4, 5));
let _: i32 = buf[[4, 0]];
}
#[test]
#[should_panic = "position (x=0, y=4) out of bounds (0..5, 0..4)"]
fn buf_index_y_out_of_bounds_should_panic() {
let buf = Buf2::new((5, 4));
let _: i32 = buf[[0, 4]];
}
#[test]
#[should_panic = "position (x=0, y=5) out of bounds (0..4, 0..5)"]
fn buf_index_row_out_of_bounds_should_panic() {
let buf = Buf2::new((4, 5));
let _: &[i32] = &buf[5usize];
}
#[test]
fn buf_slice_range_full() {
let buf: Buf2<()> = Buf2::new((4, 5));
let slice = buf.slice(..);
assert_eq!(slice.width(), 4);
assert_eq!(slice.height(), 5);
assert_eq!(slice.stride(), 4);
let slice = buf.slice((.., ..));
assert_eq!(slice.width(), 4);
assert_eq!(slice.height(), 5);
assert_eq!(slice.stride(), 4);
}
#[test]
fn buf_slice_range_inclusive() {
let buf: Buf2<()> = Buf2::new((4, 5));
let slice = buf.slice((1..=3, 0..=3));
assert_eq!(slice.width(), 3);
assert_eq!(slice.height(), 4);
assert_eq!(slice.stride(), 4);
}
#[test]
fn buf_slice_range_to() {
let buf: Buf2<()> = Buf2::new((4, 5));
let slice = buf.slice((..2, ..4));
assert_eq!(slice.width(), 2);
assert_eq!(slice.height(), 4);
assert_eq!(slice.stride(), 4);
}
#[test]
fn buf_slice_range_from() {
let buf: Buf2<()> = Buf2::new((4, 5));
let slice = buf.slice((3.., 2..));
assert_eq!(slice.width(), 1);
assert_eq!(slice.height(), 3);
assert_eq!(slice.stride(), 4);
}
#[test]
fn buf_slice_empty_range() {
let buf: Buf2<()> = Buf2::new((4, 5));
let empty = buf.slice(pt2(1, 1)..pt2(1, 3));
assert_eq!(empty.width(), 0);
assert_eq!(empty.height(), 2);
assert_eq!(empty.stride(), 4);
let empty = buf.slice(pt2(1, 1)..pt2(3, 1));
assert_eq!(empty.width(), 2);
assert_eq!(empty.height(), 0);
assert_eq!(empty.stride(), 4);
}
#[test]
#[should_panic = "range right (5) > width (4)"]
fn buf_slice_x_out_of_bounds_should_panic() {
let buf: Buf2<()> = Buf2::new((4, 5));
buf.slice((0..5, 1..3));
}
#[test]
#[should_panic = "range bottom (6) > height (5)"]
fn buf_slice_y_out_of_bounds_should_panic() {
let buf: Buf2<()> = Buf2::new((4, 5));
buf.slice((1..3, 0..6));
}
#[test]
#[should_panic = "width (4) > stride (3)"]
fn slice_stride_less_than_width_should_panic() {
let _ = Slice2::new((4, 4), 3, &[0; 16]);
}
#[test]
#[should_panic = "required size (19) > data length (16)"]
fn slice_larger_than_data_should_panic() {
let _ = Slice2::new((4, 4), 5, &[0; 16]);
}
#[test]
fn slice_extents() {
let buf: Buf2<()> = Buf2::new((10, 10));
let slice = buf.slice((1..4, 2..8));
assert_eq!(slice.width(), 3);
assert_eq!(slice.height(), 6);
assert_eq!(slice.stride(), 10);
assert_eq!(slice.data().len(), 5 * 10 + 3);
}
#[test]
fn slice_contiguity() {
let buf: Buf2<()> = Buf2::new((10, 10));
assert!(buf.is_contiguous());
assert!(buf.slice((2..2, 2..8)).is_contiguous());
assert!(buf.slice((2..8, 2..2)).is_contiguous());
assert!(buf.slice((2..8, 2..3)).is_contiguous());
assert!(buf.slice((0..10, 2..8)).is_contiguous());
assert!(buf.slice((.., 2..8)).is_contiguous());
assert!(!buf.slice((2..=2, 1..9)).is_contiguous());
assert!(!buf.slice((2..4, 0..9)).is_contiguous());
assert!(!buf.slice((2..4, 1..10)).is_contiguous());
}
#[test]
#[rustfmt::skip]
fn slice_fill() {
let mut buf = Buf2::new((5, 4));
let mut slice = buf.slice_mut((2.., 1..3));
slice.fill(1);
assert_eq!(
buf.data(),
&[0, 0, 0, 0, 0,
0, 0, 1, 1, 1,
0, 0, 1, 1, 1,
0, 0, 0, 0, 0]
);
}
#[test]
#[rustfmt::skip]
fn slice_fill_with() {
let mut buf = Buf2::new((5, 4));
let mut slice = buf.slice_mut((2.., 1..3));
slice.fill_with(|x, y| x + y);
assert_eq!(
buf.data(),
&[0, 0, 0, 0, 0,
0, 0, 0, 1, 2,
0, 0, 1, 2, 3,
0, 0, 0, 0, 0]
);
}
#[test]
#[rustfmt::skip]
fn slice_copy_from() {
let mut dest = Buf2::new((5, 4));
let src = Buf2::new_with((3, 3), |x, y| x + y);
dest.slice_mut((1..4, 1..)).copy_from(src);
assert_eq!(
dest.data(),
&[0, 0, 0, 0, 0,
0, 0, 1, 2, 0,
0, 1, 2, 3, 0,
0, 2, 3, 4, 0]
);
}
#[test]
fn slice_index() {
let buf = Buf2::new_with((5, 4), |x, y| x * 10 + y);
let slice = buf.slice((2.., 1..3));
assert_eq!(slice[[0, 0]], 21);
assert_eq!(slice[[1, 0]], 31);
assert_eq!(slice[[2, 1]], 42);
assert_eq!(slice.get([2, 1]), Some(&42));
assert_eq!(slice.get([2, 2]), None);
}
#[test]
fn slice_index_mut() {
let mut buf = Buf2::new_with((5, 5), |x, y| x * 10 + y);
let mut slice = buf.slice_mut((2.., 1..3));
slice[[2, 1]] = 123;
assert_eq!(slice[[2, 1]], 123);
assert_eq!(slice.get_mut([2, 1]), Some(&mut 123));
assert_eq!(slice.get([2, 2]), None);
buf[[2, 2]] = 321;
let slice = buf.slice((1.., 2..));
assert_eq!(slice[[1, 0]], 321);
}
#[test]
fn slice_rows() {
let buf = Buf2::new_with((5, 4), |x, y| x * 10 + y);
let slice = buf.slice((2..4, 1..));
let mut rows = slice.rows();
assert_eq!(rows.next(), Some(&[21, 31][..]));
assert_eq!(rows.next(), Some(&[22, 32][..]));
assert_eq!(rows.next(), Some(&[23, 33][..]));
assert_eq!(rows.next(), None);
}
#[test]
fn slice_rows_mut() {
let mut buf = Buf2::new_with((5, 4), |x, y| x * 10 + y);
let mut slice = buf.slice_mut((2..4, 1..));
let mut rows = slice.rows_mut();
assert_eq!(rows.next(), Some(&mut [21, 31][..]));
assert_eq!(rows.next(), Some(&mut [22, 32][..]));
assert_eq!(rows.next(), Some(&mut [23, 33][..]));
assert_eq!(rows.next(), None);
}
#[test]
fn buf_ref_as_slice() {
fn foo<T: AsSlice2<u32>>(buf: T) -> u32 {
buf.as_slice2().width()
}
let buf = Buf2::new((2, 2));
let w = foo(&buf);
assert_eq!(w, buf.width());
}
#[test]
fn buf_ref_as_slice_mut() {
fn foo<T: AsMutSlice2<u32>>(mut buf: T) {
buf.as_mut_slice2()[[1, 1]] = 42;
}
let mut buf = Buf2::new((2, 2));
foo(&mut buf);
assert_eq!(buf[[1, 1]], 42);
}
}