-
-
Notifications
You must be signed in to change notification settings - Fork 552
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a97e105
commit e2eee98
Showing
63 changed files
with
4,243 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
[package] | ||
edition = "2021" | ||
license = "MIT" | ||
name = "rspack_cacheable" | ||
version = "0.1.0" | ||
|
||
[dependencies] | ||
camino = { workspace = true } | ||
dashmap = { workspace = true } | ||
hashlink = { workspace = true } | ||
indexmap = { workspace = true } | ||
inventory = { workspace = true } | ||
json = { workspace = true } | ||
lightningcss = { workspace = true } | ||
once_cell = { workspace = true } | ||
rkyv = { workspace = true } | ||
rspack_macros = { path = "../rspack_macros" } | ||
rspack_resolver = { workspace = true } | ||
rspack_sources = { workspace = true } | ||
serde_json = { workspace = true } | ||
swc_core = { workspace = true, features = ["ecma_ast"] } | ||
ustr = { workspace = true } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
use std::{any::Any, ptr::NonNull}; | ||
|
||
use rkyv::{ | ||
de::{ErasedPtr, Pooling, PoolingState}, | ||
ser::{sharing::SharingState, Sharing}, | ||
}; | ||
|
||
use crate::{DeserializeError, SerializeError}; | ||
|
||
const CONTEXT_ADDR: usize = 0; | ||
unsafe fn default_drop(_: ErasedPtr) {} | ||
|
||
pub struct ContextGuard<'a> { | ||
context: &'a dyn Any, | ||
} | ||
|
||
impl<'a> ContextGuard<'a> { | ||
pub fn new(context: &'a dyn Any) -> Self { | ||
Self { context } | ||
} | ||
|
||
pub fn add_to_sharing<S: Sharing<SerializeError>>( | ||
&self, | ||
sharing: &mut S, | ||
) -> Result<(), SerializeError> { | ||
sharing.start_sharing(CONTEXT_ADDR); | ||
sharing.finish_sharing(CONTEXT_ADDR, self as *const _ as usize) | ||
} | ||
|
||
pub fn sharing_context<S: Sharing<SerializeError>>( | ||
sharing: &'a mut S, | ||
) -> Result<&'a dyn Any, SerializeError> { | ||
match sharing.start_sharing(CONTEXT_ADDR) { | ||
SharingState::Finished(addr) => { | ||
let guard: &Self = unsafe { &*(addr as *const Self) }; | ||
Ok(guard.context) | ||
} | ||
_ => Err(SerializeError::NoContext), | ||
} | ||
} | ||
|
||
pub fn add_to_pooling<P: Pooling<DeserializeError>>( | ||
&self, | ||
pooling: &mut P, | ||
) -> Result<(), DeserializeError> { | ||
unsafe { | ||
let ctx_ptr = ErasedPtr::new(NonNull::new_unchecked(self as *const _ as *mut ())); | ||
pooling.start_pooling(CONTEXT_ADDR); | ||
pooling.finish_pooling(CONTEXT_ADDR, ctx_ptr, default_drop) | ||
} | ||
} | ||
|
||
pub fn pooling_context<P: Pooling<DeserializeError>>( | ||
pooling: &'a mut P, | ||
) -> Result<&'a dyn Any, DeserializeError> { | ||
match pooling.start_pooling(CONTEXT_ADDR) { | ||
PoolingState::Finished(ptr) => { | ||
let guard: &Self = unsafe { &*(ptr.data_address() as *const Self) }; | ||
Ok(guard.context) | ||
} | ||
_ => Err(DeserializeError::NoContext), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
use std::any::Any; | ||
|
||
use rkyv::{ | ||
access, | ||
api::{deserialize_using, high::HighValidator}, | ||
bytecheck::CheckBytes, | ||
de::Pool, | ||
rancor::{BoxedError, Source, Strategy, Trace}, | ||
Archive, Deserialize, | ||
}; | ||
|
||
use crate::context::ContextGuard; | ||
|
||
#[derive(Debug)] | ||
pub enum DeserializeError { | ||
RkyvError(BoxedError), | ||
DynCheckBytesNotRegister, | ||
// A deserialize failed occurred | ||
DeserializeFailed(&'static str), | ||
NoContext, | ||
UnsupportedField, | ||
} | ||
|
||
impl std::fmt::Display for DeserializeError { | ||
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
// write!(f, "{}", self.inner)?; | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl std::error::Error for DeserializeError { | ||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { | ||
// self.inner.source() | ||
todo!() | ||
} | ||
} | ||
|
||
impl Trace for DeserializeError { | ||
fn trace<R>(self, _trace: R) -> Self | ||
where | ||
R: std::fmt::Debug + std::fmt::Display + Send + Sync + 'static, | ||
{ | ||
todo!() | ||
// Self::RkyvError() | ||
// inner: self.inner.trace(trace), | ||
// } | ||
} | ||
} | ||
|
||
impl Source for DeserializeError { | ||
fn new<T: std::error::Error + Send + Sync + 'static>(source: T) -> Self { | ||
Self::RkyvError(BoxedError::new(source)) | ||
} | ||
} | ||
|
||
pub type Validator<'a> = HighValidator<'a, DeserializeError>; | ||
pub type Deserializer = Strategy<Pool, DeserializeError>; | ||
|
||
pub fn from_bytes<T, C: Any>(bytes: &[u8], context: &C) -> Result<T, DeserializeError> | ||
where | ||
T: Archive, | ||
T::Archived: for<'a> CheckBytes<Validator<'a>> + Deserialize<T, Deserializer>, | ||
{ | ||
let guard = ContextGuard::new(context); | ||
let mut deserializer = Pool::default(); | ||
guard.add_to_pooling(&mut deserializer)?; | ||
deserialize_using( | ||
access::<T::Archived, DeserializeError>(bytes)?, | ||
&mut deserializer, | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
use core::marker::PhantomData; | ||
use std::{ | ||
collections::HashMap, | ||
hash::{Hash, Hasher}, | ||
}; | ||
|
||
use inventory; | ||
use rkyv::{ | ||
bytecheck::{CheckBytes, StructCheckContext}, | ||
ptr_meta::{DynMetadata, Pointee}, | ||
rancor::{Fallible, Trace}, | ||
traits::NoUndef, | ||
Archived, Portable, SerializeUnsized, | ||
}; | ||
|
||
pub mod validation; | ||
use crate::{DeserializeError, Deserializer, SerializeError, Serializer}; | ||
|
||
/// A trait object that can be archived. | ||
pub trait SerializeDyn { | ||
/// Writes the value to the serializer and returns the position it was written to. | ||
fn serialize_dyn(&self, serializer: &mut Serializer) -> Result<usize, SerializeError>; | ||
} | ||
|
||
impl<T> SerializeDyn for T | ||
where | ||
T: for<'a> SerializeUnsized<Serializer<'a>>, | ||
{ | ||
fn serialize_dyn(&self, serializer: &mut Serializer) -> Result<usize, SerializeError> { | ||
self.serialize_unsized(serializer) | ||
} | ||
} | ||
|
||
/// A trait object that can be deserialized. | ||
/// | ||
/// See [`SerializeDyn`] for more information. | ||
pub trait DeserializeDyn<T: Pointee + ?Sized> { | ||
/// Deserializes this value into the given out pointer. | ||
fn deserialize_dyn( | ||
&self, | ||
deserializer: &mut Deserializer, | ||
out: *mut T, | ||
) -> Result<(), DeserializeError>; | ||
|
||
/// Returns the pointer metadata for the deserialized form of this type. | ||
fn deserialized_pointer_metadata(&self) -> DynMetadata<T>; | ||
} | ||
|
||
/// The archived version of `DynMetadata`. | ||
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> {} | ||
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> { | ||
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", | ||
}, | ||
) | ||
})?; | ||
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, | ||
} | ||
} | ||
|
||
/// Returns the pointer metadata for the trait object this metadata refers | ||
/// to. | ||
pub fn lookup_metadata(&self) -> DynMetadata<T> { | ||
unsafe { | ||
std::mem::transmute( | ||
*DYN_REGISTRY | ||
.get(&self.dyn_id.to_native()) | ||
.expect("attempted to get vtable for an unregistered impl"), | ||
) | ||
} | ||
} | ||
} | ||
|
||
pub struct DynEntry { | ||
dyn_id: u64, | ||
vtable: usize, | ||
} | ||
|
||
impl DynEntry { | ||
pub fn new(dyn_id: u64, vtable: usize) -> Self { | ||
Self { dyn_id, vtable } | ||
} | ||
} | ||
|
||
inventory::collect!(DynEntry); | ||
|
||
static DYN_REGISTRY: std::sync::LazyLock<HashMap<u64, usize>> = std::sync::LazyLock::new(|| { | ||
let mut result = HashMap::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 | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
use std::collections::HashMap; | ||
|
||
use rkyv::bytecheck::CheckBytes; | ||
|
||
use crate::{DeserializeError, Validator}; | ||
|
||
type CheckBytesDyn = unsafe fn(*const u8, &mut Validator<'_>) -> Result<(), DeserializeError>; | ||
|
||
/// # Safety | ||
/// | ||
/// Run T::check_bytes | ||
pub unsafe fn default_check_bytes_dyn<T>( | ||
bytes: *const u8, | ||
context: &mut Validator<'_>, | ||
) -> Result<(), DeserializeError> | ||
where | ||
T: for<'a> CheckBytes<Validator<'a>>, | ||
{ | ||
T::check_bytes(bytes.cast(), context) | ||
} | ||
|
||
pub struct CheckBytesEntry { | ||
vtable: usize, | ||
check_bytes_dyn: CheckBytesDyn, | ||
} | ||
|
||
impl CheckBytesEntry { | ||
#[doc(hidden)] | ||
pub fn new(vtable: usize, check_bytes_dyn: CheckBytesDyn) -> Self { | ||
Self { | ||
vtable, | ||
check_bytes_dyn, | ||
} | ||
} | ||
} | ||
|
||
inventory::collect!(CheckBytesEntry); | ||
|
||
pub static CHECK_BYTES_REGISTRY: std::sync::LazyLock<HashMap<usize, CheckBytesDyn>> = | ||
std::sync::LazyLock::new(|| { | ||
let mut result = HashMap::default(); | ||
for entry in inventory::iter::<CheckBytesEntry> { | ||
let old_value = result.insert(entry.vtable, entry.check_bytes_dyn); | ||
if old_value.is_some() { | ||
panic!("vtable conflict, a trait implementation was likely added twice (but it's possible there was a hash collision)") | ||
} | ||
} | ||
result | ||
}); |
Oops, something went wrong.