linux_uapi/
macros.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
5/// Get the size of a field in a struct.
6///
7/// This is a compile-time equivalent of `std::mem::size_of_val(&value.field)`.
8///
9/// Usage:
10///
11/// ```
12/// struct MyStruct {
13///     field1: u8,
14///     nested: Nested,
15/// }
16///
17/// struct Nested {
18///     field2: u32,
19/// }
20///
21/// const FIELD1_SIZE: usize = size_of_field!(MyStruct, field1);
22/// const FIELD2_SIZE: usize = size_of_field!(MyStruct, nested.field2);
23/// ```
24#[macro_export]
25macro_rules! size_of_field {
26    ($type_name:ty, $($field:ident).+) => {{
27        const fn size_of_pointee<T>(_val: *const T) -> usize {
28            std::mem::size_of::<T>()
29        }
30        const fn compute() -> usize {
31            let p = ::std::mem::MaybeUninit::<$type_name>::uninit();
32            // SAFETY
33            // This operation is safe because the pointer is never dereferenced. This is making the
34            // compiler compute the type of the field and then using function specialization to
35            // compute the size of the type.
36            // Moreover, this is only used in a const context, so the value will be compiled at
37            // compilation time.
38            size_of_pointee(unsafe { &raw const (*p.as_ptr()) . $($field).+ })
39        }
40        const size: usize = compute();
41        size
42    }};
43}
44
45/// Ensure 2 different types have the same layout.
46///
47/// Usage:
48///
49/// ```
50/// check_same_layout! {
51///     Type1 = Type2 {
52///         type1_field1 => type2_field1,
53///         type1_field2 => type2_field2,
54///         ...
55///     }
56/// }
57/// ```
58#[macro_export]
59macro_rules! check_same_layout {
60    {} => {};
61    {
62        $type_name1:ty = $type_name2:ty
63        {
64            $(
65                $($field1:ident).+ => $($field2:ident).+
66            ),*
67            $(,)?
68        }
69        $($token:tt)*
70    } => {
71        $crate::__static_assertions::assert_eq_size!($type_name1, $type_name2);
72        $(
73            $crate::__static_assertions::const_assert_eq!(
74                std::mem::offset_of!($type_name1, $($field1).+),
75                std::mem::offset_of!($type_name2, $($field2).+)
76            );
77        )*
78        $crate::check_same_layout! { $($token)* }
79    };
80}
81
82/// Ensure a uapi type has the same layout in 32 and 64 bits.
83///
84/// Usage:
85///
86/// ```
87/// check_arch_independent_layout! {
88///     UapiType {
89///         field1,
90///         field2,
91///         ...
92///     }
93/// }
94/// ```
95#[macro_export]
96macro_rules! check_arch_independent_layout {
97    {} => {};
98    {
99        $type_name:ident {
100            $( $($field:ident).+ ),*
101            $(,)?
102        }
103        $($token:tt)*
104    }=> {
105        $crate::check_same_layout! {
106            $crate::$type_name = $crate::arch32::$type_name {
107                $(
108                    $($field).+ => $($field).+,
109                )*
110            }
111        }
112        $crate::check_arch_independent_layout! { $($token)* }
113    };
114}
115
116/// Ensure a custom type has the same layout as an ABI independant uapi type
117///
118/// Usage:
119///
120/// ```
121/// check_arch_independent_same_layout! {
122///     Type = UapiType {
123///         type_field1 => uapi_type_field1,
124///         type_field2 => uapi_type_field2,
125///         ...
126///     }
127/// }
128/// ```
129#[macro_export]
130macro_rules! check_arch_independent_same_layout {
131    {} => {};
132    {
133        $type_name1:ty = $type_name2:ident
134        {
135            $(
136                $($field1:ident).+ => $($field2:ident).+
137            ),*
138            $(,)?
139        }
140        $($token:tt)*
141    } => {
142        $crate::check_same_layout! {
143            $type_name1 = $crate::$type_name2
144            {
145                $($($field1).+ => $($field2).+,)*
146            }
147        }
148        $crate::check_same_layout! {
149            $type_name1 = $crate::arch32::$type_name2
150            {
151                $($($field1).+ => $($field2).+,)*
152            }
153        }
154        $crate::check_arch_independent_same_layout! { $($token)* }
155    };
156}
157
158/// Implement From/TryFrom between 2 structs.
159///
160/// This defined 4 constructs:
161/// - From / TryFrom implements the equivalent trait
162/// - BidiFrom / BidiTryFrom implements the trait in both direction
163#[macro_export]
164macro_rules! translate_data {
165    {} => {};
166    {
167        $(#[$meta:meta])*
168        BidiFrom<$type_name1:ty, $type_name2:ty> {
169            $(
170                $field1:ident = $field2:ident;
171            )*
172            $(..$($d1:expr)?, $($d2:expr)?)?
173        }
174        $($token:tt)*
175    } => {
176        $crate::translate_data! {
177            $(#[$meta])*
178            From<$type_name1> for $type_name2 {
179                $( $field2 = $field1; )*
180                $($(..$d2)?)?
181            }
182            $(#[$meta])*
183            From<$type_name2> for $type_name1 {
184                $( $field1 = $field2; )*
185                $($(..$d1)?)?
186            }
187        }
188        $crate::translate_data! { $($token)* }
189    };
190    {
191        $(#[$meta:meta])*
192        BidiTryFrom<$type_name1:ty, $type_name2:ty> {
193            $(
194                $field1:ident = $field2:ident;
195            )*
196            $(..$($d1:expr)?, $($d2:expr)?)?
197        }
198        $($token:tt)*
199    } => {
200        $crate::translate_data! {
201            $(#[$meta])*
202            TryFrom<$type_name1> for $type_name2 {
203                $( $field2 = $field1; )*
204                $($(..$d2)?)?
205            }
206            $(#[$meta])*
207            TryFrom<$type_name2> for $type_name1 {
208                $( $field1 = $field2; )*
209                $($(..$d1)?)?
210            }
211        }
212        $crate::translate_data! { $($token)* }
213    };
214    {
215        $(#[$meta:meta])*
216        From<$type_name1:ty> for $type_name2:ty {
217            $(
218                $field2:ident = $field1:ident;
219            )*
220            $(..$d:expr)?
221        }
222        $($token:tt)*
223    } => {
224        $(#[$meta])*
225        impl From<$type_name1> for $type_name2 {
226            fn from(src: $type_name1) -> Self {
227                Self {
228                    $( $field2: src.$field1.into(), )*
229                    $(..$d)?
230                }
231            }
232        }
233        $crate::translate_data! { $($token)* }
234    };
235    {
236        $(#[$meta:meta])*
237        TryFrom<$type_name1:ty> for $type_name2:ty {
238            $(
239                $field2:ident = $field1:ident $( ( $field1_default:expr ))?;
240            )*
241            $(..$d:expr)?
242        }
243        $($token:tt)*
244    } => {
245        $(#[$meta])*
246        impl TryFrom<$type_name1> for $type_name2 {
247            type Error = ();
248            fn try_from(src: $type_name1) -> Result<Self, ()> {
249                Ok(Self {
250                    $( $field2: $crate::translate_data_expr!( src.$field1 $( ( $field1_default ) )? ), )*
251                    $(..$d)?
252                })
253            }
254        }
255        $crate::translate_data! { $($token)* }
256    };
257}
258
259#[macro_export]
260macro_rules! translate_data_expr {
261    ( $src:ident . $field:ident ) => {
262        $src.$field.try_into().map_err(|_| ())?
263    };
264    ( $src:ident . $field:ident ( $field1_default:expr ) ) => {
265        $src.$field.try_into().unwrap_or($field1_default)
266    };
267}
268
269/// Implement From/TryFrom between 2 uapi struct of different ABI.
270///
271/// This defined 3 constructs:
272/// - TryFrom64 transform a 64 bits uapi struct into a 32 bits one
273/// - From32 transform a 32 bits uapi struct into a 64 bits one
274/// - BidiFrom implements both of these operations
275#[macro_export]
276macro_rules! arch_translate_data {
277    {} => {};
278    {
279        BidiFrom<$type_name:ident> {
280            $( $field:ident ),+
281            $(,)?
282        }
283        $($token:tt)*
284    } => {
285        $crate::arch_translate_data! {
286            TryFrom64<$type_name> {
287                $(
288                    $field,
289                )*
290            }
291            From32<$type_name> {
292                $(
293                    $field,
294                )*
295            }
296        }
297        $crate::arch_translate_data! { $($token)* }
298    };
299    {
300        TryFrom64<$type_name:ident> {
301            $( $field:ident ),+
302            $(,)?
303        }
304        $($token:tt)*
305    } => {
306        $crate::translate_data! {
307            #[cfg(target_arch = "aarch64")]
308            TryFrom<$crate::$type_name> for $crate::arch32::$type_name {
309                $(
310                    $field = $field;
311                )*
312                ..Default::default()
313            }
314        }
315        $crate::arch_translate_data! { $($token)* }
316    };
317    {
318        From32<$type_name:ident> {
319            $( $field:ident ),+
320            $(,)?
321        }
322        $($token:tt)*
323    } => {
324        $crate::translate_data! {
325            #[cfg(target_arch = "aarch64")]
326            From<$crate::arch32::$type_name> for $crate::$type_name {
327                $(
328                    $field = $field;
329                )*
330                ..Default::default()
331            }
332        }
333        $crate::arch_translate_data! { $($token)* }
334    };
335}
336
337/// Implement TryFrom between 2 uapi struct of different ABI with a common type.
338#[macro_export]
339macro_rules! arch_map_data {
340    {} => {};
341    {
342        BidiTryFrom<$type_name1:ty, $type_name2:ident> {
343            $(
344                $field1:ident = $field2:ident;
345            )*
346            $(..$d:expr)?
347        }
348        $($token:tt)*
349    } => {
350        $crate::translate_data! {
351            BidiTryFrom<$type_name1, $crate::$type_name2> {
352                $(
353                    $field1 = $field2;
354                )*
355                ..$($d)?, Default::default()
356            }
357            #[cfg(target_arch = "aarch64")]
358            BidiTryFrom<$type_name1, $crate::arch32::$type_name2> {
359                $(
360                    $field1 = $field2;
361                )*
362                ..$($d)?, Default::default()
363            }
364        }
365        $crate::arch_map_data! { $($token)* }
366    };
367}