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