Skip to main content

btrfs_uapi/
util.rs

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