use crate::domain::error::{LetoError, Result};
const TRANSPOSE_PAYLOAD_BUDGET_BYTES: usize = 32 * 1024;
fn transpose_tile<T>() -> usize {
let element_bytes = core::mem::size_of::<T>().max(1);
let tile_elements = (TRANSPOSE_PAYLOAD_BUDGET_BYTES / (2 * element_bytes)).max(1);
let side = tile_elements.isqrt().max(1);
1usize << side.ilog2()
}
pub fn transpose_copy<T: Clone>(
source: &[T],
destination: &mut [T],
rows: usize,
columns: usize,
) -> Result<()> {
if transpose_extent(source.len(), destination.len(), rows, columns)? == 0 {
return Ok(());
}
transpose_blocked(source, columns, destination, rows, columns);
Ok(())
}
pub fn transpose_copy_strided<T: Clone>(
source: &[T],
source_pitch: usize,
destination: &mut [T],
rows: usize,
columns: usize,
) -> Result<()> {
#[expect(
clippy::unnecessary_lazy_evaluations,
reason = "Avoid eager LetoError drop on successful arithmetic; ADR 0027"
)]
let elements = rows
.checked_mul(columns)
.ok_or_else(|| LetoError::Overflow {
reason: "strided transpose element count",
})?;
if destination.len() != elements {
return Err(LetoError::StorageError {
reason: format!(
"strided transpose destination length {} does not match expected {elements}",
destination.len()
),
});
}
if source_pitch < columns {
return Err(LetoError::StorageError {
reason: format!(
"strided transpose source pitch {source_pitch} is narrower than {columns} columns"
),
});
}
if elements == 0 {
return Ok(());
}
#[expect(
clippy::unnecessary_lazy_evaluations,
reason = "Avoid eager LetoError drop on successful arithmetic; ADR 0027"
)]
let span = (rows - 1)
.checked_mul(source_pitch)
.and_then(|leading_rows| leading_rows.checked_add(columns))
.ok_or_else(|| LetoError::Overflow {
reason: "strided transpose source span",
})?;
if source.len() < span {
return Err(LetoError::StorageError {
reason: format!(
"strided transpose source length {} is shorter than the {span} elements the window spans",
source.len()
),
});
}
isize::try_from(elements).map_err(|_| LetoError::Overflow {
reason: "strided transpose signed layout extent",
})?;
transpose_blocked(source, source_pitch, destination, rows, columns);
Ok(())
}
fn transpose_blocked<T: Clone>(
source: &[T],
pitch: usize,
destination: &mut [T],
rows: usize,
columns: usize,
) {
let (height, width) = (columns, rows);
let tile = transpose_tile::<T>();
if width >= pitch {
for row_start in (0..height).step_by(tile) {
let row_end = (row_start + tile).min(height);
for column_start in (0..width).step_by(tile) {
let column_end = (column_start + tile).min(width);
let source_columns = source[column_start * pitch..].chunks(pitch);
let destination_rows = &mut destination[row_start * width..row_end * width];
for (row_offset, destination_row) in
destination_rows.chunks_exact_mut(width).enumerate()
{
let row = row_start + row_offset;
for (target, source_column) in destination_row[column_start..column_end]
.iter_mut()
.zip(source_columns.clone())
{
*target = source_column[row].clone();
}
}
}
}
return;
}
for column_start in (0..width).step_by(tile) {
let column_end = (column_start + tile).min(width);
for row_start in (0..height).step_by(tile) {
let row_end = (row_start + tile).min(height);
let destination_rows = &mut destination[row_start * width..row_end * width];
for column in column_start..column_end {
let source_column = &source[column * pitch + row_start..column * pitch + row_end];
for (destination_row, value) in
destination_rows.chunks_exact_mut(width).zip(source_column)
{
destination_row[column] = value.clone();
}
}
}
}
}
#[inline]
fn transpose_extent(
source_len: usize,
destination_len: usize,
rows: usize,
columns: usize,
) -> Result<usize> {
#[expect(
clippy::unnecessary_lazy_evaluations,
reason = "Avoid eager LetoError drop on successful arithmetic; ADR 0027"
)]
let elements = rows
.checked_mul(columns)
.ok_or_else(|| LetoError::Overflow {
reason: "dense transpose element count",
})?;
for (role, actual) in [("source", source_len), ("destination", destination_len)] {
if actual != elements {
return Err(LetoError::StorageError {
reason: format!(
"dense transpose {role} length {actual} does not match expected {elements}"
),
});
}
}
isize::try_from(elements).map_err(|_| LetoError::Overflow {
reason: "dense transpose signed layout extent",
})?;
Ok(elements)
}