1use core::mem::*;
4
5#[derive(Debug, PartialEq)]
7pub enum EmbedValueError<E> {
8 SplitUninitError(SplitUninitError),
11 ConstructionError(E),
13}
14
15impl<E> From<SplitUninitError> for EmbedValueError<E> {
16 #[inline]
17 fn from(e: SplitUninitError) -> Self {
18 EmbedValueError::SplitUninitError(e)
19 }
20}
21
22#[inline]
44pub unsafe fn embed<'a, T, F, E>(
45 destination: &'a mut [u8],
46 f: F,
47) -> Result<&'a mut T, EmbedValueError<E>>
48where
49 F: FnOnce(&'a mut [u8]) -> Result<T, E>,
50{
51 debug_assert!(!destination.as_ptr().is_null());
52 let (_prefix, uninit_ref, suffix) = split_uninit_from_bytes(destination)?;
53 let ptr = uninit_ref.as_mut_ptr();
54 core::ptr::write(ptr, f(suffix).map_err(EmbedValueError::ConstructionError)?);
55 if let Some(ptr) = ptr.as_mut() {
57 Ok(ptr)
58 } else {
59 unreachable!("Just initialized the value and the pointer is based on a non-null slice")
60 }
61}
62
63#[inline]
80pub fn embed_uninit<'a, T, F, E>(
81 destination: &'a mut [MaybeUninit<u8>],
82 f: F,
83) -> Result<&'a mut T, EmbedValueError<E>>
84where
85 F: FnOnce(&'a mut [MaybeUninit<u8>]) -> Result<T, E>,
86{
87 debug_assert!(!destination.as_ptr().is_null());
88 let (_prefix, uninit_ref, suffix) = split_uninit_from_uninit_bytes(destination)?;
89 unsafe {
90 let ptr = uninit_ref.as_mut_ptr();
91 core::ptr::write(ptr, f(suffix).map_err(EmbedValueError::ConstructionError)?);
92 if let Some(ptr) = ptr.as_mut() {
94 Ok(ptr)
95 } else {
96 unreachable!("Just initialized the value and the pointer is based on a non-null slice")
97 }
98 }
99}
100
101#[derive(Debug, PartialEq)]
103pub enum SplitUninitError {
104 ZeroSizedTypesUnsupported,
106 Unalignable,
109 InsufficientSpace,
112}
113
114#[allow(clippy::type_complexity)]
120#[inline]
121pub fn split_uninit_from_bytes<T>(
122 destination: &mut [u8],
123) -> Result<(&mut [u8], &mut MaybeUninit<T>, &mut [u8]), SplitUninitError> {
124 debug_assert!(!destination.as_ptr().is_null());
125 let uninit_bytes = unsafe { &mut *(destination as *mut [u8] as *mut [MaybeUninit<u8>]) };
130 let (prefix, uninit_ref, suffix): (_, &mut MaybeUninit<T>, _) =
131 split_uninit_from_uninit_bytes(uninit_bytes)?;
132 let uninit_prefix = unsafe { &mut *(prefix as *mut [MaybeUninit<u8>] as *mut [u8]) };
133 let uninit_ref = unsafe { transmute(uninit_ref) };
134 let uninit_suffix = unsafe { &mut *(suffix as *mut [MaybeUninit<u8>] as *mut [u8]) };
135 Ok((uninit_prefix, uninit_ref, uninit_suffix))
136}
137
138#[allow(clippy::type_complexity)]
144#[inline]
145pub fn split_uninit_from_uninit_bytes<T>(
146 destination: &mut [MaybeUninit<u8>],
147) -> Result<
148 (
149 &mut [MaybeUninit<u8>],
150 &mut MaybeUninit<T>,
151 &mut [MaybeUninit<u8>],
152 ),
153 SplitUninitError,
154> {
155 debug_assert!(!destination.as_ptr().is_null());
156 if size_of::<T>() == 0 {
157 return Err(SplitUninitError::ZeroSizedTypesUnsupported);
158 }
159 let ptr = destination.as_mut_ptr();
160 let offset = ptr.align_offset(align_of::<T>());
161 if offset == core::usize::MAX {
162 return Err(SplitUninitError::Unalignable);
163 }
164 if offset > destination.len() {
165 return Err(SplitUninitError::InsufficientSpace);
166 }
167 if let Some(end) = offset.checked_add(size_of::<T>()) {
168 if end > destination.len() {
169 return Err(SplitUninitError::InsufficientSpace);
170 }
171 } else {
172 return Err(SplitUninitError::InsufficientSpace);
173 }
174 let (prefix, rest) = destination.split_at_mut(offset);
175 let (middle, suffix) = rest.split_at_mut(size_of::<T>());
176 let maybe_uninit = middle.as_mut_ptr() as *mut MaybeUninit<T>;
177 let maybe_uninit = if let Some(maybe_uninit) = unsafe { maybe_uninit.as_mut() } {
178 maybe_uninit
179 } else {
180 unreachable!("Should be non-null since we rely on the input byte slice being non-null.")
181 };
182 Ok((prefix, maybe_uninit, suffix))
183}
184
185#[cfg(test)]
186#[allow(dead_code)]
187mod tests {
188 use super::*;
189 #[derive(PartialEq)]
190 struct ZST;
191
192 #[derive(Default)]
193 struct TooBig {
194 colossal: [Colossal; 32],
195 }
196 #[derive(Default)]
197 struct Colossal {
198 huge: [Huge; 32],
199 }
200 #[derive(Default)]
201 struct Huge {
202 large: [Large; 32],
203 }
204 #[derive(Default)]
205 struct Large {
206 medium: [u64; 32],
207 }
208
209 #[test]
210 fn zero_sized_types_not_permitted() {
211 let mut bytes = [0u8; 64];
212 if let Err(e) = split_uninit_from_bytes::<ZST>(&mut bytes[..]) {
213 assert_eq!(SplitUninitError::ZeroSizedTypesUnsupported, e);
214 } else {
215 unreachable!("Expected an err");
216 }
217 if let Err(e) = unsafe { embed(&mut bytes[..], |_| -> Result<ZST, ()> { Ok(ZST) }) } {
218 assert_eq!(
219 EmbedValueError::SplitUninitError(SplitUninitError::ZeroSizedTypesUnsupported),
220 e
221 );
222 } else {
223 unreachable!("Expected an err");
224 }
225
226 let mut uninit_bytes: [MaybeUninit<u8>; 64] =
227 unsafe { MaybeUninit::uninit().assume_init() };
228 if let Err(e) = split_uninit_from_uninit_bytes::<ZST>(&mut uninit_bytes[..]) {
229 assert_eq!(SplitUninitError::ZeroSizedTypesUnsupported, e);
230 } else {
231 unreachable!("Expected an err");
232 }
233 if let Err(e) = embed_uninit(&mut uninit_bytes[..], |_| -> Result<ZST, ()> { Ok(ZST) }) {
234 assert_eq!(
235 EmbedValueError::SplitUninitError(SplitUninitError::ZeroSizedTypesUnsupported),
236 e
237 );
238 } else {
239 unreachable!("Expected an err");
240 }
241 }
242
243 #[test]
244 fn split_not_enough_space_detected() {
245 let mut bytes = [0u8; 64];
246 if let Err(e) = split_uninit_from_bytes::<TooBig>(&mut bytes[..]) {
247 match e {
248 SplitUninitError::InsufficientSpace | SplitUninitError::Unalignable => (),
249 _ => unreachable!("Unexpected error kind"),
250 }
251 } else {
252 unreachable!("Expected an err");
253 }
254 }
255
256 #[test]
257 fn split_uninit_not_enough_space_detected() {
258 let mut uninit_bytes: [MaybeUninit<u8>; 64] =
259 unsafe { MaybeUninit::uninit().assume_init() };
260 if let Err(e) = split_uninit_from_uninit_bytes::<TooBig>(&mut uninit_bytes[..]) {
261 match e {
262 SplitUninitError::InsufficientSpace | SplitUninitError::Unalignable => (),
263 _ => unreachable!("Unexpected error kind"),
264 }
265 } else {
266 unreachable!("Expected an err");
267 }
268 }
269
270 #[test]
271 fn split_uninit_from_bytes_observe_leftovers() {
272 let mut bytes = [0u8; 61];
273 match split_uninit_from_bytes::<[u16; 3]>(&mut bytes[..]) {
274 Ok((prefix, mid, suffix)) => {
275 *mid = MaybeUninit::new([3, 4, 5]);
276 for v in prefix {
277 assert_eq!(0, *v);
278 }
279 for v in suffix {
280 assert_eq!(0, *v);
281 }
282 }
283 Err(SplitUninitError::Unalignable) => return (), Err(e) => unreachable!("Unexpected error: {:?}", e),
285 }
286 }
287
288 #[test]
289 fn split_uninit_from_uninit_bytes_observe_leftovers() {
290 let mut bytes: [MaybeUninit<u8>; 64] = unsafe { MaybeUninit::uninit().assume_init() };
291 match split_uninit_from_uninit_bytes::<[u16; 3]>(&mut bytes[..]) {
292 Ok((prefix, mid, suffix)) => {
293 *mid = MaybeUninit::new([3, 4, 5]);
294 let had_prefix = prefix.len() > 0;
295 let had_suffix = suffix.len() > 0;
296 assert!(had_prefix | had_suffix);
297 }
298 Err(SplitUninitError::Unalignable) => return (), Err(e) => unreachable!("Unexpected error: {:?}", e),
300 }
301 }
302
303 #[test]
304 fn split_uninit_from_bytes_empty() {
305 let bytes: &mut [u8] = &mut [];
306 assert_eq!(
307 SplitUninitError::InsufficientSpace,
308 split_uninit_from_bytes::<[u16; 3]>(bytes).unwrap_err()
309 );
310 }
311
312 #[test]
313 fn split_uninit_from_uninit_bytes_empty() {
314 let bytes: &mut [MaybeUninit<u8>] = &mut [];
315 assert_eq!(
316 SplitUninitError::InsufficientSpace,
317 split_uninit_from_uninit_bytes::<[u16; 3]>(bytes).unwrap_err()
318 );
319 }
320
321 #[test]
322 fn embed_not_enough_space_detected() {
323 let mut bytes = [0u8; 64];
324 if let Err(e) = unsafe {
325 embed(&mut bytes[..], |_| -> Result<Colossal, ()> {
326 unreachable!("Don't expect this to execute since we can tell from the types that there is not enough space")
327 })
328 } {
329 match e {
330 EmbedValueError::SplitUninitError(SplitUninitError::InsufficientSpace)
331 | EmbedValueError::SplitUninitError(SplitUninitError::Unalignable) => (),
332 _ => unreachable!("Unexpected error kind"),
333 }
334 } else {
335 unreachable!("Expected an err");
336 }
337 }
338
339 #[test]
340 fn embed_uninit_not_enough_space_detected() {
341 let mut uninit_bytes: [MaybeUninit<u8>; 64] =
342 unsafe { MaybeUninit::uninit().assume_init() };
343 if let Err(e) = embed_uninit(&mut uninit_bytes[..], |_| -> Result<Colossal, ()> {
344 unreachable!("Don't expect this to execute since we can tell from the types that there is not enough space")
345 }) {
346 match e {
347 EmbedValueError::SplitUninitError(SplitUninitError::InsufficientSpace)
348 | EmbedValueError::SplitUninitError(SplitUninitError::Unalignable) => (),
349 _ => unreachable!("Unexpected error kind"),
350 }
351 } else {
352 unreachable!("Expected an err");
353 }
354 }
355
356 #[test]
357 fn happy_path_split() {
358 let mut bytes = [0u8; 512];
359 let (prefix, _large_ref, suffix) = match split_uninit_from_bytes::<Large>(&mut bytes[..]) {
360 Ok(r) => r,
361 Err(SplitUninitError::Unalignable) => return (), Err(e) => unreachable!("Unexpected error: {:?}", e),
363 };
364 assert_eq!(
365 prefix.len() + core::mem::size_of::<Large>() + suffix.len(),
366 bytes.len()
367 );
368 }
369
370 #[test]
371 fn happy_path_split_uninit() {
372 let mut uninit_bytes: [MaybeUninit<u8>; 512] =
373 unsafe { MaybeUninit::uninit().assume_init() };
374 let (prefix, _large_ref, suffix) =
375 match split_uninit_from_uninit_bytes::<Large>(&mut uninit_bytes[..]) {
376 Ok(r) => r,
377 Err(SplitUninitError::Unalignable) => return (), Err(e) => unreachable!("Unexpected error: {:?}", e),
379 };
380 assert_eq!(
381 prefix.len() + core::mem::size_of::<Large>() + suffix.len(),
382 uninit_bytes.len()
383 );
384 }
385
386 #[test]
387 fn happy_path_embed() {
388 const BACKING_BYTES_MAX_SIZE: usize = 512;
389 let mut bytes = [2u8; BACKING_BYTES_MAX_SIZE];
390 let large_ref = match unsafe {
391 embed(&mut bytes[..], |b| -> Result<Large, ()> {
392 assert!(b.iter().all(|b| *b == 2));
393 let mut l = Large::default();
394 l.medium[0] = 3;
395 l.medium[1] = 1;
396 l.medium[2] = 4;
397 Ok(l)
398 })
399 } {
400 Ok(r) => r,
401 Err(EmbedValueError::SplitUninitError(SplitUninitError::Unalignable)) => return (), Err(e) => unreachable!("Unexpected error: {:?}", e),
403 };
404
405 assert_eq!(3, large_ref.medium[0]);
406 assert_eq!(1, large_ref.medium[1]);
407 assert_eq!(4, large_ref.medium[2]);
408 }
409 #[test]
410 fn happy_path_embed_uninit() {
411 const BACKING_BYTES_MAX_SIZE: usize = 512;
412 let mut uninit_bytes: [MaybeUninit<u8>; BACKING_BYTES_MAX_SIZE] =
413 unsafe { MaybeUninit::uninit().assume_init() };
414 let large_ref = match embed_uninit(&mut uninit_bytes[..], |_| -> Result<Large, ()> {
415 let mut l = Large::default();
416 l.medium[0] = 3;
417 l.medium[1] = 1;
418 l.medium[2] = 4;
419 Ok(l)
420 }) {
421 Ok(r) => r,
422 Err(EmbedValueError::SplitUninitError(SplitUninitError::Unalignable)) => return (), Err(e) => unreachable!("Unexpected error: {:?}", e),
424 };
425 assert_eq!(3, large_ref.medium[0]);
426 assert_eq!(1, large_ref.medium[1]);
427 assert_eq!(4, large_ref.medium[2]);
428 }
429 #[test]
430 fn embed_does_not_run_drops() {
431 let mut storage: [u8; 16] = [0u8; 16];
432 #[derive(Debug)]
433 struct Target(bool);
434 impl Drop for Target {
435 fn drop(&mut self) {
436 self.0 = true;
437 }
438 }
439 let emb = unsafe {
440 embed(&mut storage[..], move |_leftovers| {
441 Result::<Target, ()>::Ok(Target(false))
442 })
443 .unwrap()
444 };
445
446 assert!(!emb.0);
447 }
448 #[test]
449 fn embed_uninit_does_not_run_drops() {
450 let mut storage: [MaybeUninit<u8>; 16] = unsafe { MaybeUninit::uninit().assume_init() };
451 #[derive(Debug)]
452 struct Target(bool);
453 impl Drop for Target {
454 fn drop(&mut self) {
455 self.0 = true;
456 }
457 }
458 let emb = embed_uninit(&mut storage[..], move |_leftovers| {
459 Result::<Target, ()>::Ok(Target(false))
460 })
461 .unwrap();
462
463 assert!(!emb.0);
464 }
465}