ipfs_unixfs/dir/builder/
custom_pb.rs

1//! Custom protobuf types which are used in encoding directorys.
2
3use super::NamedLeaf;
4use crate::pb::UnixFs;
5use cid::Cid;
6use quick_protobuf::{MessageWrite, Writer, WriterBackend};
7
8/// Newtype which uses the &[Option<(NamedLeaf)>] as Vec<PBLink>.
9pub(super) struct CustomFlatUnixFs<'a> {
10    pub(super) links: &'a [Option<NamedLeaf>],
11    pub(super) data: UnixFs<'a>,
12}
13
14impl<'a> CustomFlatUnixFs<'a> {
15    fn mapped(&self) -> impl Iterator<Item = NamedLeafAsPBLink<'_>> + '_ {
16        self.links
17            .iter()
18            .map(|triple| triple.as_ref().map(|l| NamedLeafAsPBLink(l)).unwrap())
19    }
20}
21
22impl<'a> MessageWrite for CustomFlatUnixFs<'a> {
23    fn get_size(&self) -> usize {
24        use quick_protobuf::sizeofs::*;
25
26        let links = self
27            .mapped()
28            .map(|link| 1 + sizeof_len(link.get_size()))
29            .sum::<usize>();
30
31        links + 1 + sizeof_len(self.data.get_size())
32    }
33
34    fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> quick_protobuf::Result<()> {
35        self.mapped()
36            .try_for_each(|l| w.write_with_tag(18, |w| w.write_message(&l)))?;
37        w.write_with_tag(10, |w| w.write_message(&self.data))
38    }
39}
40
41/// Custom NamedLeaf as PBLink "adapter."
42struct NamedLeafAsPBLink<'a>(&'a NamedLeaf);
43
44impl<'a> MessageWrite for NamedLeafAsPBLink<'a> {
45    fn get_size(&self) -> usize {
46        use quick_protobuf::sizeofs::*;
47
48        // ones are the tags
49        1 + sizeof_len((self.0).0.len())
50            + 1
51            + sizeof_len(WriteableCid(&(self.0).1).get_size())
52            //+ sizeof_len(self.1.link.to_bytes().len())
53            + 1
54            + sizeof_varint((self.0).2)
55    }
56
57    fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> quick_protobuf::Result<()> {
58        w.write_with_tag(10, |w| w.write_message(&WriteableCid(&(self.0).1)))?;
59        //w.write_with_tag(10, |w| w.write_bytes(&self.1.link.to_bytes()))?;
60        w.write_with_tag(18, |w| w.write_string((self.0).0.as_str()))?;
61        w.write_with_tag(24, |w| w.write_uint64((self.0).2))?;
62        Ok(())
63    }
64}
65
66/// Newtype around Cid to allow embedding it as PBLink::Hash without allocating a vector.
67struct WriteableCid<'a>(&'a Cid);
68
69impl<'a> MessageWrite for WriteableCid<'a> {
70    fn get_size(&self) -> usize {
71        use cid::Version::*;
72        use quick_protobuf::sizeofs::*;
73
74        let hash_len = self.0.hash().as_bytes().len();
75
76        match self.0.version() {
77            V0 => hash_len,
78            V1 => {
79                let version_len = 1;
80                let codec_len = sizeof_varint(u64::from(self.0.codec()));
81                version_len + codec_len + hash_len
82            }
83        }
84    }
85
86    fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> quick_protobuf::Result<()> {
87        use cid::Version::*;
88
89        match self.0.version() {
90            V0 => { /* cidv0 has only the _multi_hash */ }
91            V1 => {
92                // it is possible that CidV1 should not be linked to from a unixfs
93                // directory; at least go-ipfs 0.5 `ipfs files` denies making a cbor link
94                // but happily accepts and does refs over one.
95                w.write_u8(1)?;
96                w.write_varint(u64::from(self.0.codec()))?;
97            }
98        }
99
100        self.0
101            .hash()
102            .as_bytes()
103            .iter()
104            // while this looks bad it cannot be measured; note we cannot use the
105            // write_bytes because that is length prefixed bytes write
106            .try_for_each(|b| w.write_u8(*b))
107    }
108}