#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Route {
pub storage: u16,
pub index: u16,
}
impl Route {
pub(crate) fn input() -> Self { Self { storage: 0, index: 1 } }
}
#[cfg(not(any(feature = "test", test)))]
mod normal {
use std::any::TypeId;
use rustc_hash::FxHashMap;
use crate::{
internal::{storage::Route, Storable},
Db,
};
#[derive(Default)]
pub struct RoutingTable {
routes: FxHashMap<TypeId, Route>,
type_names: FxHashMap<Route, &'static str>,
}
impl RoutingTable {
pub fn route<T: Storable>(&self) -> Route { self.route_for(TypeId::of::<T>(), std::any::type_name::<T>()) }
pub fn route_for(&self, id: TypeId, name: &'static str) -> Route {
match self.routes.get(&id) {
Some(route) => *route,
None => panic!("Database does not contain `{}`", name),
}
}
pub fn name(&self, route: Route) -> &str { self.type_names.get(&route).unwrap() }
pub fn generate_for_db<T: Db>() -> Self {
#[cfg(feature = "debug")]
{
use std::{thread, time::Duration};
use parking_lot::deadlock;
thread::spawn(move || loop {
thread::sleep(Duration::from_secs(2));
let deadlocks = deadlock::check_deadlock();
if deadlocks.is_empty() {
continue;
}
eprintln!("{} deadlocks detected", deadlocks.len());
for (i, threads) in deadlocks.iter().enumerate() {
eprintln!("Deadlock #{}", i);
for t in threads {
eprintln!("Thread {:#?}", t.thread_id());
eprintln!("{:#?}", t.backtrace());
}
eprintln!()
}
std::process::abort();
});
}
let mut builder = RoutingTableBuilder::default();
T::init_routing(&mut builder);
builder.finish()
}
}
#[derive(Default)]
pub struct RoutingTableBuilder {
routes: FxHashMap<TypeId, Route>,
type_names: FxHashMap<Route, &'static str>,
}
impl RoutingTableBuilder {
pub fn start_route(&mut self, storage: u16) -> RouteBuilder {
RouteBuilder {
routes: &mut self.routes,
type_names: &mut self.type_names,
storage,
}
}
pub fn finish(self) -> RoutingTable {
RoutingTable {
routes: self.routes,
type_names: self.type_names,
}
}
}
pub struct RouteBuilder<'a> {
routes: &'a mut FxHashMap<TypeId, Route>,
type_names: &'a mut FxHashMap<Route, &'static str>,
storage: u16,
}
impl RouteBuilder<'_> {
pub fn add<T: Storable>(&mut self, index: u16) {
let route = Route {
storage: self.storage,
index,
};
let id = TypeId::of::<T>();
if self.routes.insert(id, route).is_some() {
panic!("Duplicate route for type `{}`", std::any::type_name::<T>());
}
self.type_names.insert(route, std::any::type_name::<T>());
}
}
}
#[cfg(any(feature = "test", test))]
mod test {
use std::{
any::TypeId,
sync::atomic::{AtomicU16, Ordering},
};
use parking_lot::{Mutex, RwLock};
use rustc_hash::FxHashMap;
use crate::{
internal::{storage::Route, Storable},
test::StorageType,
Db,
};
type GenFunc = Box<dyn FnOnce() -> (StorageType, u16) + Send>;
#[derive(Default)]
pub struct RoutingTable {
routes: RwLock<FxHashMap<TypeId, Route>>,
type_names: RwLock<FxHashMap<Route, &'static str>>,
dynamic_storage_index: u16,
next_route_index: AtomicU16,
make: Mutex<Vec<GenFunc>>,
}
impl RoutingTable {
pub fn route<T: Storable>(&self) -> Route
where
StorageType: From<<T as Storable>::Storage>,
{
let route = self.route_for(TypeId::of::<T>(), "");
self.make
.lock()
.push(Box::new(move || (T::Storage::default().into(), route.index)));
route
}
pub fn route_for(&self, id: TypeId, _: &'static str) -> Route {
*self.routes.write().entry(id).or_insert_with(|| Route {
storage: self.dynamic_storage_index,
index: self.next_route_index.fetch_add(1, Ordering::Relaxed),
})
}
pub fn name(&self, route: Route) -> &str { self.type_names.read().get(&route).unwrap() }
pub fn make(&self) -> Vec<GenFunc> {
let mut m = self.make.lock();
std::mem::take(&mut *m)
}
pub fn generate_for_db<T: Db>() -> Self {
let mut builder = RoutingTableBuilder::default();
T::init_routing(&mut builder);
builder.finish()
}
}
pub struct RoutingTableBuilder {
routes: FxHashMap<TypeId, Route>,
type_names: FxHashMap<Route, &'static str>,
dynamic_storage_index: u16,
}
impl Default for RoutingTableBuilder {
fn default() -> Self {
Self {
routes: FxHashMap::default(),
type_names: FxHashMap::default(),
dynamic_storage_index: 1,
}
}
}
impl RoutingTableBuilder {
pub fn start_route(&mut self, storage: u16) -> RouteBuilder {
self.dynamic_storage_index = self.dynamic_storage_index.max(storage + 1);
RouteBuilder {
routes: &mut self.routes,
type_names: &mut self.type_names,
storage,
}
}
pub fn finish(self) -> RoutingTable {
RoutingTable {
routes: RwLock::new(self.routes),
type_names: RwLock::new(self.type_names),
dynamic_storage_index: self.dynamic_storage_index,
next_route_index: AtomicU16::new(0),
make: Mutex::new(Vec::new()),
}
}
}
pub struct RouteBuilder<'a> {
routes: &'a mut FxHashMap<TypeId, Route>,
type_names: &'a mut FxHashMap<Route, &'static str>,
storage: u16,
}
impl RouteBuilder<'_> {
pub fn add<T: Storable>(&mut self, index: u16) {
let route = Route {
storage: self.storage,
index,
};
let id = TypeId::of::<T>();
if self.routes.insert(id, route).is_some() {
panic!("Duplicate route for type `{}`", std::any::type_name::<T>());
}
self.type_names.insert(route, std::any::type_name::<T>());
}
}
}
#[cfg(not(any(feature = "test", test)))]
pub use normal::*;
#[cfg(any(feature = "test", test))]
pub use test::*;