use std::collections::HashMap;
use std::marker::PhantomData;
use std::os::unix::io::RawFd;
use std::pin::Pin;
use crate::operation::OperationType;
#[derive(Debug)]
pub(crate) struct OperationTracker<'ring> {
in_flight: HashMap<u64, OperationHandle<'ring>>,
next_id: u64,
}
#[derive(Debug)]
pub(crate) struct OperationHandle<'ring> {
pub(crate) id: u64,
pub(crate) op_type: OperationType,
pub(crate) fd: RawFd,
pub(crate) buffer: Option<BufferOwnership>,
_phantom: PhantomData<&'ring ()>,
}
#[derive(Debug)]
pub enum BufferOwnership {
Single(Pin<Box<[u8]>>),
Vectored(Vec<Pin<Box<[u8]>>>),
}
impl<'ring> OperationTracker<'ring> {
#[allow(dead_code)] pub(crate) fn new() -> Self {
Self {
in_flight: HashMap::new(),
next_id: 1, }
}
pub(crate) fn register_operation(&mut self, op_type: OperationType, fd: RawFd) -> u64 {
self.register_operation_with_buffer(op_type, fd, None)
}
pub(crate) fn register_operation_with_buffer(
&mut self,
op_type: OperationType,
fd: RawFd,
buffer: Option<BufferOwnership>,
) -> u64 {
let id = self.next_id;
self.next_id = self.next_id.wrapping_add(1);
let handle = OperationHandle {
id,
op_type,
fd,
buffer,
_phantom: PhantomData,
};
self.in_flight.insert(id, handle);
id
}
pub(crate) fn complete_operation(&mut self, id: u64) -> Option<OperationHandle<'ring>> {
self.in_flight.remove(&id)
}
pub(crate) fn is_operation_tracked(&self, id: u64) -> bool {
self.in_flight.contains_key(&id)
}
pub(crate) fn count(&self) -> usize {
self.in_flight.len()
}
pub(crate) fn has_operations(&self) -> bool {
!self.in_flight.is_empty()
}
pub(crate) fn debug_info(&self) -> Vec<(u64, OperationType, RawFd)> {
self.in_flight
.values()
.map(|handle| (handle.id, handle.op_type, handle.fd))
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_tracker_is_empty() {
let tracker = OperationTracker::new();
assert_eq!(tracker.count(), 0);
assert!(!tracker.has_operations());
}
#[test]
fn register_and_complete_operations() {
let mut tracker = OperationTracker::new();
let id1 = tracker.register_operation(OperationType::Read, 0);
assert_eq!(tracker.count(), 1);
assert!(tracker.has_operations());
assert_eq!(id1, 1);
let id2 = tracker.register_operation(OperationType::Write, 1);
assert_eq!(tracker.count(), 2);
assert_eq!(id2, 2);
let handle = tracker.complete_operation(id1);
assert!(handle.is_some());
assert_eq!(handle.unwrap().id, id1);
assert_eq!(tracker.count(), 1);
tracker.complete_operation(id2);
assert_eq!(tracker.count(), 0);
assert!(!tracker.has_operations());
}
#[test]
fn complete_nonexistent_operation() {
let mut tracker = OperationTracker::new();
let handle = tracker.complete_operation(999);
assert!(handle.is_none());
}
#[test]
fn id_wraparound() {
let mut tracker = OperationTracker::new();
tracker.next_id = u64::MAX;
let id1 = tracker.register_operation(OperationType::Read, 0);
assert_eq!(id1, u64::MAX);
let id2 = tracker.register_operation(OperationType::Read, 0);
assert_eq!(id2, 0); }
#[test]
fn debug_info() {
let mut tracker = OperationTracker::new();
tracker.register_operation(OperationType::Read, 3);
tracker.register_operation(OperationType::Write, 4);
let info = tracker.debug_info();
assert_eq!(info.len(), 2);
let mut sorted_info = info;
sorted_info.sort_by_key(|&(id, _, _)| id);
assert_eq!(sorted_info[0], (1, OperationType::Read, 3));
assert_eq!(sorted_info[1], (2, OperationType::Write, 4));
}
}