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}