pub const DEFAULT_HOT_ALLOCATION: f64 = 0.97;
pub const DEFAULT_GHOST_ALLOCATION: f64 = 0.5;
#[derive(Debug, Clone)]
pub struct Options {
pub(crate) shards: usize,
pub(crate) hot_allocation: f64,
pub(crate) ghost_allocation: f64,
pub(crate) estimated_items_capacity: usize,
pub(crate) weight_capacity: u64,
}
#[derive(Debug, Clone, Default)]
pub struct OptionsBuilder {
shards: Option<usize>,
hot_allocation: Option<f64>,
ghost_allocation: Option<f64>,
estimated_items_capacity: Option<usize>,
weight_capacity: Option<u64>,
}
#[derive(Debug, Clone)]
pub struct Error(&'static str);
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
impl std::error::Error for Error {}
impl OptionsBuilder {
#[inline]
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn shards(&mut self, shards: usize) -> &mut Self {
self.shards = Some(shards);
self
}
#[inline]
pub fn estimated_items_capacity(&mut self, estimated_items_capacity: usize) -> &mut Self {
self.estimated_items_capacity = Some(estimated_items_capacity);
self
}
#[inline]
pub fn weight_capacity(&mut self, weight_capacity: u64) -> &mut Self {
self.weight_capacity = Some(weight_capacity);
self
}
#[inline]
pub fn hot_allocation(&mut self, hot_allocation: f64) -> &mut Self {
assert!(
hot_allocation.clamp(0.0, 1.0) == hot_allocation,
"hot_allocation must be within [0, 1]"
);
self.hot_allocation = Some(hot_allocation);
self
}
#[inline]
pub fn ghost_allocation(&mut self, ghost_allocation: f64) -> &mut Self {
assert!(
ghost_allocation.clamp(0.0, 1.0) == ghost_allocation,
"ghost_allocation must be within [0, 1]"
);
self.ghost_allocation = Some(ghost_allocation);
self
}
#[inline]
pub fn build(&self) -> Result<Options, Error> {
let shards = self.shards.unwrap_or_else(|| available_parallelism() * 4);
let hot_allocation = self.hot_allocation.unwrap_or(DEFAULT_HOT_ALLOCATION);
let ghost_allocation = self.ghost_allocation.unwrap_or(DEFAULT_GHOST_ALLOCATION);
let weight_capacity = self
.weight_capacity
.ok_or(Error("weight_capacity is not set"))?;
let estimated_items_capacity = self
.estimated_items_capacity
.ok_or(Error("estimated_items_capacity is not set"))?;
Ok(Options {
shards,
hot_allocation,
ghost_allocation,
estimated_items_capacity,
weight_capacity,
})
}
}
fn available_parallelism() -> usize {
use std::sync::atomic::{AtomicUsize, Ordering};
static AVAILABLE_PARALLELISM: AtomicUsize = AtomicUsize::new(0);
let mut ap = AVAILABLE_PARALLELISM.load(Ordering::Relaxed);
if ap == 0 {
ap = std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(1);
AVAILABLE_PARALLELISM.store(ap, Ordering::Relaxed);
}
ap
}