Skip to main content

turbo_bincode/
lib.rs

1#[doc(hidden)]
2pub mod macro_helpers;
3pub mod serde_self_describing;
4
5use std::{any::Any, ptr::copy_nonoverlapping};
6
7use ::smallvec::SmallVec;
8use bincode::{
9    BorrowDecode, Decode, Encode,
10    de::{BorrowDecoder, Decoder, DecoderImpl, read::Reader},
11    enc::{Encoder, EncoderImpl, write::Writer},
12    error::{DecodeError, EncodeError},
13};
14use turbo_tasks_hash::DeterministicHasher;
15
16pub const TURBO_BINCODE_CONFIG: bincode::config::Configuration = bincode::config::standard();
17/// Same as standard config, but since we aren't encoding we don't benefit from varint
18/// optimizations.
19pub const TURBO_BINCODE_HASH_CONFIG: bincode::config::Configuration<
20    bincode::config::LittleEndian,
21    bincode::config::Fixint,
22    bincode::config::NoLimit,
23> = TURBO_BINCODE_CONFIG.with_fixed_int_encoding();
24pub type TurboBincodeBuffer = SmallVec<[u8; 16]>;
25pub type TurboBincodeEncoder<'a> =
26    EncoderImpl<TurboBincodeWriter<'a>, bincode::config::Configuration>;
27pub type TurboBincodeDecoder<'a> =
28    DecoderImpl<TurboBincodeReader<'a>, bincode::config::Configuration, ()>;
29pub type AnyEncodeFn = fn(&dyn Any, &mut TurboBincodeEncoder<'_>) -> Result<(), EncodeError>;
30pub type AnyDecodeFn<T> = fn(&mut TurboBincodeDecoder<'_>) -> Result<T, DecodeError>;
31
32pub fn new_turbo_bincode_encoder(buf: &mut TurboBincodeBuffer) -> TurboBincodeEncoder<'_> {
33    EncoderImpl::new(TurboBincodeWriter::new(buf), TURBO_BINCODE_CONFIG)
34}
35
36pub fn new_turbo_bincode_decoder(buffer: &[u8]) -> TurboBincodeDecoder<'_> {
37    DecoderImpl::new(TurboBincodeReader::new(buffer), TURBO_BINCODE_CONFIG, ())
38}
39
40/// Encode the value into a new [`SmallVec`] using a [`TurboBincodeEncoder`].
41///
42/// Note: If you can re-use a buffer, you should. That will always be cheaper than creating a new
43/// [`SmallVec`].
44pub fn turbo_bincode_encode<T: Encode>(value: &T) -> Result<TurboBincodeBuffer, EncodeError> {
45    let mut buffer = TurboBincodeBuffer::new();
46    turbo_bincode_encode_into(value, &mut buffer)?;
47    Ok(buffer)
48}
49
50pub fn turbo_bincode_encode_into<T: Encode>(
51    value: &T,
52    buffer: &mut TurboBincodeBuffer,
53) -> Result<(), EncodeError> {
54    let mut encoder = new_turbo_bincode_encoder(buffer);
55    value.encode(&mut encoder)?;
56    Ok(())
57}
58
59/// Decode using a [`TurboBincodeDecoder`] and check that the entire slice was consumed. Returns a
60/// [`DecodeError::ArrayLengthMismatch`] if some of the slice is not consumed during decoding.
61pub fn turbo_bincode_decode<T: Decode<()>>(buf: &[u8]) -> Result<T, DecodeError> {
62    let mut decoder = new_turbo_bincode_decoder(buf);
63    let val = T::decode(&mut decoder)?;
64    let remaining_buf = decoder.reader().buffer;
65    if !remaining_buf.is_empty() {
66        return Err(DecodeError::ArrayLengthMismatch {
67            required: buf.len() - remaining_buf.len(),
68            found: buf.len(),
69        });
70    }
71    Ok(val)
72}
73
74pub struct TurboBincodeWriter<'a> {
75    pub buffer: &'a mut TurboBincodeBuffer,
76}
77
78impl<'a> TurboBincodeWriter<'a> {
79    pub fn new(buffer: &'a mut TurboBincodeBuffer) -> Self {
80        Self { buffer }
81    }
82}
83
84impl Writer for TurboBincodeWriter<'_> {
85    fn write(&mut self, bytes: &[u8]) -> Result<(), EncodeError> {
86        self.buffer.extend_from_slice(bytes);
87        Ok(())
88    }
89}
90
91/// This is equivalent to [`bincode::de::read::SliceReader`], but with a little `unsafe` code to
92/// avoid some redundant bounds checks, and `pub` access to the underlying `buffer`.
93pub struct TurboBincodeReader<'a> {
94    pub buffer: &'a [u8],
95}
96
97impl<'a> TurboBincodeReader<'a> {
98    pub fn new(buffer: &'a [u8]) -> Self {
99        Self { buffer }
100    }
101}
102
103impl Reader for TurboBincodeReader<'_> {
104    fn read(&mut self, target_buffer: &mut [u8]) -> Result<(), DecodeError> {
105        let len = target_buffer.len();
106        let (head, rest) =
107            self.buffer
108                .split_at_checked(len)
109                .ok_or_else(|| DecodeError::UnexpectedEnd {
110                    additional: len - self.buffer.len(),
111                })?;
112        // SAFETY:
113        // - We already checked the bounds.
114        // - These memory ranges can't overlap because it would violate rust aliasing rules.
115        // - `u8` is `Copy`.
116        unsafe {
117            copy_nonoverlapping(head.as_ptr(), target_buffer.as_mut_ptr(), len);
118        }
119        self.buffer = rest;
120        Ok(())
121    }
122
123    fn peek_read(&mut self, n: usize) -> Option<&[u8]> {
124        self.buffer.get(..n)
125    }
126
127    fn consume(&mut self, n: usize) {
128        self.buffer = &self.buffer[n..];
129    }
130}
131
132/// A [`Writer`] that sinks bytes directly into a [`DeterministicHasher`] instead of a buffer.
133///
134/// This allows encoding values directly to a hash without intermediate buffer allocation.
135pub struct HashWriter<'a, H: DeterministicHasher + ?Sized> {
136    hasher: &'a mut H,
137}
138
139impl<'a, H: DeterministicHasher + ?Sized> HashWriter<'a, H> {
140    /// Creates a new `HashWriter` that writes to the given hasher.
141    pub fn new(hasher: &'a mut H) -> Self {
142        Self { hasher }
143    }
144}
145
146impl<H: DeterministicHasher + ?Sized> Writer for HashWriter<'_, H> {
147    fn write(&mut self, bytes: &[u8]) -> Result<(), EncodeError> {
148        self.hasher.write_bytes(bytes);
149        Ok(())
150    }
151}
152
153/// An encoder that writes directly to a [`DeterministicHasher`].
154pub type HashEncoder<'a, H> = EncoderImpl<
155    HashWriter<'a, H>,
156    bincode::config::Configuration<
157        bincode::config::LittleEndian,
158        bincode::config::Fixint,
159        bincode::config::NoLimit,
160    >,
161>;
162
163/// Creates a new [`HashEncoder`] that encodes directly to the given hasher.
164///
165/// This is useful for computing hashes of encoded values without allocating a buffer.
166pub fn new_hash_encoder<H: DeterministicHasher + ?Sized>(hasher: &mut H) -> HashEncoder<'_, H> {
167    EncoderImpl::new(HashWriter::new(hasher), TURBO_BINCODE_HASH_CONFIG)
168}
169
170/// Represents a type that can only be encoded with a [`TurboBincodeEncoder`].
171///
172/// All traits implementing this must also implement the more generic [`Encode`] trait, but they
173/// should panic if any other encoder is used.
174///
175/// Use [`impl_encode_for_turbo_bincode_encode`] to automatically implement the [`Encode`] trait
176/// from this one.
177pub trait TurboBincodeEncode: Encode {
178    fn encode(&self, encoder: &mut TurboBincodeEncoder) -> Result<(), EncodeError>;
179}
180
181/// Represents a type that can only be decoded with a [`TurboBincodeDecoder`] and an empty `()`
182/// context.
183///
184/// All traits implementing this must also implement the more generic [`Decode`] trait, but they
185/// should panic if any other encoder is used.
186///
187/// Use [`impl_decode_for_turbo_bincode_decode`] to automatically implement the [`Decode`] trait
188/// from this one.
189pub trait TurboBincodeDecode<Context>: Decode<Context> {
190    fn decode(decoder: &mut TurboBincodeDecoder) -> Result<Self, DecodeError>;
191}
192
193#[macro_export]
194macro_rules! impl_encode_for_turbo_bincode_encode {
195    ($ty:ty) => {
196        impl $crate::macro_helpers::bincode::Encode for $ty {
197            fn encode<'a, E: $crate::macro_helpers::bincode::enc::Encoder>(
198                &self,
199                encoder: &'a mut E,
200            ) -> ::std::result::Result<(), $crate::macro_helpers::bincode::error::EncodeError> {
201                $crate::macro_helpers::encode_for_turbo_bincode_encode_impl(self, encoder)
202            }
203        }
204    };
205}
206
207#[macro_export]
208macro_rules! impl_decode_for_turbo_bincode_decode {
209    ($ty:ty) => {
210        impl<Context> $crate::macro_helpers::bincode::Decode<Context> for $ty {
211            fn decode<D: $crate::macro_helpers::bincode::de::Decoder<Context = Context>>(
212                decoder: &mut D,
213            ) -> ::std::result::Result<Self, $crate::macro_helpers::bincode::error::DecodeError>
214            {
215                $crate::macro_helpers::decode_for_turbo_bincode_decode_impl(decoder)
216            }
217        }
218    };
219}
220
221pub mod indexmap {
222    use std::hash::{BuildHasher, Hash};
223
224    use ::indexmap::IndexMap;
225
226    use super::*;
227
228    pub fn encode<E, K, V, S>(map: &IndexMap<K, V, S>, encoder: &mut E) -> Result<(), EncodeError>
229    where
230        E: Encoder,
231        K: Encode,
232        V: Encode,
233    {
234        usize::encode(&map.len(), encoder)?;
235        for (k, v) in map {
236            K::encode(k, encoder)?;
237            V::encode(v, encoder)?;
238        }
239        Ok(())
240    }
241
242    pub fn decode<Context, D, K, V, S>(decoder: &mut D) -> Result<IndexMap<K, V, S>, DecodeError>
243    where
244        D: Decoder<Context = Context>,
245        K: Decode<Context> + Eq + Hash,
246        V: Decode<Context>,
247        S: BuildHasher + Default,
248    {
249        let len = usize::decode(decoder)?;
250        let mut map = IndexMap::with_capacity_and_hasher(len, Default::default());
251        for _i in 0..len {
252            map.insert(K::decode(decoder)?, V::decode(decoder)?);
253        }
254        Ok(map)
255    }
256
257    pub fn borrow_decode<'de, Context, D, K, V, S>(
258        decoder: &mut D,
259    ) -> Result<IndexMap<K, V, S>, DecodeError>
260    where
261        D: BorrowDecoder<'de, Context = Context>,
262        K: BorrowDecode<'de, Context> + Eq + Hash,
263        V: BorrowDecode<'de, Context>,
264        S: BuildHasher + Default,
265    {
266        let len = usize::decode(decoder)?;
267        let mut map = IndexMap::with_capacity_and_hasher(len, Default::default());
268        for _i in 0..len {
269            map.insert(K::borrow_decode(decoder)?, V::borrow_decode(decoder)?);
270        }
271        Ok(map)
272    }
273
274    #[cfg(test)]
275    mod tests {
276        use bincode::{decode_from_slice, encode_to_vec};
277
278        use super::*;
279
280        #[test]
281        fn test_roundtrip() {
282            let cfg = bincode::config::standard();
283
284            #[derive(Encode, Decode)]
285            struct Wrapper(#[bincode(with = "crate::indexmap")] IndexMap<String, u32>);
286
287            let map1 = Wrapper(IndexMap::from([
288                ("key1".to_string(), 12345u32),
289                ("key2".to_string(), 23456u32),
290            ]));
291
292            let map2: Wrapper = decode_from_slice(&encode_to_vec(&map1, cfg).unwrap(), cfg)
293                .unwrap()
294                .0;
295
296            assert_eq!(map1.0, map2.0);
297        }
298    }
299}
300
301pub mod indexset {
302    use std::hash::{BuildHasher, Hash};
303
304    use ::indexmap::IndexSet;
305
306    use super::*;
307
308    pub fn encode<E, T, S>(set: &IndexSet<T, S>, encoder: &mut E) -> Result<(), EncodeError>
309    where
310        E: Encoder,
311        T: Encode,
312    {
313        usize::encode(&set.len(), encoder)?;
314        for item in set {
315            T::encode(item, encoder)?;
316        }
317        Ok(())
318    }
319
320    pub fn decode<Context, D, T, S>(decoder: &mut D) -> Result<IndexSet<T, S>, DecodeError>
321    where
322        D: Decoder<Context = Context>,
323        T: Decode<Context> + Eq + Hash,
324        S: BuildHasher + Default,
325    {
326        let len = usize::decode(decoder)?;
327        let mut set = IndexSet::with_capacity_and_hasher(len, Default::default());
328        for _i in 0..len {
329            set.insert(T::decode(decoder)?);
330        }
331        Ok(set)
332    }
333
334    pub fn borrow_decode<'de, Context, D, T, S>(
335        decoder: &mut D,
336    ) -> Result<IndexSet<T, S>, DecodeError>
337    where
338        D: BorrowDecoder<'de, Context = Context>,
339        T: BorrowDecode<'de, Context> + Eq + Hash,
340        S: BuildHasher + Default,
341    {
342        let len = usize::decode(decoder)?;
343        let mut set = IndexSet::with_capacity_and_hasher(len, Default::default());
344        for _i in 0..len {
345            set.insert(T::borrow_decode(decoder)?);
346        }
347        Ok(set)
348    }
349
350    #[cfg(test)]
351    mod tests {
352        use bincode::{decode_from_slice, encode_to_vec};
353
354        use super::*;
355
356        #[test]
357        fn test_roundtrip() {
358            let cfg = bincode::config::standard();
359
360            #[derive(Encode, Decode)]
361            struct Wrapper(#[bincode(with = "crate::indexset")] IndexSet<String>);
362
363            let set1 = Wrapper(IndexSet::from([
364                "value1".to_string(),
365                "value2".to_string(),
366                "value3".to_string(),
367            ]));
368
369            let set2: Wrapper = decode_from_slice(&encode_to_vec(&set1, cfg).unwrap(), cfg)
370                .unwrap()
371                .0;
372
373            assert_eq!(set1.0, set2.0);
374        }
375    }
376}
377
378pub mod mime_option {
379    use std::str::FromStr;
380
381    use mime::Mime;
382
383    use super::*;
384
385    pub fn encode<E: Encoder>(mime: &Option<Mime>, encoder: &mut E) -> Result<(), EncodeError> {
386        let mime_str: Option<&str> = mime.as_ref().map(AsRef::as_ref);
387        Encode::encode(&mime_str, encoder)
388    }
389
390    pub fn decode<Context, D: Decoder<Context = Context>>(
391        decoder: &mut D,
392    ) -> Result<Option<Mime>, DecodeError> {
393        if let Some(mime_str) = <Option<String> as Decode<Context>>::decode(decoder)? {
394            Ok(Some(
395                Mime::from_str(&mime_str).map_err(|e| DecodeError::OtherString(e.to_string()))?,
396            ))
397        } else {
398            Ok(None)
399        }
400    }
401
402    pub fn borrow_decode<'de, Context, D: BorrowDecoder<'de, Context = Context>>(
403        decoder: &mut D,
404    ) -> Result<Option<Mime>, DecodeError> {
405        decode(decoder)
406    }
407
408    #[cfg(test)]
409    mod tests {
410        use bincode::{decode_from_slice, encode_to_vec};
411
412        use super::*;
413
414        #[derive(Encode, Decode)]
415        struct Wrapper(#[bincode(with = "crate::mime_option")] Option<Mime>);
416
417        #[test]
418        fn test_roundtrip() {
419            let cfg = bincode::config::standard();
420
421            let mime1 = Wrapper(Some("text/html; charset=utf-8".parse().unwrap()));
422
423            let mime2: Wrapper = decode_from_slice(&encode_to_vec(&mime1, cfg).unwrap(), cfg)
424                .unwrap()
425                .0;
426
427            assert_eq!(mime1.0, mime2.0);
428        }
429
430        #[test]
431        fn test_roundtrip_none() {
432            let cfg = bincode::config::standard();
433
434            let mime1 = Wrapper(None);
435
436            let mime2: Wrapper = decode_from_slice(&encode_to_vec(&mime1, cfg).unwrap(), cfg)
437                .unwrap()
438                .0;
439
440            assert_eq!(mime1.0, mime2.0);
441        }
442    }
443}
444
445pub mod either {
446    use ::either::Either;
447
448    use super::*;
449
450    pub fn encode<E: Encoder, L: Encode, R: Encode>(
451        value: &Either<L, R>,
452        encoder: &mut E,
453    ) -> Result<(), EncodeError> {
454        value.is_left().encode(encoder)?;
455        ::either::for_both!(value, v => Encode::encode(v, encoder))
456    }
457
458    pub fn decode<
459        Context,
460        D: Decoder<Context = Context>,
461        L: Decode<Context>,
462        R: Decode<Context>,
463    >(
464        decoder: &mut D,
465    ) -> Result<Either<L, R>, DecodeError> {
466        let is_left = bool::decode(decoder)?;
467        Ok(if is_left {
468            Either::Left(L::decode(decoder)?)
469        } else {
470            Either::Right(R::decode(decoder)?)
471        })
472    }
473
474    pub fn borrow_decode<
475        'de,
476        Context,
477        D: BorrowDecoder<'de, Context = Context>,
478        L: BorrowDecode<'de, Context>,
479        R: BorrowDecode<'de, Context>,
480    >(
481        decoder: &mut D,
482    ) -> Result<Either<L, R>, DecodeError> {
483        let is_left = bool::borrow_decode(decoder)?;
484        Ok(if is_left {
485            Either::Left(L::borrow_decode(decoder)?)
486        } else {
487            Either::Right(R::borrow_decode(decoder)?)
488        })
489    }
490
491    #[cfg(test)]
492    mod tests {
493        use bincode::{decode_from_slice, encode_to_vec};
494
495        use super::*;
496
497        #[derive(Encode, Decode)]
498        struct Wrapper(#[bincode(with = "crate::either")] Either<String, u32>);
499
500        #[test]
501        fn test_roundtrip_left() {
502            let cfg = bincode::config::standard();
503
504            let either1 = Wrapper(Either::Left("hello".to_string()));
505
506            let either2: Wrapper = decode_from_slice(&encode_to_vec(&either1, cfg).unwrap(), cfg)
507                .unwrap()
508                .0;
509
510            assert_eq!(either1.0, either2.0);
511        }
512
513        #[test]
514        fn test_roundtrip_right() {
515            let cfg = bincode::config::standard();
516
517            let either1 = Wrapper(Either::Right(42u32));
518
519            let either2: Wrapper = decode_from_slice(&encode_to_vec(&either1, cfg).unwrap(), cfg)
520                .unwrap()
521                .0;
522
523            assert_eq!(either1.0, either2.0);
524        }
525    }
526}
527
528pub mod smallvec {
529    use ::smallvec::Array;
530
531    use super::*;
532
533    pub fn encode<E: Encoder, A: Array<Item = impl Encode>>(
534        vec: &SmallVec<A>,
535        encoder: &mut E,
536    ) -> Result<(), EncodeError> {
537        usize::encode(&vec.len(), encoder)?;
538        for item in vec {
539            Encode::encode(item, encoder)?;
540        }
541        Ok(())
542    }
543
544    pub fn decode<Context, D: Decoder<Context = Context>, A: Array<Item = impl Decode<Context>>>(
545        decoder: &mut D,
546    ) -> Result<SmallVec<A>, DecodeError> {
547        let len = usize::decode(decoder)?;
548        let mut vec = SmallVec::with_capacity(len);
549        for _ in 0..len {
550            vec.push(Decode::decode(decoder)?);
551        }
552        Ok(vec)
553    }
554
555    pub fn borrow_decode<
556        'de,
557        Context,
558        D: BorrowDecoder<'de, Context = Context>,
559        A: Array<Item = impl BorrowDecode<'de, Context>>,
560    >(
561        decoder: &mut D,
562    ) -> Result<SmallVec<A>, DecodeError> {
563        let len = usize::decode(decoder)?;
564        let mut vec = SmallVec::with_capacity(len);
565        for _ in 0..len {
566            vec.push(BorrowDecode::borrow_decode(decoder)?);
567        }
568        Ok(vec)
569    }
570
571    #[cfg(test)]
572    mod tests {
573        use bincode::{decode_from_slice, encode_to_vec};
574
575        use super::*;
576
577        #[test]
578        fn test_roundtrip() {
579            let cfg = bincode::config::standard();
580
581            #[derive(Encode, Decode)]
582            struct Wrapper(#[bincode(with = "crate::smallvec")] SmallVec<[u32; 4]>);
583
584            let vec1 = Wrapper(SmallVec::from_slice(&[1u32, 2, 3, 4, 5]));
585
586            let vec2: Wrapper = decode_from_slice(&encode_to_vec(&vec1, cfg).unwrap(), cfg)
587                .unwrap()
588                .0;
589
590            assert_eq!(vec1.0, vec2.0);
591        }
592    }
593}
594
595pub mod owned_cow {
596    //! Overrides the default [`BorrowDecode`] implementation to always use the owned representation
597    //! of [`Cow`], so that the resulting [`BorrowDecode`] type is independent of the [`Cow`]'s
598    //! lifetime.
599
600    use std::borrow::Cow;
601
602    use super::*;
603
604    #[allow(clippy::ptr_arg)]
605    pub fn encode<E, T>(cow: &Cow<'_, T>, encoder: &mut E) -> Result<(), EncodeError>
606    where
607        E: Encoder,
608        T: ToOwned + ?Sized,
609        for<'a> &'a T: Encode,
610    {
611        cow.encode(encoder)
612    }
613
614    pub fn decode<'cow, Context, D, T>(decoder: &mut D) -> Result<Cow<'cow, T>, DecodeError>
615    where
616        D: Decoder<Context = Context>,
617        T: ToOwned + ?Sized,
618        <T as ToOwned>::Owned: Decode<Context>,
619    {
620        Decode::decode(decoder)
621    }
622
623    pub fn borrow_decode<'de, 'cow, Context, D, T>(
624        decoder: &mut D,
625    ) -> Result<Cow<'cow, T>, DecodeError>
626    where
627        D: BorrowDecoder<'de, Context = Context>,
628        T: ToOwned + ?Sized,
629        <T as ToOwned>::Owned: Decode<Context>,
630    {
631        Decode::decode(decoder)
632    }
633}