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.
45//! The core [`Decoder`] trait.
67use core::mem::take;
8use core::ptr::NonNull;
9use core::slice;
1011use crate::{Chunk, Decode, DecodeError, Decoded, Owned, Slot, CHUNK_SIZE};
1213/// 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)]
20fn __internal_take_handles(&mut self, count: usize) -> Result<(), DecodeError>;
2122/// 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)]
27fn __internal_handles_remaining(&self) -> usize;
28}
2930/// 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.
45fn take_chunks_raw(&mut self, count: usize) -> Result<NonNull<Chunk>, DecodeError>;
4647/// 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.
53fn commit(&mut self);
5455/// Verifies that decoding finished cleanly, with no leftover chunks or resources.
56fn finish(&self) -> Result<(), DecodeError>;
57}
5859impl InternalHandleDecoder for &mut [Chunk] {
60#[inline]
61fn __internal_take_handles(&mut self, _: usize) -> Result<(), DecodeError> {
62Err(DecodeError::InsufficientHandles)
63 }
6465#[inline]
66fn __internal_handles_remaining(&self) -> usize {
670
68}
69}
7071unsafe impl Decoder for &mut [Chunk] {
72#[inline]
73fn take_chunks_raw(&mut self, count: usize) -> Result<NonNull<Chunk>, DecodeError> {
74if count > self.len() {
75return Err(DecodeError::InsufficientData);
76 }
7778let chunks = take(self);
79let (prefix, suffix) = unsafe { chunks.split_at_mut_unchecked(count) };
80*self = suffix;
81unsafe { Ok(NonNull::new_unchecked(prefix.as_mut_ptr())) }
82 }
8384#[inline]
85fn commit(&mut self) {
86// No resources to take, so commit is a no-op
87}
8889#[inline]
90fn finish(&self) -> Result<(), DecodeError> {
91if !self.is_empty() {
92return Err(DecodeError::ExtraBytes { num_extra: self.len() * CHUNK_SIZE });
93 }
9495Ok(())
96 }
97}
9899/// Extension methods for [`Decoder`].
100pub trait DecoderExt {
101/// Takes a slice of `Chunk`s from the decoder.
102fn take_chunks<'buf>(
103self: &mut &'buf mut Self,
104 count: usize,
105 ) -> Result<&'buf mut [Chunk], DecodeError>;
106107/// Takes enough chunks for a `T`, returning a `Slot` of the taken value.
108fn take_slot<'buf, T>(self: &mut &'buf mut Self) -> Result<Slot<'buf, T>, DecodeError>;
109110/// Takes enough chunks for a slice of `T`, returning a `Slot` of the taken slice.
111fn take_slice_slot<'buf, T>(
112self: &mut &'buf mut Self,
113 len: usize,
114 ) -> Result<Slot<'buf, [T]>, DecodeError>;
115116/// 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.
119fn decode_prefix<'buf, T: Decode<Self>>(
120self: &mut &'buf mut Self,
121 ) -> Result<Owned<'buf, T>, DecodeError>;
122123/// 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.
126fn decode_slice_prefix<'buf, T: Decode<Self>>(
127self: &mut &'buf mut Self,
128 len: usize,
129 ) -> Result<Owned<'buf, [T]>, DecodeError>;
130131/// 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.
135fn decode<T>(self) -> Result<Decoded<T, Self>, DecodeError>
136where
137T: Decode<Self>,
138Self: Sized;
139140/// 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.
144fn decode_slice<T>(self, len: usize) -> Result<Decoded<[T], Self>, DecodeError>
145where
146T: Decode<Self>,
147Self: Sized;
148}
149150impl<D: Decoder + ?Sized> DecoderExt for D {
151fn take_chunks<'buf>(
152self: &mut &'buf mut Self,
153 count: usize,
154 ) -> Result<&'buf mut [Chunk], DecodeError> {
155self.take_chunks_raw(count).map(|p| unsafe { slice::from_raw_parts_mut(p.as_ptr(), count) })
156 }
157158fn 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
161assert!(
162 align_of::<T>() <= CHUNK_SIZE,
163"attempted to take a slot for a type with an alignment higher \
164 than {CHUNK_SIZE}",
165 );
166167let count = size_of::<T>().div_ceil(CHUNK_SIZE);
168let chunks = self.take_chunks(count)?;
169// SAFETY: `result` is at least 8-aligned and points to at least enough
170 // bytes for a `T`.
171unsafe { Ok(Slot::new_unchecked(chunks.as_mut_ptr().cast())) }
172 }
173174fn take_slice_slot<'buf, T>(
175self: &mut &'buf mut Self,
176 len: usize,
177 ) -> Result<Slot<'buf, [T]>, DecodeError> {
178assert!(
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 );
183184let count = (size_of::<T>() * len).div_ceil(CHUNK_SIZE);
185let 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`.
188unsafe { Ok(Slot::new_slice_unchecked(chunks.as_mut_ptr().cast(), len)) }
189 }
190191fn decode_prefix<'buf, T: Decode<Self>>(
192self: &mut &'buf mut Self,
193 ) -> Result<Owned<'buf, T>, DecodeError> {
194let mut slot = self.take_slot::<T>()?;
195 T::decode(slot.as_mut(), self)?;
196self.commit();
197// SAFETY: `slot` decoded successfully and the decoder was committed. `slot` now points to a
198 // valid `T` within the decoder.
199unsafe { Ok(Owned::new_unchecked(slot.as_mut_ptr())) }
200 }
201202fn decode_slice_prefix<'buf, T: Decode<Self>>(
203self: &mut &'buf mut Self,
204 len: usize,
205 ) -> Result<Owned<'buf, [T]>, DecodeError> {
206let mut slot = self.take_slice_slot::<T>(len)?;
207for i in 0..len {
208 T::decode(slot.index(i), self)?;
209 }
210self.commit();
211// SAFETY: `slot` decoded successfully and the decoder was committed. `slot` now points to a
212 // valid `[T]` within the decoder.
213unsafe { Ok(Owned::new_unchecked(slot.as_mut_ptr())) }
214 }
215216fn decode<T>(mut self) -> Result<Decoded<T, Self>, DecodeError>
217where
218T: Decode<Self>,
219Self: Sized,
220 {
221let mut decoder = &mut self;
222let result = decoder.decode_prefix::<T>()?;
223 decoder.finish()?;
224// SAFETY: `result` points to an owned `T` contained within `decoder`.
225unsafe { Ok(Decoded::new_unchecked(result.into_raw(), self)) }
226 }
227228fn decode_slice<T: Decode<Self>>(
229mut self,
230 len: usize,
231 ) -> Result<Decoded<[T], Self>, DecodeError>
232where
233Self: Sized,
234 {
235let mut decoder = &mut self;
236let result = decoder.decode_slice_prefix::<T>(len)?;
237 decoder.finish()?;
238// SAFETY: `result` points to an owned `[T]` contained within `decoder`.
239unsafe { Ok(Decoded::new_unchecked(result.into_raw(), self)) }
240 }
241}