Skip to main content

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, ValidationError, Wire, wire,
15};
16
17#[derive(Clone, Copy)]
18#[repr(C)]
19struct Encoded {
20    maybe_num_bytes: wire::Uint32,
21    num_handles: wire::Uint16,
22    flags: wire::Uint16,
23}
24
25const INLINE_SIZE: usize = 4;
26
27/// A FIDL envelope
28#[repr(C, align(8))]
29pub union Envelope {
30    zero: [u8; 8],
31    encoded: Encoded,
32    decoded_inline: [MaybeUninit<u8>; INLINE_SIZE],
33    decoded_out_of_line: *mut (),
34}
35
36// SAFETY: `Envelope` is a union of primitive types and pointers, and contains no thread-local
37// data.
38unsafe impl Send for Envelope {}
39// SAFETY: `Envelope` contains no interior mutability.
40unsafe impl Sync for Envelope {}
41
42impl Constrained for Envelope {
43    type Constraint = ();
44
45    fn validate(_: Slot<'_, Self>, _: Self::Constraint) -> Result<(), ValidationError> {
46        Ok(())
47    }
48}
49
50// SAFETY: `Envelope` has a stable layout and no padding.
51unsafe impl Wire for Envelope {
52    type Narrowed<'de> = Self;
53
54    fn zero_padding(_: &mut MaybeUninit<Self>) {}
55}
56
57impl Envelope {
58    const IS_INLINE_BIT: u16 = 1;
59
60    /// Encodes a zero envelope into a slot.
61    #[inline]
62    pub fn encode_zero(out: &mut MaybeUninit<Self>) {
63        out.write(Envelope { zero: [0; 8] });
64    }
65
66    /// Encodes a `'static` value into an envelope with an encoder.
67    #[inline]
68    pub fn encode_value_static<W: Wire, E: InternalHandleEncoder + ?Sized>(
69        value: impl Encode<W, E>,
70        encoder: &mut E,
71        out: &mut MaybeUninit<Self>,
72        constraint: W::Constraint,
73    ) -> Result<(), EncodeError> {
74        // `unsafe` block required in the next version of munge
75        #[allow(unused_unsafe)]
76        // SAFETY: `out` is a valid mutable reference to a `MaybeUninit<Envelope>`.
77        // Destructuring it via `munge!` is safe.
78        let encoded = unsafe {
79            munge!(let Self { encoded } = out);
80            encoded
81        };
82        munge! {
83            let Encoded {
84                maybe_num_bytes,
85                num_handles,
86                flags,
87            } = encoded;
88        }
89
90        let handles_before = encoder.__internal_handle_count();
91
92        let encoded_size = size_of::<W>();
93        if encoded_size <= INLINE_SIZE {
94            // If the encoded inline value is less than 4 bytes long, we need to zero out the part
95            // that won't get written over
96            // SAFETY: `encoded_size` is <= INLINE_SIZE (4), so we are writing within the
97            // bounds of `maybe_num_bytes` (which is 4 bytes).
98            unsafe {
99                maybe_num_bytes
100                    .as_mut_ptr()
101                    .cast::<u8>()
102                    .add(encoded_size)
103                    .write_bytes(0, INLINE_SIZE - encoded_size);
104            }
105        } else {
106            return Err(EncodeError::ExpectedInline(encoded_size));
107        }
108
109        // SAFETY: `maybe_num_bytes` points to a 4-byte slot. `W` has size <= 4 and alignment
110        // requirements that are compatible (since `Envelope` is aligned to 8).
111        // Casting it to `*mut W` and dereferencing it is safe because the memory is allocated
112        // and we have exclusive access.
113        let value_out = unsafe { &mut *maybe_num_bytes.as_mut_ptr().cast() };
114        W::zero_padding(value_out);
115        value.encode(encoder, value_out, constraint)?;
116
117        flags.write(wire::Uint16(Self::IS_INLINE_BIT));
118
119        let handle_count = (encoder.__internal_handle_count() - handles_before).try_into().unwrap();
120        num_handles.write(wire::Uint16(handle_count));
121
122        Ok(())
123    }
124
125    /// Encodes a value into an envelope with an encoder.
126    #[inline]
127    pub fn encode_value<W: Wire, E: Encoder + ?Sized>(
128        value: impl Encode<W, E>,
129        encoder: &mut E,
130        out: &mut MaybeUninit<Self>,
131        constraint: W::Constraint,
132    ) -> Result<(), EncodeError> {
133        // `unsafe` block required in the next version of munge
134        #[allow(unused_unsafe)]
135        // SAFETY: `out` is a valid mutable reference to a `MaybeUninit<Envelope>`.
136        // Destructuring it via `munge!` is safe.
137        let encoded = unsafe {
138            munge!(let Self { encoded } = out);
139            encoded
140        };
141        munge! {
142            let Encoded {
143                maybe_num_bytes,
144                num_handles,
145                flags,
146            } = encoded;
147        }
148
149        let handles_before = encoder.__internal_handle_count();
150
151        let encoded_size = size_of::<W>();
152        if encoded_size <= INLINE_SIZE {
153            // If the encoded inline value is less than 4 bytes long, we need to zero out the part
154            // that won't get written over
155            // SAFETY: `encoded_size` is <= INLINE_SIZE (4), so we are writing within the
156            // bounds of `maybe_num_bytes` (which is 4 bytes).
157            unsafe {
158                maybe_num_bytes
159                    .as_mut_ptr()
160                    .cast::<u8>()
161                    .add(encoded_size)
162                    .write_bytes(0, INLINE_SIZE - encoded_size);
163            }
164            // SAFETY: `maybe_num_bytes` points to a 4-byte slot. `W` has size <= 4 and alignment
165            // requirements that are compatible (since `Envelope` is aligned to 8).
166            // Casting it to `*mut W` and dereferencing it is safe because the memory is allocated
167            // and we have exclusive access.
168            let value_out = unsafe { &mut *maybe_num_bytes.as_mut_ptr().cast() };
169            W::zero_padding(value_out);
170            value.encode(encoder, value_out, constraint)?;
171            flags.write(wire::Uint16(Self::IS_INLINE_BIT));
172        } else {
173            let bytes_before = encoder.bytes_written();
174
175            encoder.encode_next_with_constraint(value, constraint)?;
176
177            let bytes_count = (encoder.bytes_written() - bytes_before).try_into().unwrap();
178            maybe_num_bytes.write(wire::Uint32(bytes_count));
179            flags.write(wire::Uint16(0));
180        }
181
182        let handle_count = (encoder.__internal_handle_count() - handles_before).try_into().unwrap();
183        num_handles.write(wire::Uint16(handle_count));
184
185        Ok(())
186    }
187
188    /// Returns the zero envelope.
189    #[inline]
190    pub fn zero() -> Self {
191        Self { zero: [0; 8] }
192    }
193
194    /// Returns whether a envelope slot is encoded as zero.
195    #[inline]
196    pub fn is_encoded_zero(slot: Slot<'_, Self>) -> bool {
197        // `unsafe` block required in the next version of munge
198        #[allow(unused_unsafe)]
199        // SAFETY: `slot` is a valid `Slot` of `Envelope`. Destructuring it is safe.
200        let zero = unsafe {
201            munge!(let Self { zero } = slot);
202            zero
203        };
204        *zero == [0; 8]
205    }
206
207    /// Returns whether an envelope is zero.
208    #[inline]
209    pub fn is_zero(&self) -> bool {
210        // SAFETY: Reading the `zero` field of the union is safe because it is a primitive array
211        // (`[u8; 8]`) which has no validity invariants.
212        unsafe { self.zero == [0; 8] }
213    }
214
215    #[inline]
216    fn out_of_line_chunks(
217        maybe_num_bytes: Slot<'_, wire::Uint32>,
218        flags: Slot<'_, wire::Uint16>,
219    ) -> Result<Option<usize>, DecodeError> {
220        match **flags {
221            Self::IS_INLINE_BIT => Ok(None),
222            0 => {
223                let num_bytes = **maybe_num_bytes;
224                if !(num_bytes as usize).is_multiple_of(CHUNK_SIZE) {
225                    Err(DecodeError::InvalidEnvelopeSize(num_bytes))
226                } else if num_bytes <= INLINE_SIZE as u32 {
227                    Err(DecodeError::OutOfLineValueTooSmall(num_bytes))
228                } else {
229                    Ok(Some(num_bytes as usize / CHUNK_SIZE))
230                }
231            }
232            _ => Err(DecodeError::InvalidEnvelopeFlags(**flags)),
233        }
234    }
235
236    /// Decodes and discards a static type in an envelope.
237    #[inline]
238    pub fn decode_unknown_static<D: InternalHandleDecoder + ?Sized>(
239        slot: Slot<'_, Self>,
240        decoder: &mut D,
241    ) -> Result<(), DecodeError> {
242        // `unsafe` block required in the next version of munge
243        #[allow(unused_unsafe)]
244        // SAFETY: `slot` is a valid `Slot` of `Envelope`. Destructuring it is safe.
245        let encoded = unsafe {
246            munge!(let Self { encoded } = slot);
247            encoded
248        };
249        munge! {
250            let Encoded {
251                maybe_num_bytes,
252                num_handles,
253                flags,
254            } = encoded;
255        }
256
257        if let Some(count) = Self::out_of_line_chunks(maybe_num_bytes, flags)? {
258            return Err(DecodeError::ExpectedInline(count * CHUNK_SIZE));
259        }
260
261        decoder.__internal_take_handles(**num_handles as usize)?;
262
263        Ok(())
264    }
265
266    /// Decodes and discards an unknown value in an envelope.
267    #[inline]
268    pub fn decode_unknown<'de, D: Decoder<'de> + ?Sized>(
269        slot: Slot<'_, Self>,
270        decoder: &mut D,
271    ) -> Result<(), DecodeError> {
272        // `unsafe` block required in the next version of munge
273        #[allow(unused_unsafe)]
274        // SAFETY: `slot` is a valid `Slot` of `Envelope`. Destructuring it is safe.
275        let encoded = unsafe {
276            munge!(let Self { encoded } = slot);
277            encoded
278        };
279        munge! {
280            let Encoded {
281                maybe_num_bytes,
282                num_handles,
283                flags,
284            } = encoded;
285        }
286
287        if let Some(count) = Self::out_of_line_chunks(maybe_num_bytes, flags)? {
288            decoder.take_chunks(count)?;
289        }
290
291        decoder.__internal_take_handles(**num_handles as usize)?;
292
293        Ok(())
294    }
295
296    /// Decodes a value of a known type from an envelope.
297    #[inline]
298    pub fn decode_as_static<D: InternalHandleDecoder + ?Sized, T: Decode<D>>(
299        mut slot: Slot<'_, Self>,
300        decoder: &mut D,
301        constraint: T::Constraint,
302    ) -> Result<(), DecodeError> {
303        // `unsafe` block required in the next version of munge
304        #[allow(unused_unsafe)]
305        // SAFETY: `slot` is a valid `Slot` of `Envelope`. Destructuring it is safe.
306        let encoded = unsafe {
307            munge!(let Self { encoded } = slot.as_mut());
308            encoded
309        };
310        munge! {
311            let Encoded {
312                maybe_num_bytes,
313                num_handles,
314                flags,
315            } = encoded;
316        }
317
318        let handles_before = decoder.__internal_handles_remaining();
319        let num_handles = **num_handles as usize;
320
321        if let Some(count) = Self::out_of_line_chunks(maybe_num_bytes, flags)? {
322            return Err(DecodeError::ExpectedInline(count * CHUNK_SIZE));
323        }
324
325        // Decode inline value
326        if size_of::<T>() > INLINE_SIZE {
327            return Err(DecodeError::InlineValueTooBig(size_of::<T>()));
328        }
329        // `unsafe` block required in the next version of munge
330        #[allow(unused_unsafe)]
331        // SAFETY: `slot` is a valid `Slot` of `Envelope`. Destructuring it is safe.
332        let mut decoded_inline = unsafe {
333            munge!(let Self { decoded_inline } = slot);
334            decoded_inline
335        };
336        // SAFETY: `decoded_inline` is a slot for `[MaybeUninit<u8>; 4]` inside `Envelope`.
337        // We cast its pointer to `*mut T`. Since `size_of::<T>() <= 4` and `Envelope` is aligned
338        // to 8, the pointer is valid and aligned for `T`.
339        let mut slot = unsafe { Slot::<T>::new_unchecked(decoded_inline.as_mut_ptr().cast()) };
340        T::decode(slot.as_mut(), decoder, constraint)?;
341
342        let handles_consumed = handles_before - decoder.__internal_handles_remaining();
343        if handles_consumed != num_handles {
344            return Err(DecodeError::IncorrectNumberOfHandlesConsumed {
345                expected: num_handles,
346                actual: handles_consumed,
347            });
348        }
349
350        Ok(())
351    }
352
353    /// Decodes a value of a known type from an envelope.
354    #[inline]
355    pub fn decode_as<'de, D: Decoder<'de> + ?Sized, T: Decode<D>>(
356        mut slot: Slot<'_, Self>,
357        decoder: &mut D,
358        constraint: T::Constraint,
359    ) -> Result<(), DecodeError> {
360        // `unsafe` block required in the next version of munge
361        #[allow(unused_unsafe)]
362        // SAFETY: `slot` is a valid `Slot` of `Envelope`. Destructuring it is safe.
363        let encoded = unsafe {
364            munge!(let Self { encoded } = slot.as_mut());
365            encoded
366        };
367        munge! {
368            let Encoded {
369                mut maybe_num_bytes,
370                num_handles,
371                flags,
372            } = encoded;
373        }
374
375        let handles_before = decoder.__internal_handles_remaining();
376        let num_handles = **num_handles as usize;
377
378        let out_of_line_chunks = Self::out_of_line_chunks(maybe_num_bytes.as_mut(), flags)?;
379        if let Some(_count) = out_of_line_chunks {
380            // Decode out-of-line value
381            // TODO: set cap on decoder to make sure that the envelope doesn't decode more bytes
382            // than it claims that it will
383            let mut value_slot = decoder.take_slot::<T>()?;
384            let value_ptr = value_slot.as_mut_ptr();
385            T::decode(value_slot, decoder, constraint)?;
386
387            // `unsafe` block required in the next version of munge
388            #[allow(unused_unsafe)]
389            // SAFETY: `slot` is a valid `Slot` of `Envelope`. Destructuring it is safe.
390            let mut decoded_out_of_line = unsafe {
391                munge!(let Self { decoded_out_of_line } = slot);
392                decoded_out_of_line
393            };
394            // SAFETY: Identical to `ptr.write(value_ptr.cast())`, but raw
395            // pointers don't currently implement `IntoBytes`.
396            unsafe { decoded_out_of_line.as_mut_ptr().write(value_ptr.cast()) };
397        } else {
398            // Decode inline value
399            if size_of::<T>() > INLINE_SIZE {
400                return Err(DecodeError::InlineValueTooBig(size_of::<T>()));
401            }
402            // `unsafe` block required in the next version of munge
403            #[allow(unused_unsafe)]
404            // SAFETY: `slot` is a valid `Slot` of `Envelope`. Destructuring it is safe.
405            let mut decoded_inline = unsafe {
406                munge!(let Self { decoded_inline } = slot);
407                decoded_inline
408            };
409            // SAFETY: `decoded_inline` is a slot for `[MaybeUninit<u8>; 4]` inside `Envelope`. We
410            // cast its pointer to `*mut T`. Since `size_of::<T>() <= 4` and `Envelope` is aligned
411            // to 8, the pointer is valid and aligned for `T`.
412            let mut slot = unsafe { Slot::<T>::new_unchecked(decoded_inline.as_mut_ptr().cast()) };
413            T::decode(slot.as_mut(), decoder, constraint)?;
414        }
415
416        let handles_consumed = handles_before - decoder.__internal_handles_remaining();
417        if handles_consumed != num_handles {
418            return Err(DecodeError::IncorrectNumberOfHandlesConsumed {
419                expected: num_handles,
420                actual: handles_consumed,
421            });
422        }
423
424        Ok(())
425    }
426
427    /// Returns a pointer to the value contained in the envelope.
428    ///
429    /// # Safety
430    ///
431    /// `this` must point to a valid envelope that was successfully decoded.
432    #[inline]
433    pub unsafe fn as_ptr<T>(this: *mut Self) -> *mut T {
434        if size_of::<T>() <= INLINE_SIZE {
435            // SAFETY: `this` is valid and aligned as guaranteed by the caller.
436            let inline = unsafe { addr_of_mut!((*this).decoded_inline) };
437            inline.cast()
438        } else {
439            // SAFETY: `this` is valid and aligned as guaranteed by the caller, and contains
440            // a decoded out-of-line pointer.
441            unsafe { (*this).decoded_out_of_line.cast() }
442        }
443    }
444
445    /// Returns a reference to the contained `T`.
446    ///
447    /// # Safety
448    ///
449    /// The envelope must have been successfully decoded as a `T`.
450    #[inline]
451    pub unsafe fn deref_unchecked<T>(&self) -> &T {
452        // SAFETY: `self` is a valid reference, so we can cast it to a raw pointer.
453        // `Self::as_ptr` is safe to call because `self` is valid and successfully decoded as `T`
454        // (guaranteed by caller).
455        let ptr = unsafe { Self::as_ptr::<T>((self as *const Self).cast_mut()).cast_const() };
456        // SAFETY: `ptr` is valid and points to a decoded `T` (guaranteed by caller).
457        unsafe { &*ptr }
458    }
459
460    /// Returns the contained `T`.
461    ///
462    /// # Safety
463    ///
464    /// The envelope must have been successfully decoded as a `T`. Reading from
465    /// an envelope can cause undefined behavior if the underlying value is
466    /// dropped later. Precautions should be taken to ensure that values read
467    /// from an envelope are not dropped twice.
468    #[inline]
469    pub unsafe fn read_unchecked<T>(&self) -> T {
470        // SAFETY: `into_raw(this)` is guaranteed to return a pointer that is non-null, properly
471        // properly aligned, and valid for reads and writes.
472        unsafe { Self::as_ptr::<T>((self as *const Self).cast_mut()).read() }
473    }
474
475    /// Takes the contained `T` out of the envelope.
476    ///
477    /// # Safety
478    ///
479    /// The envelope must have been successfully decoded as a `T`.
480    #[inline]
481    pub unsafe fn take_unchecked<T>(&mut self) -> T {
482        // SAFETY: The caller guaranteed that the envelope was successfully
483        // decoded as a `T`. The envelope is zeroed afterward to prevent double
484        // drops.
485        let result = unsafe { self.read_unchecked() };
486        *self = Self::zero();
487        result
488    }
489
490    /// Clones the envelope, assuming that it contains an inline `T`.
491    ///
492    /// # Safety
493    ///
494    /// The envelope must have been successfully decoded inline as a `T`.
495    #[inline]
496    pub unsafe fn clone_inline_unchecked<T: Clone>(&self) -> Self {
497        debug_assert!(size_of::<T>() <= INLINE_SIZE);
498
499        union ClonedToDecodedInline<T> {
500            cloned: ManuallyDrop<T>,
501            decoded_inline: [MaybeUninit<u8>; INLINE_SIZE],
502        }
503
504        // SAFETY: The caller guarantees that the envelope contains a decoded inline `T`.
505        let cloned = unsafe { self.deref_unchecked::<T>().clone() };
506        // SAFETY: Creating `Envelope` with `decoded_inline` field is safe.
507        // `ClonedToDecodedInline` union is used to safely transmute `T` to `[MaybeUninit<u8>; 4]`.
508        unsafe {
509            Self {
510                decoded_inline: ClonedToDecodedInline { cloned: ManuallyDrop::new(cloned) }
511                    .decoded_inline,
512            }
513        }
514    }
515}