Skip to main content

btrfs_uapi/
util.rs

1//! # Shared utilities for the uapi crate
2
3/// Read a little-endian `u64` from `buf` at byte offset `off`.
4pub fn read_le_u64(buf: &[u8], off: usize) -> u64 {
5    u64::from_le_bytes(buf[off..off + 8].try_into().unwrap())
6}
7
8/// Read a little-endian `u32` from `buf` at byte offset `off`.
9pub fn read_le_u32(buf: &[u8], off: usize) -> u32 {
10    u32::from_le_bytes(buf[off..off + 4].try_into().unwrap())
11}
12
13/// Read a little-endian `u16` from `buf` at byte offset `off`.
14pub fn read_le_u16(buf: &[u8], off: usize) -> u16 {
15    u16::from_le_bytes(buf[off..off + 2].try_into().unwrap())
16}
17
18/// Return the size in bytes of a single field within a struct.
19///
20/// This is the field-level counterpart of `std::mem::size_of::<T>()`.
21/// The size is derived from the struct layout produced by bindgen, so it
22/// stays in sync with the kernel headers automatically.
23///
24/// ```ignore
25/// use btrfs_uapi::field_size;
26/// use btrfs_uapi::raw::btrfs_root_item;
27///
28/// assert_eq!(field_size!(btrfs_root_item, uuid), 16);
29/// ```
30#[macro_export]
31macro_rules! field_size {
32    ($t:ty, $f:ident) => {{
33        // Compute the field size via raw pointer arithmetic on a
34        // MaybeUninit, avoiding references to potentially misaligned
35        // fields in packed structs.
36        let uninit = std::mem::MaybeUninit::<$t>::uninit();
37        let base = uninit.as_ptr();
38
39        // SAFETY: we never dereference the pointer or read the
40        // uninitialised memory; addr_of! only computes an address.
41        let field_ptr = unsafe { std::ptr::addr_of!((*base).$f) };
42
43        // The size of the field is the distance from the field pointer
44        // to the end of the field, computed as (field_ptr + 1) - field_ptr
45        // in bytes (pointer arithmetic on the field's type).
46        //
47        // SAFETY: field_ptr and field_ptr.add(1) are both within (or one
48        // past) the same allocation represented by `uninit`.
49        unsafe {
50            (field_ptr.add(1) as *const u8).offset_from(field_ptr as *const u8)
51                as usize
52        }
53    }};
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59    use crate::raw::{
60        btrfs_dev_extent, btrfs_qgroup_info_item, btrfs_qgroup_limit_item,
61        btrfs_qgroup_status_item, btrfs_root_item, btrfs_stripe,
62    };
63
64    #[test]
65    fn field_size_matches_expected() {
66        // UUID fields are [u8; 16].
67        assert_eq!(field_size!(btrfs_root_item, uuid), 16);
68        assert_eq!(field_size!(btrfs_root_item, parent_uuid), 16);
69        assert_eq!(field_size!(btrfs_root_item, received_uuid), 16);
70
71        // Scalar __le64 fields are 8 bytes.
72        assert_eq!(field_size!(btrfs_root_item, generation), 8);
73        assert_eq!(field_size!(btrfs_root_item, flags), 8);
74        assert_eq!(field_size!(btrfs_qgroup_info_item, rfer), 8);
75        assert_eq!(field_size!(btrfs_qgroup_limit_item, max_rfer), 8);
76        assert_eq!(field_size!(btrfs_qgroup_status_item, flags), 8);
77        assert_eq!(field_size!(btrfs_dev_extent, length), 8);
78
79        // Stripe dev_uuid is [u8; 16].
80        assert_eq!(field_size!(btrfs_stripe, dev_uuid), 16);
81    }
82
83    #[test]
84    fn read_le_u64_basic() {
85        let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
86        assert_eq!(read_le_u64(&buf, 0), 0x0807060504030201);
87    }
88
89    #[test]
90    fn read_le_u64_at_offset() {
91        let buf = [0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
92        assert_eq!(read_le_u64(&buf, 2), 1);
93    }
94
95    #[test]
96    fn read_le_u32_basic() {
97        let buf = [0x78, 0x56, 0x34, 0x12];
98        assert_eq!(read_le_u32(&buf, 0), 0x12345678);
99    }
100
101    #[test]
102    fn read_le_u16_basic() {
103        let buf = [0x02, 0x01];
104        assert_eq!(read_le_u16(&buf, 0), 0x0102);
105    }
106}