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 {
118    /// Encodes this value into an encoder and output.
119    fn encode(
120        &mut self,
121        encoder: &mut E,
122        out: &mut MaybeUninit<Self::Encoded>,
123    ) -> Result<(), EncodeError>;
124}
125
126/// A type which can be encoded as FIDL when optional.
127pub trait EncodableOption {
128    /// The wire type for the optional value.
129    type EncodedOption: ZeroPadding;
130}
131
132/// Encodes an optional value.
133///
134/// # Safety
135///
136/// `encode_option` must initialize all non-padding bytes of `out`.
137pub unsafe trait EncodeOption<E: ?Sized>: EncodableOption {
138    /// Encodes this optional value into an encoder and output.
139    fn encode_option(
140        this: Option<&mut Self>,
141        encoder: &mut E,
142        out: &mut MaybeUninit<Self::EncodedOption>,
143    ) -> Result<(), EncodeError>;
144}
145
146macro_rules! impl_encode_for_primitive {
147    ($ty:ty, $enc:ty) => {
148        impl Encodable for $ty {
149            // Copy optimization for primitives is enabled if their size is <= 1 or the target is
150            // little-endian.
151            const COPY_OPTIMIZATION: CopyOptimization<Self> = unsafe {
152                CopyOptimization::enable_if(
153                    size_of::<Self>() <= 1 || cfg!(target_endian = "little"),
154                )
155            };
156
157            type Encoded = $enc;
158        }
159
160        unsafe impl<E: ?Sized> Encode<E> for $ty {
161            #[inline]
162            fn encode(
163                &mut self,
164                _: &mut E,
165                out: &mut MaybeUninit<Self::Encoded>,
166            ) -> Result<(), EncodeError> {
167                out.write(<$enc>::from(*self));
168                Ok(())
169            }
170        }
171
172        impl EncodableOption for $ty {
173            type EncodedOption = WireBox<$enc>;
174        }
175
176        unsafe impl<E: Encoder + ?Sized> EncodeOption<E> for $ty {
177            #[inline]
178            fn encode_option(
179                this: Option<&mut Self>,
180                encoder: &mut E,
181                out: &mut MaybeUninit<Self::EncodedOption>,
182            ) -> Result<(), EncodeError> {
183                if let Some(value) = this {
184                    encoder.encode_next(value)?;
185                    WireBox::encode_present(out);
186                } else {
187                    WireBox::encode_absent(out);
188                }
189
190                Ok(())
191            }
192        }
193    };
194}
195
196macro_rules! impl_encode_for_primitives {
197    ($($ty:ty, $enc:ty);* $(;)?) => {
198        $(
199            impl_encode_for_primitive!($ty, $enc);
200        )*
201    }
202}
203
204impl_encode_for_primitives! {
205    (), (); bool, bool; i8, i8; u8, u8;
206
207    i16, WireI16; i32, WireI32; i64, WireI64;
208    u16, WireU16; u32, WireU32; u64, WireU64;
209    f32, WireF32; f64, WireF64;
210
211    WireI16, WireI16; WireI32, WireI32; WireI64, WireI64;
212    WireU16, WireU16; WireU32, WireU32; WireU64, WireU64;
213    WireF32, WireF32; WireF64, WireF64;
214}
215
216impl<T: Encodable, const N: usize> Encodable for [T; N] {
217    const COPY_OPTIMIZATION: CopyOptimization<Self> =
218        unsafe { CopyOptimization::enable_if(T::COPY_OPTIMIZATION.is_enabled()) };
219
220    type Encoded = [T::Encoded; N];
221}
222
223unsafe impl<E: ?Sized, T: Encode<E>, const N: usize> Encode<E> for [T; N] {
224    fn encode(
225        &mut self,
226        encoder: &mut E,
227        out: &mut MaybeUninit<Self::Encoded>,
228    ) -> Result<(), EncodeError> {
229        if T::COPY_OPTIMIZATION.is_enabled() {
230            // SAFETY: `T` has copy optimization enabled and so is safe to copy to the output.
231            unsafe {
232                copy_nonoverlapping(self.as_ptr().cast(), out.as_mut_ptr(), 1);
233            }
234        } else {
235            for (i, item) in self.iter_mut().enumerate() {
236                let out_i =
237                    unsafe { &mut *out.as_mut_ptr().cast::<MaybeUninit<T::Encoded>>().add(i) };
238                item.encode(encoder, out_i)?;
239            }
240        }
241        Ok(())
242    }
243}
244
245impl<T: Encodable> Encodable for Box<T> {
246    type Encoded = T::Encoded;
247}
248
249unsafe impl<E: ?Sized, T: Encode<E>> Encode<E> for Box<T> {
250    fn encode(
251        &mut self,
252        encoder: &mut E,
253        out: &mut MaybeUninit<Self::Encoded>,
254    ) -> Result<(), EncodeError> {
255        T::encode(self, encoder, out)
256    }
257}
258
259#[cfg(test)]
260mod tests {
261    use crate::chunks;
262    use crate::testing::assert_encoded;
263
264    #[test]
265    fn encode_bool() {
266        assert_encoded(true, &chunks![0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
267        assert_encoded(false, &chunks![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
268    }
269
270    #[test]
271    fn encode_ints() {
272        assert_encoded(0xa3u8, &chunks![0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
273        assert_encoded(-0x45i8, &chunks![0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
274
275        assert_encoded(0x1234u16, &chunks![0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
276        assert_encoded(-0x1234i16, &chunks![0xcc, 0xed, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
277
278        assert_encoded(0x12345678u32, &chunks![0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00]);
279        assert_encoded(-0x12345678i32, &chunks![0x88, 0xa9, 0xcb, 0xed, 0x00, 0x00, 0x00, 0x00]);
280
281        assert_encoded(
282            0x123456789abcdef0u64,
283            &chunks![0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12],
284        );
285        assert_encoded(
286            -0x123456789abcdef0i64,
287            &chunks![0x10, 0x21, 0x43, 0x65, 0x87, 0xa9, 0xcb, 0xed],
288        );
289    }
290
291    #[test]
292    fn encode_floats() {
293        assert_encoded(
294            ::core::f32::consts::PI,
295            &chunks![0xdb, 0x0f, 0x49, 0x40, 0x00, 0x00, 0x00, 0x00],
296        );
297        assert_encoded(
298            ::core::f64::consts::PI,
299            &chunks![0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40],
300        );
301    }
302
303    #[test]
304    fn encode_box() {
305        assert_encoded(None::<u64>, &chunks![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
306        assert_encoded(
307            Some(0x123456789abcdef0u64),
308            &chunks![
309                0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56,
310                0x34, 0x12,
311            ],
312        );
313    }
314
315    #[test]
316    fn encode_vec() {
317        assert_encoded(
318            None::<Vec<u32>>,
319            &chunks![
320                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
321                0x00, 0x00,
322            ],
323        );
324        assert_encoded(
325            Some(vec![0x12345678u32, 0x9abcdef0u32]),
326            &chunks![
327                0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
328                0xff, 0xff, 0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a,
329            ],
330        );
331        assert_encoded(
332            Some(Vec::<u32>::new()),
333            &chunks![
334                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
335                0xff, 0xff,
336            ],
337        );
338    }
339
340    #[test]
341    fn encode_string() {
342        assert_encoded(
343            None::<String>,
344            &chunks![
345                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
346                0x00, 0x00,
347            ],
348        );
349        assert_encoded(
350            Some("0123".to_string()),
351            &chunks![
352                0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
353                0xff, 0xff, 0x30, 0x31, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00,
354            ],
355        );
356        assert_encoded(
357            Some(String::new()),
358            &chunks![
359                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
360                0xff, 0xff,
361            ],
362        );
363    }
364}