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