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;
89/// 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> {
11let doc = PBNode::try_from(block)?;
12Ok(match doc.Data {
13Some(Cow::Borrowed(slice)) => Some(slice),
14Some(Cow::Owned(_)) => unreachable!("never converted to owned"),
15None => None,
16 })
17}
1819/// 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
23T: AsRef<[u8]>,
24{
25let full = block.as_ref();
26let range = node_data(full)?
27.map(|data| subslice_to_range(full, data).expect("this has to be in range"));
28Ok(NodeData {
29 inner: block,
30 range,
31 })
32}
3334fn subslice_to_range(full: &[u8], sub: &[u8]) -> Option<Range<usize>> {
35// note this doesn't work for all types, for example () or similar ZSTs.
3637let max = full.len();
38let amt = sub.len();
3940if max < amt {
41// if the latter slice is larger than the first one, surely it isn't a subslice.
42return None;
43 }
4445let full = full.as_ptr() as usize;
46let sub = sub.as_ptr() as usize;
4748 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 continious 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}
5657/// The wrapper returned from [`wrap_node_data`], allows accessing dag-pb nodes Data.
58#[derive(PartialEq)]
59pub struct NodeData<T> {
60 inner: T,
61 range: Option<Range<usize>>,
62}
6364impl<T: AsRef<[u8]>> fmt::Debug for NodeData<T> {
65fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
66write!(
67 fmt,
68"NodeData<{}> {{ inner.len: {}, range: {:?} }}",
69 std::any::type_name::<T>(),
70self.inner.as_ref().len(),
71self.range
72 )
73 }
74}
7576impl<T: AsRef<[u8]>> NodeData<T> {
77/// Returns the dag-pb nodes Data field as slice
78pub fn node_data(&self) -> &[u8] {
79if let Some(range) = self.range.as_ref() {
80&self.inner.as_ref()[range.clone()]
81 } else {
82&[][..]
83 }
84 }
8586/// Returns access to the wrapped block representation
87pub fn get_ref(&self) -> &T {
88&self.inner
89 }
9091/// Consumes self and returns the block representation
92pub fn into_inner(self) -> T {
93self.inner
94 }
95}
9697impl<T: AsRef<[u8]>, B: AsRef<[u8]>> PartialEq<B> for NodeData<T> {
98fn eq(&self, other: &B) -> bool {
99self.node_data() == other.as_ref()
100 }
101}
102103#[cfg(test)]
104mod tests {
105use super::subslice_to_range;
106107#[test]
108fn subslice_ranges() {
109let full = &b"01234"[..];
110111for start in 0..(full.len() - 1) {
112for end in start..(full.len() - 1) {
113let sub = &full[start..end];
114assert_eq!(subslice_to_range(full, sub), Some(start..end));
115 }
116 }
117 }
118119#[test]
120fn 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.
124125let full = &b"0123456789"[..];
126127let a = &full[0..4];
128let b = &full[4..];
129130let a_sub = &a[1..3];
131let b_sub = &b[0..2];
132133assert_eq!(subslice_to_range(a, a_sub), Some(1..3));
134assert_eq!(subslice_to_range(b, b_sub), Some(0..2));
135136assert_eq!(subslice_to_range(a, b_sub), None);
137assert_eq!(subslice_to_range(b, a_sub), None);
138 }
139}