fidl_next_codec/
take.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::MaybeUninit;
6use core::ptr::copy_nonoverlapping;
7
8use crate::{
9    CopyOptimization, WireF32, WireF64, WireI16, WireI32, WireI64, WireU16, WireU32, WireU64,
10};
11
12/// `From` conversions which may take from a reference using interior mutability.
13pub trait TakeFrom<T: ?Sized> {
14    /// An optimization flag that allows the bytes of this type to be copied directly during
15    /// conversion instead of calling `take_from`.
16    ///
17    /// This optimization is disabled by default. To enable this optimization, you must unsafely
18    /// attest that `Self` is trivially copyable using [`CopyOptimization::enable`] or
19    /// [`CopyOptimization::enable_if`].
20    const COPY_OPTIMIZATION: CopyOptimization<Self> = CopyOptimization::disable();
21
22    /// Converts from the given `T`, taking any resources that can't be cloned.
23    fn take_from(from: &T) -> Self;
24}
25
26macro_rules! impl_primitive {
27    ($from:ty, $to:ty) => {
28        impl TakeFrom<$from> for $to {
29            // Copy optimization for primitives is enabled if their size if <= 1 or the target is
30            // little-endian.
31            const COPY_OPTIMIZATION: CopyOptimization<Self> = unsafe {
32                CopyOptimization::enable_if(
33                    size_of::<Self>() <= 1 || cfg!(target_endian = "little"),
34                )
35            };
36
37            #[inline]
38            fn take_from(from: &$from) -> $to {
39                (*from).into()
40            }
41        }
42    };
43}
44
45macro_rules! impl_primitives {
46    ($($from:ty, $to:ty);* $(;)?) => {
47        $(
48            impl_primitive!($from, $to);
49        )*
50    }
51}
52
53impl_primitives! {
54    bool, bool;
55
56    i8, i8;
57    WireI16, i16; WireI16, WireI16;
58    WireI32, i32; WireI32, WireI32;
59    WireI64, i64; WireI64, WireI64;
60
61    u8, u8;
62    WireU16, u16; WireU16, WireU16;
63    WireU32, u32; WireU32, WireU32;
64    WireU64, u64; WireU64, WireU64;
65
66    WireF32, f32; WireF32, WireF32;
67    WireF64, f64; WireF64, WireF64;
68}
69
70impl<T: TakeFrom<WT>, WT, const N: usize> TakeFrom<[WT; N]> for [T; N] {
71    const COPY_OPTIMIZATION: CopyOptimization<Self> =
72        unsafe { CopyOptimization::enable_if(T::COPY_OPTIMIZATION.is_enabled()) };
73
74    fn take_from(from: &[WT; N]) -> Self {
75        let mut result = MaybeUninit::<[T; N]>::uninit();
76        if T::COPY_OPTIMIZATION.is_enabled() {
77            // SAFETY: `T` has copy optimization enabled and so is safe to copy bytewise.
78            unsafe {
79                copy_nonoverlapping(from.as_ptr().cast(), result.as_mut_ptr(), 1);
80            }
81        } else {
82            for (i, item) in from.iter().enumerate() {
83                let taken = T::take_from(item);
84                unsafe {
85                    result.as_mut_ptr().cast::<T>().add(i).write(taken);
86                }
87            }
88        }
89        unsafe { result.assume_init() }
90    }
91}
92
93impl<T: TakeFrom<WT>, WT> TakeFrom<WT> for Box<T> {
94    fn take_from(from: &WT) -> Self {
95        Box::new(T::take_from(from))
96    }
97}