use super::{File, FileBacking, Metadata};
use std::io::{self, Read};
#[doc(hidden)]
pub trait ReadAdmissionControl {
fn admit_validation(&mut self, bytes: u64) -> io::Result<()>;
fn checkpoint(&mut self) -> io::Result<()>;
}
#[doc(hidden)]
pub struct AdmittedFileRead<'a> {
file: &'a File,
control: &'a mut dyn ReadAdmissionControl,
length: u64,
offset: u64,
}
impl AdmittedFileRead<'_> {
pub fn read_at(&mut self, buffer: &mut [u8], offset: u64) -> io::Result<usize> {
self.control.checkpoint()?;
let length = self
.length
.saturating_sub(offset)
.min(buffer.len() as u64)
.min(8192) as usize;
if length == 0 {
return Ok(0);
}
self.file.with_native(|native| {
crate::io::read_native_at_once(native, &mut buffer[..length], offset)
})
}
pub fn metadata(&mut self) -> io::Result<Metadata> {
self.control.checkpoint()?;
self.file.metadata()
}
}
impl Read for AdmittedFileRead<'_> {
fn read(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
let read = self.read_at(buffer, self.offset)?;
self.offset += read as u64;
Ok(read)
}
}
impl File {
#[doc(hidden)]
pub fn pin_admitted(mut self, control: &mut impl ReadAdmissionControl) -> io::Result<Self> {
control.checkpoint()?;
match &mut self.backing {
FileBacking::Immutable(file) if file.pinned.is_none() => {
file.pinned = Some(file.get_controlled(control)?.0);
}
FileBacking::Captured { file, pinned } if pinned.is_none() => {
*pinned = Some(file.get_controlled(control)?.0);
}
_ => {}
}
control.checkpoint()?;
Ok(self)
}
#[doc(hidden)]
pub fn with_admitted_read<T>(
&self,
control: &mut impl ReadAdmissionControl,
operation: impl FnOnce(&mut AdmittedFileRead<'_>) -> io::Result<T>,
) -> io::Result<(T, u64)> {
control.checkpoint()?;
let (lease, validation_bytes) = match self.immutable() {
None => (None, 0),
Some((file, pinned)) => {
let (lease, bytes) = match pinned {
Some(lease) => (lease.clone(), 0),
None => file.get_controlled(control)?,
};
(Some(lease), bytes)
}
};
control.checkpoint()?;
let file = lease.as_deref().unwrap_or(self);
let length = file.metadata()?.len();
let result = operation(&mut AdmittedFileRead {
file,
control,
length,
offset: 0,
});
control.checkpoint()?;
result.map(|value| (value, validation_bytes))
}
}