use core::{ops::Range, ptr};
pub(crate) fn shift_copyable_chunks<T>(
begin: usize,
slice: &mut [T],
iter: impl IntoIterator<Item = Range<usize>>,
) -> &mut [T]
where
T: Copy,
{
let mut new_len = begin;
if new_len > slice.len() {
return &mut [];
}
for Range { start, end } in iter {
let Some((diff @ 1..=usize::MAX, local_new_len)) = end.checked_sub(start).and_then(|diff| {
let local_new_len = new_len.checked_add(diff)?;
if local_new_len > slice.len() {
return None;
}
Some((diff, local_new_len))
}) else {
return unsafe { slice.get_unchecked_mut(..new_len) };
};
let ptr = slice.as_mut_ptr();
let src = unsafe { ptr.add(start) };
let dst = unsafe { ptr.add(new_len) };
unsafe {
ptr::copy(src, dst, diff);
}
new_len = local_new_len;
}
unsafe { slice.get_unchecked_mut(..new_len) }
}
#[cfg(kani)]
mod kani {
use crate::misc::bytes_transfer::shift_copyable_chunks;
use alloc::vec::Vec;
#[kani::proof]
fn shift_bytes() {
let begin = kani::any();
let tuples = kani::vec::any_vec::<(usize, usize), 128>();
let ranges: Vec<_> = tuples.into_iter().map(|el| el.0..el.1).collect();
let mut slice = kani::vec::any_vec::<u8, 128>();
let _ = shift_copyable_chunks(begin, &mut slice, ranges.into_iter());
}
}
#[cfg(test)]
mod test {
use crate::misc::bytes_transfer::shift_copyable_chunks;
#[test]
fn shift_bytes_has_correct_outputs() {
let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
assert_eq!(shift_copyable_chunks(2, bytes, [4..6, 8..10]), &mut [0, 1, 4, 5, 8, 9]);
assert_eq!(bytes, &mut [0, 1, 4, 5, 8, 9, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
}
}