fidl_next_codec/encode/
mod.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//! Provides encoding for FIDL types.
6
7mod error;
8
9use core::marker::PhantomData;
10use core::mem::MaybeUninit;
11use core::ptr::copy_nonoverlapping;
12
13pub use self::error::EncodeError;
14
15use crate::{
16    Encoder, EncoderExt as _, WireBox, WireF32, WireF64, WireI16, WireI32, WireI64, WireU16,
17    WireU32, WireU64,
18};
19
20/// An optimization hint about whether `T` is trivially copyable.
21pub struct CopyOptimization<T: ?Sized>(bool, PhantomData<T>);
22
23impl<T: ?Sized> CopyOptimization<T> {
24    /// Returns a `CopyOptimization` hint with the optimization enabled for `T`.
25    ///
26    /// # Safety
27    ///
28    /// `T` must not have any uninit bytes (e.g. padding).
29    pub const unsafe fn enable() -> Self {
30        Self(true, PhantomData)
31    }
32
33    /// Returns a `CopyOptimization` hint with the optimization enabled for `T` if `value` is
34    /// `true`.
35    ///
36    /// # Safety
37    ///
38    /// `T` must not have any uninit bytes (e.g. padding) if `value` is `true`.
39    pub const unsafe fn enable_if(value: bool) -> Self {
40        Self(value, PhantomData)
41    }
42
43    /// Returns a `CopyOptimization` hint with the optimization disabled for `T`.
44    pub const fn disable() -> Self {
45        Self(false, PhantomData)
46    }
47
48    /// Returns whether the optimization is enabled for `T`.
49    pub const fn is_enabled(&self) -> bool {
50        self.0
51    }
52}
53
54/// Zeroes the padding of this type.
55///
56/// # Safety
57///
58/// `zero_padding` must write zeroes to at least the padding bytes of the pointed-to memory.
59pub unsafe trait ZeroPadding: Sized {
60    /// Writes zeroes to the padding for this type, if any.
61    fn zero_padding(out: &mut MaybeUninit<Self>);
62}
63
64unsafe impl<T: ZeroPadding, const N: usize> ZeroPadding for [T; N] {
65    #[inline]
66    fn zero_padding(out: &mut MaybeUninit<Self>) {
67        for i in 0..N {
68            let out_i = unsafe { &mut *out.as_mut_ptr().cast::<MaybeUninit<T>>().add(i) };
69            T::zero_padding(out_i);
70        }
71    }
72}
73
74macro_rules! impl_zero_padding_for_primitive {
75    ($ty:ty) => {
76        unsafe impl ZeroPadding for $ty {
77            #[inline]
78            fn zero_padding(_: &mut MaybeUninit<Self>) {}
79        }
80    };
81}
82
83macro_rules! impl_zero_padding_for_primitives {
84    ($($ty:ty),* $(,)?) => {
85        $(
86            impl_zero_padding_for_primitive!($ty);
87        )*
88    }
89}
90
91impl_zero_padding_for_primitives! {
92    (), bool, i8, u8,
93    WireI16, WireI32, WireI64,
94    WireU16, WireU32, WireU64,
95    WireF32, WireF64,
96}
97
98/// A type which can be encoded as FIDL.
99pub trait Encodable {
100    /// An optimization flag that allows the bytes of this type to be copied directly during
101    /// encoding instead of calling `encode`.
102    ///
103    /// This optimization is disabled by default. To enable this optimization, you must unsafely
104    /// attest that `Self` is trivially copyable using [`CopyOptimization::enable`] or
105    /// [`CopyOptimization::enable_if`].
106    const COPY_OPTIMIZATION: CopyOptimization<Self> = CopyOptimization::disable();
107
108    /// The wire type for the value.
109    type Encoded: ZeroPadding;
110}
111
112/// Encodes a value.
113///
114/// # Safety
115///
116/// `encode` must initialize all non-padding bytes of `out`.
117pub unsafe trait Encode<E: ?Sized>: Encodable + Sized {
118    /// Encodes this value into an encoder and output.
119    fn encode(
120        self,
121        encoder: &mut E,
122        out: &mut MaybeUninit<Self::Encoded>,
123    ) -> Result<(), EncodeError>;
124}
125
126/// Encodes a reference.
127///
128/// # Safety
129///
130/// `encode` must initialize all non-padding bytes of `out`.
131pub unsafe trait EncodeRef<E: ?Sized>: Encode<E> {
132    /// Encodes this reference into an encoder and output.
133    fn encode_ref(
134        &self,
135        encoder: &mut E,
136        out: &mut MaybeUninit<Self::Encoded>,
137    ) -> Result<(), EncodeError>;
138}
139
140/// A type which can be encoded as FIDL when optional.
141pub trait EncodableOption {
142    /// The wire type for the optional value.
143    type EncodedOption: ZeroPadding;
144}
145
146/// Encodes an optional value.
147///
148/// # Safety
149///
150/// `encode_option` must initialize all non-padding bytes of `out`.
151pub unsafe trait EncodeOption<E: ?Sized>: EncodableOption + Sized {
152    /// Encodes this optional value into an encoder and output.
153    fn encode_option(
154        this: Option<Self>,
155        encoder: &mut E,
156        out: &mut MaybeUninit<Self::EncodedOption>,
157    ) -> Result<(), EncodeError>;
158}
159
160/// Encodes an optional reference.
161///
162/// # Safety
163///
164/// `encode_option_ref` must initialize all non-padding bytes of `out`.
165pub unsafe trait EncodeOptionRef<E: ?Sized>: EncodeOption<E> {
166    /// Encodes this optional reference into an encoder and output.
167    fn encode_option_ref(
168        this: Option<&Self>,
169        encoder: &mut E,
170        out: &mut MaybeUninit<Self::EncodedOption>,
171    ) -> Result<(), EncodeError>;
172}
173
174impl<T: Encodable> Encodable for &T {
175    type Encoded = T::Encoded;
176}
177
178unsafe impl<E: ?Sized, T: EncodeRef<E>> Encode<E> for &T {
179    fn encode(
180        self,
181        encoder: &mut E,
182        out: &mut MaybeUninit<Self::Encoded>,
183    ) -> Result<(), EncodeError> {
184        T::encode_ref(self, encoder, out)
185    }
186}
187
188impl<T: EncodableOption> EncodableOption for &T {
189    type EncodedOption = T::EncodedOption;
190}
191
192unsafe impl<E: ?Sized, T: EncodeOptionRef<E>> EncodeOption<E> for &T {
193    fn encode_option(
194        this: Option<Self>,
195        encoder: &mut E,
196        out: &mut MaybeUninit<Self::EncodedOption>,
197    ) -> Result<(), EncodeError> {
198        T::encode_option_ref(this, encoder, out)
199    }
200}
201
202macro_rules! impl_encode_for_primitive {
203    ($ty:ty, $enc:ty) => {
204        impl Encodable for $ty {
205            // Copy optimization for primitives is enabled if their size is <= 1 or the target is
206            // little-endian.
207            const COPY_OPTIMIZATION: CopyOptimization<Self> = unsafe {
208                CopyOptimization::enable_if(
209                    size_of::<Self>() <= 1 || cfg!(target_endian = "little"),
210                )
211            };
212
213            type Encoded = $enc;
214        }
215
216        unsafe impl<E: ?Sized> Encode<E> for $ty {
217            #[inline]
218            fn encode(
219                self,
220                encoder: &mut E,
221                out: &mut MaybeUninit<Self::Encoded>,
222            ) -> Result<(), EncodeError> {
223                self.encode_ref(encoder, out)
224            }
225        }
226
227        unsafe impl<E: ?Sized> EncodeRef<E> for $ty {
228            #[inline]
229            fn encode_ref(
230                &self,
231                _: &mut E,
232                out: &mut MaybeUninit<Self::Encoded>,
233            ) -> Result<(), EncodeError> {
234                out.write(<$enc>::from(*self));
235                Ok(())
236            }
237        }
238
239        impl EncodableOption for $ty {
240            type EncodedOption = WireBox<$enc>;
241        }
242
243        unsafe impl<E: Encoder + ?Sized> EncodeOption<E> for $ty {
244            #[inline]
245            fn encode_option(
246                this: Option<Self>,
247                encoder: &mut E,
248                out: &mut MaybeUninit<Self::EncodedOption>,
249            ) -> Result<(), EncodeError> {
250                Self::encode_option_ref(this.as_ref(), encoder, out)
251            }
252        }
253
254        unsafe impl<E: Encoder + ?Sized> EncodeOptionRef<E> for $ty {
255            #[inline]
256            fn encode_option_ref(
257                this: Option<&Self>,
258                encoder: &mut E,
259                out: &mut MaybeUninit<Self::EncodedOption>,
260            ) -> Result<(), EncodeError> {
261                if let Some(value) = this {
262                    encoder.encode_next(value)?;
263                    WireBox::encode_present(out);
264                } else {
265                    WireBox::encode_absent(out);
266                }
267
268                Ok(())
269            }
270        }
271    };
272}
273
274macro_rules! impl_encode_for_primitives {
275    ($($ty:ty, $enc:ty);* $(;)?) => {
276        $(
277            impl_encode_for_primitive!($ty, $enc);
278        )*
279    }
280}
281
282impl_encode_for_primitives! {
283    (), (); bool, bool; i8, i8; u8, u8;
284
285    i16, WireI16; i32, WireI32; i64, WireI64;
286    u16, WireU16; u32, WireU32; u64, WireU64;
287    f32, WireF32; f64, WireF64;
288
289    WireI16, WireI16; WireI32, WireI32; WireI64, WireI64;
290    WireU16, WireU16; WireU32, WireU32; WireU64, WireU64;
291    WireF32, WireF32; WireF64, WireF64;
292}
293
294impl<T: Encodable, const N: usize> Encodable for [T; N] {
295    const COPY_OPTIMIZATION: CopyOptimization<Self> =
296        unsafe { CopyOptimization::enable_if(T::COPY_OPTIMIZATION.is_enabled()) };
297
298    type Encoded = [T::Encoded; N];
299}
300
301fn encode_to_array<A, E, T, const N: usize>(
302    value: A,
303    encoder: &mut E,
304    out: &mut MaybeUninit<[T::Encoded; N]>,
305) -> Result<(), EncodeError>
306where
307    A: AsRef<[T]> + IntoIterator,
308    A::Item: Encode<E, Encoded = T::Encoded>,
309    E: ?Sized,
310    T: Encode<E>,
311{
312    if T::COPY_OPTIMIZATION.is_enabled() {
313        // SAFETY: `T` has copy optimization enabled and so is safe to copy to the output.
314        unsafe {
315            copy_nonoverlapping(value.as_ref().as_ptr().cast(), out.as_mut_ptr(), 1);
316        }
317    } else {
318        for (i, item) in value.into_iter().enumerate() {
319            // SAFETY: `out` is a `MaybeUninit<[T::Encoded; N]>` and so consists of `N` copies of
320            // `T::Encoded` in order with no additional padding. We can make a `&mut MaybeUninit` to
321            // the `i`th element by:
322            // 1. Getting a pointer to the contents of the `MaybeUninit<[T::Encoded; N]>` (the
323            //    pointer is of type `*mut [T::Encoded; N]`).
324            // 2. Casting it to `*mut MaybeUninit<T::Encoded>`. Note that `MaybeUninit<T>` always
325            //    has the same layout as `T`.
326            // 3. Adding `i` to reach the `i`th element.
327            // 4. Dereferencing as `&mut`.
328            let out_i = unsafe { &mut *out.as_mut_ptr().cast::<MaybeUninit<T::Encoded>>().add(i) };
329            item.encode(encoder, out_i)?;
330        }
331    }
332    Ok(())
333}
334
335unsafe impl<E: ?Sized, T: Encode<E>, const N: usize> Encode<E> for [T; N] {
336    fn encode(
337        self,
338        encoder: &mut E,
339        out: &mut MaybeUninit<Self::Encoded>,
340    ) -> Result<(), EncodeError> {
341        encode_to_array(self, encoder, out)
342    }
343}
344
345unsafe impl<E: ?Sized, T: EncodeRef<E>, const N: usize> EncodeRef<E> for [T; N] {
346    fn encode_ref(
347        &self,
348        encoder: &mut E,
349        out: &mut MaybeUninit<Self::Encoded>,
350    ) -> Result<(), EncodeError> {
351        encode_to_array(self, encoder, out)
352    }
353}
354
355impl<T: Encodable> Encodable for Box<T> {
356    type Encoded = T::Encoded;
357}
358
359unsafe impl<E: ?Sized, T: Encode<E>> Encode<E> for Box<T> {
360    fn encode(
361        self,
362        encoder: &mut E,
363        out: &mut MaybeUninit<Self::Encoded>,
364    ) -> Result<(), EncodeError> {
365        T::encode(*self, encoder, out)
366    }
367}
368
369unsafe impl<E: ?Sized, T: EncodeRef<E>> EncodeRef<E> for Box<T> {
370    fn encode_ref(
371        &self,
372        encoder: &mut E,
373        out: &mut MaybeUninit<Self::Encoded>,
374    ) -> Result<(), EncodeError> {
375        T::encode_ref(self, encoder, out)
376    }
377}
378
379#[cfg(test)]
380mod tests {
381    use crate::chunks;
382    use crate::testing::assert_encoded;
383
384    #[test]
385    fn encode_unit() {
386        assert_encoded((), &chunks![]);
387    }
388
389    #[test]
390    fn encode_bool() {
391        assert_encoded(true, &chunks![0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
392        assert_encoded(false, &chunks![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
393    }
394
395    #[test]
396    fn encode_ints() {
397        assert_encoded(0xa3u8, &chunks![0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
398        assert_encoded(-0x45i8, &chunks![0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
399
400        assert_encoded(0x1234u16, &chunks![0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
401        assert_encoded(-0x1234i16, &chunks![0xcc, 0xed, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
402
403        assert_encoded(0x12345678u32, &chunks![0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00]);
404        assert_encoded(-0x12345678i32, &chunks![0x88, 0xa9, 0xcb, 0xed, 0x00, 0x00, 0x00, 0x00]);
405
406        assert_encoded(
407            0x123456789abcdef0u64,
408            &chunks![0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12],
409        );
410        assert_encoded(
411            -0x123456789abcdef0i64,
412            &chunks![0x10, 0x21, 0x43, 0x65, 0x87, 0xa9, 0xcb, 0xed],
413        );
414    }
415
416    #[test]
417    fn encode_floats() {
418        assert_encoded(
419            ::core::f32::consts::PI,
420            &chunks![0xdb, 0x0f, 0x49, 0x40, 0x00, 0x00, 0x00, 0x00],
421        );
422        assert_encoded(
423            ::core::f64::consts::PI,
424            &chunks![0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40],
425        );
426    }
427
428    #[test]
429    fn encode_box() {
430        assert_encoded(None::<u64>, &chunks![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
431        assert_encoded(
432            Some(0x123456789abcdef0u64),
433            &chunks![
434                0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56,
435                0x34, 0x12,
436            ],
437        );
438    }
439
440    #[test]
441    fn encode_vec() {
442        assert_encoded(
443            None::<Vec<u32>>,
444            &chunks![
445                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
446                0x00, 0x00,
447            ],
448        );
449        assert_encoded(
450            Some(vec![0x12345678u32, 0x9abcdef0u32]),
451            &chunks![
452                0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
453                0xff, 0xff, 0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a,
454            ],
455        );
456        assert_encoded(
457            Some(Vec::<u32>::new()),
458            &chunks![
459                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
460                0xff, 0xff,
461            ],
462        );
463    }
464
465    #[test]
466    fn encode_string() {
467        assert_encoded(
468            None::<String>,
469            &chunks![
470                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
471                0x00, 0x00,
472            ],
473        );
474        assert_encoded(
475            Some("0123".to_string()),
476            &chunks![
477                0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
478                0xff, 0xff, 0x30, 0x31, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00,
479            ],
480        );
481        assert_encoded(
482            Some(String::new()),
483            &chunks![
484                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
485                0xff, 0xff,
486            ],
487        );
488    }
489}