use std::fmt;
use std::fs::OpenOptions;
use std::io::Read;
use std::iter::FromIterator;
use std::ops;
use std::path::{Path, PathBuf};
use std::sync::{Arc, RwLock};
use anyhow::Result;
use positioned_io::ReadAt;
use rayon::iter::plumbing::*;
use rayon::iter::*;
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use typenum::marker_traits::Unsigned;
use crate::hash::Algorithm;
use crate::merkle::{get_merkle_tree_height, log2_pow2, next_pow2, Element};
pub const DEFAULT_CACHED_ABOVE_BASE_LAYER_BINARY: usize = 7;
pub const DEFAULT_CACHED_ABOVE_BASE_LAYER_QUAD: usize = 4;
pub const DEFAULT_CACHED_ABOVE_BASE_LAYER_OCT: usize = 2;
pub const SMALL_TREE_BUILD: usize = 1024;
pub const BUILD_CHUNK_NODES: usize = 1024 * 4;
mod disk;
mod level_cache;
mod mmap;
mod vec;
pub use disk::DiskStore;
pub use level_cache::LevelCacheStore;
pub use mmap::MmapStore;
pub use vec::VecStore;
#[derive(Clone)]
pub struct ExternalReader<R: Read + Send + Sync> {
pub source: R,
pub read_fn: fn(start: usize, end: usize, buf: &mut [u8], source: &R) -> Result<usize>,
}
impl<R: Read + Send + Sync> ExternalReader<R> {
pub fn read(&self, start: usize, end: usize, buf: &mut [u8]) -> Result<usize> {
(self.read_fn)(start, end, buf, &self.source)
}
}
impl ExternalReader<std::fs::File> {
pub fn new_from_path(path: &PathBuf) -> Result<Self> {
let reader = OpenOptions::new().read(true).open(path)?;
Ok(ExternalReader {
source: reader,
read_fn: |start, end, buf: &mut [u8], reader: &std::fs::File| {
reader.read_exact_at(start as u64, &mut buf[0..end - start])?;
Ok(end - start)
},
})
}
}
impl<R: Read + Send + Sync> fmt::Debug for ExternalReader<R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ExternalReader")
.field("source: Read + Send + Sync", &1)
.field(
"read_fn: callback(start: usize, end: usize, buf: &mut [u8])",
&2,
)
.finish()
}
}
#[derive(Clone, Copy, Debug)]
pub enum StoreConfigDataVersion {
One = 1,
Two = 2,
}
const DEFAULT_STORE_CONFIG_DATA_VERSION: u32 = StoreConfigDataVersion::Two as u32;
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
pub struct StoreConfig {
pub path: PathBuf,
pub id: String,
pub size: Option<usize>,
pub levels: usize,
}
impl StoreConfig {
pub fn new<T: Into<PathBuf>, S: Into<String>>(path: T, id: S, levels: usize) -> Self {
StoreConfig {
path: path.into(),
id: id.into(),
size: None,
levels,
}
}
pub fn default_cached_above_base_layer(leafs: usize, branches: usize) -> usize {
let height = get_merkle_tree_height(leafs, branches);
if height <= 2 {
return 0;
}
let default_height = match branches {
2 => DEFAULT_CACHED_ABOVE_BASE_LAYER_BINARY,
4 => DEFAULT_CACHED_ABOVE_BASE_LAYER_QUAD,
_ => DEFAULT_CACHED_ABOVE_BASE_LAYER_OCT,
};
if height <= default_height {
2
} else {
default_height
}
}
pub fn data_path(path: &PathBuf, id: &str) -> PathBuf {
Path::new(&path).join(format!(
"sc-{:0>2}-data-{}.dat",
DEFAULT_STORE_CONFIG_DATA_VERSION, id
))
}
pub fn from_config<S: Into<String>>(config: &StoreConfig, id: S, size: Option<usize>) -> Self {
let val = if let Some(size) = size {
Some(size)
} else {
config.size
};
StoreConfig {
path: config.path.clone(),
id: id.into(),
size: val,
levels: config.levels,
}
}
}
pub trait Store<E: Element>: std::fmt::Debug + Send + Sync + Sized {
fn new_with_config(size: usize, branches: usize, config: StoreConfig) -> Result<Self>;
fn new(size: usize) -> Result<Self>;
fn new_from_slice_with_config(
size: usize,
branches: usize,
data: &[u8],
config: StoreConfig,
) -> Result<Self>;
fn new_from_slice(size: usize, data: &[u8]) -> Result<Self>;
fn new_from_disk(size: usize, branches: usize, config: &StoreConfig) -> Result<Self>;
fn write_at(&mut self, el: E, index: usize) -> Result<()>;
fn copy_from_slice(&mut self, buf: &[u8], start: usize) -> Result<()>;
fn compact(&mut self, branches: usize, config: StoreConfig, store_version: u32)
-> Result<bool>;
fn reinit(&mut self) -> Result<()> {
Ok(())
}
fn delete(config: StoreConfig) -> Result<()>;
fn read_at(&self, index: usize) -> Result<E>;
fn read_range(&self, r: ops::Range<usize>) -> Result<Vec<E>>;
fn read_into(&self, pos: usize, buf: &mut [u8]) -> Result<()>;
fn read_range_into(&self, start: usize, end: usize, buf: &mut [u8]) -> Result<()>;
fn len(&self) -> usize;
fn loaded_from_disk(&self) -> bool;
fn is_empty(&self) -> bool;
fn push(&mut self, el: E) -> Result<()>;
fn last(&self) -> Result<E> {
self.read_at(self.len() - 1)
}
fn sync(&self) -> Result<()> {
Ok(())
}
#[inline]
fn build_small_tree<A: Algorithm<E>, U: Unsigned>(
&mut self,
leafs: usize,
height: usize,
) -> Result<E> {
ensure!(leafs % 2 == 0, "Leafs must be a power of two");
let mut level: usize = 0;
let mut width = leafs;
let mut level_node_index = 0;
let branches = U::to_usize();
let shift = log2_pow2(branches);
while width > 1 {
let (layer, write_start) = {
let (read_start, write_start) = if level == 0 {
(0, Store::len(self))
} else {
(level_node_index, level_node_index + width)
};
let layer: Vec<_> = self
.read_range(read_start..read_start + width)?
.par_chunks(branches)
.map(|nodes| A::default().multi_node(&nodes, level))
.collect();
(layer, write_start)
};
for (i, node) in layer.into_iter().enumerate() {
self.write_at(node, write_start + i)?;
}
level_node_index += width;
level += 1;
width >>= shift; }
ensure!(height == level + 1, "Invalid tree height");
self.last()
}
fn process_layer<A: Algorithm<E>, U: Unsigned>(
&mut self,
width: usize,
level: usize,
read_start: usize,
write_start: usize,
) -> Result<()> {
let branches = U::to_usize();
let data_lock = Arc::new(RwLock::new(self));
ensure!(BUILD_CHUNK_NODES % branches == 0, "Invalid chunk size");
Vec::from_iter((read_start..read_start + width).step_by(BUILD_CHUNK_NODES))
.par_iter()
.try_for_each(|&chunk_index| -> Result<()> {
let chunk_size = std::cmp::min(BUILD_CHUNK_NODES, read_start + width - chunk_index);
let chunk_nodes = {
data_lock
.read()
.unwrap()
.read_range(chunk_index..chunk_index + chunk_size)?
};
let write_delta = (chunk_index - read_start) / branches;
let nodes_size = (chunk_nodes.len() / branches) * E::byte_len();
let hashed_nodes_as_bytes = chunk_nodes.chunks(branches).fold(
Vec::with_capacity(nodes_size),
|mut acc, nodes| {
let h = A::default().multi_node(&nodes, level);
acc.extend_from_slice(h.as_ref());
acc
},
);
ensure!(
hashed_nodes_as_bytes.len() == chunk_size / branches * E::byte_len(),
"Invalid hashed node length"
);
data_lock
.write()
.unwrap()
.copy_from_slice(&hashed_nodes_as_bytes, write_start + write_delta)
})
}
fn build<A: Algorithm<E>, U: Unsigned>(
&mut self,
leafs: usize,
height: usize,
_config: Option<StoreConfig>,
) -> Result<E> {
let branches = U::to_usize();
ensure!(
next_pow2(branches) == branches,
"branches MUST be a power of 2"
);
ensure!(Store::len(self) == leafs, "Inconsistent data");
ensure!(leafs % 2 == 0, "Leafs must be a power of two");
if leafs <= SMALL_TREE_BUILD {
return self.build_small_tree::<A, U>(leafs, height);
}
let shift = log2_pow2(branches);
let mut level: usize = 0;
let mut width = leafs;
let mut level_node_index = 0;
while width > 1 {
let (read_start, write_start) = if level == 0 {
(0, Store::len(self))
} else {
(level_node_index, level_node_index + width)
};
self.process_layer::<A, U>(width, level, read_start, write_start)?;
level_node_index += width;
level += 1;
width >>= shift; }
ensure!(height == level + 1, "Invalid tree height");
self.last()
}
}
macro_rules! impl_parallel_iter {
($name:ident, $producer:ident, $iter:ident) => {
impl<E: Element> ParallelIterator for $name<E> {
type Item = E;
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
bridge(self, consumer)
}
fn opt_len(&self) -> Option<usize> {
Some(Store::len(self))
}
}
impl<'a, E: Element> ParallelIterator for &'a $name<E> {
type Item = E;
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
bridge(self, consumer)
}
fn opt_len(&self) -> Option<usize> {
Some(Store::len(*self))
}
}
impl<E: Element> IndexedParallelIterator for $name<E> {
fn drive<C>(self, consumer: C) -> C::Result
where
C: Consumer<Self::Item>,
{
bridge(self, consumer)
}
fn len(&self) -> usize {
Store::len(self)
}
fn with_producer<CB>(self, callback: CB) -> CB::Output
where
CB: ProducerCallback<Self::Item>,
{
callback.callback(<$producer<E>>::new(0, Store::len(&self), &self))
}
}
impl<'a, E: Element> IndexedParallelIterator for &'a $name<E> {
fn drive<C>(self, consumer: C) -> C::Result
where
C: Consumer<Self::Item>,
{
bridge(self, consumer)
}
fn len(&self) -> usize {
Store::len(*self)
}
fn with_producer<CB>(self, callback: CB) -> CB::Output
where
CB: ProducerCallback<Self::Item>,
{
callback.callback(<$producer<E>>::new(0, Store::len(self), self))
}
}
#[derive(Debug, Clone)]
pub struct $producer<'data, E: 'data + Element> {
pub(crate) current: usize,
pub(crate) end: usize,
pub(crate) store: &'data $name<E>,
}
impl<'data, E: 'data + Element> $producer<'data, E> {
pub fn new(current: usize, end: usize, store: &'data $name<E>) -> Self {
Self {
current,
end,
store,
}
}
pub fn len(&self) -> usize {
self.end - self.current
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl<'data, E: 'data + Element> Producer for $producer<'data, E> {
type Item = E;
type IntoIter = $iter<'data, E>;
fn into_iter(self) -> Self::IntoIter {
let $producer {
current,
end,
store,
} = self;
$iter {
current,
end,
store,
err: false,
}
}
fn split_at(self, index: usize) -> (Self, Self) {
let len = self.len();
if len == 0 {
return (
<$producer<E>>::new(0, 0, &self.store),
<$producer<E>>::new(0, 0, &self.store),
);
}
let current = self.current;
let first_end = current + std::cmp::min(len, index);
debug_assert!(first_end >= current);
debug_assert!(current + len >= first_end);
(
<$producer<E>>::new(current, first_end, &self.store),
<$producer<E>>::new(first_end, current + len, &self.store),
)
}
}
#[derive(Debug)]
pub struct $iter<'data, E: 'data + Element> {
current: usize,
end: usize,
err: bool,
store: &'data $name<E>,
}
impl<'data, E: 'data + Element> $iter<'data, E> {
fn is_done(&self) -> bool {
!self.err && self.len() == 0
}
}
impl<'data, E: 'data + Element> Iterator for $iter<'data, E> {
type Item = E;
fn next(&mut self) -> Option<Self::Item> {
if self.is_done() {
return None;
}
match self.store.read_at(self.current) {
Ok(el) => {
self.current += 1;
Some(el)
}
_ => {
self.err = true;
None
}
}
}
}
impl<'data, E: 'data + Element> ExactSizeIterator for $iter<'data, E> {
fn len(&self) -> usize {
debug_assert!(self.current <= self.end);
self.end - self.current
}
}
impl<'data, E: 'data + Element> DoubleEndedIterator for $iter<'data, E> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.is_done() {
return None;
}
match self.store.read_at(self.end - 1) {
Ok(el) => {
self.end -= 1;
Some(el)
}
_ => {
self.err = true;
None
}
}
}
}
};
}
impl_parallel_iter!(VecStore, VecStoreProducer, VecStoreIter);
impl_parallel_iter!(DiskStore, DiskStoreProducer, DiskIter);