#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[repr(u8)]
pub enum OpKind {
Connect = 1,
Recv = 2,
Send = 3,
Close = 4,
Nop = 5,
}
impl OpKind {
#[inline]
#[must_use]
pub const fn from_u8(v: u8) -> Option<Self> {
match v {
1 => Some(Self::Connect),
2 => Some(Self::Recv),
3 => Some(Self::Send),
4 => Some(Self::Close),
5 => Some(Self::Nop),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct UserData(u64);
impl UserData {
const TOKEN_MASK: u64 = 0x00FF_FFFF_FFFF_FFFF;
#[inline]
#[must_use]
pub const fn new(kind: OpKind, token: u64) -> Self {
Self(((kind as u64) << 56) | (token & Self::TOKEN_MASK))
}
#[inline]
#[must_use]
pub const fn from_raw(raw: u64) -> Self {
Self(raw)
}
#[inline]
#[must_use]
pub const fn raw(self) -> u64 {
self.0
}
#[inline]
#[must_use]
pub fn kind(self) -> Option<OpKind> {
OpKind::from_u8((self.0 >> 56) as u8)
}
#[inline]
#[must_use]
pub const fn token(self) -> u64 {
self.0 & Self::TOKEN_MASK
}
}
#[derive(Debug, Clone, Copy)]
pub struct Completion {
pub user_data: UserData,
pub result: i32,
pub flags: u32,
}
impl Completion {
pub fn to_result(self) -> std::io::Result<usize> {
if self.result >= 0 {
#[allow(clippy::cast_sign_loss)]
Ok(self.result as usize)
} else {
Err(std::io::Error::from_raw_os_error(-self.result))
}
}
#[inline]
#[must_use]
pub fn buffer_id(self) -> Option<u16> {
io_uring::cqueue::buffer_select(self.flags)
}
#[inline]
#[must_use]
pub fn has_more(self) -> bool {
io_uring::cqueue::more(self.flags)
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
pub struct SqeFlags(u8);
impl SqeFlags {
pub const NONE: Self = Self(0);
pub const IO_LINK: Self = Self(io_uring::squeue::Flags::IO_LINK.bits());
#[inline]
#[must_use]
pub const fn empty() -> Self {
Self::NONE
}
#[inline]
#[must_use]
pub const fn is_empty(self) -> bool {
self.0 == 0
}
#[inline]
pub(crate) fn into_io_uring(self) -> io_uring::squeue::Flags {
io_uring::squeue::Flags::from_bits_truncate(self.0)
}
}
impl std::ops::BitOr for SqeFlags {
type Output = Self;
#[inline]
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
impl std::ops::BitOrAssign for SqeFlags {
#[inline]
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn user_data_roundtrip() {
let ud = UserData::new(OpKind::Recv, 0x1234_5678_9ABC);
assert_eq!(ud.kind(), Some(OpKind::Recv));
assert_eq!(ud.token(), 0x1234_5678_9ABC);
let raw = ud.raw();
let restored = UserData::from_raw(raw);
assert_eq!(restored, ud);
}
#[test]
fn token_high_bits_truncated() {
let ud = UserData::new(OpKind::Send, 0xFFFF_FFFF_FFFF_FFFF);
assert_eq!(ud.kind(), Some(OpKind::Send));
assert_eq!(ud.token(), 0x00FF_FFFF_FFFF_FFFF);
}
#[test]
fn all_kinds_distinct() {
let a = UserData::new(OpKind::Connect, 1);
let b = UserData::new(OpKind::Recv, 1);
let c = UserData::new(OpKind::Send, 1);
let d = UserData::new(OpKind::Close, 1);
assert_ne!(a.raw(), b.raw());
assert_ne!(b.raw(), c.raw());
assert_ne!(c.raw(), d.raw());
assert_eq!(a.token(), 1);
assert_eq!(d.token(), 1);
}
#[test]
fn unknown_kind_returns_none() {
let bogus = UserData::from_raw(0xFF00_0000_0000_0000);
assert!(bogus.kind().is_none());
}
#[test]
fn completion_to_result_positive() {
let c = Completion {
user_data: UserData::new(OpKind::Recv, 0),
result: 42,
flags: 0,
};
assert_eq!(c.to_result().unwrap(), 42);
}
#[test]
fn completion_to_result_errno() {
let c = Completion {
user_data: UserData::new(OpKind::Connect, 0),
result: -libc::ECONNREFUSED,
flags: 0,
};
let err = c.to_result().unwrap_err();
assert_eq!(err.raw_os_error(), Some(libc::ECONNREFUSED));
}
}