Skip to content

Commit c17d228

Browse files
committed
Turbopack: Add traits for types that require TurboBincodeEncoder or TurboBincodeDecoder
1 parent dbfa121 commit c17d228

File tree

5 files changed

+119
-0
lines changed

5 files changed

+119
-0
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,7 @@ tracing = "0.1.37"
474474
tracing-subscriber = "0.3.16"
475475
triomphe = { git = "https://github.com/sokra/triomphe", branch = "sokra/unstable" }
476476
unsize = "1.1.0"
477+
unty = "0.0.4"
477478
url = "2.2.2"
478479
urlencoding = "2.1.2"
479480
uuid = "1.18.1"

turbopack/crates/turbo-bincode/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ ringmap = { workspace = true }
1919
serde = { workspace = true }
2020
serde_json = { workspace = true }
2121
smallvec = { workspace = true }
22+
unty = { workspace = true }

turbopack/crates/turbo-bincode/src/lib.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
#[doc(hidden)]
2+
pub mod macro_helpers;
3+
14
use std::ptr::copy_nonoverlapping;
25

36
use ::smallvec::SmallVec;
@@ -115,6 +118,57 @@ impl Reader for TurboBincodeReader<'_> {
115118
}
116119
}
117120

121+
/// Represents a type that can only be encoded with a [`TurboBincodeEncoder`].
122+
///
123+
/// All traits implementing this must also implement the more generic [`Encode`] trait, but they
124+
/// should panic if any other encoder is used.
125+
///
126+
/// Use [`impl_encode_for_turbo_bincode_encode`] to automatically implement the [`Encode`] trait
127+
/// from this one.
128+
pub trait TurboBincodeEncode: Encode {
129+
fn encode(&self, encoder: &mut TurboBincodeEncoder) -> Result<(), EncodeError>;
130+
}
131+
132+
/// Represents a type that can only be decoded with a [`TurboBincodeDecoder`] and an empty `()`
133+
/// context.
134+
///
135+
/// All traits implementing this must also implement the more generic [`Decode`] trait, but they
136+
/// should panic if any other encoder is used.
137+
///
138+
/// Use [`impl_decode_for_turbo_bincode_decode`] to automatically implement the [`Decode`] trait
139+
/// from this one.
140+
pub trait TurboBincodeDecode<Context>: Decode<Context> {
141+
fn decode(decoder: &mut TurboBincodeDecoder) -> Result<Self, DecodeError>;
142+
}
143+
144+
#[macro_export]
145+
macro_rules! impl_encode_for_turbo_bincode_encode {
146+
($ty:ty) => {
147+
impl $crate::macro_helpers::bincode::Encode for $ty {
148+
fn encode<'a, E: $crate::macro_helpers::bincode::enc::Encoder>(
149+
&self,
150+
encoder: &'a mut E,
151+
) -> ::std::result::Result<(), $crate::macro_helpers::bincode::error::EncodeError> {
152+
$crate::macro_helpers::encode_for_turbo_bincode_encode_impl(self, encoder)
153+
}
154+
}
155+
};
156+
}
157+
158+
#[macro_export]
159+
macro_rules! impl_decode_for_turbo_bincode_decode {
160+
($ty:ty) => {
161+
impl<Context> $crate::macro_helpers::bincode::Decode<Context> for $ty {
162+
fn decode<D: $crate::macro_helpers::bincode::de::Decoder<Context = Context>>(
163+
decoder: &mut D,
164+
) -> ::std::result::Result<Self, $crate::macro_helpers::bincode::error::DecodeError>
165+
{
166+
$crate::macro_helpers::decode_for_turbo_bincode_decode_impl(decoder)
167+
}
168+
}
169+
};
170+
}
171+
118172
pub mod indexmap {
119173
use std::hash::{BuildHasher, Hash};
120174

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
use std::{any::type_name, mem::transmute};
2+
3+
pub use bincode;
4+
use bincode::{
5+
de::Decoder,
6+
enc::Encoder,
7+
error::{DecodeError, EncodeError},
8+
};
9+
10+
use crate::{TurboBincodeDecode, TurboBincodeDecoder, TurboBincodeEncode, TurboBincodeEncoder};
11+
12+
#[track_caller]
13+
pub fn encode_for_turbo_bincode_encode_impl<'a, T: TurboBincodeEncode, E: Encoder>(
14+
value: &T,
15+
encoder: &'a mut E,
16+
) -> Result<(), EncodeError> {
17+
let encoder = if unty::type_equal::<E, TurboBincodeEncoder>() {
18+
// SAFETY: Transmute is safe because `&mut E` is `&mut TurboBincodeEncoder`:
19+
// - `unty::type_equal::<E, TurboBincodeEncoder>()` does not check lifetimes, but does check
20+
// the type and layout, so we know those are correct.
21+
// - The transmuted encoder cannot escape this function, and we know that the lifetime of
22+
// `'a` is at least as long as the function.
23+
// - Lifetimes don't change layout. This is not strictly guaranteed, but if this assumption
24+
// is broken, we'd have a different type id (type ids are derived from layout
25+
// information), `type_equal` would return `false`, and we'd panic instead of violating
26+
// memory safety.
27+
// - Two mutable references have the same layout and alignment when they reference exactly
28+
// the same type.
29+
// - The explicit lifetime ('a) avoids creating an implitly unbounded lifetime.
30+
unsafe { transmute::<&'a mut E, &'a mut TurboBincodeEncoder>(encoder) }
31+
} else {
32+
unreachable!(
33+
"{} implements TurboBincodeEncode, but was called with a {} encoder implementation",
34+
type_name::<T>(),
35+
type_name::<E>(),
36+
)
37+
};
38+
TurboBincodeEncode::encode(value, encoder)
39+
}
40+
41+
#[track_caller]
42+
pub fn decode_for_turbo_bincode_decode_impl<
43+
'a,
44+
Context,
45+
T: TurboBincodeDecode<Context>,
46+
D: Decoder<Context = Context>,
47+
>(
48+
decoder: &'a mut D,
49+
) -> Result<T, DecodeError> {
50+
let decoder = if unty::type_equal::<D, TurboBincodeDecoder>() {
51+
// SAFETY: See notes on the `Encode::encode` implementation on
52+
// `encode_for_turbo_bincode_encode_impl`.
53+
unsafe { transmute::<&'a mut D, &'a mut TurboBincodeDecoder<'a>>(decoder) }
54+
} else {
55+
unreachable!(
56+
"{} implements TurboBincodeDecode, but was called with a {} decoder implementation",
57+
type_name::<T>(),
58+
type_name::<D>(),
59+
)
60+
};
61+
TurboBincodeDecode::decode(decoder)
62+
}

0 commit comments

Comments
 (0)