use crate::DefaultDB;
use crate::Storable;
use crate::arena::{Arena, ArenaHash, Sp};
#[cfg(feature = "gc-v1")]
use crate::backend::OnDiskObject;
use crate::backend::StorageBackend;
use crate::db::{DB, DummyArbitrary, InMemoryDB};
use derive_where::derive_where;
use parking_lot::{Mutex, MutexGuard};
#[cfg(feature = "proptest")]
use proptest::arbitrary::Arbitrary;
#[cfg(feature = "proptest")]
use proptest::strategy::{BoxedStrategy, Strategy};
use std::any::{Any, TypeId};
use std::marker::PhantomData;
use std::ops::Deref;
use std::sync::{Arc, LazyLock};
pub const DEFAULT_CACHE_SIZE: usize = 10_000;
#[derive(Clone, Debug)]
pub struct Storage<D: DB = DefaultDB> {
pub arena: Arena<D>,
}
impl<D: DB> Storage<D> {
pub fn new(cache_size: usize, db: D) -> Self {
let arena = Arena::<D>::new_from_backend(StorageBackend::new(cache_size, db));
Self { arena }
}
pub fn new_from_arena(arena: Arena<D>) -> Self {
Self { arena }
}
}
impl<D: DB> Deref for Storage<D> {
type Target = Arena<D>;
fn deref(&self) -> &Arena<D> {
&self.arena
}
}
impl<D: Default + DB> Default for Storage<D> {
fn default() -> Self {
Self::new(DEFAULT_CACHE_SIZE, D::default())
}
}
type StorageMap = std::collections::HashMap<TypeId, Arc<dyn Any + Sync + Send>>;
static STORAGES: LazyLock<Mutex<StorageMap>> =
LazyLock::new(|| Mutex::new(std::collections::HashMap::new()));
pub fn default_storage<D: DB + Any>() -> Arc<Storage<D>> {
match try_get_default_storage() {
Some(arc) => arc,
_ => {
if TypeId::of::<D>() == TypeId::of::<InMemoryDB>() {
set_default_storage(Storage::<D>::default).unwrap_or_else(|s| s)
} else {
panic!(
"default storage is not set! you probably need to call set_default_storage in your initialization code"
)
}
}
}
}
pub fn try_get_default_storage<D: DB + Any>() -> Option<Arc<Storage<D>>> {
let storages = STORAGES.lock();
try_get_default_storage_locked(&storages)
}
fn try_get_default_storage_locked<D: DB + Any>(
storages: &MutexGuard<StorageMap>,
) -> Option<Arc<Storage<D>>> {
storages.get(&TypeId::of::<Storage<D>>()).map(|arc| {
arc.clone()
.downcast::<Storage<D>>()
.expect("impossible: we only insert Storage<D>")
})
}
pub fn set_default_storage<D: DB + Any>(
mk_value: impl FnOnce() -> Storage<D>,
) -> Result<Arc<Storage<D>>, Arc<Storage<D>>> {
let mut storages = STORAGES.lock();
match try_get_default_storage_locked(&storages) {
Some(arc) => Err(arc),
_ => {
let storage = mk_value();
let arc = Arc::new(storage);
storages.insert(TypeId::of::<Storage<D>>(), arc.clone());
Ok(arc)
}
}
}
pub fn unsafe_drop_default_storage<D: DB + Any>() {
STORAGES.lock().remove(&TypeId::of::<Storage<D>>());
}
#[derive(Clone)]
#[derive_where(Debug; D)]
pub struct WrappedDB<D: DB, T> {
db: D,
tag: PhantomData<T>,
}
impl<D: DB, T> WrappedDB<D, T> {
pub fn wrap(db: D) -> Self {
Self {
db,
tag: PhantomData,
}
}
}
impl<D: Default + DB, T> Default for WrappedDB<D, T> {
fn default() -> Self {
Self {
db: Default::default(),
tag: Default::default(),
}
}
}
impl<D: DB, T: Sync + Send + 'static> DB for WrappedDB<D, T> {
type Hasher = D::Hasher;
#[cfg(feature = "gc-v1")]
type ScanResumeHandle = D::ScanResumeHandle;
fn get_node(
&self,
key: &ArenaHash<Self::Hasher>,
) -> Option<crate::backend::OnDiskObject<Self::Hasher>> {
self.db.get_node(key)
}
#[cfg(not(feature = "layout-v2"))]
fn get_unreachable_keys(&self) -> std::vec::Vec<ArenaHash<Self::Hasher>> {
self.db.get_unreachable_keys()
}
fn insert_node(
&mut self,
key: ArenaHash<Self::Hasher>,
object: crate::backend::OnDiskObject<Self::Hasher>,
) {
self.db.insert_node(key, object)
}
fn delete_node(&mut self, key: &ArenaHash<Self::Hasher>) {
self.db.delete_node(key)
}
fn get_root_count(&self, key: &ArenaHash<Self::Hasher>) -> u32 {
self.db.get_root_count(key)
}
fn set_root_count(&mut self, key: ArenaHash<Self::Hasher>, count: u32) {
self.db.set_root_count(key, count)
}
fn get_roots(&self) -> std::collections::HashMap<ArenaHash<Self::Hasher>, u32> {
self.db.get_roots()
}
fn size(&self) -> usize {
self.db.size()
}
fn batch_update<I>(&mut self, iter: I)
where
I: Iterator<Item = (ArenaHash<Self::Hasher>, crate::db::Update<Self::Hasher>)>,
{
self.db.batch_update(iter)
}
fn batch_get_nodes<I>(
&self,
keys: I,
) -> std::vec::Vec<(
ArenaHash<Self::Hasher>,
Option<crate::backend::OnDiskObject<Self::Hasher>>,
)>
where
I: Iterator<Item = ArenaHash<Self::Hasher>>,
{
self.db.batch_get_nodes(keys)
}
fn bfs_get_nodes<C>(
&self,
key: &ArenaHash<Self::Hasher>,
cache_get: C,
truncate: bool,
max_depth: Option<usize>,
max_count: Option<usize>,
) -> std::vec::Vec<(
ArenaHash<Self::Hasher>,
crate::backend::OnDiskObject<Self::Hasher>,
)>
where
C: Fn(&ArenaHash<Self::Hasher>) -> Option<crate::backend::OnDiskObject<Self::Hasher>>,
{
self.db
.bfs_get_nodes(key, cache_get, truncate, max_depth, max_count)
}
#[cfg(feature = "gc-v1")]
fn scan(
&self,
resume_from: Option<Self::ScanResumeHandle>,
batch_size: usize,
) -> (
Vec<(ArenaHash<Self::Hasher>, OnDiskObject<Self::Hasher>)>,
Option<Self::ScanResumeHandle>,
) {
self.db.scan(resume_from, batch_size)
}
}
#[cfg(feature = "proptest")]
impl<D: DB + Arbitrary, T> Arbitrary for WrappedDB<D, T> {
type Parameters = D::Parameters;
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
D::arbitrary_with(params)
.prop_map(|db| WrappedDB {
db,
tag: PhantomData,
})
.boxed()
}
}
impl<D: DB + DummyArbitrary, T> DummyArbitrary for WrappedDB<D, T> {}
impl<T: serde::Serialize + Storable<D>, D: DB> serde::Serialize for Sp<T, D> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
<T as serde::Serialize>::serialize(self, serializer)
}
}
impl<'a, T: serde::Deserialize<'a> + Storable<D>, D: DB> serde::Deserialize<'a> for Sp<T, D> {
fn deserialize<D2>(deserializer: D2) -> Result<Self, D2::Error>
where
D2: serde::Deserializer<'a>,
{
T::deserialize(deserializer).map(Sp::new)
}
}