#![allow(unsafe_code)]
#[cfg(any(linux_like, target_os = "wasi"))]
use crate::backend::c;
use crate::event::Timespec;
use crate::fd::RawFd;
use crate::{backend, io};
#[cfg(any(windows, target_os = "wasi"))]
use core::mem::align_of;
use core::mem::size_of;
#[cfg(target_os = "wasi")]
#[repr(C)]
struct FD_SET {
fd_count: usize,
fd_array: [i32; c::FD_SETSIZE],
}
#[cfg(windows)]
use windows_sys::Win32::Networking::WinSock::FD_SET;
#[cfg(all(
target_pointer_width = "64",
any(windows, target_os = "freebsd", target_os = "dragonfly")
))]
#[repr(transparent)]
#[derive(Copy, Clone, Default)]
pub struct FdSetElement(pub(crate) u64);
#[cfg(linux_like)]
#[repr(transparent)]
#[derive(Copy, Clone, Default)]
pub struct FdSetElement(pub(crate) c::c_ulong);
#[cfg(not(any(
linux_like,
target_os = "wasi",
all(
target_pointer_width = "64",
any(windows, target_os = "freebsd", target_os = "dragonfly")
)
)))]
#[repr(transparent)]
#[derive(Copy, Clone, Default)]
pub struct FdSetElement(pub(crate) u32);
#[cfg(target_os = "wasi")]
#[repr(transparent)]
#[derive(Copy, Clone, Default)]
pub struct FdSetElement(pub(crate) usize);
pub unsafe fn select(
nfds: i32,
readfds: Option<&mut [FdSetElement]>,
writefds: Option<&mut [FdSetElement]>,
exceptfds: Option<&mut [FdSetElement]>,
timeout: Option<&Timespec>,
) -> io::Result<i32> {
backend::event::syscalls::select(nfds, readfds, writefds, exceptfds, timeout)
}
#[cfg(not(any(windows, target_os = "wasi")))]
const BITS: usize = size_of::<FdSetElement>() * 8;
#[doc(alias = "FD_SET")]
#[inline]
pub fn fd_set_insert(fds: &mut [FdSetElement], fd: RawFd) {
#[cfg(not(any(windows, target_os = "wasi")))]
{
let fd = fd as usize;
fds[fd / BITS].0 |= 1 << (fd % BITS);
}
#[cfg(any(windows, target_os = "wasi"))]
{
let set = unsafe { &mut *fds.as_mut_ptr().cast::<FD_SET>() };
let fd_count = set.fd_count;
let fd_array = &set.fd_array[..fd_count as usize];
if !fd_array.contains(&(fd as _)) {
let fd_array = &mut set.fd_array[..fd_count as usize + 1];
set.fd_count = fd_count + 1;
fd_array[fd_count as usize] = fd as _;
}
}
}
#[doc(alias = "FD_CLR")]
#[inline]
pub fn fd_set_remove(fds: &mut [FdSetElement], fd: RawFd) {
#[cfg(not(any(windows, target_os = "wasi")))]
{
let fd = fd as usize;
fds[fd / BITS].0 &= !(1 << (fd % BITS));
}
#[cfg(any(windows, target_os = "wasi"))]
{
let set = unsafe { &mut *fds.as_mut_ptr().cast::<FD_SET>() };
let fd_count = set.fd_count;
let fd_array = &set.fd_array[..fd_count as usize];
if let Some(pos) = fd_array.iter().position(|p| *p as RawFd == fd) {
set.fd_count = fd_count - 1;
set.fd_array[pos] = *set.fd_array.last().unwrap();
}
}
}
#[inline]
pub fn fd_set_bound(fds: &[FdSetElement]) -> RawFd {
#[cfg(not(any(windows, target_os = "wasi")))]
{
if let Some(position) = fds.iter().rposition(|element| element.0 != 0) {
let element = fds[position].0;
(position * BITS + (BITS - element.leading_zeros() as usize)) as RawFd
} else {
0
}
}
#[cfg(any(windows, target_os = "wasi"))]
{
let set = unsafe { &*fds.as_ptr().cast::<FD_SET>() };
let fd_count = set.fd_count;
let fd_array = &set.fd_array[..fd_count as usize];
let mut max = 0;
for fd in fd_array {
if *fd >= max {
max = *fd + 1;
}
}
max as RawFd
}
}
#[inline]
pub fn fd_set_num_elements(set_count: usize, nfds: RawFd) -> usize {
#[cfg(any(windows, target_os = "wasi"))]
{
let _ = nfds;
fd_set_num_elements_for_fd_array(set_count)
}
#[cfg(not(any(windows, target_os = "wasi")))]
{
let _ = set_count;
fd_set_num_elements_for_bitvector(nfds)
}
}
#[cfg(any(windows, target_os = "wasi"))]
#[inline]
pub(crate) fn fd_set_num_elements_for_fd_array(set_count: usize) -> usize {
core::cmp::max(
fd_set_num_elements_for_fd_array_raw(set_count),
div_ceil(size_of::<FD_SET>(), size_of::<FdSetElement>()),
)
}
#[cfg(any(windows, target_os = "wasi"))]
#[inline]
fn fd_set_num_elements_for_fd_array_raw(set_count: usize) -> usize {
div_ceil(
core::cmp::max(align_of::<FD_SET>(), align_of::<RawFd>()) + set_count * size_of::<RawFd>(),
size_of::<FdSetElement>(),
)
}
#[cfg(not(any(windows, target_os = "wasi")))]
#[inline]
pub(crate) fn fd_set_num_elements_for_bitvector(nfds: RawFd) -> usize {
let nfds = nfds as usize;
div_ceil(nfds, BITS)
}
fn div_ceil(lhs: usize, rhs: usize) -> usize {
let d = lhs / rhs;
let r = lhs % rhs;
if r > 0 {
d + 1
} else {
d
}
}
#[doc(alias = "FD_ISSET")]
#[cfg(not(any(windows, target_os = "wasi")))]
pub struct FdSetIter<'a> {
current: RawFd,
fds: &'a [FdSetElement],
}
#[doc(alias = "FD_ISSET")]
#[cfg(any(windows, target_os = "wasi"))]
pub struct FdSetIter<'a> {
current: usize,
fds: &'a [FdSetElement],
}
impl<'a> FdSetIter<'a> {
pub fn new(fds: &'a [FdSetElement]) -> Self {
Self { current: 0, fds }
}
}
#[cfg(not(any(windows, target_os = "wasi")))]
impl<'a> Iterator for FdSetIter<'a> {
type Item = RawFd;
fn next(&mut self) -> Option<Self::Item> {
if let Some(element) = self.fds.get(self.current as usize / BITS) {
let shifted = element.0 >> ((self.current as usize % BITS) as u32);
if shifted != 0 {
let fd = self.current + shifted.trailing_zeros() as RawFd;
self.current = fd + 1;
return Some(fd);
}
if let Some(index) = self.fds[(self.current as usize / BITS) + 1..]
.iter()
.position(|element| element.0 != 0)
{
let index = index + (self.current as usize / BITS) + 1;
let element = self.fds[index].0;
let fd = (index * BITS) as RawFd + element.trailing_zeros() as RawFd;
self.current = fd + 1;
return Some(fd);
}
}
None
}
}
#[cfg(any(windows, target_os = "wasi"))]
impl<'a> Iterator for FdSetIter<'a> {
type Item = RawFd;
fn next(&mut self) -> Option<Self::Item> {
let current = self.current;
let set = unsafe { &*self.fds.as_ptr().cast::<FD_SET>() };
let fd_count = set.fd_count;
let fd_array = &set.fd_array[..fd_count as usize];
if current == fd_count as usize {
return None;
}
let fd = fd_array[current as usize];
self.current = current + 1;
Some(fd as RawFd)
}
}
#[cfg(test)]
mod tests {
use super::*;
use core::mem::{align_of, size_of};
#[test]
#[cfg(any(windows, target_os = "wasi"))]
fn layouts() {
assert_eq!(align_of::<FdSetElement>(), align_of::<FD_SET>());
assert_eq!(
fd_set_num_elements_for_fd_array_raw(
memoffset::span_of!(FD_SET, fd_array).len() / size_of::<RawFd>()
) * size_of::<FdSetElement>(),
size_of::<FD_SET>()
);
assert_eq!(
fd_set_num_elements_for_fd_array(
memoffset::span_of!(FD_SET, fd_array).len() / size_of::<RawFd>()
) * size_of::<FdSetElement>(),
size_of::<FD_SET>()
);
assert_eq!(
fd_set_num_elements_for_fd_array(0) * size_of::<FdSetElement>(),
size_of::<FD_SET>()
);
}
#[test]
#[cfg(any(bsd, linux_kernel))]
fn layouts() {
use crate::backend::c;
assert_eq!(align_of::<FdSetElement>(), align_of::<c::fd_set>());
assert_eq!(
fd_set_num_elements_for_bitvector(c::FD_SETSIZE as RawFd) * size_of::<FdSetElement>(),
size_of::<c::fd_set>()
);
}
}