use alloc::{vec, vec::Vec};
use bytemuck::{AnyBitPattern, NoUninit};
use core::mem::{align_of, size_of};
pub fn with_scratch<R>(size: usize, mut f: impl FnMut(&mut [u8]) -> R) -> R {
let mut result = None;
with_scratch_dyn(size, &mut |block| result = Some(f(block)));
result.unwrap()
}
fn with_scratch_dyn(size: usize, f: &mut dyn FnMut(&mut [u8])) {
#[inline(never)]
fn on_stack<const SIZE: usize>(size: usize, f: &mut dyn FnMut(&mut [u8])) {
f(&mut [0u8; SIZE][..size])
}
if size <= 512 {
on_stack::<512>(size, f)
} else if size <= 1024 {
on_stack::<1024>(size, f)
} else if size <= 2048 {
on_stack::<2048>(size, f)
} else if size <= 4096 {
on_stack::<4096>(size, f)
} else if size <= 8192 {
on_stack::<8192>(size, f)
} else if size <= 16384 {
on_stack::<16384>(size, f)
} else {
let mut block: Vec<u8> = vec![0; size];
f(&mut block)
}
}
pub fn take_slice<T>(bytes: &mut [u8], len: usize) -> Option<(&mut [T], &mut [u8])>
where
T: AnyBitPattern + NoUninit,
{
if len == 0 {
return Some((Default::default(), bytes));
}
let padding = (bytes.as_ptr() as usize).wrapping_neg() & (align_of::<T>() - 1);
let bytes = bytes.get_mut(padding..)?;
let byte_len = len.checked_mul(size_of::<T>())?;
if byte_len > bytes.len() {
return None;
}
let (mine, rest) = bytes.split_at_mut(byte_len);
let mine = bytemuck::try_cast_slice_mut(mine).ok()?;
Some((mine, rest))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_bucket_hands_back_the_size_asked_for() {
for size in [0, 1, 512, 513, 4096, 16384, 16385, 40000] {
assert_eq!(with_scratch(size, |block| block.len()), size);
}
}
#[test]
fn a_block_arrives_zeroed() {
for size in [8, 700, 20000] {
assert!(with_scratch(size, |block| block
.iter()
.all(|byte| *byte == 0)));
}
}
#[test]
fn a_block_is_carved_into_aligned_slices() {
with_scratch(64, |block| {
let (first, rest) = take_slice::<u32>(block, 3).unwrap();
assert_eq!(first.len(), 3);
assert_eq!(first.as_ptr() as usize % align_of::<u32>(), 0);
let (second, rest) = take_slice::<u16>(rest, 5).unwrap();
assert_eq!(second.len(), 5);
assert_eq!(second.as_ptr() as usize % align_of::<u16>(), 0);
assert!(rest.len() <= 42, "{} left", rest.len());
first[0] = 7;
second[4] = 9;
});
}
#[test]
fn an_empty_slice_takes_nothing() {
let mut block = [0u8; 4];
let (taken, rest) = take_slice::<u64>(&mut block, 0).unwrap();
assert!(taken.is_empty());
assert_eq!(rest.len(), 4);
}
fn unaligned(block: &mut [u8]) -> &mut [u8] {
let base = block.as_ptr() as usize;
let offset = usize::from(base % align_of::<i32>() == 0);
let block = &mut block[offset..];
assert_ne!(block.as_ptr() as usize % align_of::<i32>(), 0);
block
}
#[test]
fn an_unaligned_block_is_aligned_before_anything_is_taken() {
let mut block = [0u8; 40];
let (taken, _) = take_slice::<i32>(unaligned(&mut block), 8).unwrap();
assert_eq!(taken.as_ptr() as usize % align_of::<i32>(), 0);
}
#[test]
fn aligning_comes_out_of_the_block() {
let mut block = [0u8; 40];
assert!(take_slice::<i32>(unaligned(&mut block), 10).is_none());
}
#[test]
fn a_block_too_small_for_what_is_asked_refuses() {
let mut block = [0u8; 8];
assert!(take_slice::<u32>(&mut block, 3).is_none());
let (first, rest) = take_slice::<u32>(&mut block, 1).unwrap();
assert_eq!(first.len(), 1);
assert!(take_slice::<u32>(rest, 2).is_none());
assert!(take_slice::<u32>(&mut block, usize::MAX).is_none());
}
}