Skip to main content

turbo_tasks_bytes/
bytes.rs

1use std::{
2    ops::Deref,
3    str::{Utf8Error, from_utf8},
4};
5
6use anyhow::Result;
7use bincode::{
8    Decode, Encode,
9    de::Decoder,
10    enc::Encoder,
11    error::{DecodeError, EncodeError},
12    impl_borrow_decode,
13};
14use bytes::Bytes as CBytes;
15
16/// Bytes is a thin wrapper around [bytes::Bytes], implementing easy
17/// conversion to/from, bincode support, and Vc containers.
18#[derive(Clone, Debug, Default)]
19#[turbo_tasks::value(transparent, serialization = "custom")]
20pub struct Bytes(#[turbo_tasks(trace_ignore)] CBytes);
21
22impl Bytes {
23    pub fn to_str(&self) -> Result<&'_ str, Utf8Error> {
24        from_utf8(&self.0)
25    }
26}
27
28impl Encode for Bytes {
29    fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
30        self[..].encode(encoder)
31    }
32}
33
34impl<Context> Decode<Context> for Bytes {
35    fn decode<D: Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> {
36        // bincode uses the same encoding for slices and vecs
37        // https://docs.rs/bincode/latest/bincode/spec/index.html#linear-collections-vec-arrays-etc
38        Ok(Bytes(CBytes::from(Vec::<u8>::decode(decoder)?)))
39    }
40}
41
42impl_borrow_decode!(Bytes);
43
44impl Deref for Bytes {
45    type Target = CBytes;
46    fn deref(&self) -> &Self::Target {
47        &self.0
48    }
49}
50
51/// Types that `impl From<CustomType> for Bytes {}`
52///
53/// Unfortunately, we cannot just use the more generic `Into<Bytes>` without running afoul of the
54/// `From<X> for X` base case, causing conflicting impls.
55pub trait IntoBytes: Into<CBytes> {}
56impl IntoBytes for &'static [u8] {}
57impl IntoBytes for &'static str {}
58impl IntoBytes for Vec<u8> {}
59impl IntoBytes for Box<[u8]> {}
60impl IntoBytes for String {}
61
62impl<T: IntoBytes> From<T> for Bytes {
63    fn from(value: T) -> Self {
64        Bytes(value.into())
65    }
66}
67
68impl From<CBytes> for Bytes {
69    fn from(value: CBytes) -> Self {
70        Bytes(value)
71    }
72}
73
74impl From<Bytes> for CBytes {
75    fn from(value: Bytes) -> Self {
76        value.0
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83
84    impl PartialEq<&str> for Bytes {
85        fn eq(&self, other: &&str) -> bool {
86            self.0 == other
87        }
88    }
89
90    #[test]
91    fn into_bytes() {
92        let s = "foo".to_string();
93        assert_eq!(Bytes::from(b"foo" as &'static [u8]), "foo");
94        assert_eq!(Bytes::from("foo"), "foo");
95        assert_eq!(Bytes::from(s.as_bytes().to_vec()), "foo");
96        assert_eq!(Bytes::from(s.as_bytes().to_vec().into_boxed_slice()), "foo");
97        assert_eq!(Bytes::from(s), "foo");
98    }
99
100    #[test]
101    fn bincode() {
102        let s = Bytes::from("test");
103        let c = bincode::config::standard();
104        let decoded: Bytes = bincode::decode_from_slice(&bincode::encode_to_vec(&s, c).unwrap(), c)
105            .unwrap()
106            .0;
107        assert_eq!(decoded, s);
108    }
109
110    #[test]
111    fn from_into() {
112        let b = Bytes::from("foo");
113        let cb = CBytes::from("foo");
114        assert_eq!(Bytes::from(cb), "foo");
115        assert_eq!(CBytes::from(b), "foo");
116    }
117
118    #[test]
119    fn deref() {
120        let b = Bytes::from("foo");
121        assert_eq!(*b, CBytes::from("foo"));
122    }
123
124    #[test]
125    fn to_str() {
126        let cb = Bytes::from("foo");
127        assert_eq!(cb.to_str(), Ok("foo"));
128
129        let b = Bytes::from("💩".as_bytes()[0..3].to_vec());
130        assert!(b.to_str().is_err());
131    }
132}