1use cid::Cid;
2use ipld::raw::RawCodec;
3
4mod reader_v1;
5use crate::{error::CarError, header::CarHeader, section::Section, unixfs::UnixFs, Ipld};
6use integer_encoding::VarIntReader;
7use std::{
8 collections::VecDeque,
9 io::{self, Read, Seek},
10};
11
12pub(crate) use reader_v1::CarReaderV1;
13
14const MAX_ALLOWED_SECTION_SIZE: usize = 32 << 20;
15
16pub fn read_block<R>(mut reader: R) -> Result<Option<Vec<u8>>, CarError>
17where
18 R: std::io::Read,
19{
20 let l: usize = match reader.read_varint() {
21 Ok(i) => i,
22 Err(e) => {
23 if e.kind() == std::io::ErrorKind::UnexpectedEof {
24 return Ok(None);
25 }
26 return Err(CarError::IO(e));
27 }
28 };
29 if l > MAX_ALLOWED_SECTION_SIZE {
30 return Err(CarError::TooLargeSection(l));
31 }
32 let mut data = vec![0u8; l];
33 reader.read_exact(&mut data[..])?;
34 Ok(Some(data))
35}
36
37pub(crate) fn read_section<R>(mut reader: R) -> Result<Option<Section>, CarError>
38where
39 R: io::Read + io::Seek,
40{
41 let len: usize = match reader.read_varint() {
42 Ok(i) => i,
43 Err(e) => {
44 if e.kind() == io::ErrorKind::UnexpectedEof {
45 return Ok(None);
46 }
47 return Err(CarError::IO(e));
48 }
49 };
50 let start = reader.stream_position()?;
51 if len > MAX_ALLOWED_SECTION_SIZE {
52 return Err(CarError::TooLargeSection(len));
53 }
54 let cid = Cid::read_bytes(&mut reader).map_err(|e| CarError::Parsing(e.to_string()))?;
55 let pos = reader.stream_position()?;
56 let l = len - ((pos - start) as usize);
57 reader.seek(io::SeekFrom::Current(l as _))?;
58 Ok(Some(Section::new(cid, pos, l)))
59}
60
61pub trait CarReader {
62 fn header(&self) -> &CarHeader;
63
64 fn sections(&self) -> Vec<Section>;
65
66 fn read_section_data(&mut self, cid: &Cid) -> Result<Vec<u8>, CarError>;
67
68 fn ipld(&mut self, cid: &Cid) -> Result<Ipld, CarError>;
69
70 #[inline(always)]
71 fn unixfs(&mut self, cid: &Cid) -> Result<UnixFs, CarError> {
72 let fs_ipld = self.ipld(cid)?;
73 (*cid, fs_ipld).try_into()
74 }
75
76 fn search_file_cid_inner(
77 &mut self,
78 searchq: &mut VecDeque<Cid>,
79 f: &str,
80 ) -> Result<Cid, CarError> {
81 let raw_code: u64 = RawCodec.into();
82 while let Some(root_cid) = searchq.pop_front() {
83 let codec = root_cid.codec();
84 if codec == raw_code {
85 continue;
86 }
87 let fs_ipld = self.ipld(&root_cid)?;
88 if matches!(fs_ipld, Ipld::Map(_)) {
89 let unixfs: UnixFs = (root_cid, fs_ipld).try_into()?;
90 for ufs in unixfs.links() {
91 if ufs.name == f {
92 return Ok(ufs.hash);
93 }
94 searchq.push_back(ufs.hash);
95 }
96 }
97 }
98 Err(CarError::NotFound(format!("search {f} fail.")))
99 }
100
101 #[inline]
102 fn search_file_cid(&mut self, f: &str) -> Result<Cid, CarError> {
103 let roots = self.header().roots();
104 let mut searchq = VecDeque::new();
105 for root in roots.into_iter() {
106 searchq.push_back(root);
107 match self.search_file_cid_inner(&mut searchq, f) {
108 Ok(o) => return Ok(o),
109 Err(CarError::NotFound(_)) => continue,
110 Err(e) => return Err(e),
111 }
112 }
113 Err(CarError::NotFound(format!("search {f} fail.")))
114 }
115}
116
117#[inline(always)]
118pub fn new_v1<R>(inner: R) -> Result<impl CarReader, CarError>
119where
120 R: Read + Seek,
121{
122 CarReaderV1::new(inner)
123}