use std::ops::Range;
use std::path::Path;
use std::sync::Arc;
use fs_err as fs;
use crate::common::ext::aligned_vec::ACow;
use crate::common::generic_consts::AccessPattern;
use crate::common::universal_io::{
ListedFile, OpenOptions, UioResult, UniversalIoError, UniversalRead, UniversalReadFileOps,
UniversalReadFs, UserData, local_file_ops,
};
mod cached_slice;
mod controller;
mod pipeline;
#[cfg(test)]
mod tests;
pub use cached_slice::CachedSlice;
use controller::{CacheController, CacheRead};
use pipeline::DiskCacheReadPipeline;
use super::UniversalKind;
const BLOCK_SIZE: usize = 16 * 1024;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct FileId(u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct BlockOffset(u32);
impl BlockOffset {
fn bytes(self) -> usize {
self.0 as usize * BLOCK_SIZE
}
}
#[derive(Copy, Hash, PartialEq, Eq, Clone, Debug)]
struct BlockId {
file_id: FileId,
offset: BlockOffset,
}
struct BlockRequest {
key: BlockId,
range: Range<usize>,
}
#[derive(Debug, Clone)]
pub struct BlockCacheConfigContext {
pub controller: Arc<CacheController>,
}
impl Default for BlockCacheConfigContext {
fn default() -> Self {
let controller = CacheController::global()
.expect("CacheController::initialize_global must be called before BlockCacheConfigContext::default()")
.clone();
BlockCacheConfigContext { controller }
}
}
#[derive(Debug, Clone)]
pub struct BlockCacheFs {
controller: Arc<CacheController>,
}
impl UniversalReadFileOps for BlockCacheFs {
type ContextConfig = BlockCacheConfigContext;
fn from_context(ctx: BlockCacheConfigContext) -> UioResult<Self> {
Ok(Self {
controller: ctx.controller,
})
}
fn list_files(&self, prefix_path: &Path) -> UioResult<Vec<ListedFile>> {
local_file_ops::local_list_files(prefix_path)
}
fn exists(&self, path: &Path) -> UioResult<bool> {
fs::exists(path).map_err(UniversalIoError::from)
}
}
impl UniversalReadFs for BlockCacheFs {
type File = CachedSlice;
type OpenExtra = ();
fn open(
&self,
path: impl AsRef<Path>,
options: OpenOptions,
_extra: (),
) -> UioResult<CachedSlice> {
let OpenOptions {
writeable,
need_sequential: _,
populate: _,
advice: _,
} = options;
debug_assert!(!writeable);
CachedSlice::open(&self.controller, path.as_ref())
.map_err(|err| UniversalIoError::extract_not_found(err, path.as_ref()))
}
}
impl UniversalRead for CachedSlice {
type Fs = BlockCacheFs;
type ReadPipeline<'a, U>
= DiskCacheReadPipeline<'a, U>
where
Self: 'a,
U: UserData;
fn reopen(&mut self) -> UioResult<()> {
*self = CachedSlice::open(&self.controller, &self.path)
.map_err(|err| UniversalIoError::extract_not_found(err, &self.path))?;
Ok(())
}
fn read_bytes<P: AccessPattern>(
&self,
range: Range<u64>,
_access_pattern: P,
align: usize,
) -> UioResult<ACow<'_>> {
let start = usize::try_from(range.start).expect("range.start is within usize");
let end = usize::try_from(range.end).expect("range.end is within usize");
Ok(self.get_range_bytes(start..end, align)?)
}
fn len<T>(&self) -> UioResult<u64> {
Ok(Self::len::<T>(self) as u64)
}
fn populate(&self) -> UioResult<()> {
Ok(self.populate()?)
}
fn populate_auto() -> bool {
false
}
fn clear_ram_cache(&self) -> UioResult<()> {
Ok(())
}
fn kind() -> UniversalKind {
UniversalKind::DiskCache
}
}