use std::ptr::NonNull;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum IoState {
Idle = 0,
Submitted = 1,
InProgress = 2,
Completed = 3,
Cancelled = 4,
Failed = 5,
}
impl IoState {
#[must_use]
pub const fn is_finished(self) -> bool {
matches!(self, Self::Completed | Self::Cancelled | Self::Failed)
}
#[must_use]
pub const fn is_in_progress(self) -> bool {
matches!(self, Self::Submitted | Self::InProgress)
}
#[must_use]
pub fn is_success(self) -> bool {
matches!(self, Self::Completed)
}
}
#[derive(Debug, Clone)]
pub struct SubmitEntry {
pub fd: i32,
pub opcode: u8,
pub flags: u16,
pub user_data: u64,
pub buf_ptr: Option<NonNull<u8>>,
pub buf_len: u32,
pub offset: u64,
pub addr: Option<SockAddr>,
}
#[derive(Debug, Clone)]
pub struct SockAddr {
pub storage: libc::sockaddr_storage,
pub len: libc::socklen_t,
}
impl SockAddr {
pub unsafe fn from_raw(storage: libc::sockaddr_storage, len: libc::socklen_t) -> Self {
Self { storage, len }
}
}
impl SubmitEntry {
#[must_use]
pub const fn new(fd: i32, opcode: u8, user_data: u64) -> Self {
Self {
fd,
opcode,
flags: 0,
user_data,
buf_ptr: None,
buf_len: 0,
offset: 0,
addr: None,
}
}
#[must_use]
pub unsafe fn read(fd: i32, buf: *mut u8, buf_len: u32, user_data: u64) -> Self {
Self {
fd,
opcode: super::opcode::READ,
flags: 0,
user_data,
buf_ptr: NonNull::new(buf),
buf_len,
offset: 0,
addr: None,
}
}
#[must_use]
pub unsafe fn write(fd: i32, buf: *const u8, buf_len: u32, user_data: u64) -> Self {
Self {
fd,
opcode: super::opcode::WRITE,
flags: 0,
user_data,
buf_ptr: NonNull::new(buf.cast_mut()),
buf_len,
offset: 0,
addr: None,
}
}
#[must_use]
pub unsafe fn with_buffer(mut self, buf: *mut u8, buf_len: u32) -> Self {
self.buf_ptr = NonNull::new(buf);
self.buf_len = buf_len;
self
}
#[must_use]
pub const fn with_offset(mut self, offset: u64) -> Self {
self.offset = offset;
self
}
#[must_use]
pub const fn with_flags(mut self, flags: u16) -> Self {
self.flags = flags;
self
}
#[must_use]
pub fn with_addr(mut self, addr: SockAddr) -> Self {
self.addr = Some(addr);
self
}
#[must_use]
pub unsafe fn buffer(&self) -> Option<&[u8]> {
self.buf_ptr
.map(|ptr| std::slice::from_raw_parts(ptr.as_ptr(), self.buf_len as usize))
}
#[must_use]
pub unsafe fn buffer_mut(&self) -> Option<&mut [u8]> {
self.buf_ptr
.map(|ptr| std::slice::from_raw_parts_mut(ptr.as_ptr(), self.buf_len as usize))
}
}
#[derive(Debug, Clone, Copy)]
pub struct CompletionEntry {
pub user_data: u64,
pub result: i32,
pub flags: u32,
}
impl CompletionEntry {
#[must_use]
pub const fn new(user_data: u64, result: i32, flags: u32) -> Self {
Self {
user_data,
result,
flags,
}
}
#[must_use]
pub const fn is_success(self) -> bool {
self.result >= 0
}
#[must_use]
pub const fn is_error(self) -> bool {
self.result < 0
}
#[must_use]
pub const fn bytes_transferred(self) -> Option<u32> {
if self.result >= 0 {
Some(self.result as u32)
} else {
None
}
}
#[must_use]
pub const fn error_code(self) -> Option<i32> {
if self.result < 0 {
Some(-self.result)
} else {
None
}
}
#[must_use = "the result of the operation should be checked"]
pub fn into_result(self) -> std::io::Result<u32> {
if self.result >= 0 {
Ok(self.result as u32)
} else {
Err(std::io::Error::from_raw_os_error(-self.result))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_io_state() {
assert!(IoState::Completed.is_finished());
assert!(IoState::Cancelled.is_finished());
assert!(IoState::Failed.is_finished());
assert!(!IoState::Idle.is_finished());
assert!(!IoState::Submitted.is_finished());
assert!(IoState::Submitted.is_in_progress());
assert!(IoState::InProgress.is_in_progress());
assert!(!IoState::Idle.is_in_progress());
}
#[test]
fn test_completion_entry() {
let entry = CompletionEntry::new(123, 1024, 0);
assert!(entry.is_success());
assert!(!entry.is_error());
assert_eq!(entry.bytes_transferred(), Some(1024));
assert_eq!(entry.into_result().unwrap(), 1024);
let entry = CompletionEntry::new(456, -2, 0);
assert!(!entry.is_success());
assert!(entry.is_error());
assert_eq!(entry.bytes_transferred(), None);
assert_eq!(entry.error_code(), Some(2));
}
#[test]
fn test_submit_entry_builder() {
let buf = [0u8; 1024];
let entry = unsafe {
SubmitEntry::read(0, buf.as_ptr() as *mut u8, 1024, 42)
.with_offset(100)
.with_flags(0x0001)
};
assert_eq!(entry.fd, 0);
assert_eq!(entry.opcode, super::super::opcode::READ);
assert_eq!(entry.user_data, 42);
assert_eq!(entry.buf_len, 1024);
assert_eq!(entry.offset, 100);
assert_eq!(entry.flags, 0x0001);
}
#[test]
fn test_interest_builder() {
let interest = crate::driver::Interest::readable()
.with_writable()
.with_edge();
assert!(interest.readable);
assert!(interest.writable);
assert!(interest.edge);
}
}