pub mod deque;
pub mod lru;
use std::{
error::Error,
fs::File,
io::{self, Read, Seek, Write},
path::Path,
};
use crate::cache_key::CacheKey;
pub type Address = String;
#[derive(PartialEq, Eq, Default)]
pub enum StorageFlushStrategy {
#[default]
Never,
Immediately,
RecordCount(usize),
}
pub trait StorageStrategy {
const ON_DELETE_ITEMS_COUNT_PERCENTAGE: usize;
fn insert(&mut self, cache_key: CacheKey, address: Address) -> Result<(), Box<dyn Error>>;
fn get(&self, cache_key: &CacheKey) -> Option<&Address>;
fn memory_max_size(&mut self, size: usize);
fn get_in_memory_size(&self) -> usize;
fn as_bytes(&self) -> Vec<u8>;
fn read(&mut self, storage: &mut Storage) -> io::Result<()>;
fn flush(&self, storage: &mut Storage) -> io::Result<()>;
fn evict_if_needed(&mut self, storage: &mut Storage, address_len: usize) -> io::Result<()>;
fn evict(&mut self, storage: &mut Storage) -> io::Result<()>;
fn in_memory_record_count(&self) -> usize;
}
#[cfg(feature = "testing")]
pub trait StorageStrategyWithCapacity {
fn with_capacity(capacity: usize) -> Self;
}
pub struct Storage {
file: File,
is_dirty: bool,
}
impl Storage {
pub fn try_new<P: AsRef<Path>>(path: P) -> io::Result<Self> {
let file = File::options()
.read(true)
.write(true)
.create(true)
.truncate(false)
.open(path)?;
Ok(Self {
file,
is_dirty: false,
})
}
pub fn len(&self) -> io::Result<u64> {
Ok(self.file.metadata()?.len())
}
pub fn read(&mut self) -> io::Result<Vec<u8>> {
let mut buf = Vec::new();
self.file.seek(io::SeekFrom::Start(0))?;
self.file.read_to_end(&mut buf)?;
Ok(buf)
}
pub fn write(&mut self, bytes: &[u8]) -> io::Result<()> {
self.is_dirty = true;
self.file.write_all(bytes)
}
pub fn truncate_and_write(&mut self, bytes: &[u8]) -> io::Result<()> {
self.file.set_len(0)?;
self.file.seek(io::SeekFrom::Start(0))?;
self.write(bytes)
}
pub fn sync(&mut self) -> io::Result<()> {
if self.is_dirty {
self.file.flush()?;
self.is_dirty = false;
}
Ok(())
}
}
impl Drop for Storage {
fn drop(&mut self) {
let _ = self.sync();
}
}