Skip to content

Commit dbfa121

Browse files
committed
Turbopack: Add custom bincode reader/writer implementations
1 parent 9ed4581 commit dbfa121

File tree

1 file changed

+111
-2
lines changed
  • turbopack/crates/turbo-bincode/src

1 file changed

+111
-2
lines changed

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

Lines changed: 111 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,120 @@
1+
use std::ptr::copy_nonoverlapping;
2+
13
use ::smallvec::SmallVec;
24
use bincode::{
35
BorrowDecode, Decode, Encode,
4-
de::{BorrowDecoder, Decoder},
5-
enc::Encoder,
6+
de::{BorrowDecoder, Decoder, DecoderImpl, read::Reader},
7+
enc::{Encoder, EncoderImpl, write::Writer},
68
error::{DecodeError, EncodeError},
79
};
810

11+
pub const TURBO_BINCODE_CONFIG: bincode::config::Configuration = bincode::config::standard();
12+
pub type TurboBincodeBuffer = SmallVec<[u8; 16]>;
13+
pub type TurboBincodeEncoder<'a> =
14+
EncoderImpl<TurboBincodeWriter<'a>, bincode::config::Configuration>;
15+
pub type TurboBincodeDecoder<'a> =
16+
DecoderImpl<TurboBincodeReader<'a>, bincode::config::Configuration, ()>;
17+
18+
fn new_turbo_bincode_encoder(buf: &mut TurboBincodeBuffer) -> TurboBincodeEncoder<'_> {
19+
EncoderImpl::new(TurboBincodeWriter::new(buf), TURBO_BINCODE_CONFIG)
20+
}
21+
22+
fn new_turbo_bincode_decoder(buffer: &[u8]) -> TurboBincodeDecoder<'_> {
23+
DecoderImpl::new(TurboBincodeReader::new(buffer), TURBO_BINCODE_CONFIG, ())
24+
}
25+
26+
/// Encode the value into a new [`SmallVec`] using a [`TurboBincodeEncoder`].
27+
///
28+
/// Note: If you can re-use a buffer, you should. That will always be cheaper than creating a new
29+
/// [`SmallVec`].
30+
pub fn turbo_bincode_encode<T: Encode>(value: &T) -> Result<TurboBincodeBuffer, EncodeError> {
31+
let mut buffer = TurboBincodeBuffer::new();
32+
turbo_bincode_encode_into(value, &mut buffer)?;
33+
Ok(buffer)
34+
}
35+
36+
pub fn turbo_bincode_encode_into<T: Encode>(
37+
value: &T,
38+
buffer: &mut TurboBincodeBuffer,
39+
) -> Result<(), EncodeError> {
40+
let mut encoder = new_turbo_bincode_encoder(buffer);
41+
value.encode(&mut encoder)?;
42+
Ok(())
43+
}
44+
45+
/// Decode using a [`TurboBincodeDecoder`] and check that the entire slice was consumed. Returns a
46+
/// [`DecodeError::ArrayLengthMismatch`] if some of the slice is not consumed during decoding.
47+
pub fn turbo_bincode_decode<T: Decode<()>>(buf: &[u8]) -> Result<T, DecodeError> {
48+
let mut decoder = new_turbo_bincode_decoder(buf);
49+
let val = T::decode(&mut decoder)?;
50+
let remaining_buf = decoder.reader().buffer;
51+
if !remaining_buf.is_empty() {
52+
return Err(DecodeError::ArrayLengthMismatch {
53+
required: buf.len() - remaining_buf.len(),
54+
found: buf.len(),
55+
});
56+
}
57+
Ok(val)
58+
}
59+
60+
pub struct TurboBincodeWriter<'a> {
61+
pub buffer: &'a mut TurboBincodeBuffer,
62+
}
63+
64+
impl<'a> TurboBincodeWriter<'a> {
65+
pub fn new(buffer: &'a mut TurboBincodeBuffer) -> Self {
66+
Self { buffer }
67+
}
68+
}
69+
70+
impl Writer for TurboBincodeWriter<'_> {
71+
fn write(&mut self, bytes: &[u8]) -> Result<(), EncodeError> {
72+
self.buffer.extend_from_slice(bytes);
73+
Ok(())
74+
}
75+
}
76+
77+
/// This is equivalent to [`bincode::de::read::SliceReader`], but with a little `unsafe` code to
78+
/// avoid some redundant bounds checks, and `pub` access to the underlying `buffer`.
79+
pub struct TurboBincodeReader<'a> {
80+
pub buffer: &'a [u8],
81+
}
82+
83+
impl<'a> TurboBincodeReader<'a> {
84+
pub fn new(buffer: &'a [u8]) -> Self {
85+
Self { buffer }
86+
}
87+
}
88+
89+
impl Reader for TurboBincodeReader<'_> {
90+
fn read(&mut self, target_buffer: &mut [u8]) -> Result<(), DecodeError> {
91+
let len = target_buffer.len();
92+
let (head, rest) =
93+
self.buffer
94+
.split_at_checked(len)
95+
.ok_or_else(|| DecodeError::UnexpectedEnd {
96+
additional: len - self.buffer.len(),
97+
})?;
98+
// SAFETY:
99+
// - We already checked the bounds.
100+
// - These memory ranges can't overlap because it would violate rust aliasing rules.
101+
// - `u8` is `Copy`.
102+
unsafe {
103+
copy_nonoverlapping(head.as_ptr(), target_buffer.as_mut_ptr(), len);
104+
}
105+
self.buffer = rest;
106+
Ok(())
107+
}
108+
109+
fn peek_read(&mut self, n: usize) -> Option<&[u8]> {
110+
self.buffer.get(..n)
111+
}
112+
113+
fn consume(&mut self, n: usize) {
114+
self.buffer = &self.buffer[n..];
115+
}
116+
}
117+
9118
pub mod indexmap {
10119
use std::hash::{BuildHasher, Hash};
11120

0 commit comments

Comments
 (0)