btrfs_uapi/util.rs
1//! # Shared utilities for the uapi crate
2//!
3//! Provides the `field_size!` macro for computing field sizes from bindgen
4//! struct definitions.
5
6/// Return the size in bytes of a single field within a struct.
7///
8/// This is the field-level counterpart of `std::mem::size_of::<T>()`.
9/// The size is derived from the struct layout produced by bindgen, so it
10/// stays in sync with the kernel headers automatically.
11///
12/// ```ignore
13/// use btrfs_uapi::field_size;
14/// use btrfs_uapi::raw::btrfs_root_item;
15///
16/// assert_eq!(field_size!(btrfs_root_item, uuid), 16);
17/// ```
18#[macro_export]
19macro_rules! field_size {
20 ($t:ty, $f:ident) => {{
21 // Compute the field size via raw pointer arithmetic on a
22 // MaybeUninit, avoiding references to potentially misaligned
23 // fields in packed structs.
24 let uninit = std::mem::MaybeUninit::<$t>::uninit();
25 let base = uninit.as_ptr();
26
27 // SAFETY: we never dereference the pointer or read the
28 // uninitialised memory; addr_of! only computes an address.
29 let field_ptr = unsafe { std::ptr::addr_of!((*base).$f) };
30
31 // The size of the field is the distance from the field pointer
32 // to the end of the field, computed as (field_ptr + 1) - field_ptr
33 // in bytes (pointer arithmetic on the field's type).
34 //
35 // SAFETY: field_ptr and field_ptr.add(1) are both within (or one
36 // past) the same allocation represented by `uninit`.
37 unsafe {
38 (field_ptr.add(1) as *const u8).offset_from(field_ptr as *const u8)
39 as usize
40 }
41 }};
42}
43
44#[cfg(test)]
45mod tests {
46 use crate::raw::{
47 btrfs_dev_extent, btrfs_qgroup_info_item, btrfs_qgroup_limit_item,
48 btrfs_qgroup_status_item, btrfs_root_item, btrfs_stripe,
49 };
50
51 #[test]
52 fn field_size_matches_expected() {
53 // UUID fields are [u8; 16].
54 assert_eq!(field_size!(btrfs_root_item, uuid), 16);
55 assert_eq!(field_size!(btrfs_root_item, parent_uuid), 16);
56 assert_eq!(field_size!(btrfs_root_item, received_uuid), 16);
57
58 // Scalar __le64 fields are 8 bytes.
59 assert_eq!(field_size!(btrfs_root_item, generation), 8);
60 assert_eq!(field_size!(btrfs_root_item, flags), 8);
61 assert_eq!(field_size!(btrfs_qgroup_info_item, rfer), 8);
62 assert_eq!(field_size!(btrfs_qgroup_limit_item, max_rfer), 8);
63 assert_eq!(field_size!(btrfs_qgroup_status_item, flags), 8);
64 assert_eq!(field_size!(btrfs_dev_extent, length), 8);
65
66 // Stripe dev_uuid is [u8; 16].
67 assert_eq!(field_size!(btrfs_stripe, dev_uuid), 16);
68 }
69}