fidl_next_codec/
decoder.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! The core [`Decoder`] trait.
6
7use core::mem::take;
8use core::ptr::NonNull;
9use core::slice;
10
11use crate::{Chunk, Decode, DecodeError, Decoded, Owned, Slot, CHUNK_SIZE};
12
13/// A decoder for FIDL handles (internal).
14pub trait InternalHandleDecoder {
15    /// Takes the next `count` handles from the decoder.
16    ///
17    /// This method exposes details about Fuchsia resources that plain old FIDL shouldn't need to
18    /// know about. Do not use this method outside of this crate.
19    #[doc(hidden)]
20    fn __internal_take_handles(&mut self, count: usize) -> Result<(), DecodeError>;
21
22    /// Returns the number of handles remaining in the decoder.
23    ///
24    /// This method exposes details about Fuchsia resources that plain old FIDL shouldn't need to
25    /// know about. Do not use this method outside of this crate.
26    #[doc(hidden)]
27    fn __internal_handles_remaining(&self) -> usize;
28}
29
30/// A decoder for FIDL messages.
31///
32/// # Safety
33///
34/// Pointers returned from `take_chunks` must:
35///
36/// - Point to `count` initialized `Chunk`s
37/// - Be valid for reads and writes
38/// - Remain valid until the decoder is dropped
39///
40/// The decoder **may be moved** without invalidating the returned pointers.
41pub unsafe trait Decoder: InternalHandleDecoder {
42    /// Takes a slice of `Chunk`s from the decoder, returning a pointer to them.
43    ///
44    /// Returns `Err` if the decoder doesn't have enough chunks left.
45    fn take_chunks_raw(&mut self, count: usize) -> Result<NonNull<Chunk>, DecodeError>;
46
47    /// Commits to any decoding operations which are in progress.
48    ///
49    /// Resources like handles may be taken from a decoder during decoding. However, decoding may
50    /// fail after those resources are taken but before decoding completes. To ensure that resources
51    /// are always dropped, taken resources are still considered owned by the decoder until `commit`
52    /// is called. After `commit`, ownership of those resources is transferred to the decoded data.
53    fn commit(&mut self);
54
55    /// Verifies that decoding finished cleanly, with no leftover chunks or resources.
56    fn finish(&self) -> Result<(), DecodeError>;
57}
58
59impl InternalHandleDecoder for &mut [Chunk] {
60    #[inline]
61    fn __internal_take_handles(&mut self, _: usize) -> Result<(), DecodeError> {
62        Err(DecodeError::InsufficientHandles)
63    }
64
65    #[inline]
66    fn __internal_handles_remaining(&self) -> usize {
67        0
68    }
69}
70
71unsafe impl Decoder for &mut [Chunk] {
72    #[inline]
73    fn take_chunks_raw(&mut self, count: usize) -> Result<NonNull<Chunk>, DecodeError> {
74        if count > self.len() {
75            return Err(DecodeError::InsufficientData);
76        }
77
78        let chunks = take(self);
79        let (prefix, suffix) = unsafe { chunks.split_at_mut_unchecked(count) };
80        *self = suffix;
81        unsafe { Ok(NonNull::new_unchecked(prefix.as_mut_ptr())) }
82    }
83
84    #[inline]
85    fn commit(&mut self) {
86        // No resources to take, so commit is a no-op
87    }
88
89    #[inline]
90    fn finish(&self) -> Result<(), DecodeError> {
91        if !self.is_empty() {
92            return Err(DecodeError::ExtraBytes { num_extra: self.len() * CHUNK_SIZE });
93        }
94
95        Ok(())
96    }
97}
98
99/// Extension methods for [`Decoder`].
100pub trait DecoderExt {
101    /// Takes a slice of `Chunk`s from the decoder.
102    fn take_chunks<'buf>(
103        self: &mut &'buf mut Self,
104        count: usize,
105    ) -> Result<&'buf mut [Chunk], DecodeError>;
106
107    /// Takes enough chunks for a `T`, returning a `Slot` of the taken value.
108    fn take_slot<'buf, T>(self: &mut &'buf mut Self) -> Result<Slot<'buf, T>, DecodeError>;
109
110    /// Takes enough chunks for a slice of `T`, returning a `Slot` of the taken slice.
111    fn take_slice_slot<'buf, T>(
112        self: &mut &'buf mut Self,
113        len: usize,
114    ) -> Result<Slot<'buf, [T]>, DecodeError>;
115
116    /// Decodes an `Owned` value from the decoder without finishing it.
117    ///
118    /// On success, returns `Ok` of an `Owned` value. Returns `Err` if decoding failed.
119    fn decode_prefix<'buf, T: Decode<Self>>(
120        self: &mut &'buf mut Self,
121    ) -> Result<Owned<'buf, T>, DecodeError>;
122
123    /// Decodes an `Owned` slice from the decoder without finishing it.
124    ///
125    /// On success, returns `Ok` of an `Owned` slice. Returns `Err` if decoding failed.
126    fn decode_slice_prefix<'buf, T: Decode<Self>>(
127        self: &mut &'buf mut Self,
128        len: usize,
129    ) -> Result<Owned<'buf, [T]>, DecodeError>;
130
131    /// Decodes a value from the decoder and finishes it.
132    ///
133    /// On success, returns `Ok` of a `Decoded` value with the decoder. Returns `Err` if decoding
134    /// failed or the decoder finished with an error.
135    fn decode<T>(self) -> Result<Decoded<T, Self>, DecodeError>
136    where
137        T: Decode<Self>,
138        Self: Sized;
139
140    /// Decodes a slice from the decoder and finishes it.
141    ///
142    /// On success, returns `Ok` of a `Decoded` slice with the decoder. Returns `Err` if decoding
143    /// failed or the decoder finished with an error.
144    fn decode_slice<T>(self, len: usize) -> Result<Decoded<[T], Self>, DecodeError>
145    where
146        T: Decode<Self>,
147        Self: Sized;
148}
149
150impl<D: Decoder + ?Sized> DecoderExt for D {
151    fn take_chunks<'buf>(
152        self: &mut &'buf mut Self,
153        count: usize,
154    ) -> Result<&'buf mut [Chunk], DecodeError> {
155        self.take_chunks_raw(count).map(|p| unsafe { slice::from_raw_parts_mut(p.as_ptr(), count) })
156    }
157
158    fn take_slot<'buf, T>(self: &mut &'buf mut Self) -> Result<Slot<'buf, T>, DecodeError> {
159        // TODO: might be able to move this into a const for guaranteed const
160        // eval
161        assert!(
162            align_of::<T>() <= CHUNK_SIZE,
163            "attempted to take a slot for a type with an alignment higher \
164             than {CHUNK_SIZE}",
165        );
166
167        let count = size_of::<T>().div_ceil(CHUNK_SIZE);
168        let chunks = self.take_chunks(count)?;
169        // SAFETY: `result` is at least 8-aligned and points to at least enough
170        // bytes for a `T`.
171        unsafe { Ok(Slot::new_unchecked(chunks.as_mut_ptr().cast())) }
172    }
173
174    fn take_slice_slot<'buf, T>(
175        self: &mut &'buf mut Self,
176        len: usize,
177    ) -> Result<Slot<'buf, [T]>, DecodeError> {
178        assert!(
179            align_of::<T>() <= CHUNK_SIZE,
180            "attempted to take a slice slot for a type with an alignment \
181             higher than {CHUNK_SIZE}",
182        );
183
184        let count = (size_of::<T>() * len).div_ceil(CHUNK_SIZE);
185        let chunks = self.take_chunks(count)?;
186        // SAFETY: `result` is at least 8-aligned and points to at least enough
187        // bytes for a slice of `T` of length `len`.
188        unsafe { Ok(Slot::new_slice_unchecked(chunks.as_mut_ptr().cast(), len)) }
189    }
190
191    fn decode_prefix<'buf, T: Decode<Self>>(
192        self: &mut &'buf mut Self,
193    ) -> Result<Owned<'buf, T>, DecodeError> {
194        let mut slot = self.take_slot::<T>()?;
195        T::decode(slot.as_mut(), self)?;
196        self.commit();
197        // SAFETY: `slot` decoded successfully and the decoder was committed. `slot` now points to a
198        // valid `T` within the decoder.
199        unsafe { Ok(Owned::new_unchecked(slot.as_mut_ptr())) }
200    }
201
202    fn decode_slice_prefix<'buf, T: Decode<Self>>(
203        self: &mut &'buf mut Self,
204        len: usize,
205    ) -> Result<Owned<'buf, [T]>, DecodeError> {
206        let mut slot = self.take_slice_slot::<T>(len)?;
207        for i in 0..len {
208            T::decode(slot.index(i), self)?;
209        }
210        self.commit();
211        // SAFETY: `slot` decoded successfully and the decoder was committed. `slot` now points to a
212        // valid `[T]` within the decoder.
213        unsafe { Ok(Owned::new_unchecked(slot.as_mut_ptr())) }
214    }
215
216    fn decode<T>(mut self) -> Result<Decoded<T, Self>, DecodeError>
217    where
218        T: Decode<Self>,
219        Self: Sized,
220    {
221        let mut decoder = &mut self;
222        let result = decoder.decode_prefix::<T>()?;
223        decoder.finish()?;
224        // SAFETY: `result` points to an owned `T` contained within `decoder`.
225        unsafe { Ok(Decoded::new_unchecked(result.into_raw(), self)) }
226    }
227
228    fn decode_slice<T: Decode<Self>>(
229        mut self,
230        len: usize,
231    ) -> Result<Decoded<[T], Self>, DecodeError>
232    where
233        Self: Sized,
234    {
235        let mut decoder = &mut self;
236        let result = decoder.decode_slice_prefix::<T>(len)?;
237        decoder.finish()?;
238        // SAFETY: `result` points to an owned `[T]` contained within `decoder`.
239        unsafe { Ok(Decoded::new_unchecked(result.into_raw(), self)) }
240    }
241}