mod fditer;
pub use fditer::FdIter;
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
target_os = "solaris",
target_os = "illumos",
))]
mod dirfd;
#[derive(Clone, Debug)]
pub struct FdIterBuilder {
possible: bool,
#[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
skip_nfds: bool,
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
target_os = "solaris",
target_os = "illumos",
))]
dirfd: bool,
}
impl FdIterBuilder {
#[inline]
pub fn new() -> Self {
Self {
possible: false,
#[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
skip_nfds: false,
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
target_os = "solaris",
target_os = "illumos",
))]
dirfd: true,
}
}
#[inline]
pub fn possible(&mut self, possible: bool) -> &mut Self {
self.possible = possible;
self
}
#[allow(unused_variables)]
#[inline]
pub fn threadsafe(&mut self, threadsafe: bool) -> &mut Self {
#[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
{
self.skip_nfds = threadsafe;
}
self
}
#[allow(unused_variables)]
#[inline]
pub fn allow_filesystem(&mut self, fs: bool) -> &mut Self {
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
target_os = "solaris",
target_os = "illumos",
))]
{
self.dirfd = fs;
}
self
}
pub fn iter_from(&self, mut minfd: libc::c_int) -> FdIter {
if minfd < 0 {
minfd = 0;
}
FdIter {
curfd: minfd,
possible: self.possible,
maxfd: None,
#[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
skip_nfds: self.skip_nfds,
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
target_os = "solaris",
target_os = "illumos",
))]
dirfd_iter: if self.dirfd {
dirfd::DirFdIter::open(minfd)
} else {
None
},
}
}
}
impl Default for FdIterBuilder {
#[inline]
fn default() -> Self {
Self::new()
}
}
#[inline]
pub fn iter_open_fds(minfd: libc::c_int) -> FdIter {
FdIterBuilder::new().iter_from(minfd)
}
#[inline]
pub fn iter_open_fds_threadsafe(minfd: libc::c_int) -> FdIter {
FdIterBuilder::new().threadsafe(true).iter_from(minfd)
}
#[inline]
pub fn iter_possible_fds(minfd: libc::c_int) -> FdIter {
FdIterBuilder::new().possible(true).iter_from(minfd)
}
#[inline]
pub fn iter_possible_fds_threadsafe(minfd: libc::c_int) -> FdIter {
FdIterBuilder::new()
.possible(true)
.threadsafe(true)
.iter_from(minfd)
}
#[cfg(test)]
mod tests {
use super::*;
fn open_files() -> [libc::c_int; 10] {
let mut fds = [-1; 10];
for cur_fd in fds.iter_mut() {
*cur_fd = unsafe { libc::open("/\0".as_ptr() as *const libc::c_char, libc::O_RDONLY) };
assert!(*cur_fd >= 0);
}
fds
}
unsafe fn close_files(fds: &[libc::c_int]) {
for &fd in fds {
libc::close(fd);
}
}
#[test]
fn test_size_hint_open() {
test_size_hint_generic(FdIterBuilder::new().threadsafe(false).iter_from(0));
test_size_hint_generic(FdIterBuilder::new().threadsafe(true).iter_from(0));
let fds = open_files();
test_size_hint_generic(FdIterBuilder::new().threadsafe(false).iter_from(0));
test_size_hint_generic(FdIterBuilder::new().threadsafe(true).iter_from(0));
unsafe {
close_files(&fds);
}
}
#[test]
fn test_size_hint_possible() {
test_size_hint_generic(
FdIterBuilder::new()
.possible(true)
.threadsafe(false)
.iter_from(0),
);
test_size_hint_generic(
FdIterBuilder::new()
.possible(true)
.threadsafe(true)
.iter_from(0),
);
let fds = open_files();
test_size_hint_generic(
FdIterBuilder::new()
.possible(true)
.threadsafe(false)
.iter_from(0),
);
test_size_hint_generic(
FdIterBuilder::new()
.possible(true)
.threadsafe(true)
.iter_from(0),
);
unsafe {
close_files(&fds);
}
}
fn test_size_hint_generic(mut fditer: FdIter) {
let (mut init_low, mut init_high) = fditer.size_hint();
if let Some(init_high) = init_high {
assert!(init_high >= init_low);
}
let mut i = 0;
while let Some(_fd) = fditer.next() {
let (cur_low, cur_high) = fditer.size_hint();
let adj_low = cur_low + i + 1;
let adj_high = if let Some(cur_high) = cur_high {
assert!(cur_high >= cur_low);
Some(cur_high + i + 1)
} else {
None
};
if adj_low > init_low {
init_low = adj_low;
}
if let Some(adj_high) = adj_high {
if let Some(ihigh) = init_high {
if adj_high < ihigh {
init_high = Some(adj_high);
}
} else {
init_high = Some(adj_high);
}
}
i += 1;
}
let (final_low, _) = fditer.size_hint();
assert_eq!(final_low, 0);
assert!(i >= init_low);
if let Some(init_high) = init_high {
assert!(i <= init_high);
}
}
#[test]
fn test_fused_open() {
test_fused_generic(FdIterBuilder::new().threadsafe(false).iter_from(0));
test_fused_generic(FdIterBuilder::new().threadsafe(true).iter_from(0));
let fds = open_files();
test_fused_generic(FdIterBuilder::new().threadsafe(false).iter_from(0));
test_fused_generic(FdIterBuilder::new().threadsafe(true).iter_from(0));
unsafe {
close_files(&fds);
}
}
#[test]
fn test_fused_possible() {
test_fused_generic(
FdIterBuilder::new()
.possible(true)
.threadsafe(false)
.iter_from(0),
);
test_fused_generic(
FdIterBuilder::new()
.possible(true)
.threadsafe(true)
.iter_from(0),
);
let fds = open_files();
test_fused_generic(
FdIterBuilder::new()
.possible(true)
.threadsafe(false)
.iter_from(0),
);
test_fused_generic(
FdIterBuilder::new()
.possible(true)
.threadsafe(true)
.iter_from(0),
);
unsafe {
close_files(&fds);
}
}
fn test_fused_generic(mut fditer: FdIter) {
fditer.by_ref().count();
assert_eq!(fditer.next(), None);
}
}