use std::borrow::Cow;
use std::ops::Range;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use fs_err as fs;
use crate::common::generic_consts::AccessPattern;
use crate::common::universal_io::{
Item, OpenOptions, ReadRange, Result, 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::{BorrowedDiskCacheReadPipeline, OwnedDiskCacheReadPipeline};
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) -> Result<Self> {
Ok(Self {
controller: ctx.controller,
})
}
fn list_files(&self, prefix_path: &Path) -> Result<Vec<PathBuf>> {
local_file_ops::local_list_files(prefix_path)
}
fn exists(&self, path: &Path) -> Result<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: (),
) -> Result<CachedSlice> {
let OpenOptions {
writeable,
need_sequential: _,
populate: _,
advice: _,
} = options;
debug_assert!(!writeable);
Ok(CachedSlice::open(&self.controller, path.as_ref())?)
}
}
impl UniversalRead for CachedSlice {
type Fs = BlockCacheFs;
type BorrowedReadPipeline<'a, T, U>
= BorrowedDiskCacheReadPipeline<'a, T, U>
where
Self: 'a,
T: Item,
U: UserData;
type OwnedReadPipeline<T, U>
= OwnedDiskCacheReadPipeline<T, U>
where
T: Item,
U: UserData;
fn reopen(&mut self) -> Result<()> {
*self = CachedSlice::open(&self.controller, &self.path)?;
Ok(())
}
fn read<P: AccessPattern, T: Item>(&self, range: ReadRange) -> Result<Cow<'_, [T]>> {
let elem_start = usize::try_from(range.byte_offset).expect("range.start is within usize")
/ size_of::<T>();
let elem_length = usize::try_from(range.length).expect("range.length is within usize");
let range = elem_start..elem_start + elem_length;
Ok(self.get_range(range)?)
}
fn len<T>(&self) -> Result<u64> {
Ok(Self::len::<T>(self) as u64)
}
fn populate(&self) -> Result<()> {
Ok(self.populate()?)
}
fn clear_ram_cache(&self) -> Result<()> {
Ok(())
}
fn kind() -> UniversalKind {
UniversalKind::DiskCache
}
}