Skip to main content

rspack_cacheable/dyn/
mod.rs

1use core::marker::PhantomData;
2use std::{
3  collections::HashMap,
4  hash::{Hash, Hasher},
5};
6
7use inventory;
8use rkyv::{
9  Archived, Portable, SerializeUnsized,
10  bytecheck::{CheckBytes, StructCheckContext},
11  ptr_meta::{DynMetadata, Pointee},
12  rancor::{Fallible, Trace},
13  traits::NoUndef,
14};
15
16pub mod validation;
17mod vtable_ptr;
18
19pub use vtable_ptr::VTablePtr;
20
21use crate::{Deserializer, Result, Serializer};
22
23/// A trait object that can be archived.
24pub trait SerializeDyn {
25  /// Writes the value to the serializer and returns the position it was written to.
26  fn serialize_dyn(&self, serializer: &mut Serializer) -> Result<usize>;
27}
28
29impl<T> SerializeDyn for T
30where
31  T: for<'a> SerializeUnsized<Serializer<'a>>,
32{
33  fn serialize_dyn(&self, serializer: &mut Serializer) -> Result<usize> {
34    self.serialize_unsized(serializer)
35  }
36}
37
38/// A trait object that can be deserialized.
39///
40/// See [`SerializeDyn`] for more information.
41pub trait DeserializeDyn<T: Pointee + ?Sized> {
42  /// Deserializes this value into the given out pointer.
43  fn deserialize_dyn(&self, deserializer: &mut Deserializer, out: *mut T) -> Result<()>;
44
45  /// Returns the pointer metadata for the deserialized form of this type.
46  fn deserialized_pointer_metadata(&self) -> DynMetadata<T>;
47}
48
49/// The archived version of `DynMetadata`.
50pub struct ArchivedDynMetadata<T: ?Sized> {
51  dyn_id: Archived<u64>,
52  phantom: PhantomData<T>,
53}
54
55impl<T: ?Sized> Default for ArchivedDynMetadata<T> {
56  fn default() -> Self {
57    Self {
58      dyn_id: Archived::<u64>::from_native(0),
59      phantom: PhantomData::default(),
60    }
61  }
62}
63impl<T: ?Sized> Hash for ArchivedDynMetadata<T> {
64  #[inline]
65  fn hash<H: Hasher>(&self, state: &mut H) {
66    Hash::hash(&self.dyn_id, state);
67  }
68}
69impl<T: ?Sized> PartialEq for ArchivedDynMetadata<T> {
70  #[inline]
71  fn eq(&self, other: &ArchivedDynMetadata<T>) -> bool {
72    self.dyn_id == other.dyn_id
73  }
74}
75impl<T: ?Sized> Eq for ArchivedDynMetadata<T> {}
76#[allow(clippy::non_canonical_partial_ord_impl)]
77impl<T: ?Sized> PartialOrd for ArchivedDynMetadata<T> {
78  #[inline]
79  fn partial_cmp(&self, other: &ArchivedDynMetadata<T>) -> Option<::core::cmp::Ordering> {
80    Some(self.dyn_id.cmp(&other.dyn_id))
81  }
82}
83impl<T: ?Sized> Ord for ArchivedDynMetadata<T> {
84  #[inline]
85  fn cmp(&self, other: &ArchivedDynMetadata<T>) -> ::core::cmp::Ordering {
86    self.dyn_id.cmp(&other.dyn_id)
87  }
88}
89impl<T: ?Sized> Clone for ArchivedDynMetadata<T> {
90  fn clone(&self) -> ArchivedDynMetadata<T> {
91    *self
92  }
93}
94impl<T: ?Sized> Copy for ArchivedDynMetadata<T> {}
95impl<T: ?Sized> Unpin for ArchivedDynMetadata<T> {}
96unsafe impl<T: ?Sized> Sync for ArchivedDynMetadata<T> {}
97unsafe impl<T: ?Sized> Send for ArchivedDynMetadata<T> {}
98unsafe impl<T: ?Sized> NoUndef for ArchivedDynMetadata<T> {}
99unsafe impl<T: ?Sized> Portable for ArchivedDynMetadata<T> {}
100unsafe impl<T: ?Sized, C> CheckBytes<C> for ArchivedDynMetadata<T>
101where
102  C: Fallible + ?Sized,
103  C::Error: Trace,
104  Archived<u64>: CheckBytes<C>,
105  PhantomData<T>: CheckBytes<C>,
106{
107  unsafe fn check_bytes(
108    value: *const Self,
109    context: &mut C,
110  ) -> ::core::result::Result<(), C::Error> {
111    unsafe {
112      Archived::<u64>::check_bytes(&raw const (*value).dyn_id, context).map_err(|e| {
113        C::Error::trace(
114          e,
115          StructCheckContext {
116            struct_name: "ArchivedDynMetadata",
117            field_name: "dyn_id",
118          },
119        )
120      })?;
121    }
122    unsafe {
123      PhantomData::<T>::check_bytes(&raw const (*value).phantom, context).map_err(|e| {
124        C::Error::trace(
125          e,
126          StructCheckContext {
127            struct_name: "ArchivedDynMetadata",
128            field_name: "phantom",
129          },
130        )
131      })?;
132    }
133    Ok(())
134  }
135}
136
137impl<T: ?Sized> ArchivedDynMetadata<T> {
138  pub fn new(dyn_id: u64) -> Self {
139    Self {
140      dyn_id: Archived::<u64>::from_native(dyn_id),
141      phantom: PhantomData,
142    }
143  }
144
145  /// Returns the pointer metadata for the trait object this metadata refers to.
146  pub fn lookup_metadata(&self) -> DynMetadata<T> {
147    unsafe {
148      DYN_REGISTRY
149        .get(&self.dyn_id.to_native())
150        .expect("attempted to get vtable for an unregistered impl")
151        .cast()
152    }
153  }
154}
155
156pub struct DynEntry {
157  dyn_id: u64,
158  vtable: VTablePtr,
159}
160
161impl DynEntry {
162  pub const fn new(dyn_id: u64, vtable: VTablePtr) -> Self {
163    Self { dyn_id, vtable }
164  }
165}
166
167inventory::collect!(DynEntry);
168
169static DYN_REGISTRY: std::sync::LazyLock<HashMap<u64, VTablePtr>> =
170  std::sync::LazyLock::new(|| {
171    let mut result = HashMap::default();
172    for entry in inventory::iter::<DynEntry> {
173      let old_value = result.insert(entry.dyn_id, entry.vtable);
174      if old_value.is_some() {
175        panic!("cacheable_dyn init global REGISTRY error, duplicate implementation.")
176      }
177    }
178    result.shrink_to_fit();
179    result
180  });