use core::marker::PhantomData;
use std::hash::{Hash, Hasher};
use inventory;
use rkyv::{
Archived, Portable, SerializeUnsized,
bytecheck::{CheckBytes, StructCheckContext},
ptr_meta::{DynMetadata, Pointee},
rancor::{Fallible, Trace},
traits::NoUndef,
};
pub mod validation;
mod vtable_ptr;
use rustc_hash::FxHashMap;
pub use vtable_ptr::VTablePtr;
use crate::{Deserializer, Result, Serializer};
pub trait SerializeDyn {
fn serialize_dyn(&self, serializer: &mut Serializer) -> Result<usize>;
}
impl<T> SerializeDyn for T
where
T: for<'a> SerializeUnsized<Serializer<'a>>,
{
fn serialize_dyn(&self, serializer: &mut Serializer) -> Result<usize> {
self.serialize_unsized(serializer)
}
}
pub trait DeserializeDyn<T: Pointee + ?Sized> {
fn deserialize_dyn(&self, deserializer: &mut Deserializer, out: *mut T) -> Result<()>;
fn deserialized_pointer_metadata(&self) -> DynMetadata<T>;
}
pub struct ArchivedDynMetadata<T: ?Sized> {
dyn_id: Archived<u64>,
phantom: PhantomData<T>,
}
impl<T: ?Sized> Default for ArchivedDynMetadata<T> {
fn default() -> Self {
Self {
dyn_id: Archived::<u64>::from_native(0),
phantom: PhantomData::default(),
}
}
}
impl<T: ?Sized> Hash for ArchivedDynMetadata<T> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
Hash::hash(&self.dyn_id, state);
}
}
impl<T: ?Sized> PartialEq for ArchivedDynMetadata<T> {
#[inline]
fn eq(&self, other: &ArchivedDynMetadata<T>) -> bool {
self.dyn_id == other.dyn_id
}
}
impl<T: ?Sized> Eq for ArchivedDynMetadata<T> {}
#[allow(clippy::non_canonical_partial_ord_impl)]
impl<T: ?Sized> PartialOrd for ArchivedDynMetadata<T> {
#[inline]
fn partial_cmp(&self, other: &ArchivedDynMetadata<T>) -> Option<::core::cmp::Ordering> {
Some(self.dyn_id.cmp(&other.dyn_id))
}
}
impl<T: ?Sized> Ord for ArchivedDynMetadata<T> {
#[inline]
fn cmp(&self, other: &ArchivedDynMetadata<T>) -> ::core::cmp::Ordering {
self.dyn_id.cmp(&other.dyn_id)
}
}
impl<T: ?Sized> Clone for ArchivedDynMetadata<T> {
fn clone(&self) -> ArchivedDynMetadata<T> {
*self
}
}
impl<T: ?Sized> Copy for ArchivedDynMetadata<T> {}
impl<T: ?Sized> Unpin for ArchivedDynMetadata<T> {}
unsafe impl<T: ?Sized> Sync for ArchivedDynMetadata<T> {}
unsafe impl<T: ?Sized> Send for ArchivedDynMetadata<T> {}
unsafe impl<T: ?Sized> NoUndef for ArchivedDynMetadata<T> {}
unsafe impl<T: ?Sized> Portable for ArchivedDynMetadata<T> {}
unsafe impl<T: ?Sized, C> CheckBytes<C> for ArchivedDynMetadata<T>
where
C: Fallible + ?Sized,
C::Error: Trace,
Archived<u64>: CheckBytes<C>,
PhantomData<T>: CheckBytes<C>,
{
unsafe fn check_bytes(
value: *const Self,
context: &mut C,
) -> ::core::result::Result<(), C::Error> {
unsafe {
Archived::<u64>::check_bytes(&raw const (*value).dyn_id, context).map_err(|e| {
C::Error::trace(
e,
StructCheckContext {
struct_name: "ArchivedDynMetadata",
field_name: "dyn_id",
},
)
})?;
}
unsafe {
PhantomData::<T>::check_bytes(&raw const (*value).phantom, context).map_err(|e| {
C::Error::trace(
e,
StructCheckContext {
struct_name: "ArchivedDynMetadata",
field_name: "phantom",
},
)
})?;
}
Ok(())
}
}
impl<T: ?Sized> ArchivedDynMetadata<T> {
pub fn new(dyn_id: u64) -> Self {
Self {
dyn_id: Archived::<u64>::from_native(dyn_id),
phantom: PhantomData,
}
}
pub fn lookup_metadata(&self) -> DynMetadata<T> {
unsafe {
DYN_REGISTRY
.get(&self.dyn_id.to_native())
.expect("attempted to get vtable for an unregistered impl")
.cast()
}
}
}
pub struct DynEntry {
dyn_id: u64,
vtable: VTablePtr,
}
impl DynEntry {
pub const fn new(dyn_id: u64, vtable: VTablePtr) -> Self {
Self { dyn_id, vtable }
}
}
inventory::collect!(DynEntry);
static DYN_REGISTRY: std::sync::LazyLock<FxHashMap<u64, VTablePtr>> =
std::sync::LazyLock::new(|| {
let mut result = FxHashMap::default();
for entry in inventory::iter::<DynEntry> {
let old_value = result.insert(entry.dyn_id, entry.vtable);
if old_value.is_some() {
panic!("cacheable_dyn init global REGISTRY error, duplicate implementation.")
}
}
result.shrink_to_fit();
result
});