use crate::{
core::{GridError, Layout, Pos, RowMajor},
grid::{BoundedGrid, GridBase, GridReadUnchecked, GridWriteUnchecked},
};
use core::marker::PhantomData;
mod array;
pub use array::ArrayGrid;
pub mod bits;
mod iter;
mod r#mut;
mod slice;
use ixy::index::ColMajor;
pub use slice::*;
#[cfg(feature = "alloc")]
mod vec;
#[cfg(feature = "alloc")]
pub use vec::VecGrid;
#[derive(Debug, Clone)]
pub struct GridBuf<T, B, L = RowMajor>
where
B: AsRef<[T]>,
L: Layout,
{
buffer: B,
width: usize,
height: usize,
_element: PhantomData<T>,
_layout: PhantomData<L>,
}
impl<T, B> GridBuf<T, B, RowMajor>
where
B: AsRef<[T]>,
{
pub fn with_buffer_row_major(
buffer: B,
width: usize,
height: usize,
) -> Result<Self, GridError> {
Self::with_buffer(buffer, width, height)
}
pub unsafe fn with_buffer_row_major_unchecked(buffer: B, width: usize, height: usize) -> Self {
unsafe { Self::with_buffer_unchecked(buffer, width, height) }
}
}
impl<T, B> GridBuf<T, B, ColMajor>
where
B: AsRef<[T]>,
{
pub fn with_buffer_col_major(
buffer: B,
width: usize,
height: usize,
) -> Result<Self, GridError> {
Self::with_buffer(buffer, width, height)
}
pub unsafe fn with_buffer_col_major_unchecked(buffer: B, width: usize, height: usize) -> Self {
unsafe { Self::with_buffer_unchecked(buffer, width, height) }
}
}
impl<T, B, L> GridBuf<T, B, L>
where
B: AsRef<[T]>,
L: Layout,
{
pub fn with_buffer(buffer: B, width: usize, height: usize) -> Result<Self, GridError> {
let expected_size = width * height;
if buffer.as_ref().len() != expected_size {
return Err(GridError);
}
Ok(unsafe { Self::with_buffer_unchecked(buffer, width, height) })
}
pub unsafe fn with_buffer_unchecked(buffer: B, width: usize, height: usize) -> Self {
debug_assert_eq!(
buffer.as_ref().len(),
width * height,
"Buffer size does not match grid dimensions"
);
Self {
buffer,
width,
height,
_element: PhantomData,
_layout: PhantomData,
}
}
pub fn get(&self, pos: Pos) -> Option<&T> {
if pos.x < self.width && pos.y < self.height {
Some(&self.buffer.as_ref()[L::to_1d(pos, self.width).index])
} else {
None
}
}
#[must_use]
pub fn into_inner(self) -> (B, usize, usize) {
(self.buffer, self.width, self.height)
}
#[allow(clippy::iter_without_into_iter)]
pub fn iter(&self) -> core::slice::Iter<'_, T> {
self.buffer.as_ref().iter()
}
}
impl<T, B, L> GridBase for GridBuf<T, B, L>
where
B: AsRef<[T]>,
L: Layout,
{
type Element = T;
}
unsafe impl<T, B, L> BoundedGrid for GridBuf<T, B, L>
where
B: AsRef<[T]>,
L: Layout,
{
fn width(&self) -> usize {
self.width
}
fn height(&self) -> usize {
self.height
}
}
impl<T, B, L> GridReadUnchecked for GridBuf<T, B, L>
where
B: AsRef<[T]>,
L: Layout,
{
unsafe fn get_unchecked(&self, pos: Pos) -> &T {
let index = L::to_1d(pos, self.width).index;
unsafe { self.buffer.as_ref().get_unchecked(index) }
}
}
impl<T, B, L> GridWriteUnchecked for GridBuf<T, B, L>
where
B: AsRef<[T]> + AsMut<[T]>,
L: Layout,
{
unsafe fn set_unchecked(&mut self, pos: Pos, value: T) {
let index = L::to_1d(pos, self.width).index;
unsafe { *self.buffer.as_mut().get_unchecked_mut(index) = value }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn impl_bounded_grid() {
let grid = VecGrid::new_filled_row_major(5, 4, 0);
assert_eq!(grid.width(), 5);
assert_eq!(grid.height(), 4);
}
#[test]
fn impl_get_unchecked() {
let grid = VecGrid::new_filled_row_major(5, 4, 42);
let pos = Pos::new(2, 3);
assert_eq!(unsafe { grid.get_unchecked(pos) }, &42);
}
#[test]
fn impl_set_unchecked() {
let mut grid = VecGrid::new_row_major(5, 4);
let pos = Pos::new(2, 3);
unsafe { grid.set_unchecked(pos, 99) };
assert_eq!(unsafe { grid.get_unchecked(pos) }, &99);
}
}