diff --git a/src/affine.rs b/src/affine.rs index b0152a5..9fc7b3c 100644 --- a/src/affine.rs +++ b/src/affine.rs @@ -10,16 +10,17 @@ impl ImageCloner<'_, CHANNELS> { /// ``` #[must_use = "function does not modify the original image"] pub fn flip_v(&self) -> Image, CHANNELS> { - let mut out = self.alloc(); + let mut out = self.uninit(); for y in 0..self.height() { for x in 0..self.width() { // SAFETY: looping over self, all ok (could be safe versions, bounds would be elided) let p = unsafe { self.pixel(x, y) }; // SAFETY: looping over self. - unsafe { out.set_pixel(x, self.height() - y - 1, p) }; + unsafe { out.write(&p, (x, self.height() - y - 1)) }; } } - out + // SAFETY: init + unsafe { out.assume_init() } } /// Flip an image horizontally @@ -30,16 +31,17 @@ impl ImageCloner<'_, CHANNELS> { /// ``` #[must_use = "function does not modify the original image"] pub fn flip_h(&self) -> Image, CHANNELS> { - let mut out = self.alloc(); + let mut out = self.uninit(); for y in 0..self.height() { for x in 0..self.width() { // SAFETY: looping over self, all ok let p = unsafe { self.pixel(x, y) }; // SAFETY: looping over self, all ok - unsafe { out.set_pixel(self.width() - x - 1, y, p) }; + unsafe { out.write(&p, (self.width() - x - 1, y)) }; } } - out + // SAFETY: init + unsafe { out.assume_init() } } } diff --git a/src/cloner.rs b/src/cloner.rs index b38aedd..f110c1c 100644 --- a/src/cloner.rs +++ b/src/cloner.rs @@ -5,7 +5,7 @@ //! # let i = Image::<_, 1>::alloc(5, 5); //! unsafe { i.cloner().rot_270() }; //! ``` -use crate::Image; +use crate::{uninit, Image}; /// A neat way to clone a image. /// @@ -19,6 +19,11 @@ impl<'a, const C: usize> ImageCloner<'a, C> { self.0.to_owned() } + /// create a new uninit image the right size for use + pub(crate) fn uninit(&self) -> uninit::Image { + uninit::Image::new(self.width, self.height) + } + /// Create a [`ImageCloner`] from a [Image]<&\[[u8]\]> pub const fn from(i: Image<&'a [u8], C>) -> Self { Self(i) diff --git a/src/lib.rs b/src/lib.rs index 6e7131f..098fc03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,6 +49,7 @@ //! - `default`: \[`save`, `scale`\]. #![feature( maybe_uninit_write_slice, + hint_assert_unchecked, slice_swap_unchecked, generic_const_exprs, slice_as_chunks, @@ -72,7 +73,7 @@ missing_docs )] #![allow(clippy::zero_prefixed_literal, incomplete_features)] -use std::{num::NonZeroU32, ops::Range}; +use std::{hint::assert_unchecked, num::NonZeroU32, ops::Range}; mod affine; #[cfg(feature = "blur")] @@ -122,25 +123,6 @@ impl CopyWithinUnchecked for [T] { } } -/// like assert!(), but causes undefined behaviour at runtime when the condition is not met. -/// -/// # Safety -/// -/// UB if condition is false. -macro_rules! assert_unchecked { - ($cond:expr) => {{ - if !$cond { - #[cfg(debug_assertions)] - let _ = ::core::ptr::NonNull::<()>::dangling().as_ref(); // force unsafe wrapping block - #[cfg(debug_assertions)] - panic!("assertion failed: {} returned false", stringify!($cond)); - #[cfg(not(debug_assertions))] - std::hint::unreachable_unchecked() - } - }}; -} -use assert_unchecked; - trait At { fn at(self, x: u32, y: u32) -> usize; } @@ -455,9 +437,9 @@ impl Image { T: AsRef<[U]>, { // SAFETY: 0 sized images illegal - unsafe { assert_unchecked!(self.len() > CHANNELS) }; + unsafe { assert_unchecked(self.len() > CHANNELS) }; // SAFETY: no half pixels! - unsafe { assert_unchecked!(self.len() % CHANNELS == 0) }; + unsafe { assert_unchecked(self.len() % CHANNELS == 0) }; self.buffer().as_ref().array_chunks::() } @@ -546,9 +528,9 @@ impl + AsRef<[u8]>, const CHANNELS: usize> Image { /// Returns a iterator over every pixel, mutably pub fn chunked_mut(&mut self) -> impl Iterator { // SAFETY: 0 sized images are not allowed - unsafe { assert_unchecked!(self.len() > CHANNELS) }; + unsafe { assert_unchecked(self.len() > CHANNELS) }; // SAFETY: buffer cannot have half pixels - unsafe { assert_unchecked!(self.len() % CHANNELS == 0) }; + unsafe { assert_unchecked(self.len() % CHANNELS == 0) }; self.buffer.as_mut().array_chunks_mut::() } diff --git a/src/overlay.rs b/src/overlay.rs index dbb7a0a..eef4272 100644 --- a/src/overlay.rs +++ b/src/overlay.rs @@ -160,7 +160,7 @@ impl + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt> for Im #[inline] unsafe fn overlay_at(&mut self, with: &Image, x: u32, y: u32) -> &mut Self { // SAFETY: caller upholds this - unsafe { assert_unchecked!(x + with.width() <= self.width()) }; + unsafe { assert_unchecked(x + with.width() <= self.width()) }; debug_assert!(y + with.height() <= self.height()); for j in 0..with.height() { let i_x = j as usize * with.width() as usize * 4 diff --git a/src/span.rs b/src/span.rs index 7ac76c9..69a01ad 100644 --- a/src/span.rs +++ b/src/span.rs @@ -32,3 +32,11 @@ impl Span for Range<(u32, u32)> { i.at::(sx, sy)..i.at::(ex, ey) } } + +impl Sealed for (u32, u32) {} +impl Span for (u32, u32) { + #[inline(always)] + fn range(self, i: (u32, u32)) -> Range { + i.at::(self.0, self.1)..i.at::(self.0, self.1) + C + } +} diff --git a/src/uninit.rs b/src/uninit.rs index 3555beb..c4c341b 100644 --- a/src/uninit.rs +++ b/src/uninit.rs @@ -1,9 +1,14 @@ //! the houser of uninitialized memory. €$@!0В𴬔!℡ //! //! contains [`Image`], an uninitialized image. -use std::{mem::MaybeUninit, num::NonZeroU32}; +use std::{ + hint::assert_unchecked, + mem::MaybeUninit, + num::NonZeroU32, + ops::{Index, IndexMut}, +}; -use crate::CopyWithinUnchecked; +use crate::{span::Span, CopyWithinUnchecked}; /// A uninitialized image. Be sure to initialize it! pub struct Image { @@ -13,6 +18,14 @@ pub struct Image { height: NonZeroU32, } +impl Index for Image { + type Output = [T]; + + fn index(&self, index: I) -> &Self::Output { + &self.buffer()[index.range::((self.width(), self.height()))] + } +} + impl Image { /// Create a new uninit image. This is not init. pub fn new(width: NonZeroU32, height: NonZeroU32) -> Self { @@ -27,19 +40,31 @@ impl Image { /// /// # Safety /// index must be in bounds. - pub unsafe fn write(&mut self, data: &[T], i: impl crate::span::Span) { - let range = i.range::((self.width(), self.height())); - // SAFETY: write - let dat = unsafe { self.buf().get_unchecked_mut(range) }; + /// data and indexed range must have same len. + pub unsafe fn write(&mut self, data: &[T], i: impl Span) { + // SAFETY: caller + let dat = unsafe { self.slice(i) }; + // SAFETY: caller + unsafe { assert_unchecked(dat.len() == data.len()) }; MaybeUninit::write_slice(dat, data); } + /// Slice the image. + /// + /// # Safety + /// index must be in bounds. + pub unsafe fn slice(&mut self, i: impl Span) -> &mut [MaybeUninit] { + let range = i.range::((self.width(), self.height())); + // SAFETY: assured + unsafe { self.buf().get_unchecked_mut(range) } + } + /// Copy a range to a position. /// /// # Safety /// /// both parts must be in bounds. - pub unsafe fn copy_within(&mut self, i: impl crate::span::Span, to: usize) { + pub unsafe fn copy_within(&mut self, i: impl Span, to: usize) { let range = i.range::((self.width(), self.height())); // SAFETY: copy! unsafe { self.buf().copy_within_unchecked(range, to) };