use std::fmt::Debug;
use crate::{
error::{Error, Result},
util::{CACHE_LINE_BYTE_SIZE, tracing_wrappers::*},
};
fn alloc_zeroed_fallible(
total_len: usize,
bytes_per_row: usize,
num_rows: usize,
) -> Result<Vec<u8>> {
let mut storage = Vec::new();
storage
.try_reserve(total_len)
.map_err(|_| Error::ImageOutOfMemory(bytes_per_row, num_rows))?;
storage.resize(total_len, 0);
Ok(storage)
}
#[derive(Debug, Clone)]
pub(super) struct RawImageBuffer {
storage: Vec<u8>,
offset: usize,
bytes_per_row: usize,
num_rows: usize,
bytes_between_rows: usize,
}
impl RawImageBuffer {
pub(super) fn check_vals(num_rows: usize, bytes_per_row: usize, bytes_between_rows: usize) {
if num_rows > 0 {
assert!(bytes_per_row > 0);
assert!(bytes_between_rows >= bytes_per_row);
assert!(
bytes_between_rows
.checked_mul(num_rows - 1)
.unwrap()
.checked_add(bytes_per_row)
.unwrap()
<= isize::MAX as usize
);
}
}
#[inline(always)]
pub(super) fn is_aligned(&self, align: usize) -> bool {
if self.num_rows == 0 {
return true;
}
self.bytes_per_row.is_multiple_of(align)
&& self.bytes_between_rows.is_multiple_of(align)
&& self.data_ptr_addr().is_multiple_of(align)
}
#[inline(always)]
fn data_ptr_addr(&self) -> usize {
if self.storage.is_empty() {
0
} else {
(self.storage.as_ptr() as usize) + self.offset
}
}
pub(super) fn minimum_allocation_size(&self) -> usize {
if self.num_rows == 0 {
0
} else {
(self.num_rows - 1) * self.bytes_between_rows + self.bytes_per_row
}
}
#[inline]
pub(super) fn byte_size(&self) -> (usize, usize) {
(self.bytes_per_row, self.num_rows)
}
#[inline]
pub(super) fn dimensions(&self) -> (usize, usize, usize) {
(self.bytes_per_row, self.num_rows, self.bytes_between_rows)
}
#[inline]
pub(super) fn data_slice(&self) -> &[u8] {
let size = self.minimum_allocation_size();
if size == 0 {
&[]
} else {
&self.storage[self.offset..self.offset + size]
}
}
#[inline]
pub(super) fn data_slice_mut(&mut self) -> &mut [u8] {
let size = self.minimum_allocation_size();
if size == 0 {
&mut []
} else {
let start = self.offset;
&mut self.storage[start..start + size]
}
}
#[inline(always)]
pub(super) fn row(&self, row: usize) -> &[u8] {
assert!(row < self.num_rows);
let start = self.offset + row * self.bytes_between_rows;
&self.storage[start..start + self.bytes_per_row]
}
#[inline(always)]
pub(super) fn row_mut(&mut self, row: usize) -> &mut [u8] {
assert!(row < self.num_rows);
let start = self.offset + row * self.bytes_between_rows;
&mut self.storage[start..start + self.bytes_per_row]
}
#[inline(always)]
pub(super) fn distinct_rows_mut<I: DistinctRowsIndexes>(&mut self, rows: I) -> I::Output<'_> {
rows.get_rows_mut(self)
}
pub(super) fn try_allocate(byte_size: (usize, usize), uninit: bool) -> Result<RawImageBuffer> {
let (bytes_per_row, num_rows) = byte_size;
if bytes_per_row == 0 || num_rows == 0 {
return Ok(RawImageBuffer {
storage: Vec::new(),
offset: 0,
bytes_per_row: 0,
num_rows: 0,
bytes_between_rows: 0,
});
}
if bytes_per_row as u64 >= i64::MAX as u64 / 4 || num_rows as u64 >= i64::MAX as u64 / 4 {
return Err(Error::ImageSizeTooLarge(bytes_per_row, num_rows));
}
debug!("trying to allocate image");
let bytes_between_rows =
bytes_per_row.div_ceil(CACHE_LINE_BYTE_SIZE) * CACHE_LINE_BYTE_SIZE;
let data_len = (num_rows - 1)
.checked_mul(bytes_between_rows)
.and_then(|v| v.checked_add(bytes_per_row))
.ok_or(Error::ImageSizeTooLarge(bytes_per_row, num_rows))?;
assert_ne!(data_len, 0);
let total_len = data_len
.checked_add(CACHE_LINE_BYTE_SIZE - 1)
.ok_or(Error::ImageSizeTooLarge(bytes_per_row, num_rows))?;
let storage = if uninit {
#[cfg(feature = "allow-unsafe")]
{
let mut v = Vec::new();
v.try_reserve(total_len)
.map_err(|_| Error::ImageOutOfMemory(bytes_per_row, num_rows))?;
#[allow(unsafe_code)]
unsafe {
v.set_len(total_len);
}
v
}
#[cfg(not(feature = "allow-unsafe"))]
{
alloc_zeroed_fallible(total_len, bytes_per_row, num_rows)?
}
} else {
alloc_zeroed_fallible(total_len, bytes_per_row, num_rows)?
};
let base_ptr = storage.as_ptr() as usize;
let aligned_ptr = base_ptr.div_ceil(CACHE_LINE_BYTE_SIZE) * CACHE_LINE_BYTE_SIZE;
let offset = aligned_ptr - base_ptr;
Ok(RawImageBuffer {
storage,
offset,
bytes_per_row,
num_rows,
bytes_between_rows,
})
}
pub(super) fn try_clone(&self) -> Result<Self> {
let out = RawImageBuffer::try_allocate(self.byte_size(), true)?;
assert_eq!(self.bytes_per_row, out.bytes_per_row);
assert_eq!(self.bytes_between_rows, out.bytes_between_rows);
assert_eq!(self.num_rows, out.num_rows);
let data_len = self.minimum_allocation_size();
if data_len != 0 {
let mut result = out;
let src = &self.storage[self.offset..self.offset + data_len];
let dst = &mut result.storage[result.offset..result.offset + data_len];
dst.copy_from_slice(src);
Ok(result)
} else {
Ok(out)
}
}
pub(super) fn deallocate(&mut self) {
self.storage = Vec::new();
self.offset = 0;
self.num_rows = 0;
self.bytes_per_row = 0;
self.bytes_between_rows = 0;
}
}
#[allow(private_interfaces)]
pub trait DistinctRowsIndexes {
type Output<'a>;
type CastOutput<'a, T: 'static>;
fn get_rows_mut<'a>(&self, image: &'a mut RawImageBuffer) -> Self::Output<'a>;
fn cast_rows<'a, T: crate::image::ImageDataType>(
rows: Self::Output<'a>,
) -> Self::CastOutput<'a, T>;
}
#[allow(private_interfaces)]
impl<const S: usize> DistinctRowsIndexes for [usize; S] {
type Output<'a> = [&'a mut [u8]; S];
type CastOutput<'a, T: 'static> = [&'a mut [T]; S];
#[inline(always)]
fn get_rows_mut<'a>(&self, image: &'a mut RawImageBuffer) -> Self::Output<'a> {
for i in 0..S {
assert!(self[i] < image.num_rows);
for j in i + 1..S {
assert_ne!(self[i], self[j]);
}
}
let ranges: [std::ops::Range<usize>; S] = std::array::from_fn(|i| {
let start = image.offset + self[i] * image.bytes_between_rows;
start..start + image.bytes_per_row
});
let storage = &mut image.storage[..];
get_distinct_slices(storage, ranges)
}
#[inline(always)]
fn cast_rows<'a, T: crate::image::ImageDataType>(
rows: Self::Output<'a>,
) -> Self::CastOutput<'a, T> {
rows.map(|row| crate::image::typed::cast_row_mut(row))
}
}
fn get_distinct_slices<const S: usize>(
data: &mut [u8],
ranges: [std::ops::Range<usize>; S],
) -> [&mut [u8]; S] {
let mut indexed: [(usize, std::ops::Range<usize>); S] =
std::array::from_fn(|i| (i, ranges[i].clone()));
for i in 1..S {
let mut j = i;
while j > 0 && indexed[j].1.start < indexed[j - 1].1.start {
indexed.swap(j, j - 1);
j -= 1;
}
}
for i in 1..S {
assert!(
indexed[i].1.start >= indexed[i - 1].1.end,
"overlapping row ranges"
);
}
let mut slices: Vec<(usize, &mut [u8])> = Vec::with_capacity(S);
let mut remaining = data;
let mut consumed = 0usize;
for item in indexed.iter().take(S) {
let (orig_idx, ref range) = *item;
let skip = range.start - consumed;
let len = range.len();
let (_, rest) = remaining.split_at_mut(skip);
let (chunk, rest2) = rest.split_at_mut(len);
slices.push((orig_idx, chunk));
remaining = rest2;
consumed = range.end;
}
slices.sort_by_key(|(idx, _)| *idx);
let mut iter = slices.into_iter().map(|(_, s)| s);
std::array::from_fn(|_| iter.next().unwrap())
}