use std::io;
#[cfg(unix)]
use std::os::unix::io::RawFd;
#[cfg(unix)]
pub type RawSource = RawFd;
#[cfg(windows)]
pub type RawSource = windows_sys::Win32::Foundation::HANDLE;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Interest(u8);
impl Interest {
pub const READABLE: Interest = Interest(0b0000_0001);
pub const WRITABLE: Interest = Interest(0b0000_0010);
#[inline]
pub fn is_readable(self) -> bool {
self.0 & Self::READABLE.0 != 0
}
#[inline]
pub fn is_writable(self) -> bool {
self.0 & Self::WRITABLE.0 != 0
}
#[inline]
pub(crate) fn bits(self) -> u8 {
self.0
}
}
impl std::ops::BitOr for Interest {
type Output = Interest;
#[inline]
fn bitor(self, rhs: Self) -> Self::Output {
Interest(self.0 | rhs.0)
}
}
impl std::ops::BitOrAssign for Interest {
#[inline]
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
#[derive(Debug, Clone, Copy)]
pub struct Event {
pub token: usize,
pub readable: bool,
pub writable: bool,
}
impl Event {
#[inline]
pub(crate) fn new(token: usize, readable: bool, writable: bool) -> Self {
Self {
token,
readable,
writable,
}
}
}
pub type Events = Vec<Event>;
#[inline]
pub fn events_with_capacity(cap: usize) -> Events {
Vec::with_capacity(cap)
}
#[cfg(unix)]
mod unix_impl {
use super::*;
use libc::{c_int, fcntl, F_GETFL, F_SETFL, O_NONBLOCK};
pub fn set_nonblocking(fd: RawSource) -> io::Result<()> {
let flags = unsafe { fcntl(fd, F_GETFL) };
if flags == -1 {
return Err(io::Error::last_os_error());
}
let rc = unsafe { fcntl(fd, F_SETFL, flags | O_NONBLOCK) };
if rc == -1 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
pub fn close_fd(fd: RawSource) -> io::Result<()> {
let rc = unsafe { libc::close(fd) };
if rc == -1 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
pub fn create_pipe() -> io::Result<(RawSource, RawSource)> {
let mut fds: [c_int; 2] = [0; 2];
let rc = unsafe { libc::pipe(fds.as_mut_ptr()) };
if rc == -1 {
return Err(io::Error::last_os_error());
}
let (r, w) = (fds[0], fds[1]);
set_nonblocking(r)?;
set_nonblocking(w)?;
Ok((r, w))
}
}
#[cfg(unix)]
pub use unix_impl::{close_fd, create_pipe, set_nonblocking};
#[cfg(windows)]
mod windows_impl {
use super::*;
pub fn set_nonblocking(handle: RawSource) -> io::Result<()> {
let mut nonblocking: u32 = 1;
let ret = unsafe {
windows_sys::Win32::Networking::WinSock::ioctlsocket(
handle as usize, windows_sys::Win32::Networking::WinSock::FIONBIO as i32,
&mut nonblocking,
)
};
if ret != 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
pub fn close_fd(handle: RawSource) -> io::Result<()> {
let ok = unsafe { windows_sys::Win32::Foundation::CloseHandle(handle) };
if ok == 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
pub fn create_pipe() -> io::Result<(RawSource, RawSource)> {
let mut read_handle: RawSource = 0;
let mut write_handle: RawSource = 0;
let ok = unsafe {
windows_sys::Win32::System::Pipes::CreatePipe(
&mut read_handle,
&mut write_handle,
std::ptr::null(), 0, )
};
if ok == 0 {
Err(io::Error::last_os_error())
} else {
Ok((read_handle, write_handle))
}
}
}
#[cfg(windows)]
pub use windows_impl::{close_fd, create_pipe, set_nonblocking};
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn interest_readable_bit() {
assert!(Interest::READABLE.is_readable());
assert!(!Interest::READABLE.is_writable());
}
#[test]
fn interest_writable_bit() {
assert!(Interest::WRITABLE.is_writable());
assert!(!Interest::WRITABLE.is_readable());
}
#[test]
fn interest_bitor() {
let both = Interest::READABLE | Interest::WRITABLE;
assert!(both.is_readable());
assert!(both.is_writable());
}
#[test]
fn event_fields() {
let e = Event::new(42, true, false);
assert_eq!(e.token, 42);
assert!(e.readable);
assert!(!e.writable);
}
#[test]
fn events_capacity() {
let ev = events_with_capacity(64);
assert_eq!(ev.len(), 0);
assert!(ev.capacity() >= 64);
}
#[cfg(unix)]
#[test]
fn create_pipe_returns_valid_fds() {
let (r, w) = create_pipe().expect("pipe creation failed");
let byte: u8 = 0xAB;
let written = unsafe { libc::write(w, &byte as *const u8 as *const _, 1) };
assert_eq!(written, 1);
let mut buf: u8 = 0;
let read = unsafe { libc::read(r, &mut buf as *mut u8 as *mut _, 1) };
assert_eq!(read, 1);
assert_eq!(buf, 0xAB);
close_fd(r).unwrap();
close_fd(w).unwrap();
}
}