polars_io/utils/byte_source/
dio_align.rs1#[cfg(all(target_os = "linux", target_env = "gnu"))]
19use std::os::fd::AsRawFd;
20
21#[cfg(target_os = "linux")]
23const FALLBACK_ALIGN: usize = 4096;
24
25#[cfg(target_os = "linux")]
30const MIN_MEM_ALIGN: usize = 512;
31
32#[derive(Debug, Clone, Copy)]
33pub struct DioAlign {
34 pub offset: usize,
36 pub memory: usize,
39}
40
41#[cfg(all(target_os = "linux", target_env = "gnu"))]
43#[derive(Debug, Clone)]
44enum StatxAlign {
45 Known(DioAlign),
47 Unsupported,
50 Unknown,
52}
53
54impl DioAlign {
55 pub fn span(&self, offset: u64, len: usize) -> (u64, u64, usize) {
62 debug_assert!(self.offset > 0);
63
64 let a = self.offset as u64;
65 let lo = offset & !(a - 1);
66 let hi = (offset + len as u64).next_multiple_of(a);
67 (lo, hi, (offset - lo) as usize)
68 }
69
70 #[cfg(target_os = "linux")]
75 pub fn probe(file: &std::fs::File) -> Option<Self> {
76 #[cfg(target_env = "gnu")]
77 match statx_dioalign(file) {
78 StatxAlign::Unsupported => return None,
79 StatxAlign::Known(a) => return Some(Self::new(a.offset, a.memory)),
80 StatxAlign::Unknown => {},
81 }
82
83 sysfs_logical_block_size(file).map(|a| Self::new(a.offset, a.memory))
84 }
85
86 #[cfg(not(target_os = "linux"))]
87 pub fn probe(_file: &std::fs::File) -> Option<Self> {
88 None
89 }
90
91 #[cfg(target_os = "linux")]
93 fn new(offset: usize, memory: usize) -> Self {
94 Self {
95 offset: normalize(offset),
96 memory: normalize(memory).max(MIN_MEM_ALIGN),
97 }
98 }
99}
100
101#[cfg(target_os = "linux")]
102fn normalize(v: usize) -> usize {
103 if v == 0 || !v.is_power_of_two() {
104 FALLBACK_ALIGN
105 } else {
106 v
107 }
108}
109
110#[cfg(all(target_os = "linux", target_env = "gnu"))]
111fn statx_dioalign(file: &std::fs::File) -> StatxAlign {
112 let mut stx: libc::statx = unsafe { std::mem::zeroed() };
113 let rc = unsafe {
114 libc::statx(
115 file.as_raw_fd(),
116 c"".as_ptr(),
117 libc::AT_EMPTY_PATH,
118 libc::STATX_DIOALIGN,
119 &mut stx,
120 )
121 };
122 if rc != 0 || stx.stx_mask & libc::STATX_DIOALIGN == 0 {
123 return StatxAlign::Unknown;
124 }
125
126 let offset = stx.stx_dio_offset_align as usize;
127 let memory = stx.stx_dio_mem_align as usize;
128
129 if offset == 0 || memory == 0 {
130 StatxAlign::Unsupported
131 } else {
132 StatxAlign::Known(DioAlign { offset, memory })
133 }
134}
135
136#[cfg(target_os = "linux")]
142fn sysfs_logical_block_size(file: &std::fs::File) -> Option<DioAlign> {
143 use std::os::unix::fs::MetadataExt;
144
145 let dev = file.metadata().ok()?.dev();
146 let (major, minor) = (libc::major(dev), libc::minor(dev));
147 let path = format!("/sys/dev/block/{major}:{minor}/queue/logical_block_size");
148 let v: usize = std::fs::read_to_string(path).ok()?.trim().parse().ok()?;
149
150 Some(DioAlign {
153 offset: v,
154 memory: v,
155 })
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn span_invariants() {
164 for align in [512usize, 4096] {
165 let a = DioAlign {
166 offset: align,
167 memory: align,
168 };
169 for (off, len) in [(206959u64, 206955usize), (4095, 2), (0, 1), (0, align)] {
170 let (lo, hi, pad) = a.span(off, len);
171 assert_eq!(lo as usize % align, 0, "lo unaligned");
172 assert_eq!((hi - lo) as usize % align, 0, "span unaligned");
173 assert!(hi >= off + len as u64, "span does not cover the range");
174 assert!(pad + len <= (hi - lo) as usize, "pad + len exceeds span");
175 }
176 }
177 }
178}