rust_unixfs/
dagpb.rs

1//! dag-pb support operations. Placing this module inside unixfs module is a bit unfortunate but
2//! follows from the inseparability of dag-pb and UnixFS.
3use crate::pb::PBNode;
4use alloc::borrow::Cow;
5use core::convert::TryFrom;
6use core::fmt;
7use core::ops::Range;
8
9/// Extracts the PBNode::Data field from the block as it appears on the block.
10pub fn node_data(block: &[u8]) -> Result<Option<&[u8]>, quick_protobuf::Error> {
11    let doc = PBNode::try_from(block)?;
12    Ok(match doc.Data {
13        Some(Cow::Borrowed(slice)) => Some(slice),
14        Some(Cow::Owned(_)) => unreachable!("never converted to owned"),
15        None => None,
16    })
17}
18
19/// Creates a wrapper around the given block representation which does not consume the block
20/// representation but allows accessing the dag-pb node Data.
21pub fn wrap_node_data<T>(block: T) -> Result<NodeData<T>, quick_protobuf::Error>
22where
23    T: AsRef<[u8]>,
24{
25    let full = block.as_ref();
26    let range = node_data(full)?
27        .map(|data| subslice_to_range(full, data).expect("this has to be in range"));
28    Ok(NodeData {
29        inner: block,
30        range,
31    })
32}
33
34fn subslice_to_range(full: &[u8], sub: &[u8]) -> Option<Range<usize>> {
35    // note this doesn't work for all types, for example () or similar ZSTs.
36
37    let max = full.len();
38    let amt = sub.len();
39
40    if max < amt {
41        // if the latter slice is larger than the first one, surely it isn't a subslice.
42        return None;
43    }
44
45    let full = full.as_ptr() as usize;
46    let sub = sub.as_ptr() as usize;
47
48    sub.checked_sub(full)
49        // not needed as it would divide by one: .map(|diff| diff / mem::size_of::<T>())
50        //
51        // if there are two slices of a continuous chunk, [A|B] we need to make sure B will not be
52        // calculated as subslice of A
53        .and_then(|start| if start >= max { None } else { Some(start) })
54        .map(|start| start..(start + amt))
55}
56
57/// The wrapper returned from [`wrap_node_data`], allows accessing dag-pb nodes Data.
58#[derive(PartialEq, Eq)]
59pub struct NodeData<T> {
60    inner: T,
61    range: Option<Range<usize>>,
62}
63
64impl<T: AsRef<[u8]>> fmt::Debug for NodeData<T> {
65    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
66        write!(
67            fmt,
68            "NodeData<{}> {{ inner.len: {}, range: {:?} }}",
69            std::any::type_name::<T>(),
70            self.inner.as_ref().len(),
71            self.range
72        )
73    }
74}
75
76impl<T: AsRef<[u8]>> NodeData<T> {
77    /// Returns the dag-pb nodes Data field as slice
78    pub fn node_data(&self) -> &[u8] {
79        if let Some(range) = self.range.as_ref() {
80            &self.inner.as_ref()[range.clone()]
81        } else {
82            &[][..]
83        }
84    }
85
86    /// Returns access to the wrapped block representation
87    pub fn get_ref(&self) -> &T {
88        &self.inner
89    }
90
91    /// Consumes self and returns the block representation
92    pub fn into_inner(self) -> T {
93        self.inner
94    }
95}
96
97impl<T: AsRef<[u8]>, B: AsRef<[u8]>> PartialEq<B> for NodeData<T> {
98    fn eq(&self, other: &B) -> bool {
99        self.node_data() == other.as_ref()
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::subslice_to_range;
106
107    #[test]
108    fn subslice_ranges() {
109        let full = &b"01234"[..];
110
111        for start in 0..(full.len() - 1) {
112            for end in start..(full.len() - 1) {
113                let sub = &full[start..end];
114                assert_eq!(subslice_to_range(full, sub), Some(start..end));
115            }
116        }
117    }
118
119    #[test]
120    fn not_in_following_subslice() {
121        // this could be done with two distinct/disjoint 'static slices but there might not be any
122        // guarantees it working in all rust released and unreleased versions, and with different
123        // linkers.
124
125        let full = &b"0123456789"[..];
126
127        let a = &full[0..4];
128        let b = &full[4..];
129
130        let a_sub = &a[1..3];
131        let b_sub = &b[0..2];
132
133        assert_eq!(subslice_to_range(a, a_sub), Some(1..3));
134        assert_eq!(subslice_to_range(b, b_sub), Some(0..2));
135
136        assert_eq!(subslice_to_range(a, b_sub), None);
137        assert_eq!(subslice_to_range(b, a_sub), None);
138    }
139}