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::ptr::copy_nonoverlapping;
11
12pub use self::error::EncodeError;
13
14use crate::{
15    Encoder, EncoderExt as _, Slot, WireBox, WireF32, WireF64, WireI16, WireI32, WireI64, WireU16,
16    WireU32, WireU64,
17};
18
19/// An optimization hint about whether `T` is trivially copyable.
20pub struct CopyOptimization<T: ?Sized>(bool, PhantomData<T>);
21
22impl<T: ?Sized> CopyOptimization<T> {
23    /// Returns a `CopyOptimization` hint with the optimization enabled for `T`.
24    ///
25    /// # Safety
26    ///
27    /// `T` must not have any uninit bytes (e.g. padding).
28    pub const unsafe fn enable() -> Self {
29        Self(true, PhantomData)
30    }
31
32    /// Returns a `CopyOptimization` hint with the optimization enabled for `T` if `value` is
33    /// `true`.
34    ///
35    /// # Safety
36    ///
37    /// `T` must not have any uninit bytes (e.g. padding) if `value` is `true`.
38    pub const unsafe fn enable_if(value: bool) -> Self {
39        Self(value, PhantomData)
40    }
41
42    /// Returns a `CopyOptimization` hint with the optimization disabled for `T`.
43    pub const fn disable() -> Self {
44        Self(false, PhantomData)
45    }
46
47    /// Returns whether the optimization is enabled for `T`.
48    pub const fn is_enabled(&self) -> bool {
49        self.0
50    }
51}
52
53/// Zeroes the padding of this type.
54///
55/// # Safety
56///
57/// `zero_padding` must write zeroes to at least the padding bytes of the pointed-to memory.
58pub unsafe trait ZeroPadding {
59    /// Writes zeroes to the padding for this type, if any.
60    ///
61    /// # Safety
62    ///
63    /// `ptr` must point to memory suitable for holding this type.
64    unsafe fn zero_padding(ptr: *mut Self);
65}
66
67unsafe impl<T: ZeroPadding, const N: usize> ZeroPadding for [T; N] {
68    #[inline]
69    unsafe fn zero_padding(backing: *mut Self) {
70        for i in 0..N {
71            unsafe {
72                T::zero_padding(backing.cast::<T>().add(i));
73            }
74        }
75    }
76}
77
78macro_rules! impl_zero_padding_for_primitive {
79    ($ty:ty) => {
80        unsafe impl ZeroPadding for $ty {
81            #[inline]
82            unsafe fn zero_padding(_: *mut Self) {}
83        }
84    };
85}
86
87macro_rules! impl_zero_padding_for_primitives {
88    ($($ty:ty),* $(,)?) => {
89        $(
90            impl_zero_padding_for_primitive!($ty);
91        )*
92    }
93}
94
95impl_zero_padding_for_primitives! {
96    (), bool, i8, u8,
97    WireI16, WireI32, WireI64,
98    WireU16, WireU32, WireU64,
99    WireF32, WireF64,
100}
101
102/// A type which can be encoded as FIDL.
103pub trait Encodable {
104    /// An optimization flag that allows the bytes of this type to be copied directly during
105    /// encoding instead of calling `encode`.
106    ///
107    /// This optimization is disabled by default. To enable this optimization, you must unsafely
108    /// attest that `Self` is trivially copyable using [`CopyOptimization::enable`] or
109    /// [`CopyOptimization::enable_if`].
110    const COPY_OPTIMIZATION: CopyOptimization<Self> = CopyOptimization::disable();
111
112    /// The wire type for the value.
113    type Encoded: ZeroPadding;
114}
115
116/// Encodes a value.
117pub trait Encode<E: ?Sized>: Encodable {
118    /// Encodes this value into an encoder and slot.
119    fn encode(&mut self, encoder: &mut E, slot: Slot<'_, Self::Encoded>)
120        -> Result<(), EncodeError>;
121}
122
123/// A type which can be encoded as FIDL when optional.
124pub trait EncodableOption {
125    /// The wire type for the optional value.
126    type EncodedOption: ZeroPadding;
127}
128
129/// Encodes an optional value.
130pub trait EncodeOption<E: ?Sized>: EncodableOption {
131    /// Encodes this optional value into an encoder and slot.
132    fn encode_option(
133        this: Option<&mut Self>,
134        encoder: &mut E,
135        slot: Slot<'_, Self::EncodedOption>,
136    ) -> Result<(), EncodeError>;
137}
138
139macro_rules! impl_encode_for_primitive {
140    ($ty:ty, $enc:ty) => {
141        impl Encodable for $ty {
142            // Copy optimization for primitives is enabled if their size is <= 1 or the target is
143            // little-endian.
144            const COPY_OPTIMIZATION: CopyOptimization<Self> = unsafe {
145                CopyOptimization::enable_if(
146                    size_of::<Self>() <= 1 || cfg!(target_endian = "little"),
147                )
148            };
149
150            type Encoded = $enc;
151        }
152
153        impl<E: ?Sized> Encode<E> for $ty {
154            #[inline]
155            fn encode(
156                &mut self,
157                _: &mut E,
158                mut slot: Slot<'_, Self::Encoded>,
159            ) -> Result<(), EncodeError> {
160                slot.write(<$enc>::from(*self));
161                Ok(())
162            }
163        }
164
165        impl EncodableOption for $ty {
166            type EncodedOption = WireBox<$enc>;
167        }
168
169        impl<E: Encoder + ?Sized> EncodeOption<E> for $ty {
170            #[inline]
171            fn encode_option(
172                this: Option<&mut Self>,
173                encoder: &mut E,
174                slot: Slot<'_, Self::EncodedOption>,
175            ) -> Result<(), EncodeError> {
176                if let Some(value) = this {
177                    encoder.encode_next(value)?;
178                    WireBox::encode_present(slot);
179                } else {
180                    WireBox::encode_absent(slot);
181                }
182
183                Ok(())
184            }
185        }
186    };
187}
188
189macro_rules! impl_encode_for_primitives {
190    ($($ty:ty, $enc:ty);* $(;)?) => {
191        $(
192            impl_encode_for_primitive!($ty, $enc);
193        )*
194    }
195}
196
197impl_encode_for_primitives! {
198    (), (); bool, bool; i8, i8; u8, u8;
199
200    i16, WireI16; i32, WireI32; i64, WireI64;
201    u16, WireU16; u32, WireU32; u64, WireU64;
202    f32, WireF32; f64, WireF64;
203
204    WireI16, WireI16; WireI32, WireI32; WireI64, WireI64;
205    WireU16, WireU16; WireU32, WireU32; WireU64, WireU64;
206    WireF32, WireF32; WireF64, WireF64;
207}
208
209impl<T: Encodable, const N: usize> Encodable for [T; N] {
210    const COPY_OPTIMIZATION: CopyOptimization<Self> =
211        unsafe { CopyOptimization::enable_if(T::COPY_OPTIMIZATION.is_enabled()) };
212
213    type Encoded = [T::Encoded; N];
214}
215
216impl<E: ?Sized, T: Encode<E>, const N: usize> Encode<E> for [T; N] {
217    fn encode(
218        &mut self,
219        encoder: &mut E,
220        mut slot: Slot<'_, Self::Encoded>,
221    ) -> Result<(), EncodeError> {
222        if T::COPY_OPTIMIZATION.is_enabled() {
223            // SAFETY: `T` has copy optimization enabled and so is safe to copy to the output.
224            unsafe {
225                copy_nonoverlapping(self.as_ptr().cast(), slot.as_mut_ptr(), 1);
226            }
227        } else {
228            for (i, item) in self.iter_mut().enumerate() {
229                item.encode(encoder, slot.index(i))?;
230            }
231        }
232        Ok(())
233    }
234}
235
236impl<T: Encodable> Encodable for Box<T> {
237    type Encoded = T::Encoded;
238}
239
240impl<E: ?Sized, T: Encode<E>> Encode<E> for Box<T> {
241    fn encode(
242        &mut self,
243        encoder: &mut E,
244        slot: Slot<'_, Self::Encoded>,
245    ) -> Result<(), EncodeError> {
246        T::encode(self, encoder, slot)
247    }
248}
249
250#[cfg(test)]
251mod tests {
252    use crate::chunks;
253    use crate::testing::assert_encoded;
254
255    #[test]
256    fn encode_bool() {
257        assert_encoded(true, &chunks![0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
258        assert_encoded(false, &chunks![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
259    }
260
261    #[test]
262    fn encode_ints() {
263        assert_encoded(0xa3u8, &chunks![0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
264        assert_encoded(-0x45i8, &chunks![0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
265
266        assert_encoded(0x1234u16, &chunks![0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
267        assert_encoded(-0x1234i16, &chunks![0xcc, 0xed, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
268
269        assert_encoded(0x12345678u32, &chunks![0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00]);
270        assert_encoded(-0x12345678i32, &chunks![0x88, 0xa9, 0xcb, 0xed, 0x00, 0x00, 0x00, 0x00]);
271
272        assert_encoded(
273            0x123456789abcdef0u64,
274            &chunks![0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12],
275        );
276        assert_encoded(
277            -0x123456789abcdef0i64,
278            &chunks![0x10, 0x21, 0x43, 0x65, 0x87, 0xa9, 0xcb, 0xed],
279        );
280    }
281
282    #[test]
283    fn encode_floats() {
284        assert_encoded(
285            ::core::f32::consts::PI,
286            &chunks![0xdb, 0x0f, 0x49, 0x40, 0x00, 0x00, 0x00, 0x00],
287        );
288        assert_encoded(
289            ::core::f64::consts::PI,
290            &chunks![0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40],
291        );
292    }
293
294    #[test]
295    fn encode_box() {
296        assert_encoded(None::<u64>, &chunks![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
297        assert_encoded(
298            Some(0x123456789abcdef0u64),
299            &chunks![
300                0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56,
301                0x34, 0x12,
302            ],
303        );
304    }
305
306    #[test]
307    fn encode_vec() {
308        assert_encoded(
309            None::<Vec<u32>>,
310            &chunks![
311                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
312                0x00, 0x00,
313            ],
314        );
315        assert_encoded(
316            Some(vec![0x12345678u32, 0x9abcdef0u32]),
317            &chunks![
318                0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
319                0xff, 0xff, 0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a,
320            ],
321        );
322        assert_encoded(
323            Some(Vec::<u32>::new()),
324            &chunks![
325                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
326                0xff, 0xff,
327            ],
328        );
329    }
330
331    #[test]
332    fn encode_string() {
333        assert_encoded(
334            None::<String>,
335            &chunks![
336                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
337                0x00, 0x00,
338            ],
339        );
340        assert_encoded(
341            Some("0123".to_string()),
342            &chunks![
343                0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
344                0xff, 0xff, 0x30, 0x31, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00,
345            ],
346        );
347        assert_encoded(
348            Some(String::new()),
349            &chunks![
350                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
351                0xff, 0xff,
352            ],
353        );
354    }
355}