use std::borrow::Cow;
use std::fmt::Debug;
use std::ops::Range;
use std::path::Path;
use super::{Item, ReadPipeline, UniversalReadFs, UserData};
use crate::common::ext::aligned_vec::ACow;
use crate::common::generic_consts::{AccessPattern, Sequential};
use crate::common::universal_io::cached_fs::FileInfo;
use crate::common::universal_io::{ReadBytesItem, ReadRange, UioResult, UniversalIoError, UniversalKind};
#[expect(clippy::len_without_is_empty)]
pub trait UniversalRead: Sized + Debug + Send + Sync {
type Fs: UniversalReadFs<File = Self>;
type ReadPipeline<'file, U>: ReadPipeline<'file, U, File = Self>
where
Self: 'file,
U: UserData;
fn reopen(&mut self) -> UioResult<()>;
fn schedule_reopen<F: FnOnce(&Path) -> Option<FileInfo>>(
&mut self,
get_file_info: F,
) -> UioResult<()> {
let _ = get_file_info;
Ok(())
}
#[inline]
fn read<P: AccessPattern, T: Item>(
&self,
range: ReadRange,
access_pattern: P,
) -> UioResult<Cow<'_, [T]>> {
let bytes = self.read_bytes(
range.into_byte_range::<T>(),
access_pattern,
align_of::<T>(),
)?;
Ok(bytes.try_cast_bytemuck().unwrap())
}
fn read_bytes<P: AccessPattern>(
&self,
range: Range<u64>,
access_pattern: P,
align: usize,
) -> UioResult<ACow<'_>>;
fn read_whole<T: Item>(&self) -> UioResult<Cow<'_, [T]>> {
let range = ReadRange {
byte_offset: 0,
length: self.len::<T>()?,
};
self.read(range, Sequential)
}
fn read_batch<P, T, U, E>(
&self,
ranges: impl IntoIterator<Item = (U, ReadRange)>,
_access_pattern: P,
mut callback: impl FnMut(U, &[T]) -> Result<(), E>,
) -> Result<(), E>
where
P: AccessPattern,
T: Item,
U: UserData,
E: From<UniversalIoError>,
{
let mut pipeline = Self::ReadPipeline::<'_, U>::new()?;
let mut ranges = ranges.into_iter();
loop {
while pipeline.can_schedule()
&& let Some((user_data, range)) = ranges.next()
{
let range = range.into_byte_range::<T>();
pipeline.schedule::<P>(user_data, self, range, align_of::<T>())?;
}
let Some((user_data, data)) = pipeline.wait_bytemuck()? else {
break;
};
callback(user_data, &data)?;
}
Ok(())
}
fn read_iter<P: AccessPattern, T: Item, U: UserData>(
&self,
ranges: impl IntoIterator<Item = (U, ReadRange)>,
_access_pattern: P,
) -> UioResult<impl Iterator<Item = UioResult<(U, Cow<'_, [T]>)>>> {
let mut pipeline = Self::ReadPipeline::<'_, U>::new()?;
let mut ranges = ranges.into_iter();
Ok(std::iter::from_fn(move || {
while pipeline.can_schedule()
&& let Some((user_data, range)) = ranges.next()
{
let range = range.into_byte_range::<T>();
if let Err(err) = pipeline.schedule::<P>(user_data, self, range, align_of::<T>()) {
return Some(Err(err));
}
}
pipeline.wait_bytemuck().transpose()
}))
}
fn read_bytes_iter<P: AccessPattern, U: UserData>(
&self,
ranges: impl IntoIterator<Item = ReadBytesItem<U>>,
_access_pattern: P,
) -> UioResult<impl Iterator<Item = UioResult<(U, ACow<'_>)>>> {
let mut pipeline = Self::ReadPipeline::<'_, U>::new()?;
let mut ranges = ranges.into_iter();
Ok(std::iter::from_fn(move || {
while pipeline.can_schedule()
&& let Some(item) = ranges.next()
{
let ReadBytesItem {
user_data,
range,
align,
} = item;
if let Err(err) = pipeline.schedule::<P>(user_data, self, range, align) {
return Some(Err(err));
}
}
pipeline.wait().transpose()
}))
}
fn len<T>(&self) -> UioResult<u64>;
fn populate(&self) -> UioResult<()>;
fn populate_auto() -> bool;
fn clear_ram_cache(&self) -> UioResult<()>;
fn kind() -> UniversalKind;
}