1use alloc::{vec, vec::Vec};
8use bytemuck::{AnyBitPattern, NoUninit};
9use core::mem::{align_of, size_of};
10
11pub fn with_scratch<R>(size: usize, mut f: impl FnMut(&mut [u8]) -> R) -> R {
17 let mut result = None;
18 with_scratch_dyn(size, &mut |block| result = Some(f(block)));
19 result.unwrap()
21}
22
23fn with_scratch_dyn(size: usize, f: &mut dyn FnMut(&mut [u8])) {
26 #[inline(never)]
29 fn on_stack<const SIZE: usize>(size: usize, f: &mut dyn FnMut(&mut [u8])) {
30 f(&mut [0u8; SIZE][..size])
31 }
32 if size <= 512 {
33 on_stack::<512>(size, f)
34 } else if size <= 1024 {
35 on_stack::<1024>(size, f)
36 } else if size <= 2048 {
37 on_stack::<2048>(size, f)
38 } else if size <= 4096 {
39 on_stack::<4096>(size, f)
40 } else if size <= 8192 {
41 on_stack::<8192>(size, f)
42 } else if size <= 16384 {
43 on_stack::<16384>(size, f)
44 } else {
45 let mut block: Vec<u8> = vec![0; size];
46 f(&mut block)
47 }
48}
49
50pub fn take_slice<T>(bytes: &mut [u8], len: usize) -> Option<(&mut [T], &mut [u8])>
58where
59 T: AnyBitPattern + NoUninit,
60{
61 if len == 0 {
62 return Some((Default::default(), bytes));
63 }
64 let padding = (bytes.as_ptr() as usize).wrapping_neg() & (align_of::<T>() - 1);
65 let bytes = bytes.get_mut(padding..)?;
66 let byte_len = len.checked_mul(size_of::<T>())?;
68 if byte_len > bytes.len() {
69 return None;
70 }
71 let (mine, rest) = bytes.split_at_mut(byte_len);
72 let mine = bytemuck::try_cast_slice_mut(mine).ok()?;
74 Some((mine, rest))
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn every_bucket_hands_back_the_size_asked_for() {
83 for size in [0, 1, 512, 513, 4096, 16384, 16385, 40000] {
86 assert_eq!(with_scratch(size, |block| block.len()), size);
87 }
88 }
89
90 #[test]
91 fn a_block_arrives_zeroed() {
92 for size in [8, 700, 20000] {
95 assert!(with_scratch(size, |block| block
96 .iter()
97 .all(|byte| *byte == 0)));
98 }
99 }
100
101 #[test]
102 fn a_block_is_carved_into_aligned_slices() {
103 with_scratch(64, |block| {
104 let (first, rest) = take_slice::<u32>(block, 3).unwrap();
105 assert_eq!(first.len(), 3);
106 assert_eq!(first.as_ptr() as usize % align_of::<u32>(), 0);
107 let (second, rest) = take_slice::<u16>(rest, 5).unwrap();
108 assert_eq!(second.len(), 5);
109 assert_eq!(second.as_ptr() as usize % align_of::<u16>(), 0);
110 assert!(rest.len() <= 42, "{} left", rest.len());
113 first[0] = 7;
114 second[4] = 9;
115 });
116 }
117
118 #[test]
119 fn an_empty_slice_takes_nothing() {
120 let mut block = [0u8; 4];
123 let (taken, rest) = take_slice::<u64>(&mut block, 0).unwrap();
124 assert!(taken.is_empty());
125 assert_eq!(rest.len(), 4);
126 }
127
128 fn unaligned(block: &mut [u8]) -> &mut [u8] {
130 let base = block.as_ptr() as usize;
131 let offset = usize::from(base % align_of::<i32>() == 0);
132 let block = &mut block[offset..];
133 assert_ne!(block.as_ptr() as usize % align_of::<i32>(), 0);
134 block
135 }
136
137 #[test]
138 fn an_unaligned_block_is_aligned_before_anything_is_taken() {
139 let mut block = [0u8; 40];
140 let (taken, _) = take_slice::<i32>(unaligned(&mut block), 8).unwrap();
141 assert_eq!(taken.as_ptr() as usize % align_of::<i32>(), 0);
142 }
143
144 #[test]
145 fn aligning_comes_out_of_the_block() {
146 let mut block = [0u8; 40];
149 assert!(take_slice::<i32>(unaligned(&mut block), 10).is_none());
150 }
151
152 #[test]
153 fn a_block_too_small_for_what_is_asked_refuses() {
154 let mut block = [0u8; 8];
155 assert!(take_slice::<u32>(&mut block, 3).is_none());
156 let (first, rest) = take_slice::<u32>(&mut block, 1).unwrap();
159 assert_eq!(first.len(), 1);
160 assert!(take_slice::<u32>(rest, 2).is_none());
161 assert!(take_slice::<u32>(&mut block, usize::MAX).is_none());
163 }
164}