fidl_next_codec/copy_optimization.rs
1// Copyright 2025 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::marker::PhantomData;
6
7use crate::{WireF32, WireF64, WireI16, WireI32, WireI64, WireU16, WireU32, WireU64};
8
9/// An optimization hint about whether the conversion from `T` to `U` is equivalent to copying the
10/// raw bytes of `T`.
11pub struct CopyOptimization<T: ?Sized, U: ?Sized>(bool, PhantomData<(*mut T, *mut U)>);
12
13impl<T: ?Sized, U: ?Sized> CopyOptimization<T, U> {
14 /// Returns a `CopyOptimization` hint with the optimization enabled.
15 ///
16 /// # Safety
17 ///
18 /// `T` and `U` must be the same size and must not have any uninit bytes (e.g. padding).
19 pub const unsafe fn enable() -> Self {
20 Self(true, PhantomData)
21 }
22
23 /// Returns a `CopyOptimization` hint with the optimization enabled if `value` is `true`.
24 ///
25 /// # Safety
26 ///
27 /// `T` and `U` must be the same size and must not have any uninit bytes (e.g. padding) if
28 /// `value` is `true`.
29 pub const unsafe fn enable_if(value: bool) -> Self {
30 Self(value, PhantomData)
31 }
32
33 /// Returns a `CopyOptimization` hint with the optimization disabled.
34 pub const fn disable() -> Self {
35 Self(false, PhantomData)
36 }
37
38 /// Returns whether the optimization is enabled.
39 pub const fn is_enabled(&self) -> bool {
40 self.0
41 }
42
43 /// Infers whether the conversion from `[T; N]` to `[U; N]` is copy-optimizable based on the
44 /// conversion from `T` to `U`.
45 pub const fn infer_array<const N: usize>(&self) -> CopyOptimization<[T; N], [U; N]>
46 where
47 T: Sized,
48 U: Sized,
49 {
50 unsafe { CopyOptimization::enable_if(self.is_enabled()) }
51 }
52
53 /// Infers whether the conversion from `[T]` to `[U]` is copy-optimizable based on the
54 /// conversion from `T` to `U`.
55 pub const fn infer_slice(&self) -> CopyOptimization<[T], [U]>
56 where
57 T: Sized,
58 U: Sized,
59 {
60 unsafe { CopyOptimization::enable_if(self.is_enabled()) }
61 }
62}
63
64impl<T: ?Sized> CopyOptimization<T, T> {
65 /// Returns an enabled `CopyOptimization`, as copy optimization is always enabled from a type to
66 /// itself.
67 pub const fn identity() -> Self {
68 unsafe { Self::enable() }
69 }
70}
71
72macro_rules! impl_primitive {
73 ($ty:ty) => {
74 impl CopyOptimization<$ty, $ty> {
75 /// Whether copy optimization between the two primitive types is enabled.
76 pub const PRIMITIVE: Self = Self::identity();
77 }
78 };
79 ($natural:ty, $wire:ty) => {
80 impl_primitive!($wire);
81
82 impl CopyOptimization<$natural, $wire> {
83 /// Whether copy optimization between the two primitive types is enabled.
84 pub const PRIMITIVE: Self =
85 // SAFETY: Copy optimization for primitives is enabled if their size if <= 1 or the
86 // target is little-endian.
87 unsafe {
88 CopyOptimization::enable_if(
89 size_of::<Self>() <= 1 || cfg!(target_endian = "little"),
90 )
91 };
92 }
93
94 impl CopyOptimization<$wire, $natural> {
95 /// Whether copy optimization between the two primitive types is enabled.
96 pub const PRIMITIVE: Self =
97 // SAFETY: Copy optimization between these two primitives is commutative.
98 unsafe {
99 CopyOptimization::enable_if(
100 CopyOptimization::<$natural, $wire>::PRIMITIVE.is_enabled(),
101 )
102 };
103 }
104 };
105}
106
107macro_rules! impl_primitives {
108 ($($natural:ty $(, $wire:ty)?);* $(;)?) => {
109 $(
110 impl_primitive!($natural $(, $wire)?);
111 )*
112 }
113}
114
115impl_primitives! {
116 ();
117
118 bool;
119
120 i8;
121 i16, WireI16;
122 i32, WireI32;
123 i64, WireI64;
124
125 u8;
126 u16, WireU16;
127 u32, WireU32;
128 u64, WireU64;
129
130 f32, WireF32;
131 f64, WireF64;
132}