ingot_types/
accessor.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! Primitive types and core traits needed to generate and use
6//! `ingot` packets.
7
8use super::*;
9
10/// A tool for converting zerocopy's `Ref<_, T>`s into `&T`/`&mut T`
11/// based on need and input B mutability.
12///
13/// This primarily reduces pointer sizes for fixed-width packet parts as
14/// an internal component of all `Valid` packets.
15///
16/// Zerocopy issue [#368](https://github.com/google/zerocopy/issues/368)
17/// would obviate the need for this, but I do not believe it is under active
18/// development. It would also open us back up to allowing any `B` rather than
19/// simply those which are [`IntoByteSlice`].
20pub struct Accessor<B, T> {
21    item_ptr: *mut T,
22    storage: PhantomData<B>,
23}
24
25impl<B: SplitByteSlice, T: FromBytes + IntoBytes + KnownLayout + Immutable>
26    Accessor<B, T>
27{
28    /// Parses out a fixed-width packet chunk `T` from the start of a given
29    /// buffer.
30    pub fn read_from_prefix<'a>(buf: B) -> Result<(Self, B), ParseError>
31    where
32        B: 'a + IntoBufPointer<'a>,
33        T: 'a,
34    {
35        // SAFETY:
36        // We use the exact same bounds on T as Ref::into_mut from zerocopy,
37        // allowing us to grant both read/write access to the T depending
38        // on B's mutability.
39        // Additionally, a valid parse via Ref guarantees that ptr
40        // alignment and length are as required for the type to be considered
41        // as a T.
42        // Unfortunately, we can't escape a Ref back into its inner B,
43        // so we need to check this on the derived &[u8] first.
44        // ByteSlice / IntoByteSlice guarantee stability, e.g.
45        // that the deref, stored, and into buffers all have identical
46        // pointers and lengths.
47        let len = {
48            let (r, _): (Ref<&[u8], T>, _) =
49                Ref::from_prefix(buf.as_bytes())
50                    .map_err(|_| ParseError::TooSmall)?;
51            Ref::bytes(&r).len()
52        };
53
54        let (acc, rest) = unsafe {
55            let (keep, rest) = buf.split_at_unchecked(len);
56
57            (
58                Self {
59                    item_ptr: keep.into_buf_ptr() as *mut _,
60                    storage: PhantomData,
61                },
62                rest,
63            )
64        };
65
66        Ok((acc, rest))
67    }
68}
69
70impl<B: ByteSlice, T: FromBytes + KnownLayout + Immutable> Deref
71    for Accessor<B, T>
72{
73    type Target = T;
74
75    fn deref(&self) -> &Self::Target {
76        // SAFETY:
77        // Self was created from a valid reference to T (guaranteed by `Ref::into_ref`).
78        unsafe { &*(self.item_ptr) }
79    }
80}
81
82impl<B: ByteSliceMut, T: FromBytes + KnownLayout + Immutable> DerefMut
83    for Accessor<B, T>
84{
85    fn deref_mut(&mut self) -> &mut Self::Target {
86        // SAFETY:
87        // The ByteSliceMut bound informs us that the base reference must have been
88        // exclusive. Given that we have &mut self here, we can recreate the mutable
89        // reference.
90        unsafe { &mut (*self.item_ptr) }
91    }
92}