rspack_cacheable/dyn/
mod.rs1use 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
23pub trait SerializeDyn {
25 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
38pub trait DeserializeDyn<T: Pointee + ?Sized> {
42 fn deserialize_dyn(&self, deserializer: &mut Deserializer, out: *mut T) -> Result<()>;
44
45 fn deserialized_pointer_metadata(&self) -> DynMetadata<T>;
47}
48
49pub 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 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 });