#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum OpTag {
RecvMulti = 0,
Send = 2,
SendMsgZc = 3,
Close = 4,
Shutdown = 5,
EventFdRead = 6,
TlsSend = 7,
Connect = 8,
Timeout = 9,
Cancel = 10,
TickTimeout = 11,
Timer = 12,
RecvMsgUdp = 13,
SendMsgUdp = 14,
NvmeCmd = 15,
DirectIo = 16,
#[cfg(feature = "timestamps")]
RecvMsgMultiTs = 17,
Fs = 18,
PidfdPoll = 19,
SendRecvBuf = 20,
}
impl OpTag {
pub fn from_u8(v: u8) -> Option<Self> {
match v {
0 => Some(OpTag::RecvMulti),
2 => Some(OpTag::Send),
3 => Some(OpTag::SendMsgZc),
4 => Some(OpTag::Close),
5 => Some(OpTag::Shutdown),
6 => Some(OpTag::EventFdRead),
7 => Some(OpTag::TlsSend),
8 => Some(OpTag::Connect),
9 => Some(OpTag::Timeout),
10 => Some(OpTag::Cancel),
11 => Some(OpTag::TickTimeout),
12 => Some(OpTag::Timer),
13 => Some(OpTag::RecvMsgUdp),
14 => Some(OpTag::SendMsgUdp),
15 => Some(OpTag::NvmeCmd),
16 => Some(OpTag::DirectIo),
#[cfg(feature = "timestamps")]
17 => Some(OpTag::RecvMsgMultiTs),
18 => Some(OpTag::Fs),
19 => Some(OpTag::PidfdPoll),
20 => Some(OpTag::SendRecvBuf),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct UserData(pub(crate) u64);
impl UserData {
const TAG_SHIFT: u64 = 56;
const CONN_SHIFT: u64 = 32;
const TAG_MASK: u64 = 0xFF << Self::TAG_SHIFT;
const CONN_MASK: u64 = 0x00FF_FFFF << Self::CONN_SHIFT;
const PAYLOAD_MASK: u64 = 0xFFFF_FFFF;
#[inline]
pub fn encode(tag: OpTag, conn_index: u32, payload: u32) -> Self {
debug_assert!(conn_index < (1 << 24), "conn_index exceeds 24 bits");
let v = ((tag as u64) << Self::TAG_SHIFT)
| (((conn_index as u64) & 0x00FF_FFFF) << Self::CONN_SHIFT)
| (payload as u64);
UserData(v)
}
#[inline]
pub fn tag(self) -> Option<OpTag> {
let raw = ((self.0 & Self::TAG_MASK) >> Self::TAG_SHIFT) as u8;
OpTag::from_u8(raw)
}
#[inline]
pub fn conn_index(self) -> u32 {
((self.0 & Self::CONN_MASK) >> Self::CONN_SHIFT) as u32
}
#[inline]
pub fn payload(self) -> u32 {
(self.0 & Self::PAYLOAD_MASK) as u32
}
#[inline]
pub fn raw(self) -> u64 {
self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip_all_tags() {
for tag_val in 0..=10u8 {
let tag = match OpTag::from_u8(tag_val) {
Some(t) => t,
None => continue,
};
let conn = 0x00AB_CDEFu32 & 0x00FF_FFFF;
let payload = 0xDEAD_BEEFu32;
let ud = UserData::encode(tag, conn, payload);
assert_eq!(ud.tag(), Some(tag));
assert_eq!(ud.conn_index(), conn);
assert_eq!(ud.payload(), payload);
}
}
#[test]
fn zero_values() {
let ud = UserData::encode(OpTag::RecvMulti, 0, 0);
assert_eq!(ud.tag(), Some(OpTag::RecvMulti));
assert_eq!(ud.conn_index(), 0);
assert_eq!(ud.payload(), 0);
}
#[test]
fn max_conn_index() {
let max_conn = (1u32 << 24) - 1;
let ud = UserData::encode(OpTag::RecvMulti, max_conn, 0xFFFF_FFFF);
assert_eq!(ud.conn_index(), max_conn);
assert_eq!(ud.payload(), 0xFFFF_FFFF);
}
#[test]
fn invalid_tag() {
let ud = UserData(0xFF << 56);
assert_eq!(ud.tag(), None);
}
}