use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::sync::OnceLock;
use serde::{de::DeserializeOwned, Serialize};
use crate::types::{TypeNode, TYPE_ANY};
#[derive(Debug)]
pub enum SlotValueError {
EncodeFailed(Box<dyn std::error::Error + Send + Sync>),
DecodeFailed(Box<dyn std::error::Error + Send + Sync>),
UnknownTypeHash(u64),
}
impl std::fmt::Display for SlotValueError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::EncodeFailed(err) => write!(f, "SlotValue::to_wire_bytes failed: {err}"),
Self::DecodeFailed(err) => write!(f, "SlotValue decode failed: {err}"),
Self::UnknownTypeHash(hash) => write!(
f,
"SlotValue decode: no registered decoder for type_hash {hash:#018x}",
),
}
}
}
impl std::error::Error for SlotValueError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::EncodeFailed(err) => Some(err.as_ref()),
Self::DecodeFailed(err) => Some(err.as_ref()),
Self::UnknownTypeHash(_) => None,
}
}
}
pub trait SlotValue: Any + Send + Sync {
fn as_any(&self) -> &dyn Any;
fn into_any_boxed(self: Box<Self>) -> Box<dyn Any + Send + Sync>;
fn clone_boxed(&self) -> Box<dyn SlotValue>;
fn to_wire_bytes(&self) -> Result<Vec<u8>, SlotValueError>;
fn type_hash(&self) -> u64;
fn runtime_type(&self) -> &'static TypeNode {
let tid = self.as_any().type_id();
runtime_type_registry()
.get(&tid)
.copied()
.unwrap_or(&TYPE_ANY)
}
fn charged_bytes(&self) -> usize {
let any = self.as_any();
let tid = any.type_id();
match charged_bytes_registry().get(&tid) {
Some(f) => f(any),
None => 0,
}
}
}
pub struct RuntimeTypeBinding {
pub type_id_fn: fn() -> TypeId,
pub type_node: &'static TypeNode,
}
inventory::collect!(RuntimeTypeBinding);
pub fn runtime_type_registry() -> &'static HashMap<TypeId, &'static TypeNode> {
static REG: OnceLock<HashMap<TypeId, &'static TypeNode>> = OnceLock::new();
REG.get_or_init(|| {
let mut m: HashMap<TypeId, &'static TypeNode> = HashMap::new();
for binding in inventory::iter::<RuntimeTypeBinding> {
m.insert((binding.type_id_fn)(), binding.type_node);
}
m
})
}
pub type ChargedBytesFn = fn(&dyn Any) -> usize;
pub struct ChargedBytesBinding {
pub type_id_fn: fn() -> TypeId,
pub resolve_fn: ChargedBytesFn,
}
inventory::collect!(ChargedBytesBinding);
pub fn charged_bytes_registry() -> &'static HashMap<TypeId, ChargedBytesFn> {
static REG: OnceLock<HashMap<TypeId, ChargedBytesFn>> = OnceLock::new();
REG.get_or_init(|| {
let mut m: HashMap<TypeId, ChargedBytesFn> = HashMap::new();
for binding in inventory::iter::<ChargedBytesBinding> {
m.insert((binding.type_id_fn)(), binding.resolve_fn);
}
m
})
}
#[macro_export]
macro_rules! register_charged_bytes {
($t:ty, $resolve:expr) => {
$crate::inventory::submit! {
$crate::slot_value::ChargedBytesBinding {
type_id_fn: || ::std::any::TypeId::of::<$t>(),
resolve_fn: |any| {
let resolve: fn(&$t) -> usize = $resolve;
match any.downcast_ref::<$t>() {
Some(v) => resolve(v),
None => 0,
}
},
}
}
};
}
pub type WireDecodeFn = fn(&[u8]) -> Result<Box<dyn SlotValue>, SlotValueError>;
pub struct WireDecoderBinding {
pub type_hash_fn: fn() -> u64,
pub decode_fn: WireDecodeFn,
}
inventory::collect!(WireDecoderBinding);
pub fn wire_decoder_registry() -> &'static HashMap<u64, WireDecodeFn> {
static REG: OnceLock<HashMap<u64, WireDecodeFn>> = OnceLock::new();
REG.get_or_init(|| {
let mut m: HashMap<u64, WireDecodeFn> = HashMap::new();
for binding in inventory::iter::<WireDecoderBinding> {
m.insert((binding.type_hash_fn)(), binding.decode_fn);
}
m
})
}
#[macro_export]
macro_rules! register_type_node {
($t:ty, $node:expr) => {
$crate::inventory::submit! {
$crate::slot_value::RuntimeTypeBinding {
type_id_fn: || ::std::any::TypeId::of::<$t>(),
type_node: $node,
}
}
$crate::inventory::submit! {
$crate::slot_value::WireDecoderBinding {
type_hash_fn: || $crate::slot_value::type_hash_of::<$t>(),
decode_fn: |bytes| {
$crate::bincode::deserialize::<$t>(bytes)
.map(|v| Box::new(v) as Box<dyn $crate::slot_value::SlotValue>)
.map_err(|e| $crate::slot_value::SlotValueError::DecodeFailed(Box::new(e)))
},
}
}
};
}
impl<T> SlotValue for T
where
T: Any + Send + Sync + Clone + Serialize + DeserializeOwned,
{
fn as_any(&self) -> &dyn Any {
self
}
fn into_any_boxed(self: Box<Self>) -> Box<dyn Any + Send + Sync> {
self
}
fn clone_boxed(&self) -> Box<dyn SlotValue> {
Box::new(self.clone())
}
fn to_wire_bytes(&self) -> Result<Vec<u8>, SlotValueError> {
bincode::serialize(self).map_err(|e| SlotValueError::EncodeFailed(Box::new(e)))
}
fn type_hash(&self) -> u64 {
type_hash_of::<T>()
}
}
#[inline]
pub fn type_hash_of<T: ?Sized + 'static>() -> u64 {
fnv1a_64(std::any::type_name::<T>().as_bytes())
}
#[inline]
pub const fn fnv1a_64(bytes: &[u8]) -> u64 {
let mut h: u64 = 0xcbf2_9ce4_8422_2325;
let mut i = 0;
while i < bytes.len() {
h ^= bytes[i] as u64;
h = h.wrapping_mul(0x0000_0100_0000_01b3);
i += 1;
}
h
}