use super::{CatalogError, MAX_NDIM};
pub(crate) fn chunk_grid_counts(shape: &[u64], chunk_shape: &[u64]) -> Vec<u64> {
shape
.iter()
.zip(chunk_shape.iter())
.map(|(&s, &cs)| s.div_ceil(cs))
.collect()
}
pub(crate) fn total_chunk_count(counts: &[u64]) -> Result<u64, CatalogError> {
counts.iter().try_fold(1u64, |a, &b| {
a.checked_mul(b).ok_or(CatalogError::InvalidWriteSpec(
"chunk grid element count overflow",
))
})
}
pub(crate) fn tile_raw_byte_len(
shape: &[u64],
chunk_shape: &[u64],
chunk_coord: &[u64],
ndim: usize,
elem_size: usize,
) -> Result<u64, CatalogError> {
let tile = tile_extent(shape, chunk_shape, chunk_coord, ndim);
let nelem: u64 = tile.iter().try_fold(1u64, |a, &b| a.checked_mul(b)).ok_or(
CatalogError::InvalidWriteSpec("tile element count overflow"),
)?;
nelem
.checked_mul(
u64::try_from(elem_size)
.map_err(|_| CatalogError::InvalidWriteSpec("element size overflow"))?,
)
.ok_or(CatalogError::InvalidWriteSpec("tile byte length overflow"))
}
pub(crate) fn tile_extent(
shape: &[u64],
chunk_shape: &[u64],
chunk_coord: &[u64],
ndim: usize,
) -> Vec<u64> {
(0..ndim)
.map(|d| {
let start = chunk_coord[d] * chunk_shape[d];
let end = (start + chunk_shape[d]).min(shape[d]);
end - start
})
.collect()
}
pub(crate) fn row_major_stride_elems(shape: &[u64], d: usize) -> u64 {
shape[d + 1..].iter().product()
}
pub(crate) fn linear_elem_row_major(
global: &[u64],
shape: &[u64],
ndim: usize,
) -> Result<u64, CatalogError> {
let mut idx: u64 = 0;
for d in 0..ndim {
let g = global[d];
if g >= shape[d] {
return Err(CatalogError::InvalidWriteSpec(
"tile extraction produced out-of-bounds index",
));
}
let stride = row_major_stride_elems(shape, d);
idx = idx
.checked_add(
g.checked_mul(stride)
.ok_or(CatalogError::InvalidWriteSpec("linear index overflow"))?,
)
.ok_or(CatalogError::InvalidWriteSpec("linear index overflow"))?;
}
Ok(idx)
}
pub(crate) fn local_coords_from_linear(k: u64, tile: &[u64], ndim: usize) -> [u64; MAX_NDIM] {
let mut rem = k;
let mut local = [0u64; MAX_NDIM];
for d in (0..ndim).rev() {
let td = tile[d];
local[d] = rem % td;
rem /= td;
}
local
}
pub(crate) fn chunk_coord_from_linear(k: u64, counts: &[u64], ndim: usize) -> [u64; MAX_NDIM] {
let mut rem = k;
let mut coord = [0u64; MAX_NDIM];
for d in (0..ndim).rev() {
let c = counts[d];
coord[d] = rem % c;
rem /= c;
}
coord
}
pub(crate) fn ap_intersects_half_open(
s: u64,
e: u64,
step: u64,
interval_lo: u64,
interval_hi: u64,
) -> bool {
if step == 0 || s >= e || interval_lo >= interval_hi {
return false;
}
let end = interval_hi.min(e);
if interval_lo >= end {
return false;
}
let k_lo = if s >= interval_lo {
0
} else {
(interval_lo - s).div_ceil(step)
};
let max_valid = end.saturating_sub(1);
if s > max_valid {
return false;
}
let k_hi = (max_valid - s) / step;
k_lo <= k_hi
}
pub fn chunk_coords_intersecting_strided(
shape: &[u64],
chunk_shape: &[u64],
g0: &[u64],
g1_exclusive: &[u64],
step: &[u64],
) -> Result<Vec<[u64; MAX_NDIM]>, CatalogError> {
let ndim = shape.len();
if chunk_shape.len() != ndim
|| g0.len() != ndim
|| g1_exclusive.len() != ndim
|| step.len() != ndim
{
return Err(CatalogError::InvalidWriteSpec(
"shape, chunk_shape, global box, and step must have the same rank",
));
}
for d in 0..ndim {
if step[d] == 0 {
return Err(CatalogError::InvalidWriteSpec(
"step must be >= 1 on every axis",
));
}
if g1_exclusive[d] > shape[d] || g0[d] >= g1_exclusive[d] {
return Err(CatalogError::InvalidWriteSpec(
"global selection box must satisfy 0 <= start < stop <= shape[d] on every axis",
));
}
}
let counts = chunk_grid_counts(shape, chunk_shape);
let n = total_chunk_count(&counts)?;
let mut out = Vec::new();
for k in 0..n {
let c = chunk_coord_from_linear(k, &counts, ndim);
let mut touch = true;
for d in 0..ndim {
let cs = chunk_shape[d];
let tile_start = c[d].saturating_mul(cs);
let tile_end_exclusive = (tile_start + cs).min(shape[d]);
if !ap_intersects_half_open(
g0[d],
g1_exclusive[d],
step[d],
tile_start,
tile_end_exclusive,
) {
touch = false;
break;
}
}
if touch {
out.push(c);
}
}
Ok(out)
}
pub fn chunk_coords_intersecting_global_box(
shape: &[u64],
chunk_shape: &[u64],
g0: &[u64],
g1_exclusive: &[u64],
) -> Result<Vec<[u64; MAX_NDIM]>, CatalogError> {
let ndim = shape.len();
if chunk_shape.len() != ndim || g0.len() != ndim || g1_exclusive.len() != ndim {
return Err(CatalogError::InvalidWriteSpec(
"shape, chunk_shape, and global box must have the same rank",
));
}
for d in 0..ndim {
if g1_exclusive[d] > shape[d] || g0[d] >= g1_exclusive[d] {
return Err(CatalogError::InvalidWriteSpec(
"global selection box must satisfy 0 <= start < stop <= shape[d] on every axis",
));
}
}
let steps = [1u64; MAX_NDIM];
chunk_coords_intersecting_strided(shape, chunk_shape, g0, g1_exclusive, &steps[..ndim])
}
pub(crate) fn extract_tile_row_major(
full: &[u8],
shape: &[u64],
chunk_shape: &[u64],
chunk_coord: &[u64],
ndim: usize,
elem_size: usize,
) -> Result<Vec<u8>, CatalogError> {
let tile = tile_extent(shape, chunk_shape, chunk_coord, ndim);
let nelem: u64 = tile.iter().try_fold(1u64, |a, &b| a.checked_mul(b)).ok_or(
CatalogError::InvalidWriteSpec("tile element count overflow"),
)?;
let nbytes_u64 = nelem
.checked_mul(
u64::try_from(elem_size)
.map_err(|_| CatalogError::InvalidWriteSpec("element size overflow"))?,
)
.ok_or(CatalogError::InvalidWriteSpec("tile byte length overflow"))?;
let nbytes = usize::try_from(nbytes_u64).map_err(|_| CatalogError::TooLargeForPlatform {
field: "tile_byte_length",
value: nbytes_u64,
})?;
let mut out = vec![0u8; nbytes];
let mut o = 0usize;
for k in 0..nelem {
let local = local_coords_from_linear(k, &tile, ndim);
let mut global = [0u64; MAX_NDIM];
for d in 0..ndim {
global[d] = chunk_coord[d] * chunk_shape[d] + local[d];
}
let li = linear_elem_row_major(&global[..ndim], shape, ndim)?;
let src = usize::try_from(li)
.map_err(|_| CatalogError::TooLargeForPlatform {
field: "linear_element_index",
value: li,
})?
.checked_mul(elem_size)
.ok_or(CatalogError::InvalidWriteSpec("byte offset overflow"))?;
if src + elem_size > full.len() {
return Err(CatalogError::InvalidWriteSpec(
"full tensor buffer shorter than implied by shape",
));
}
out[o..o + elem_size].copy_from_slice(&full[src..src + elem_size]);
o += elem_size;
}
Ok(out)
}
pub(crate) fn write_tile_row_major_into(
full: &[u8],
shape: &[u64],
chunk_shape: &[u64],
chunk_coord: &[u64],
ndim: usize,
elem_size: usize,
buf: &mut [u8],
) -> Result<(), CatalogError> {
let tile = extract_tile_row_major(full, shape, chunk_shape, chunk_coord, ndim, elem_size)?;
if tile.len() != buf.len() {
return Err(CatalogError::InvalidWriteSpec(
"tile buffer length mismatch for chunk",
));
}
buf.copy_from_slice(&tile);
Ok(())
}
#[allow(dead_code)]
pub(crate) fn extract_f32_tile_row_major(
full: &[u8],
shape: &[u64],
chunk_shape: &[u64],
chunk_coord: &[u64],
ndim: usize,
) -> Result<Vec<u8>, CatalogError> {
extract_tile_row_major(full, shape, chunk_shape, chunk_coord, ndim, 4)
}