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