From 0dcdf4c45e45944458dba582e86ffcab927b8e9a Mon Sep 17 00:00:00 2001 From: Sami Daniel Date: Fri, 13 Feb 2026 22:09:27 -0300 Subject: [PATCH] Simplify and optimize inotify and fanotify read_events APIs Remove logic with `MaybeUninit` + `copy_nonoverlapping` and use `ptr::read_unaligned`, leaving the code more concise and clean on both sides, without the redundant check using `.min()` Use `Vec::with_capacity(nread / size)` instead of allocating empty space. Useful for increasing performance in case of event bursts. Allocates space to spare, but is still more performant than allocating almost empty space. Reduce the event name iteration window to the maximum byte size (which is `event.len`), without risking reading beyond the event name in any way and without needing to scan again using `CStr::from_ptr` and increasing perfomance --- changelog/2741.changed.md | 9 ++++++++ src/sys/fanotify.rs | 17 +++++++--------- src/sys/inotify.rs | 43 +++++++++++++++++++-------------------- 3 files changed, 37 insertions(+), 32 deletions(-) create mode 100644 changelog/2741.changed.md diff --git a/changelog/2741.changed.md b/changelog/2741.changed.md new file mode 100644 index 0000000000..09555bd50e --- /dev/null +++ b/changelog/2741.changed.md @@ -0,0 +1,9 @@ +In both the inotify and fanotify `read_events(...)` APIs: + +Remove logic with `MaybeUninit` + `ptr::copy_nonoverlapping` and use `ptr::read_unaligned`, leaving the code more concise and clean on both sides, without the redundant check using `.min()` + +Use `Vec::with_capacity(nread / size)` instead of allocating empty space. Useful for increasing performance in case of event bursts. Allocates space to spare, but is still more performant than allocating almost empty space + +In inotify `read_events`: + +Reduce the event name iteration window to the maximum byte size (which is `event.len`), without risking reading beyond the event name in any way and without needing to scan again using `CStr::from_ptr` and increasing perfomance diff --git a/src/sys/fanotify.rs b/src/sys/fanotify.rs index 0b5e43f30d..b9bbe6a1d1 100644 --- a/src/sys/fanotify.rs +++ b/src/sys/fanotify.rs @@ -15,7 +15,7 @@ use crate::fcntl::OFlag; use crate::unistd::{close, read, write}; use crate::{NixPath, Result}; use std::marker::PhantomData; -use std::mem::{size_of, MaybeUninit}; +use std::mem::{size_of}; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd}; use std::ptr; @@ -358,21 +358,18 @@ impl Fanotify { let metadata_size = size_of::(); const BUFSIZ: usize = 4096; let mut buffer = [0u8; BUFSIZ]; - let mut events = Vec::new(); let mut offset = 0; let nread = read(&self.fd, &mut buffer)?; + let mut events = Vec::with_capacity(nread / metadata_size); + while (nread - offset) >= metadata_size { let metadata = unsafe { - let mut metadata = - MaybeUninit::::uninit(); - ptr::copy_nonoverlapping( - buffer.as_ptr().add(offset), - metadata.as_mut_ptr().cast(), - (BUFSIZ - offset).min(metadata_size), - ); - metadata.assume_init() + ptr::read_unaligned( + buffer.as_ptr().add(offset) + as *const libc::fanotify_event_metadata, + ) }; events.push(FanotifyEvent(metadata)); diff --git a/src/sys/inotify.rs b/src/sys/inotify.rs index 0cc65f41a6..4b223d890c 100644 --- a/src/sys/inotify.rs +++ b/src/sys/inotify.rs @@ -28,9 +28,9 @@ use crate::unistd::read; use crate::NixPath; use crate::Result; use cfg_if::cfg_if; -use libc::{c_char, c_int}; -use std::ffi::{CStr, OsStr, OsString}; -use std::mem::{size_of, MaybeUninit}; +use libc::c_int; +use std::ffi::{OsStr, OsString}; +use std::mem::size_of; use std::os::unix::ffi::OsStrExt; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd}; use std::ptr; @@ -204,33 +204,32 @@ impl Inotify { let header_size = size_of::(); const BUFSIZ: usize = 4096; let mut buffer = [0u8; BUFSIZ]; - let mut events = Vec::new(); - let mut offset = 0; let nread = read(&self.fd, &mut buffer)?; + let mut events = Vec::with_capacity(nread / header_size); + let mut offset = 0; + while (nread - offset) >= header_size { let event = unsafe { - let mut event = MaybeUninit::::uninit(); - ptr::copy_nonoverlapping( - buffer.as_ptr().add(offset), - event.as_mut_ptr().cast(), - (BUFSIZ - offset).min(header_size), - ); - event.assume_init() + ptr::read_unaligned( + buffer.as_ptr().add(offset) + as *const libc::inotify_event, + ) }; - let name = match event.len { - 0 => None, - _ => { - let ptr = unsafe { - buffer.as_ptr().add(offset + header_size) - as *const c_char - }; - let cstr = unsafe { CStr::from_ptr(ptr) }; + let name = if event.len == 0 { + None + } else { + let name_start = offset + header_size; + let name_bytes = + &buffer[name_start..name_start + event.len as usize]; - Some(OsStr::from_bytes(cstr.to_bytes()).to_owned()) - } + let len = name_bytes + .iter() + .position(|&b| b == 0) + .unwrap_or(name_bytes.len()); + Some(OsStr::from_bytes(&name_bytes[..len]).to_owned()) }; events.push(InotifyEvent {