use std::fmt;
use std::fs::{remove_file, File};
use std::io::Read;
use std::marker::PhantomData;
use std::ops;
use std::path::Path;
use anyhow::{Context, Result};
use positioned_io::ReadAt;
use crate::merkle::{get_merkle_tree_leafs, next_pow2, Element};
use crate::store::{ExternalReader, Store, StoreConfig};
pub struct LevelCacheStore<E: Element, R: Read + Send + Sync> {
len: usize,
elem_len: usize,
file: File,
data_width: usize,
cache_index_start: usize,
store_size: usize,
reader: Option<ExternalReader<R>>,
_e: PhantomData<E>,
}
impl<E: Element, R: Read + Send + Sync> fmt::Debug for LevelCacheStore<E, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LevelCacheStore")
.field("len", &self.len)
.field("elem_len", &self.len)
.field("data_width", &self.data_width)
.field("cache_index_start", &self.cache_index_start)
.field("store_size", &self.store_size)
.finish()
}
}
impl<E: Element, R: Read + Send + Sync> LevelCacheStore<E, R> {
pub fn new_from_disk_with_reader(
store_range: usize,
config: &StoreConfig,
reader: ExternalReader<R>,
) -> Result<Self> {
let data_path = StoreConfig::data_path(&config.path, &config.id);
let file = File::open(data_path)?;
let metadata = file.metadata()?;
let store_size = metadata.len() as usize;
let size = get_merkle_tree_leafs(store_range);
ensure!(
size == next_pow2(size),
"Inconsistent merkle tree height detected"
);
let store_range = store_range * E::byte_len();
let cache_size = ((2 * size - 1) >> config.levels) * E::byte_len();
let cache_index_start = store_range - cache_size;
ensure!(
store_size == cache_size,
"Inconsistent store size detected with external reader"
);
Ok(LevelCacheStore {
len: store_range / E::byte_len(),
elem_len: E::byte_len(),
file,
data_width: size,
cache_index_start,
store_size,
reader: Some(reader),
_e: Default::default(),
})
}
pub fn set_external_reader(&mut self, reader: ExternalReader<R>) -> Result<bool> {
let cache_size = (2 * self.data_width - 1) * self.elem_len - self.cache_index_start;
self.reader = Some(reader);
Ok(self.store_size == cache_size)
}
}
impl<E: Element, R: Read + Send + Sync> Store<E> for LevelCacheStore<E, R> {
fn new_with_config(size: usize, config: StoreConfig) -> Result<Self> {
let data_path = StoreConfig::data_path(&config.path, &config.id);
if Path::new(&data_path).exists() {
return Self::new_from_disk(size, &config);
}
bail!("Cannot create a LevelCacheStore in this way. Try DiskStore::compact");
}
fn new(_size: usize) -> Result<Self> {
unimplemented!("LevelCacheStore requires a StoreConfig");
}
fn new_from_slice_with_config(
_size: usize,
_data: &[u8],
_config: StoreConfig,
) -> Result<Self> {
unimplemented!("Cannot create a LevelCacheStore in this way. Try 'new_from_disk'.");
}
fn new_from_slice(_size: usize, _data: &[u8]) -> Result<Self> {
unimplemented!("LevelCacheStore requires a StoreConfig");
}
fn new_from_disk(store_range: usize, config: &StoreConfig) -> Result<Self> {
let data_path = StoreConfig::data_path(&config.path, &config.id);
let file = File::open(data_path)?;
let metadata = file.metadata()?;
let store_size = metadata.len() as usize;
let size = get_merkle_tree_leafs(store_range);
ensure!(
size == next_pow2(size),
"Inconsistent merkle tree height detected"
);
let store_range = store_range * E::byte_len();
let cache_size = ((2 * size - 1) >> config.levels) * E::byte_len();
let cache_index_start = store_range - cache_size;
ensure!(
store_size == size * E::byte_len() + cache_size,
"Inconsistent store size detected"
);
Ok(LevelCacheStore {
len: store_range / E::byte_len(),
elem_len: E::byte_len(),
file,
data_width: size,
cache_index_start,
store_size,
reader: None,
_e: Default::default(),
})
}
fn write_at(&mut self, _el: E, _index: usize) -> Result<()> {
unimplemented!("Not supported by the LevelCacheStore");
}
fn copy_from_slice(&mut self, _buf: &[u8], _start: usize) -> Result<()> {
unimplemented!("Not supported by the LevelCacheStore");
}
fn read_at(&self, index: usize) -> Result<E> {
let start = index * self.elem_len;
let end = start + self.elem_len;
let len = self.len * self.elem_len;
ensure!(start < len, "start out of range {} >= {}", start, len);
ensure!(end <= len, "end out of range {} > {}", end, len);
ensure!(
start <= self.data_width * self.elem_len || start >= self.cache_index_start,
"out of bounds"
);
Ok(E::from_slice(&self.store_read_range(start, end)?))
}
fn read_into(&self, index: usize, buf: &mut [u8]) -> Result<()> {
let start = index * self.elem_len;
let end = start + self.elem_len;
let len = self.len * self.elem_len;
ensure!(start < len, "start out of range {} >= {}", start, len);
ensure!(end <= len, "end out of range {} > {}", end, len);
ensure!(
start <= self.data_width * self.elem_len || start >= self.cache_index_start,
"out of bounds"
);
self.store_read_into(start, end, buf)
}
fn read_range_into(&self, start: usize, end: usize, buf: &mut [u8]) -> Result<()> {
let start = start * self.elem_len;
let end = end * self.elem_len;
let len = self.len * self.elem_len;
ensure!(start < len, "start out of range {} >= {}", start, len);
ensure!(end <= len, "end out of range {} > {}", end, len);
ensure!(
start <= self.data_width * self.elem_len || start >= self.cache_index_start,
"out of bounds"
);
self.store_read_into(start, end, buf)
}
fn read_range(&self, r: ops::Range<usize>) -> Result<Vec<E>> {
let start = r.start * self.elem_len;
let end = r.end * self.elem_len;
let len = self.len * self.elem_len;
ensure!(start < len, "start out of range {} >= {}", start, len);
ensure!(end <= len, "end out of range {} > {}", end, len);
ensure!(
start <= self.data_width * self.elem_len || start >= self.cache_index_start,
"out of bounds"
);
Ok(self
.store_read_range(start, end)?
.chunks(self.elem_len)
.map(E::from_slice)
.collect())
}
fn len(&self) -> usize {
self.len
}
fn loaded_from_disk(&self) -> bool {
true
}
fn compact(&mut self, _config: StoreConfig, _store_version: u32) -> Result<bool> {
bail!("Cannot compact this type of Store");
}
fn delete(config: StoreConfig) -> Result<()> {
let path = StoreConfig::data_path(&config.path, &config.id);
remove_file(&path).with_context(|| format!("Failed to delete {:?}", &path))
}
fn is_empty(&self) -> bool {
self.len == 0
}
fn push(&mut self, el: E) -> Result<()> {
let len = self.len;
ensure!(
(len + 1) * self.elem_len <= self.store_size(),
"not enough space, len: {}, E size {}, store len {}",
len,
self.elem_len,
self.store_size()
);
self.write_at(el, len)
}
fn sync(&self) -> Result<()> {
self.file.sync_all().context("failed to sync file")
}
}
impl<E: Element, R: Read + Send + Sync> LevelCacheStore<E, R> {
pub fn store_size(&self) -> usize {
self.store_size
}
pub fn store_read_range(&self, start: usize, end: usize) -> Result<Vec<u8>> {
let read_len = end - start;
let mut read_data = vec![0; read_len];
let mut adjusted_start = start;
ensure!(
start <= self.data_width * self.elem_len || start >= self.cache_index_start,
"out of bounds"
);
if start < self.data_width * self.elem_len && self.reader.is_some() {
self.reader
.as_ref()
.unwrap()
.read(start, end, &mut read_data)
.with_context(|| {
format!(
"failed to read {} bytes from file at offset {}",
end - start,
start
)
})?;
return Ok(read_data);
}
if start >= self.cache_index_start {
let v1 = self.reader.is_none();
adjusted_start = if v1 {
start - self.cache_index_start + (self.data_width * self.elem_len)
} else {
start - self.cache_index_start
};
}
self.file
.read_exact_at(adjusted_start as u64, &mut read_data)
.with_context(|| {
format!(
"failed to read {} bytes from file at offset {}",
read_len, start
)
})?;
Ok(read_data)
}
pub fn store_read_into(&self, start: usize, end: usize, buf: &mut [u8]) -> Result<()> {
assert!(start <= self.data_width * self.elem_len || start >= self.cache_index_start);
if start < self.data_width * self.elem_len && self.reader.is_some() {
self.reader
.as_ref()
.unwrap()
.read(start, end, buf)
.with_context(|| {
format!(
"failed to read {} bytes from file at offset {}",
end - start,
start
)
})?;
} else {
let adjusted_start = if start >= self.cache_index_start {
if self.reader.is_none() {
start - self.cache_index_start + (self.data_width * self.elem_len)
} else {
start - self.cache_index_start
}
} else {
start
};
self.file
.read_exact_at(adjusted_start as u64, buf)
.with_context(|| {
format!(
"failed to read {} bytes from file at offset {}",
end - start,
start
)
})?;
}
Ok(())
}
pub fn store_copy_from_slice(&mut self, _start: usize, _slice: &[u8]) {
unimplemented!("Not supported by the LevelCacheStore");
}
}