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::{DeserializeError, Deserializer, SerializeError, 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, SerializeError>;
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, SerializeError> {
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(
44    &self,
45    deserializer: &mut Deserializer,
46    out: *mut T,
47  ) -> Result<(), DeserializeError>;
48
49  /// Returns the pointer metadata for the deserialized form of this type.
50  fn deserialized_pointer_metadata(&self) -> DynMetadata<T>;
51}
52
53/// The archived version of `DynMetadata`.
54pub struct ArchivedDynMetadata<T: ?Sized> {
55  dyn_id: Archived<u64>,
56  phantom: PhantomData<T>,
57}
58
59impl<T: ?Sized> Default for ArchivedDynMetadata<T> {
60  fn default() -> Self {
61    Self {
62      dyn_id: Archived::<u64>::from_native(0),
63      phantom: PhantomData::default(),
64    }
65  }
66}
67impl<T: ?Sized> Hash for ArchivedDynMetadata<T> {
68  #[inline]
69  fn hash<H: Hasher>(&self, state: &mut H) {
70    Hash::hash(&self.dyn_id, state);
71  }
72}
73impl<T: ?Sized> PartialEq for ArchivedDynMetadata<T> {
74  #[inline]
75  fn eq(&self, other: &ArchivedDynMetadata<T>) -> bool {
76    self.dyn_id == other.dyn_id
77  }
78}
79impl<T: ?Sized> Eq for ArchivedDynMetadata<T> {}
80#[allow(clippy::non_canonical_partial_ord_impl)]
81impl<T: ?Sized> PartialOrd for ArchivedDynMetadata<T> {
82  #[inline]
83  fn partial_cmp(&self, other: &ArchivedDynMetadata<T>) -> Option<::core::cmp::Ordering> {
84    Some(self.dyn_id.cmp(&other.dyn_id))
85  }
86}
87impl<T: ?Sized> Ord for ArchivedDynMetadata<T> {
88  #[inline]
89  fn cmp(&self, other: &ArchivedDynMetadata<T>) -> ::core::cmp::Ordering {
90    self.dyn_id.cmp(&other.dyn_id)
91  }
92}
93impl<T: ?Sized> Clone for ArchivedDynMetadata<T> {
94  fn clone(&self) -> ArchivedDynMetadata<T> {
95    *self
96  }
97}
98impl<T: ?Sized> Copy for ArchivedDynMetadata<T> {}
99impl<T: ?Sized> Unpin for ArchivedDynMetadata<T> {}
100unsafe impl<T: ?Sized> Sync for ArchivedDynMetadata<T> {}
101unsafe impl<T: ?Sized> Send for ArchivedDynMetadata<T> {}
102unsafe impl<T: ?Sized> NoUndef for ArchivedDynMetadata<T> {}
103unsafe impl<T: ?Sized> Portable for ArchivedDynMetadata<T> {}
104unsafe impl<T: ?Sized, C> CheckBytes<C> for ArchivedDynMetadata<T>
105where
106  C: Fallible + ?Sized,
107  C::Error: Trace,
108  Archived<u64>: CheckBytes<C>,
109  PhantomData<T>: CheckBytes<C>,
110{
111  unsafe fn check_bytes(
112    value: *const Self,
113    context: &mut C,
114  ) -> ::core::result::Result<(), C::Error> {
115    unsafe {
116      Archived::<u64>::check_bytes(&raw const (*value).dyn_id, context).map_err(|e| {
117        C::Error::trace(
118          e,
119          StructCheckContext {
120            struct_name: "ArchivedDynMetadata",
121            field_name: "dyn_id",
122          },
123        )
124      })?;
125    }
126    unsafe {
127      PhantomData::<T>::check_bytes(&raw const (*value).phantom, context).map_err(|e| {
128        C::Error::trace(
129          e,
130          StructCheckContext {
131            struct_name: "ArchivedDynMetadata",
132            field_name: "phantom",
133          },
134        )
135      })?;
136    }
137    Ok(())
138  }
139}
140
141impl<T: ?Sized> ArchivedDynMetadata<T> {
142  pub fn new(dyn_id: u64) -> Self {
143    Self {
144      dyn_id: Archived::<u64>::from_native(dyn_id),
145      phantom: PhantomData,
146    }
147  }
148
149  /// Returns the pointer metadata for the trait object this metadata refers to.
150  pub fn lookup_metadata(&self) -> DynMetadata<T> {
151    unsafe {
152      DYN_REGISTRY
153        .get(&self.dyn_id.to_native())
154        .expect("attempted to get vtable for an unregistered impl")
155        .cast()
156    }
157  }
158}
159
160pub struct DynEntry {
161  dyn_id: u64,
162  vtable: VTablePtr,
163}
164
165impl DynEntry {
166  pub const fn new(dyn_id: u64, vtable: VTablePtr) -> Self {
167    Self { dyn_id, vtable }
168  }
169}
170
171inventory::collect!(DynEntry);
172
173static DYN_REGISTRY: std::sync::LazyLock<HashMap<u64, VTablePtr>> =
174  std::sync::LazyLock::new(|| {
175    let mut result = HashMap::default();
176    for entry in inventory::iter::<DynEntry> {
177      let old_value = result.insert(entry.dyn_id, entry.vtable);
178      if old_value.is_some() {
179        panic!("cacheable_dyn init global REGISTRY error, duplicate implementation.")
180      }
181    }
182    result.shrink_to_fit();
183    result
184  });