fidl_next_codec/wire/
envelope.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
5use core::mem::{ManuallyDrop, MaybeUninit};
6use core::ptr::addr_of_mut;
7
8use munge::munge;
9
10use crate::decoder::InternalHandleDecoder;
11use crate::encoder::InternalHandleEncoder;
12use crate::{
13    Decode, DecodeError, Decoder, DecoderExt as _, Encode, EncodeError, Encoder, EncoderExt as _,
14    Slot, WireU16, WireU32, ZeroPadding, CHUNK_SIZE,
15};
16
17#[derive(Clone, Copy)]
18#[repr(C)]
19struct Encoded {
20    maybe_num_bytes: WireU32,
21    num_handles: WireU16,
22    flags: WireU16,
23}
24
25const INLINE_SIZE: usize = 4;
26
27/// A FIDL envelope
28#[repr(C, align(8))]
29pub union WireEnvelope {
30    zero: [u8; 8],
31    encoded: Encoded,
32    decoded_inline: [MaybeUninit<u8>; INLINE_SIZE],
33    decoded_out_of_line: *mut (),
34}
35
36unsafe impl ZeroPadding for WireEnvelope {
37    fn zero_padding(_: &mut MaybeUninit<Self>) {}
38}
39
40impl WireEnvelope {
41    const IS_INLINE_BIT: u16 = 1;
42
43    /// Encodes a zero envelope into a slot.
44    #[inline]
45    pub fn encode_zero(out: &mut MaybeUninit<Self>) {
46        out.write(WireEnvelope { zero: [0; 8] });
47    }
48
49    /// Encodes a `'static` value into an envelope with an encoder.
50    #[inline]
51    pub fn encode_value_static<E: InternalHandleEncoder + ?Sized, T: Encode<E>>(
52        value: &mut T,
53        encoder: &mut E,
54        out: &mut MaybeUninit<Self>,
55    ) -> Result<(), EncodeError> {
56        munge! {
57            let Self {
58                encoded: Encoded {
59                    maybe_num_bytes,
60                    num_handles,
61                    flags,
62                },
63            } = out;
64        }
65
66        let handles_before = encoder.__internal_handle_count();
67
68        let encoded_size = size_of::<T::Encoded>();
69        if encoded_size <= INLINE_SIZE {
70            // If the encoded inline value is less than 4 bytes long, we need to zero out the part
71            // that won't get written over
72            unsafe {
73                maybe_num_bytes
74                    .as_mut_ptr()
75                    .cast::<u8>()
76                    .add(encoded_size)
77                    .write_bytes(0, INLINE_SIZE - encoded_size);
78            }
79        } else {
80            return Err(EncodeError::ExpectedInline(encoded_size));
81        }
82
83        let value_out = unsafe { &mut *maybe_num_bytes.as_mut_ptr().cast() };
84        T::Encoded::zero_padding(value_out);
85        value.encode(encoder, value_out)?;
86
87        flags.write(WireU16(Self::IS_INLINE_BIT));
88
89        let handle_count = (encoder.__internal_handle_count() - handles_before).try_into().unwrap();
90        num_handles.write(WireU16(handle_count));
91
92        Ok(())
93    }
94
95    /// Encodes a value into an envelope with an encoder.
96    #[inline]
97    pub fn encode_value<E: Encoder + ?Sized, T: Encode<E>>(
98        value: &mut T,
99        encoder: &mut E,
100        out: &mut MaybeUninit<Self>,
101    ) -> Result<(), EncodeError> {
102        munge! {
103            let Self {
104                encoded: Encoded {
105                    maybe_num_bytes,
106                    num_handles,
107                    flags,
108                },
109            } = out;
110        }
111
112        let handles_before = encoder.__internal_handle_count();
113
114        let encoded_size = size_of::<T::Encoded>();
115        if encoded_size <= INLINE_SIZE {
116            // If the encoded inline value is less than 4 bytes long, we need to zero out the part
117            // that won't get written over
118            unsafe {
119                maybe_num_bytes
120                    .as_mut_ptr()
121                    .cast::<u8>()
122                    .add(encoded_size)
123                    .write_bytes(0, INLINE_SIZE - encoded_size);
124            }
125            let value_out = unsafe { &mut *maybe_num_bytes.as_mut_ptr().cast() };
126            T::Encoded::zero_padding(value_out);
127            value.encode(encoder, value_out)?;
128            flags.write(WireU16(Self::IS_INLINE_BIT));
129        } else {
130            let bytes_before = encoder.bytes_written();
131
132            encoder.encode_next(value)?;
133
134            let bytes_count = (encoder.bytes_written() - bytes_before).try_into().unwrap();
135            maybe_num_bytes.write(WireU32(bytes_count));
136            flags.write(WireU16(0));
137        }
138
139        let handle_count = (encoder.__internal_handle_count() - handles_before).try_into().unwrap();
140        num_handles.write(WireU16(handle_count));
141
142        Ok(())
143    }
144
145    /// Returns the zero envelope.
146    #[inline]
147    pub fn zero() -> Self {
148        Self { zero: [0; 8] }
149    }
150
151    /// Returns whether a envelope slot is encoded as zero.
152    #[inline]
153    pub fn is_encoded_zero(slot: Slot<'_, Self>) -> bool {
154        munge!(let Self { zero } = slot);
155        *zero == [0; 8]
156    }
157
158    /// Returns whether an envelope is zero.
159    #[inline]
160    pub fn is_zero(&self) -> bool {
161        unsafe { self.zero == [0; 8] }
162    }
163
164    #[inline]
165    fn out_of_line_chunks(
166        maybe_num_bytes: Slot<'_, WireU32>,
167        flags: Slot<'_, WireU16>,
168    ) -> Result<Option<usize>, DecodeError> {
169        if **flags & Self::IS_INLINE_BIT == 0 {
170            let num_bytes = **maybe_num_bytes;
171            if num_bytes as usize % CHUNK_SIZE != 0 {
172                return Err(DecodeError::InvalidEnvelopeSize(num_bytes));
173            }
174            if num_bytes <= INLINE_SIZE as u32 {
175                return Err(DecodeError::OutOfLineValueTooSmall(num_bytes));
176            }
177            Ok(Some(num_bytes as usize / CHUNK_SIZE))
178        } else {
179            Ok(None)
180        }
181    }
182
183    /// Decodes and discards a static type in an envelope.
184    #[inline]
185    pub fn decode_unknown_static<D: InternalHandleDecoder + ?Sized>(
186        slot: Slot<'_, Self>,
187        decoder: &mut D,
188    ) -> Result<(), DecodeError> {
189        munge! {
190            let Self {
191                encoded: Encoded {
192                    maybe_num_bytes,
193                    num_handles,
194                    flags,
195                },
196            } = slot;
197        }
198
199        if let Some(count) = Self::out_of_line_chunks(maybe_num_bytes, flags)? {
200            return Err(DecodeError::ExpectedInline(count * CHUNK_SIZE));
201        }
202
203        decoder.__internal_take_handles(**num_handles as usize)?;
204
205        Ok(())
206    }
207
208    /// Decodes and discards an unknown value in an envelope.
209    #[inline]
210    pub fn decode_unknown<D: Decoder + ?Sized>(
211        slot: Slot<'_, Self>,
212        mut decoder: &mut D,
213    ) -> Result<(), DecodeError> {
214        munge! {
215            let Self {
216                encoded: Encoded {
217                    maybe_num_bytes,
218                    num_handles,
219                    flags,
220                },
221            } = slot;
222        }
223
224        if let Some(count) = Self::out_of_line_chunks(maybe_num_bytes, flags)? {
225            decoder.take_chunks(count)?;
226        }
227
228        decoder.__internal_take_handles(**num_handles as usize)?;
229
230        Ok(())
231    }
232
233    /// Decodes a value of a known type from an envelope.
234    #[inline]
235    pub fn decode_as_static<D: InternalHandleDecoder + ?Sized, T: Decode<D>>(
236        mut slot: Slot<'_, Self>,
237        decoder: &mut D,
238    ) -> Result<(), DecodeError> {
239        munge! {
240            let Self {
241                encoded: Encoded {
242                    maybe_num_bytes,
243                    num_handles,
244                    flags,
245                },
246             } = slot.as_mut();
247        }
248
249        let handles_before = decoder.__internal_handles_remaining();
250        let num_handles = **num_handles as usize;
251
252        if let Some(count) = Self::out_of_line_chunks(maybe_num_bytes, flags)? {
253            return Err(DecodeError::ExpectedInline(count * CHUNK_SIZE));
254        }
255
256        // Decode inline value
257        if size_of::<T>() > INLINE_SIZE {
258            return Err(DecodeError::InlineValueTooBig(size_of::<T>()));
259        }
260        munge!(let Self { mut decoded_inline } = slot);
261        let mut slot = unsafe { Slot::<T>::new_unchecked(decoded_inline.as_mut_ptr().cast()) };
262        T::decode(slot.as_mut(), decoder)?;
263
264        let handles_consumed = handles_before - decoder.__internal_handles_remaining();
265        if handles_consumed != num_handles {
266            return Err(DecodeError::IncorrectNumberOfHandlesConsumed {
267                expected: num_handles,
268                actual: handles_consumed,
269            });
270        }
271
272        Ok(())
273    }
274
275    /// Decodes a value of a known type from an envelope.
276    #[inline]
277    pub fn decode_as<D: Decoder + ?Sized, T: Decode<D>>(
278        mut slot: Slot<'_, Self>,
279        mut decoder: &mut D,
280    ) -> Result<(), DecodeError> {
281        munge! {
282            let Self {
283                encoded: Encoded {
284                    mut maybe_num_bytes,
285                    num_handles,
286                    flags,
287                },
288             } = slot.as_mut();
289        }
290
291        let handles_before = decoder.__internal_handles_remaining();
292        let num_handles = **num_handles as usize;
293
294        let out_of_line_chunks = Self::out_of_line_chunks(maybe_num_bytes.as_mut(), flags)?;
295        if let Some(_count) = out_of_line_chunks {
296            // Decode out-of-line value
297            // TODO: set cap on decoder to make sure that the envelope doesn't decode more bytes
298            // than it claims that it will
299            let mut value_slot = decoder.take_slot::<T>()?;
300            let value_ptr = value_slot.as_mut_ptr();
301            T::decode(value_slot, decoder)?;
302
303            munge!(let Self { mut decoded_out_of_line } = slot);
304            // SAFETY: Identical to `ptr.write(value_ptr.cast())`, but raw
305            // pointers don't currently implement `IntoBytes`.
306            unsafe { decoded_out_of_line.as_mut_ptr().write(value_ptr.cast()) };
307        } else {
308            // Decode inline value
309            if size_of::<T>() > INLINE_SIZE {
310                return Err(DecodeError::InlineValueTooBig(size_of::<T>()));
311            }
312            munge!(let Self { mut decoded_inline } = slot);
313            let mut slot = unsafe { Slot::<T>::new_unchecked(decoded_inline.as_mut_ptr().cast()) };
314            T::decode(slot.as_mut(), decoder)?;
315        }
316
317        let handles_consumed = handles_before - decoder.__internal_handles_remaining();
318        if handles_consumed != num_handles {
319            return Err(DecodeError::IncorrectNumberOfHandlesConsumed {
320                expected: num_handles,
321                actual: handles_consumed,
322            });
323        }
324
325        Ok(())
326    }
327
328    #[inline]
329    unsafe fn as_ptr<T>(this: *mut Self) -> *mut T {
330        if size_of::<T>() <= INLINE_SIZE {
331            let inline = unsafe { addr_of_mut!((*this).decoded_inline) };
332            inline.cast()
333        } else {
334            unsafe { (*this).decoded_out_of_line.cast() }
335        }
336    }
337
338    /// Returns a reference to the contained `T`.
339    ///
340    /// # Safety
341    ///
342    /// The envelope must have been successfully decoded as a `T`.
343    #[inline]
344    pub unsafe fn deref_unchecked<T>(&self) -> &T {
345        let ptr = unsafe { Self::as_ptr::<T>((self as *const Self).cast_mut()).cast_const() };
346        unsafe { &*ptr }
347    }
348
349    /// Clones the envelope, assuming that it contains an inline `T`.
350    ///
351    /// # Safety
352    ///
353    /// The envelope must have been successfully decoded as a `T`.
354    #[inline]
355    pub unsafe fn clone_unchecked<T: Clone>(&self) -> Self {
356        debug_assert_eq!(size_of::<T>(), INLINE_SIZE);
357
358        union ClonedToDecodedInline<T> {
359            cloned: ManuallyDrop<T>,
360            decoded_inline: [MaybeUninit<u8>; INLINE_SIZE],
361        }
362
363        let cloned = unsafe { self.deref_unchecked::<T>().clone() };
364        unsafe {
365            Self {
366                decoded_inline: ClonedToDecodedInline { cloned: ManuallyDrop::new(cloned) }
367                    .decoded_inline,
368            }
369        }
370    }
371}