Skip to content

Commit

Permalink
Improve buf doc comments and correctness
Browse files Browse the repository at this point in the history
  • Loading branch information
jdahlstrom committed Nov 30, 2024
1 parent ec99422 commit 6f551dc
Showing 1 changed file with 90 additions and 73 deletions.
163 changes: 90 additions & 73 deletions core/src/util/buf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
use alloc::{vec, vec::Vec};
use core::fmt::{self, Debug, Formatter};
use core::iter;
use core::ops::{Deref, DerefMut};

use crate::util::Dims;

use inner::Inner;

//
Expand Down Expand Up @@ -36,10 +38,7 @@ pub trait AsMutSlice2<T> {
/// without explicitly copying the contents to a new, larger buffer.
///
/// `Buf2` stores its elements contiguously, in standard row-major order,
/// such that the coordinate pair (x, y) maps to the index
/// ```text
/// buf.width() * y + x
/// ```
/// such that the coordinate pair (x, y) maps to index `buf.width() * y + x`
/// in the backing vector.
///
/// # Examples
Expand Down Expand Up @@ -92,34 +91,45 @@ impl<T> Buf2<T> {
/// # Examples
/// ```
/// use retrofire_core::util::buf::Buf2;
///
/// let buf = Buf2::new_from((3, 3), 1..);
/// assert_eq!(buf.data(), [1, 2, 3,
/// 4, 5, 6,
/// 7, 8, 9]);
/// ```
///
/// # Panics
/// If `dims.0 * dims.1 > isize::MAX`, or if `init` has fewer than `w * h`
/// elements.
pub fn new_from<I>(dims @ (w, h): Dims, init: I) -> Self
/// If `w * h > isize::MAX`, or if `init` has fewer than `w * h` elements.
pub fn new_from<I>((w, h): Dims, init: I) -> Self
where
I: IntoIterator<Item = T>,
{
let wh = w as u64 * h as u64; // u32->u64, cannot overflow
let Ok(len) = isize::try_from(wh).and_then(usize::try_from) else {
panic!("w * h cannot exceed isize::MAX ({w} * {h} = {wh})")
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 mut data = Vec::with_capacity(len);
data.extend(init.into_iter().take(len));
Self(Inner::new(dims, w, data))
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))
}

/// Returns a buffer of the given size, with every element initialized
/// to `T::default()`.
/// Returns a buffer of size `w` × `h`, with every element initialized to
/// `T::default()`.
///
/// # Examples
/// ```
/// use retrofire_core::util::buf::Buf2;
///
/// let buf: Buf2<i32> = Buf2::new((3, 3));
/// assert_eq!(buf.data(), [0, 0, 0,
/// 0, 0, 0,
Expand All @@ -128,12 +138,12 @@ impl<T> Buf2<T> {
///
/// # Panics
/// If `w * h > isize::MAX`.
pub fn new(dims: Dims) -> Self
pub fn new((w, h): Dims) -> Self
where
T: Default + Clone,
{
let data = vec![T::default(); (dims.0 * dims.1) as usize];
Self(Inner::new(dims, dims.0, data))
let data = vec![T::default(); (w * h) as usize];
Self(Inner::new((w, h), w, data))
}

/// Returns a buffer of size `w` × `h`, initialized by repeatedly calling
Expand All @@ -146,6 +156,7 @@ impl<T> Buf2<T> {
/// # Examples
/// ```
/// use retrofire_core::util::buf::Buf2;
///
/// let buf = Buf2::new_with((3, 3), |x, y| 10 * y + x);
/// assert_eq!(buf.data(), [ 0, 1, 2,
/// 10, 11, 12,
Expand All @@ -154,15 +165,22 @@ impl<T> Buf2<T> {
///
/// # Panics
/// If `w * h > isize::MAX`.
pub fn new_with<F>(dims: Dims, init_fn: F) -> Self
pub fn new_with<F>((w, h): Dims, mut init_fn: F) -> Self
where
F: Clone + FnMut(u32, u32) -> T,
F: FnMut(u32, u32) -> T,
{
let init = (0..dims.1).flat_map(move |y| {
let mut init_fn = init_fn.clone();
(0..dims.0).map(move |x| init_fn(x, y)) //
});
Self::new_from(dims, init)
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)
}),
)
}

/// Returns a view of the backing data of `self`.
Expand Down Expand Up @@ -322,13 +340,14 @@ impl<'a, T> DerefMut for MutSlice2<'a, T> {
}

pub mod inner {
use core::fmt::Formatter;
use core::marker::PhantomData;
use core::ops::{Deref, DerefMut, Index, IndexMut, Range};
use core::{
fmt::Formatter,
iter::zip,
marker::PhantomData,
ops::{Deref, DerefMut, Index, IndexMut, Range},
};

use crate::math::vec::Vec2u;
use crate::util::rect::Rect;
use crate::util::Dims;
use crate::{math::vec::Vec2u, util::rect::Rect, util::Dims};

use super::{AsSlice2, MutSlice2, Slice2};

Expand Down Expand Up @@ -447,21 +466,19 @@ pub mod inner {
/// # Panics
/// if `stride < w` or if the slice would overflow `data`.
#[rustfmt::skip]
pub(super) fn new(dims: Dims, stride: u32, data: D) -> Self {
let (w, h) = dims;
pub(super) fn new(dims @ (w, h): Dims, stride: u32, data: D) -> Self {
assert!(w <= stride, "width ({w}) > stride ({stride})");

assert!(w <= stride,
"width ({w}) > stride ({stride})");
let len = data.len() as u32;
let len = data.len();
assert!(
h <= 1 || stride <= len,
h <= 1 || stride as usize <= len,
"stride ({stride}) > data length ({len})"
);
assert!(h <= len, "height ({h}) > data length ({len})");
assert!(h as usize <= len, "height ({h}) > data length ({len})");
if h > 0 {
let size = (h - 1) * stride + w;
assert!(
size <= len,
size as usize <= len,
"required size ({size}) > data length ({len})"
);
}
Expand Down Expand Up @@ -503,10 +520,10 @@ pub mod inner {
.map(|row| &row[..self.dims.0 as usize])
}

/// Returns an iterator over all the elements of `self` in row-major order.
/// Returns an iterator over the elements of `self` in row-major order.
///
/// First returns the elements on row 0 from left to right, followed by the elements
/// on row 1, and so on.
/// First returns the elements on row 0 from left to right, followed by
/// the elements on row 1, and so on.
pub fn iter(&self) -> impl Iterator<Item = &'_ T> {
self.rows().flatten()
}
Expand Down Expand Up @@ -550,45 +567,40 @@ pub mod inner {
.for_each(|row| row.fill(val.clone()));
}
}
/// Fills `self` by repeatedly calling the function.
/// Fills `self` by calling a function for each element.
///
/// For each element, `init_fn(x, y)` is invoked, where `x` is the column
/// index and `y` the row index of the element being initialized. The
/// function invocations occur in row-major order.
/// Calls `f(x, y)` for every element, where `x` and `y` are the column
/// and row indices of the element. Proceeds in row-major order.
pub fn fill_with<F>(&mut self, mut fill_fn: F)
where
F: Copy + FnMut(u32, u32) -> T,
F: FnMut(u32, u32) -> T,
{
let (w, h) = self.dims;
let mut fill = (0..h).flat_map(move |y| {
(0..w).map(move |x| fill_fn(x, y)) //
});
if self.is_contiguous() {
self.data.fill_with(|| fill.next().unwrap());
} else {
self.rows_mut().for_each(|row| {
row.fill_with(|| fill.next().unwrap()); //
})
for (row, y) in zip(self.rows_mut(), 0..) {
for (item, x) in zip(row, 0..) {
*item = fill_fn(x, y);
}
}
}

/// Copies each element in `src` to the same position in `self`.
/// Copies each element in `other` to the same position in `self`.
///
/// This operation is often called "blitting".
///
/// # Panics
/// if the dimensions of `self` and `src` don't match.
/// if the dimensions of `self` and `other` do not match.
#[doc(alias = "blit")]
pub fn copy_from(&mut self, src: impl AsSlice2<T>)
pub fn copy_from(&mut self, other: impl AsSlice2<T>)
where
T: Copy,
{
let src = src.as_slice2();
let (sw, sh) = self.dims;
let (ow, oh) = src.dims;
assert_eq!(sw, ow, "width ({sw}) != source width ({ow})",);
assert_eq!(sh, oh, "height ({sh}) != source height ({oh})",);
for (dest, src) in self.rows_mut().zip(src.rows()) {
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);
}
}
Expand All @@ -614,9 +626,12 @@ pub mod inner {
impl<T, D: Deref<Target = [T]>> Index<usize> for Inner<T, D> {
type Output = [T];

/// Returns a reference to the row of `self` at index `i`.
/// Returns a reference to the row at index `i`.
///
/// The returned slice has length `self.width()`.
///
/// # Panics
/// If `row >= self.height()`.
#[inline]
fn index(&self, i: usize) -> &[T] {
let idx = self.to_index_strict(0, i as u32);
Expand All @@ -630,9 +645,12 @@ pub mod inner {
Self: Index<usize, Output = [T]>,
D: DerefMut<Target = [T]>,
{
/// Returns a mutable reference to the row of `self` at index `i`.
/// Returns a mutable reference to the row at index `i`.
///
/// The returned slice has length `self.width()`.
///
/// # Panics
/// If `row >= self.height()`.
#[inline]
fn index_mut(&mut self, row: usize) -> &mut [T] {
let idx = self.to_index_strict(0, row as u32);
Expand All @@ -648,10 +666,10 @@ pub mod inner {
{
type Output = T;

/// Returns a reference to the element of `self` at position `pos`.
/// Returns a reference to the element at position `pos`.
///
/// # Panics
/// If `pos` is out of bounds of `self`.
/// If `pos` is out of bounds.
#[inline]
fn index(&self, pos: Pos) -> &T {
let [x, y] = pos.into().0;
Expand All @@ -664,11 +682,10 @@ pub mod inner {
D: DerefMut<Target = [T]>,
Pos: Into<Vec2u>,
{
/// Returns a mutable reference to the element of `self`
/// at position `pos`.
/// Returns a mutable reference to the element at position `pos`.
///
/// # Panics
/// If `pos` is out of bounds of `self`.
/// If `pos` is out of bounds.
#[inline]
fn index_mut(&mut self, pos: Pos) -> &mut T {
let [x, y] = pos.into().0;
Expand Down

0 comments on commit 6f551dc

Please sign in to comment.