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 implement From<X> for Bytes {}
52/// Unfortunately, we cannot just use the more generic `Into<Bytes>` without
53/// running afoul of the `From<X> for X` base case, causing conflicting impls.
54pub trait IntoBytes: Into<CBytes> {}
55impl IntoBytes for &'static [u8] {}
56impl IntoBytes for &'static str {}
57impl IntoBytes for Vec<u8> {}
58impl IntoBytes for Box<[u8]> {}
59impl IntoBytes for String {}
60
61impl<T: IntoBytes> From<T> for Bytes {
62    fn from(value: T) -> Self {
63        Bytes(value.into())
64    }
65}
66
67impl From<CBytes> for Bytes {
68    fn from(value: CBytes) -> Self {
69        Bytes(value)
70    }
71}
72
73impl From<Bytes> for CBytes {
74    fn from(value: Bytes) -> Self {
75        value.0
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    impl PartialEq<&str> for Bytes {
84        fn eq(&self, other: &&str) -> bool {
85            self.0 == other
86        }
87    }
88
89    #[test]
90    fn into_bytes() {
91        let s = "foo".to_string();
92        assert_eq!(Bytes::from(b"foo" as &'static [u8]), "foo");
93        assert_eq!(Bytes::from("foo"), "foo");
94        assert_eq!(Bytes::from(s.as_bytes().to_vec()), "foo");
95        assert_eq!(Bytes::from(s.as_bytes().to_vec().into_boxed_slice()), "foo");
96        assert_eq!(Bytes::from(s), "foo");
97    }
98
99    #[test]
100    fn bincode() {
101        let s = Bytes::from("test");
102        let c = bincode::config::standard();
103        let decoded: Bytes = bincode::decode_from_slice(&bincode::encode_to_vec(&s, c).unwrap(), c)
104            .unwrap()
105            .0;
106        assert_eq!(decoded, s);
107    }
108
109    #[test]
110    fn from_into() {
111        let b = Bytes::from("foo");
112        let cb = CBytes::from("foo");
113        assert_eq!(Bytes::from(cb), "foo");
114        assert_eq!(CBytes::from(b), "foo");
115    }
116
117    #[test]
118    fn deref() {
119        let b = Bytes::from("foo");
120        assert_eq!(*b, CBytes::from("foo"));
121    }
122
123    #[test]
124    fn to_str() {
125        let cb = Bytes::from("foo");
126        assert_eq!(cb.to_str(), Ok("foo"));
127
128        let b = Bytes::from("💩".as_bytes()[0..3].to_vec());
129        assert!(b.to_str().is_err());
130    }
131}