Skip to main content

rspack_cacheable/dyn/
mod.rs

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