use std::sync::Arc;
use std::task::Context;
use std::task::Poll;
use vortex_array::buffer::BufferHandle;
use vortex_buffer::Alignment;
use vortex_error::VortexResult;
use vortex_error::vortex_panic;
mod source;
pub use source::ReadAtIoSource;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct IoRequestId(pub u32);
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum IoTarget {
Size,
Range {
offset: u64,
len: usize,
alignment: Alignment,
},
}
impl IoTarget {
pub fn range(offset: u64, len: usize) -> Self {
Self::Range {
offset,
len,
alignment: Alignment::none(),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum IoIntent {
Fetch,
Prefetch,
Announce,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct IoRequest {
pub intent: IoIntent,
pub request: IoRequestId,
pub target: IoTarget,
}
pub type IoBatch = Vec<IoRequest>;
#[derive(Debug, Clone)]
pub enum IoResult {
Size(u64),
Bytes(BufferHandle),
}
impl IoResult {
pub fn matches(&self, target: &IoTarget) -> bool {
matches!(
(self, target),
(Self::Size(_), IoTarget::Size) | (Self::Bytes(_), IoTarget::Range { .. })
)
}
pub fn kind(&self) -> &'static str {
match self {
Self::Size(_) => "size",
Self::Bytes(_) => "bytes",
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct IoOwnerId(pub u64);
pub struct Completion {
pub owner: IoOwnerId,
pub request: IoRequestId,
pub result: VortexResult<IoResult>,
}
pub trait IoSource: Send + Sync {
fn submit(&self, owner: IoOwnerId, batch: IoBatch) -> VortexResult<()>;
fn poll(&self) -> VortexResult<Option<Completion>>;
fn poll_completion(&self, cx: &mut Context<'_>) -> Poll<VortexResult<Completion>>;
fn wait(&self) -> VortexResult<Completion>;
fn release(&self, owner: IoOwnerId);
fn clear(&self);
}
pub trait IoService: Send + Sync {
fn session(&self) -> Arc<dyn IoSource>;
}
pub trait IoConsumer {
fn set_io_result(&mut self, request: IoRequestId, result: IoResult);
}
#[derive(Default)]
pub struct IoSlot {
next_id: u32,
request: Option<IoRequest>,
result: Option<IoResult>,
}
impl IoSlot {
pub fn issue(&mut self, target: IoTarget) -> IoBatch {
if self.request.is_some() || self.result.is_some() {
vortex_panic!("IoSlot: issue while a request is outstanding");
}
let request = IoRequest {
intent: IoIntent::Fetch,
request: IoRequestId(self.next_id),
target,
};
self.next_id += 1;
self.request = Some(request);
vec![request]
}
pub fn is_waiting(&self) -> bool {
self.request.is_some()
}
pub fn deliver(&mut self, id: IoRequestId, result: IoResult) {
let Some(request) = self.request.take_if(|request| request.request == id) else {
vortex_panic!("IoSlot: delivery of {id:?}, which is not outstanding");
};
if !result.matches(&request.target) {
vortex_panic!(
"IoSlot: {id:?} for {:?} answered with {}",
request.target,
result.kind()
);
}
self.result = Some(result);
}
pub fn take(&mut self) -> Option<IoResult> {
self.result.take()
}
}
#[cfg(test)]
mod tests;