Skip to main content

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;
8
9use crate::{CHUNK_SIZE, Chunk, Decode, DecodeError, Slot};
10
11/// A decoder for FIDL handles (internal).
12pub trait InternalHandleDecoder {
13    /// Takes the next `count` handles from the decoder.
14    ///
15    /// This method exposes details about Fuchsia resources that plain old FIDL shouldn't need to
16    /// know about. Do not use this method outside of this crate.
17    #[doc(hidden)]
18    fn __internal_take_handles(&mut self, count: usize) -> Result<(), DecodeError>;
19
20    /// Returns the number of handles remaining in the decoder.
21    ///
22    /// This method exposes details about Fuchsia resources that plain old FIDL shouldn't need to
23    /// know about. Do not use this method outside of this crate.
24    #[doc(hidden)]
25    fn __internal_handles_remaining(&self) -> usize;
26}
27
28/// A decoder for FIDL messages.
29pub trait Decoder<'de>: InternalHandleDecoder {
30    /// Takes a slice of `Chunk`s from the decoder.
31    fn take_chunks(&mut self, count: usize) -> Result<&'de mut [Chunk], DecodeError>;
32
33    /// Commits to any decoding operations which are in progress.
34    ///
35    /// Resources like handles may be taken from a decoder during decoding. However, decoding may
36    /// fail after those resources are taken but before decoding completes. To ensure that resources
37    /// are always dropped, taken resources are still considered owned by the decoder until `commit`
38    /// is called. After `commit`, ownership of those resources is transferred to the decoded data.
39    fn commit(&mut self);
40
41    /// Verifies that decoding finished cleanly, with no leftover chunks or resources.
42    fn finish(&self) -> Result<(), DecodeError>;
43}
44
45impl InternalHandleDecoder for &mut [Chunk] {
46    #[inline]
47    fn __internal_take_handles(&mut self, _: usize) -> Result<(), DecodeError> {
48        Err(DecodeError::InsufficientHandles)
49    }
50
51    #[inline]
52    fn __internal_handles_remaining(&self) -> usize {
53        0
54    }
55}
56
57impl<'de> Decoder<'de> for &'de mut [Chunk] {
58    #[inline]
59    fn take_chunks(&mut self, count: usize) -> Result<&'de mut [Chunk], DecodeError> {
60        if count > self.len() {
61            return Err(DecodeError::InsufficientData);
62        }
63
64        let chunks = take(self);
65        // SAFETY: We just checked that `count <= self.len()`.
66        let (prefix, suffix) = unsafe { chunks.split_at_mut_unchecked(count) };
67        *self = suffix;
68        Ok(prefix)
69    }
70
71    #[inline]
72    fn commit(&mut self) {
73        // No resources to take, so commit is a no-op
74    }
75
76    #[inline]
77    fn finish(&self) -> Result<(), DecodeError> {
78        if !self.is_empty() {
79            return Err(DecodeError::ExtraBytes { num_extra: self.len() * CHUNK_SIZE });
80        }
81
82        Ok(())
83    }
84}
85
86/// Extension methods for [`Decoder`].
87pub trait DecoderExt<'de> {
88    /// Takes enough chunks for a `T`, returning a `Slot` of the taken value.
89    fn take_slot<T>(&mut self) -> Result<Slot<'de, T>, DecodeError>;
90
91    /// Takes enough chunks for a slice of `T`, returning a `Slot` of the taken
92    /// slice.
93    fn take_slice_slot<T>(&mut self, len: usize) -> Result<Slot<'de, [T]>, DecodeError>;
94
95    /// Decodes an owned value from the decoder without finishing it.
96    ///
97    /// On success, returns `Ok` of an owned value. Returns `Err` if decoding
98    /// failed.
99    fn decode_prefix<T>(&mut self) -> Result<T, DecodeError>
100    where
101        T: Decode<Self, Constraint = ()>;
102
103    /// Decodes an owned value from the decoder with some constraint without
104    /// finishing it.
105    ///
106    /// On success, returns `Ok` of an owned value. Returns `Err` if decoding
107    /// failed.
108    fn decode_prefix_with_constraint<T>(
109        &mut self,
110        constraint: T::Constraint,
111    ) -> Result<T, DecodeError>
112    where
113        T: Decode<Self>;
114
115    /// Decodes an owned value from the decoder and finishes it.
116    ///
117    /// On success, returns `Ok` of an owned value. Returns `Err` if decoding
118    /// failed.
119    fn decode<T>(&mut self) -> Result<T, DecodeError>
120    where
121        T: Decode<Self, Constraint = ()>;
122
123    /// Decodes an owned value from the decoder with some constraint and
124    /// finishes it.
125    ///
126    /// On success, returns `Ok` of an owned value. Returns `Err` if decoding
127    /// failed.
128    fn decode_with_constraint<T>(&mut self, constraint: T::Constraint) -> Result<T, DecodeError>
129    where
130        T: Decode<Self>;
131}
132
133impl<'de, D: Decoder<'de> + ?Sized> DecoderExt<'de> for D {
134    fn take_slot<T>(&mut self) -> Result<Slot<'de, T>, DecodeError> {
135        // TODO: might be able to move this into a const for guaranteed const
136        // eval
137        assert!(
138            align_of::<T>() <= CHUNK_SIZE,
139            "attempted to take a slot for a type with an alignment higher \
140             than {CHUNK_SIZE}",
141        );
142
143        let count = size_of::<T>().div_ceil(CHUNK_SIZE);
144        let chunks = self.take_chunks(count)?;
145        // SAFETY: `result` is at least 8-aligned and points to at least enough
146        // bytes for a `T`.
147        unsafe { Ok(Slot::new_unchecked(chunks.as_mut_ptr().cast())) }
148    }
149
150    fn take_slice_slot<T>(&mut self, len: usize) -> Result<Slot<'de, [T]>, DecodeError> {
151        assert!(
152            align_of::<T>() <= CHUNK_SIZE,
153            "attempted to take a slice slot for a type with an alignment \
154             higher than {CHUNK_SIZE}",
155        );
156
157        let slice_byte_length = size_of::<T>() * len;
158        let chunk_count = slice_byte_length.div_ceil(CHUNK_SIZE);
159        let chunk_length = CHUNK_SIZE * chunk_count;
160        let padding_length = chunk_length - slice_byte_length;
161        let chunks_ptr = self.take_chunks(chunk_count)?.as_mut_ptr();
162        let padding: &[u8] = unsafe {
163            core::slice::from_raw_parts(
164                chunks_ptr.cast::<u8>().add(slice_byte_length),
165                padding_length,
166            )
167        };
168        if padding.iter().any(|byte| *byte != 0) {
169            return Err(DecodeError::InvalidPadding);
170        }
171
172        // SAFETY: `result` is at least 8-aligned and points to at least enough
173        // bytes for a slice of `T` of length `len`.
174        unsafe { Ok(Slot::new_slice_unchecked(chunks_ptr.cast(), len)) }
175    }
176
177    fn decode_prefix<T>(&mut self) -> Result<T, DecodeError>
178    where
179        T: Decode<Self, Constraint = ()>,
180    {
181        self.decode_prefix_with_constraint(())
182    }
183
184    fn decode_prefix_with_constraint<T>(
185        &mut self,
186        constraint: T::Constraint,
187    ) -> Result<T, DecodeError>
188    where
189        T: Decode<Self>,
190    {
191        let mut slot = self.take_slot::<T>()?;
192        T::decode(slot.as_mut(), self, constraint)?;
193        self.commit();
194        // SAFETY: `slot` decoded successfully and the decoder was committed. `slot` now points to a
195        // valid `T` within the decoder.
196        unsafe { Ok(slot.as_mut_ptr().read()) }
197    }
198
199    fn decode<T>(&mut self) -> Result<T, DecodeError>
200    where
201        T: Decode<Self, Constraint = ()>,
202    {
203        self.decode_with_constraint(())
204    }
205
206    fn decode_with_constraint<T>(&mut self, constraint: T::Constraint) -> Result<T, DecodeError>
207    where
208        T: Decode<Self>,
209    {
210        let result = self.decode_prefix_with_constraint(constraint)?;
211        self.finish()?;
212        Ok(result)
213    }
214}