1#![allow(unsafe_code)]
7
8#[cfg(any(linux_like, target_os = "wasi"))]
9use crate::backend::c;
10use crate::event::Timespec;
11use crate::fd::RawFd;
12use crate::{backend, io};
13#[cfg(any(windows, target_os = "wasi"))]
14use core::mem::align_of;
15use core::mem::size_of;
16
17#[cfg(target_os = "wasi")]
21#[repr(C)]
22struct FD_SET {
23    fd_count: usize,
25    fd_array: [i32; c::FD_SETSIZE],
27}
28
29#[cfg(windows)]
30use windows_sys::Win32::Networking::WinSock::FD_SET;
31
32#[cfg(all(
34    target_pointer_width = "64",
35    any(windows, target_os = "freebsd", target_os = "dragonfly")
36))]
37#[repr(transparent)]
38#[derive(Copy, Clone, Default)]
39pub struct FdSetElement(pub(crate) u64);
40
41#[cfg(linux_like)]
43#[repr(transparent)]
44#[derive(Copy, Clone, Default)]
45pub struct FdSetElement(pub(crate) c::c_ulong);
46
47#[cfg(not(any(
49    linux_like,
50    target_os = "wasi",
51    all(
52        target_pointer_width = "64",
53        any(windows, target_os = "freebsd", target_os = "dragonfly")
54    )
55)))]
56#[repr(transparent)]
57#[derive(Copy, Clone, Default)]
58pub struct FdSetElement(pub(crate) u32);
59
60#[cfg(target_os = "wasi")]
62#[repr(transparent)]
63#[derive(Copy, Clone, Default)]
64pub struct FdSetElement(pub(crate) usize);
65
66pub unsafe fn select(
119    nfds: i32,
120    readfds: Option<&mut [FdSetElement]>,
121    writefds: Option<&mut [FdSetElement]>,
122    exceptfds: Option<&mut [FdSetElement]>,
123    timeout: Option<&Timespec>,
124) -> io::Result<i32> {
125    backend::event::syscalls::select(nfds, readfds, writefds, exceptfds, timeout)
126}
127
128#[cfg(not(any(windows, target_os = "wasi")))]
129const BITS: usize = size_of::<FdSetElement>() * 8;
130
131#[doc(alias = "FD_SET")]
133#[inline]
134pub fn fd_set_insert(fds: &mut [FdSetElement], fd: RawFd) {
135    #[cfg(not(any(windows, target_os = "wasi")))]
136    {
137        let fd = fd as usize;
138        fds[fd / BITS].0 |= 1 << (fd % BITS);
139    }
140
141    #[cfg(any(windows, target_os = "wasi"))]
142    {
143        let set = unsafe { &mut *fds.as_mut_ptr().cast::<FD_SET>() };
144        let fd_count = set.fd_count;
145        let fd_array = &set.fd_array[..fd_count as usize];
146
147        if !fd_array.contains(&(fd as _)) {
148            let fd_array = &mut set.fd_array[..fd_count as usize + 1];
149            set.fd_count = fd_count + 1;
150            fd_array[fd_count as usize] = fd as _;
151        }
152    }
153}
154
155#[doc(alias = "FD_CLR")]
157#[inline]
158pub fn fd_set_remove(fds: &mut [FdSetElement], fd: RawFd) {
159    #[cfg(not(any(windows, target_os = "wasi")))]
160    {
161        let fd = fd as usize;
162        fds[fd / BITS].0 &= !(1 << (fd % BITS));
163    }
164
165    #[cfg(any(windows, target_os = "wasi"))]
166    {
167        let set = unsafe { &mut *fds.as_mut_ptr().cast::<FD_SET>() };
168        let fd_count = set.fd_count;
169        let fd_array = &set.fd_array[..fd_count as usize];
170
171        if let Some(pos) = fd_array.iter().position(|p| *p as RawFd == fd) {
172            set.fd_count = fd_count - 1;
173            set.fd_array[pos] = *set.fd_array.last().unwrap();
174        }
175    }
176}
177
178#[inline]
180pub fn fd_set_bound(fds: &[FdSetElement]) -> RawFd {
181    #[cfg(not(any(windows, target_os = "wasi")))]
182    {
183        if let Some(position) = fds.iter().rposition(|element| element.0 != 0) {
184            let element = fds[position].0;
185            (position * BITS + (BITS - element.leading_zeros() as usize)) as RawFd
186        } else {
187            0
188        }
189    }
190
191    #[cfg(any(windows, target_os = "wasi"))]
192    {
193        let set = unsafe { &*fds.as_ptr().cast::<FD_SET>() };
194        let fd_count = set.fd_count;
195        let fd_array = &set.fd_array[..fd_count as usize];
196        let mut max = 0;
197        for fd in fd_array {
198            if *fd >= max {
199                max = *fd + 1;
200            }
201        }
202        max as RawFd
203    }
204}
205
206#[inline]
209pub fn fd_set_num_elements(set_count: usize, nfds: RawFd) -> usize {
210    #[cfg(any(windows, target_os = "wasi"))]
211    {
212        let _ = nfds;
213
214        fd_set_num_elements_for_fd_array(set_count)
215    }
216
217    #[cfg(not(any(windows, target_os = "wasi")))]
218    {
219        let _ = set_count;
220
221        fd_set_num_elements_for_bitvector(nfds)
222    }
223}
224
225#[cfg(any(windows, target_os = "wasi"))]
228#[inline]
229pub(crate) fn fd_set_num_elements_for_fd_array(set_count: usize) -> usize {
230    core::cmp::max(
232        fd_set_num_elements_for_fd_array_raw(set_count),
233        div_ceil(size_of::<FD_SET>(), size_of::<FdSetElement>()),
234    )
235}
236
237#[cfg(any(windows, target_os = "wasi"))]
240#[inline]
241fn fd_set_num_elements_for_fd_array_raw(set_count: usize) -> usize {
242    div_ceil(
245        core::cmp::max(align_of::<FD_SET>(), align_of::<RawFd>()) + set_count * size_of::<RawFd>(),
246        size_of::<FdSetElement>(),
247    )
248}
249
250#[cfg(not(any(windows, target_os = "wasi")))]
253#[inline]
254pub(crate) fn fd_set_num_elements_for_bitvector(nfds: RawFd) -> usize {
255    let nfds = nfds as usize;
257    div_ceil(nfds, BITS)
258}
259
260fn div_ceil(lhs: usize, rhs: usize) -> usize {
261    let d = lhs / rhs;
262    let r = lhs % rhs;
263    if r > 0 {
264        d + 1
265    } else {
266        d
267    }
268}
269
270#[doc(alias = "FD_ISSET")]
272#[cfg(not(any(windows, target_os = "wasi")))]
273pub struct FdSetIter<'a> {
274    current: RawFd,
275    fds: &'a [FdSetElement],
276}
277
278#[doc(alias = "FD_ISSET")]
280#[cfg(any(windows, target_os = "wasi"))]
281pub struct FdSetIter<'a> {
282    current: usize,
283    fds: &'a [FdSetElement],
284}
285
286impl<'a> FdSetIter<'a> {
287    pub fn new(fds: &'a [FdSetElement]) -> Self {
289        Self { current: 0, fds }
290    }
291}
292
293#[cfg(not(any(windows, target_os = "wasi")))]
294impl<'a> Iterator for FdSetIter<'a> {
295    type Item = RawFd;
296
297    fn next(&mut self) -> Option<Self::Item> {
298        if let Some(element) = self.fds.get(self.current as usize / BITS) {
299            let shifted = element.0 >> ((self.current as usize % BITS) as u32);
301            if shifted != 0 {
302                let fd = self.current + shifted.trailing_zeros() as RawFd;
303                self.current = fd + 1;
304                return Some(fd);
305            }
306
307            if let Some(index) = self.fds[(self.current as usize / BITS) + 1..]
309                .iter()
310                .position(|element| element.0 != 0)
311            {
312                let index = index + (self.current as usize / BITS) + 1;
313                let element = self.fds[index].0;
314                let fd = (index * BITS) as RawFd + element.trailing_zeros() as RawFd;
315                self.current = fd + 1;
316                return Some(fd);
317            }
318        }
319        None
320    }
321}
322
323#[cfg(any(windows, target_os = "wasi"))]
324impl<'a> Iterator for FdSetIter<'a> {
325    type Item = RawFd;
326
327    fn next(&mut self) -> Option<Self::Item> {
328        let current = self.current;
329
330        let set = unsafe { &*self.fds.as_ptr().cast::<FD_SET>() };
331        let fd_count = set.fd_count;
332        let fd_array = &set.fd_array[..fd_count as usize];
333
334        if current == fd_count as usize {
335            return None;
336        }
337        let fd = fd_array[current as usize];
338        self.current = current + 1;
339        Some(fd as RawFd)
340    }
341}
342
343#[cfg(test)]
344mod tests {
345    use super::*;
346    use core::mem::{align_of, size_of};
347
348    #[test]
349    #[cfg(any(windows, target_os = "wasi"))]
350    fn layouts() {
351        assert_eq!(align_of::<FdSetElement>(), align_of::<FD_SET>());
353
354        assert_eq!(
357            fd_set_num_elements_for_fd_array_raw(
358                memoffset::span_of!(FD_SET, fd_array).len() / size_of::<RawFd>()
359            ) * size_of::<FdSetElement>(),
360            size_of::<FD_SET>()
361        );
362        assert_eq!(
363            fd_set_num_elements_for_fd_array(
364                memoffset::span_of!(FD_SET, fd_array).len() / size_of::<RawFd>()
365            ) * size_of::<FdSetElement>(),
366            size_of::<FD_SET>()
367        );
368
369        assert_eq!(
371            fd_set_num_elements_for_fd_array(0) * size_of::<FdSetElement>(),
372            size_of::<FD_SET>()
373        );
374    }
375
376    #[test]
377    #[cfg(any(bsd, linux_kernel))]
378    fn layouts() {
379        use crate::backend::c;
380
381        assert_eq!(align_of::<FdSetElement>(), align_of::<c::fd_set>());
383
384        assert_eq!(
387            fd_set_num_elements_for_bitvector(c::FD_SETSIZE as RawFd) * size_of::<FdSetElement>(),
388            size_of::<c::fd_set>()
389        );
390    }
391}