diff --git a/util/src/zerocopy/mod.rs b/util/src/zerocopy/mod.rs index 6856bea..9711fe6 100644 --- a/util/src/zerocopy/mod.rs +++ b/util/src/zerocopy/mod.rs @@ -1,3 +1,20 @@ +//! This module provides utilities for working with zero-copy references +//! and slices. +//! +//! It offers the following primary abstractions and traits: +//! +//! - [`RefMaker`](crate::zerocopy::RefMaker): A helper structure for safely +//! creating `zerocopy::Ref` references from byte slices. +//! - [`ZerocopyEmancipateExt`](crate::zerocopy::ZerocopyEmancipateExt): +//! A trait to convert `Ref` into a borrowed `Ref<&[u8], T>`. +//! - [`ZerocopyEmancipateMutExt`](crate::zerocopy::ZerocopyEmancipateMutExt): +//! A trait to convert `Ref` into a borrowed mutable `Ref<&mut [u8], T>`. +//! - [`ZerocopySliceExt`](crate::zerocopy::ZerocopySliceExt): Extension methods +//! for parsing byte slices into zero-copy references. +//! - [`ZerocopyMutSliceExt`](crate::zerocopy::ZerocopyMutSliceExt): +//! Extension methods for parsing and zeroizing byte slices into zero-copy +//! references. + mod ref_maker; mod zerocopy_ref_ext; mod zerocopy_slice_ext; diff --git a/util/src/zerocopy/ref_maker.rs b/util/src/zerocopy/ref_maker.rs index 1ba24e8..f0c46ed 100644 --- a/util/src/zerocopy/ref_maker.rs +++ b/util/src/zerocopy/ref_maker.rs @@ -1,74 +1,206 @@ -use std::marker::PhantomData; +//! A module providing the [`RefMaker`] type and its associated methods for constructing +//! [`zerocopy::Ref`] references from byte buffers. use anyhow::{ensure, Context}; +use std::marker::PhantomData; use zerocopy::{ByteSlice, ByteSliceMut, Ref}; use zeroize::Zeroize; use crate::zeroize::ZeroizedExt; +/// A convenience type for working with buffers and extracting [`zerocopy::Ref`] +/// references. +/// +/// `RefMaker` holds a buffer and a target type parameter `T`. Using `RefMaker`, +/// you can validate that the provided buffer is large enough for `T` and then +/// parse out a strongly-typed reference (`Ref`) to that data. It also provides +/// methods for extracting prefixes and suffixes from the buffer. +/// +/// # Example +/// +/// ``` +/// # use zerocopy::{AsBytes, FromBytes, FromZeroes, Ref};/// +/// # use rosenpass_util::zerocopy::RefMaker; +/// +/// #[derive(FromBytes, FromZeroes, AsBytes)] +/// #[repr(C)] +/// struct Header { +/// field1: u32, +/// field2: u16, +/// field3: u16, +/// } +/// #[repr(align(4))] +/// struct AlignedBuf([u8; 8]); +/// let bytes = AlignedBuf([0xAA, 0xBB, 0xCC, 0xDD, +/// 0x00, 0x10, 0x20, 0x30]); +/// let rm = RefMaker::<&[u8], Header>::new(&bytes.0); +/// let header_ref: Ref<&[u8], Header> = rm.parse().unwrap(); +/// assert_eq!(header_ref.field1, 0xDDCCBBAA); +/// assert_eq!(header_ref.field2, 0x1000); +/// assert_eq!(header_ref.field3, 0x3020); +/// ``` #[derive(Clone, Copy, Debug)] -/// A convenience type for working with mutable references to a buffer and an -/// expected target type. pub struct RefMaker { buf: B, _phantom_t: PhantomData, } impl RefMaker { - /// Creates a new RefMaker with the given buffer + /// Creates a new `RefMaker` with the given buffer. + /// + /// # Example + /// + /// ``` + /// # use rosenpass_util::zerocopy::RefMaker; + /// let buffer = [0u8; 10]; + /// let rm: RefMaker<_, u32> = RefMaker::new(buffer); + /// ``` pub fn new(buf: B) -> Self { let _phantom_t = PhantomData; Self { buf, _phantom_t } } - /// Returns the size in bytes needed for target type T + /// Returns the size in bytes required by the target type `T`. + /// This is currently defined as [`std::mem::size_of::`] of `T`. pub const fn target_size() -> usize { std::mem::size_of::() } - /// Consumes this RefMaker and returns the inner buffer + /// Consumes this `RefMaker` and returns the inner buffer. pub fn into_buf(self) -> B { self.buf } - /// Returns a reference to the inner buffer + /// Returns a reference to the inner buffer. pub fn buf(&self) -> &B { &self.buf } - /// Returns a mutable reference to the inner buffer + /// Returns a mutable reference to the inner buffer. pub fn buf_mut(&mut self) -> &mut B { &mut self.buf } } impl RefMaker { - /// Parses the buffer into a reference of type T + /// Parses the buffer into a [`zerocopy::Ref`]. + /// + /// This will fail if the buffer is smaller than `size_of::`. + /// + /// # Errors + /// + /// Returns an error if the buffer is undersized or if parsing fails. + /// + /// # Example + /// + /// ``` + /// # use zerocopy::{AsBytes, FromBytes, FromZeroes, Ref}; + /// # use rosenpass_util::zerocopy::RefMaker; + /// + /// #[derive(FromBytes, FromZeroes, AsBytes, Debug)] + /// #[repr(C)] + /// struct Data(u32); + /// + /// let bytes: &[u8] = &[0x01, 0x00, 0x00, 0x00]; + /// let data_ref: Ref<&[u8], Data> = RefMaker::<_, Data>::new(bytes).parse().unwrap(); + /// assert_eq!(data_ref.0, 1); + /// + /// // errors if buffer is undersized + /// let bytes: &[u8] = &[0x01, 0x02, 0x03]; + /// let parse_error = RefMaker::<_, Data>::new(bytes).parse() + /// .expect_err("Should error"); + /// assert_eq!(parse_error.to_string(), + /// "Buffer is undersized at 3 bytes (need 4 bytes)!"); + /// + /// // errors if the byte buffer is misaligned + /// let bytes = [1u8, 2, 3, 4, 5, 6, 7, 8]; + /// let parse_error = RefMaker::<_, Data>::new(&bytes[1..5]).parse() + /// .expect_err("Should error"); + /// assert_eq!(parse_error.to_string(), "Parser error!"); + /// ``` pub fn parse(self) -> anyhow::Result> { self.ensure_fit()?; Ref::::new(self.buf).context("Parser error!") } - /// Splits the buffer into a RefMaker containing the first `target_size` bytes and the remaining tail + /// Splits the internal buffer into a `RefMaker` containing a buffer with + /// exactly `size_of::()` bytes and the remaining tail of the previous + /// internal buffer. + /// + /// # Errors + /// + /// Returns an error if the buffer is undersized. + /// + /// # Example + /// + /// ``` + /// # use rosenpass_util::zerocopy::RefMaker; + /// let bytes: &[u8] = &[1,2,3,4,5,6,7,8]; + /// let (prefix_rm, tail) = RefMaker::<_, u32>::new(bytes).from_prefix_with_tail().unwrap(); + /// assert_eq!(prefix_rm.bytes(), &[1,2,3,4]); + /// assert_eq!(tail, &[5,6,7,8]); + /// ``` pub fn from_prefix_with_tail(self) -> anyhow::Result<(Self, B)> { self.ensure_fit()?; let (head, tail) = self.buf.split_at(Self::target_size()); Ok((Self::new(head), tail)) } - /// Splits the buffer into two RefMakers, with the first containing the first `target_size` bytes + /// Splits the buffer into two `RefMaker`s, with the first containing the + /// first `size_of::()` bytes and the second containing the remaining + /// tail buffer. + /// + /// # Errors + /// + /// Returns an error if the buffer is undersized. + /// + /// # Example + /// + /// ``` + /// # use rosenpass_util::zerocopy::RefMaker; + /// let bytes: &[u8] = &[1,2,3,4,5,6,7,8,9,10]; + /// let (prefix_rm, tail) = RefMaker::<_, u32>::new(bytes).split_prefix().unwrap(); + /// assert_eq!(prefix_rm.bytes(), &[1,2,3,4]); + /// assert_eq!(tail.bytes(), &[5,6,7,8,9,10]); + /// ``` pub fn split_prefix(self) -> anyhow::Result<(Self, Self)> { self.ensure_fit()?; let (head, tail) = self.buf.split_at(Self::target_size()); Ok((Self::new(head), Self::new(tail))) } - /// Returns a RefMaker containing only the first `target_size` bytes + /// Returns a `RefMaker` containing only the first `size_of::()` bytes. + /// + /// # Errors + /// + /// Returns an error if the buffer is undersized. + /// + /// # Example + /// ``` + /// # use rosenpass_util::zerocopy::RefMaker; + /// let bytes: &[u8] = &[1,2,3,4,5,6,7,8,9,10]; + /// let prefix_rm = RefMaker::<_, u32>::new(bytes).from_prefix().unwrap(); + /// assert_eq!(prefix_rm.bytes(), &[1,2,3,4]); + /// ``` pub fn from_prefix(self) -> anyhow::Result { Ok(Self::from_prefix_with_tail(self)?.0) } - /// Splits the buffer into a RefMaker containing the last `target_size` bytes and the preceding head + /// Splits the buffer into a `RefMaker` containing the last `size_of::()` + /// bytes as [RefMaker] and the preceding bytes as a buffer. + /// + /// # Errors + /// + /// Returns an error if the buffer is undersized. + /// + /// # Example + /// ``` + /// # use rosenpass_util::zerocopy::RefMaker; + /// let bytes: &[u8] = &[1,2,3,4,5,6,7,8,9,10]; + /// let (suffix_rm, head) = RefMaker::<_, u32>::new(bytes).from_suffix_with_head().unwrap(); + /// assert_eq!(suffix_rm.bytes(), &[7,8,9,10]); + /// assert_eq!(head, &[1,2,3,4,5,6]); + /// ``` pub fn from_suffix_with_head(self) -> anyhow::Result<(Self, B)> { self.ensure_fit()?; let point = self.bytes().len() - Self::target_size(); @@ -76,7 +208,22 @@ impl RefMaker { Ok((Self::new(tail), head)) } - /// Splits the buffer into two RefMakers, with the second containing the last `target_size` bytes + /// Splits the buffer into two `RefMaker`s, with the second containing the + /// last `size_of::()` bytes, and the first containing the remaining + /// preceding bytes. + /// + /// # Errors + /// + /// Returns an error if the buffer is undersized. + /// + /// # Example + /// ``` + /// # use rosenpass_util::zerocopy::RefMaker; + /// let bytes: &[u8] = &[1,2,3,4,5,6,7,8,9,10]; + /// let (head, tail) = RefMaker::<_, u32>::new(bytes).split_suffix().unwrap(); + /// assert_eq!(head.bytes(), &[1,2,3,4,5,6]); + /// assert_eq!(tail.bytes(), &[7,8,9,10]); + /// ``` pub fn split_suffix(self) -> anyhow::Result<(Self, Self)> { self.ensure_fit()?; let point = self.bytes().len() - Self::target_size(); @@ -84,17 +231,46 @@ impl RefMaker { Ok((Self::new(head), Self::new(tail))) } - /// Returns a RefMaker containing only the last `target_size` bytes + /// Returns a `RefMaker` containing only the last `target_size()` bytes. + /// + /// # Errors + /// + /// Returns an error if the buffer is undersized. + /// + /// # Example + /// ``` + /// # use rosenpass_util::zerocopy::RefMaker; + /// let bytes: &[u8] = &[1,2,3,4,5,6,7,8,9,10]; + /// let suffix_rm = RefMaker::<_, u32>::new(bytes).from_suffix().unwrap(); + /// assert_eq!(suffix_rm.bytes(), &[7,8,9,10]); + /// ``` pub fn from_suffix(self) -> anyhow::Result { Ok(Self::from_suffix_with_head(self)?.0) } - /// Returns a reference to the underlying bytes + /// Returns a reference to the underlying bytes. pub fn bytes(&self) -> &[u8] { self.buf().deref() } - /// Ensures the buffer is large enough to hold type T + /// Ensures that the buffer is large enough to hold a `T`. + /// + /// # Errors + /// + /// Returns an error if the buffer is undersized. + /// + /// # Example + /// + /// ``` + /// # use rosenpass_util::zerocopy::RefMaker; + /// let bytes: &[u8] = &[1,2,3,4,5,6,7,8,9,10]; + /// let rm = RefMaker::<_, u32>::new(bytes); + /// rm.ensure_fit().unwrap(); + /// + /// let bytes: &[u8] = &[1,2,3]; + /// let rm = RefMaker::<_, u32>::new(bytes); + /// assert!(rm.ensure_fit().is_err()); + /// ``` pub fn ensure_fit(&self) -> anyhow::Result<()> { let have = self.bytes().len(); let need = Self::target_size(); @@ -107,12 +283,30 @@ impl RefMaker { } impl RefMaker { - /// Creates a zeroed reference of type T from the buffer + /// Creates a zeroized reference of type `T` from the buffer. + /// + /// # Errors + /// + /// Returns an error if the buffer is undersized. + /// + /// # Example + /// + /// ``` + /// # use zerocopy::{AsBytes, FromBytes, FromZeroes, Ref}; /// + /// # use rosenpass_util::zerocopy::RefMaker; + /// #[derive(FromBytes, FromZeroes, AsBytes)] + /// #[repr(C)] + /// struct Data([u8; 4]); + /// + /// let mut bytes = [0xFF; 4]; + /// let data_ref: Ref<&mut [u8], Data> = RefMaker::<_, Data>::new(&mut bytes[..]).make_zeroized().unwrap(); + /// assert_eq!(data_ref.0, [0,0,0,0]); + /// ``` pub fn make_zeroized(self) -> anyhow::Result> { self.zeroized().parse() } - /// Returns a mutable reference to the underlying bytes + /// Returns a mutable reference to the underlying bytes. pub fn bytes_mut(&mut self) -> &mut [u8] { self.buf_mut().deref_mut() } diff --git a/util/src/zerocopy/zerocopy_ref_ext.rs b/util/src/zerocopy/zerocopy_ref_ext.rs index f8eecc7..fa3c1b3 100644 --- a/util/src/zerocopy/zerocopy_ref_ext.rs +++ b/util/src/zerocopy/zerocopy_ref_ext.rs @@ -1,14 +1,64 @@ +//! Extension traits for converting `Ref` into references backed by +//! standard slices. + use zerocopy::{ByteSlice, ByteSliceMut, Ref}; /// A trait for converting a `Ref` into a `Ref<&[u8], T>`. +/// +/// This can be useful when you need a reference that is tied to a slice rather +/// than the original buffer type `B`. +/// +/// Note: This trait is implemented to [`Ref`] of byte slices (`&[u8]`). pub trait ZerocopyEmancipateExt { - /// Converts this reference into a reference backed by a byte slice. + /// Converts this reference into a reference backed by a plain byte slice. + /// + /// # Example + /// + /// ``` + /// # use std::ops::Deref; + /// # use zerocopy::{AsBytes, ByteSlice, FromBytes, FromZeroes, Ref}; + /// # use rosenpass_util::zerocopy::ZerocopyEmancipateExt; + /// #[derive(FromBytes, FromZeroes, AsBytes)] + /// #[repr(C)] + /// struct Data(u32); + /// #[repr(align(4))] + /// struct AlignedBuf([u8; 4]); + /// let bytes = AlignedBuf([0xAA, 0xBB, 0xCC, 0xDD]); + /// let r = Ref::<&[u8], Data>::new(&bytes.0).unwrap(); + /// let emancipated: Ref<&[u8], Data> = r.emancipate(); // same data, but guaranteed &[u8] backing + /// assert_eq!(emancipated.0, 0xDDCCBBAA); + /// ``` fn emancipate(&self) -> Ref<&[u8], T>; } /// A trait for converting a `Ref` into a mutable `Ref<&mut [u8], T>`. +/// +/// Similar to [`ZerocopyEmancipateExt`], but for mutable references. +/// +/// Note: this trait is implemented to [`Ref`] of mutable byte +/// slices (`&mut [u8]`). pub trait ZerocopyEmancipateMutExt { - /// Converts this reference into a mutable reference backed by a byte slice. + /// Converts this reference into a mutable reference backed by a plain + /// mutable byte slice. + /// + /// # Example + /// + /// ``` + /// # use zerocopy::{AsBytes, FromBytes, FromZeroes, Ref}; + /// # use rosenpass_util::zerocopy::{ZerocopyEmancipateMutExt}; + /// #[derive(FromBytes, FromZeroes, AsBytes)] + /// #[repr(C)] + /// struct Data(u32); + /// #[repr(align(4))] + /// struct AlignedBuf([u8; 4]); + /// let mut bytes = AlignedBuf([0xAA, 0xBB, 0xCC, 0xDD]); + /// let mut r = Ref::<&mut [u8], Data>::new(&mut bytes.0).unwrap(); + /// let mut emancipated: Ref<&mut [u8], Data> = r.emancipate_mut(); // same data, but guaranteed &[u8] backing + /// assert_eq!(emancipated.0, 0xDDCCBBAA); + /// emancipated.0 = 0x33221100; + /// drop(emancipated); + /// assert_eq!(bytes.0, [0x00, 0x11, 0x22, 0x33]); + /// ``` fn emancipate_mut(&mut self) -> Ref<&mut [u8], T>; } diff --git a/util/src/zerocopy/zerocopy_slice_ext.rs b/util/src/zerocopy/zerocopy_slice_ext.rs index ef068d5..3dd5393 100644 --- a/util/src/zerocopy/zerocopy_slice_ext.rs +++ b/util/src/zerocopy/zerocopy_slice_ext.rs @@ -1,25 +1,113 @@ +//! Extension traits for parsing slices into [`zerocopy::Ref`] values using the +//! [`RefMaker`] abstraction. + use zerocopy::{ByteSlice, ByteSliceMut, Ref}; use super::RefMaker; -/// Extension trait for zero-copy slice operations. +/// Extension trait for performing zero-copy parsing operations on byte slices. +/// +/// This trait adds methods for creating [`Ref`] references from +/// slices by using the [`RefMaker`] type internally. pub trait ZerocopySliceExt: Sized + ByteSlice { /// Creates a new `RefMaker` for the given slice. + /// + /// # Example + /// + /// ``` + /// # use zerocopy::{AsBytes, FromBytes, FromZeroes}; + /// # use rosenpass_util::zerocopy::{RefMaker, ZerocopySliceExt}; + /// + /// #[derive(FromBytes, FromZeroes, AsBytes)] + /// #[repr(C)] + /// struct Data(u32); + /// + /// let rm: RefMaker<&[u8], Data> = [3,0,0,0].zk_ref_maker(); + /// assert_eq!(rm.bytes(), &[3,0,0,0]); + /// assert_eq!(rm.parse().unwrap().0, 3); + /// ``` fn zk_ref_maker(self) -> RefMaker { RefMaker::::new(self) } - /// Parses the slice into a zero-copy reference. + /// Parses the given slice into a zero-copy reference of the given type `T`. + /// + /// # Errors + /// + /// Returns an error if the slice is too small. + /// + /// # Example + /// + /// ``` + /// # use zerocopy::{AsBytes, FromBytes, FromZeroes}; + /// # use rosenpass_util::zerocopy::ZerocopySliceExt; + /// + /// #[derive(FromBytes, FromZeroes, AsBytes)] + /// #[repr(C)] + /// struct Data(u16, u16); + /// #[repr(align(4))] + /// struct AlignedBuf([u8; 4]); + /// let bytes = AlignedBuf([0x01,0x02,0x03,0x04]); + /// let data_ref = bytes.0.zk_parse::().unwrap(); + /// assert_eq!(data_ref.0, 0x0201); + /// assert_eq!(data_ref.1, 0x0403); + /// ``` fn zk_parse(self) -> anyhow::Result> { self.zk_ref_maker().parse() } /// Parses a prefix of the slice into a zero-copy reference. + /// + /// Uses only the first [`std::mem::size_of::()`] bytes of `T`. + /// + /// # Errors + /// + /// Returns an error if the slice is too small. + /// + /// # Example + /// + /// ``` + /// # use zerocopy::{AsBytes, FromBytes, FromZeroes}; + /// # use rosenpass_util::zerocopy::ZerocopySliceExt; + /// #[derive(FromBytes, FromZeroes, AsBytes)] + /// #[repr(C)] + /// struct Header(u32); + /// #[repr(align(4))] + /// struct AlignedBuf([u8; 8]); + /// let bytes = AlignedBuf([0xAA, 0xBB, 0xCC, 0xDD, + /// 0x00, 0x10, 0x20, 0x30]); + /// + /// let header_ref = bytes.0.zk_parse_prefix::
().unwrap(); + /// assert_eq!(header_ref.0, 0xDDCCBBAA); + /// ``` fn zk_parse_prefix(self) -> anyhow::Result> { self.zk_ref_maker().from_prefix()?.parse() } /// Parses a suffix of the slice into a zero-copy reference. + /// + /// Uses only the last [`std::mem::size_of::()`] bytes of `T`. + /// + /// # Errors + /// + /// Returns an error if the slice is too small. + /// + /// # Example + /// + /// ``` + /// # use zerocopy::{AsBytes, FromBytes, FromZeroes}; + /// # use rosenpass_util::zerocopy::ZerocopySliceExt; + /// #[derive(FromBytes, FromZeroes, AsBytes)] + /// #[repr(C)] + /// struct Header(u32); + /// #[repr(align(4))] + /// struct AlignedBuf([u8; 8]); + /// let bytes = AlignedBuf([0xAA, 0xBB, 0xCC, 0xDD, + /// 0x00, 0x10, 0x20, 0x30]); + /// + /// let header_ref = bytes.0.zk_parse_suffix::
().unwrap(); + /// assert_eq!(header_ref.0, 0x30201000); + /// ``` fn zk_parse_suffix(self) -> anyhow::Result> { self.zk_ref_maker().from_suffix()?.parse() } @@ -27,19 +115,90 @@ pub trait ZerocopySliceExt: Sized + ByteSlice { impl ZerocopySliceExt for B {} -/// Extension trait for zero-copy slice operations with mutable slices. +/// Extension trait for zero-copy parsing of mutable slices with zeroization +/// capabilities. +/// +/// Provides convenience methods to create zero-initialized references. pub trait ZerocopyMutSliceExt: ZerocopySliceExt + Sized + ByteSliceMut { - /// Creates a new zeroed reference from the entire slice. + /// Creates a new zeroized reference from the entire slice. + /// + /// This zeroizes the slice first, then provides a `Ref`. + /// + /// # Errors + /// + /// Returns an error if the slice is too small. + /// + /// # Example + /// + /// ``` + /// # use zerocopy::{AsBytes, FromBytes, FromZeroes}; + /// # use rosenpass_util::zerocopy::ZerocopyMutSliceExt; + /// #[derive(FromBytes, FromZeroes, AsBytes)] + /// #[repr(C)] + /// struct Data([u8; 4]); + /// #[repr(align(4))] + /// struct AlignedBuf([u8; 4]); + /// let mut bytes = AlignedBuf([0xFF; 4]); + /// let data_ref = bytes.0.zk_zeroized::().unwrap(); + /// assert_eq!(data_ref.0, [0,0,0,0]); + /// assert_eq!(bytes.0, [0, 0, 0, 0]); + /// ``` fn zk_zeroized(self) -> anyhow::Result> { self.zk_ref_maker().make_zeroized() } - /// Creates a new zeroed reference from a prefix of the slice. + /// Creates a new zeroized reference from the prefix of the slice. + /// + /// Zeroizes the first `target_size()` bytes of the slice, then returns a + /// `Ref`. + /// + /// # Errors + /// + /// Returns an error if the slice is too small. + /// + /// # Example + /// + /// ``` + /// # use zerocopy::{AsBytes, FromBytes, FromZeroes}; + /// # use rosenpass_util::zerocopy::ZerocopyMutSliceExt; + /// #[derive(FromBytes, FromZeroes, AsBytes)] + /// #[repr(C)] + /// struct Data([u8; 4]); + /// #[repr(align(4))] + /// struct AlignedBuf([u8; 6]); + /// let mut bytes = AlignedBuf([0xFF; 6]); + /// let data_ref = bytes.0.zk_zeroized_from_prefix::().unwrap(); + /// assert_eq!(data_ref.0, [0,0,0,0]); + /// assert_eq!(bytes.0, [0, 0, 0, 0, 0xFF, 0xFF]); + /// ``` fn zk_zeroized_from_prefix(self) -> anyhow::Result> { self.zk_ref_maker().from_prefix()?.make_zeroized() } - /// Creates a new zeroed reference from a suffix of the slice. + /// Creates a new zeroized reference from the suffix of the slice. + /// + /// Zeroizes the last `target_size()` bytes of the slice, then returns a + /// `Ref`. + /// + /// # Errors + /// + /// Returns an error if the slice is too small. + /// + /// # Example + /// + /// ``` + /// # use zerocopy::{AsBytes, FromBytes, FromZeroes}; + /// # use rosenpass_util::zerocopy::ZerocopyMutSliceExt; + /// #[derive(FromBytes, FromZeroes, AsBytes)] + /// #[repr(C)] + /// struct Data([u8; 4]); + /// #[repr(align(4))] + /// struct AlignedBuf([u8; 6]); + /// let mut bytes = AlignedBuf([0xFF; 6]); + /// let data_ref = bytes.0.zk_zeroized_from_suffix::().unwrap(); + /// assert_eq!(data_ref.0, [0,0,0,0]); + /// assert_eq!(bytes.0, [0xFF, 0xFF, 0, 0, 0, 0]); + /// ``` fn zk_zeroized_from_suffix(self) -> anyhow::Result> { self.zk_ref_maker().from_suffix()?.make_zeroized() }