rs_car_sync/
car_header.rs

1use std::io::Read;
2
3use crate::{
4    carv1_header::{decode_carv1_header, CarV1Header},
5    carv2_header::{decode_carv2_header, CarV2Header, CARV2_HEADER_SIZE, CARV2_PRAGMA_SIZE},
6    error::CarDecodeError,
7    varint::read_varint_u64,
8    Cid,
9};
10
11/// Arbitrary high value to prevent big allocations
12const MAX_HEADER_LEN: u64 = 1048576;
13/// Arbitrary high value to prevent big allocations
14const MAX_PADDING_LEN: usize = 1073741824;
15
16#[derive(Debug, PartialEq)]
17pub(crate) enum StreamEnd {
18    AfterNBytes(usize),
19    OnBlockEOF,
20}
21
22#[derive(Debug, PartialEq, Clone, Copy)]
23pub enum CarVersion {
24    V1 = 1,
25    V2 = 2,
26}
27
28#[derive(Debug)]
29pub struct CarHeader {
30    pub version: CarVersion,
31    pub roots: Vec<Cid>,
32    pub characteristics_v2: Option<u128>,
33    pub(crate) eof_stream: StreamEnd,
34}
35
36impl CarHeader {}
37
38pub(crate) fn read_car_header<R: Read>(r: &mut R) -> Result<CarHeader, CarDecodeError> {
39    let (header, _) = read_carv1_header(r)?;
40
41    match header.version {
42        1 => Ok(CarHeader {
43            version: CarVersion::V1,
44            roots: header.roots.ok_or(CarDecodeError::InvalidCarV1Header(
45                "v1 header has not roots".to_owned(),
46            ))?,
47            characteristics_v2: None,
48            eof_stream: StreamEnd::OnBlockEOF,
49        }),
50        2 => {
51            let (header_v2, (header_v1, header_v1_len)) = read_carv2_header(r)?;
52            let blocks_len = header_v2.data_size as usize - header_v1_len;
53            Ok(CarHeader {
54                version: CarVersion::V2,
55                roots: header_v1.roots.ok_or(CarDecodeError::InvalidCarV1Header(
56                    "v1 header has not roots".to_owned(),
57                ))?,
58                characteristics_v2: Some(header_v2.characteristics),
59                eof_stream: StreamEnd::AfterNBytes(blocks_len),
60            })
61        }
62        _ => Err(CarDecodeError::UnsupportedCarVersion {
63            version: header.version,
64        }),
65    }
66}
67
68/// # Returns
69///
70/// (header, total header byte length including varint)
71fn read_carv1_header<R: Read>(src: &mut R) -> Result<(CarV1Header, usize), CarDecodeError> {
72    // Decode header varint
73    let (header_len, varint_len) = read_varint_u64(src)?.ok_or(
74        CarDecodeError::InvalidCarV1Header("invalid header varint".to_string()),
75    )?;
76
77    if header_len > MAX_HEADER_LEN {
78        return Err(CarDecodeError::InvalidCarV1Header(format!(
79            "header len too big {}",
80            header_len
81        )));
82    }
83
84    let mut header_buf = vec![0u8; header_len as usize];
85    src.read_exact(&mut header_buf)?;
86
87    let header = decode_carv1_header(&header_buf)?;
88
89    Ok((header, header_len as usize + varint_len))
90}
91
92fn read_carv2_header<R: Read>(
93    r: &mut R,
94) -> Result<(CarV2Header, (CarV1Header, usize)), CarDecodeError> {
95    let mut header_buf = [0u8; CARV2_HEADER_SIZE];
96    r.read_exact(&mut header_buf)?;
97
98    let header_v2 = decode_carv2_header(&header_buf)?;
99
100    // Read padding, and throw away
101    let padding_len = header_v2.data_offset as usize - CARV2_PRAGMA_SIZE - CARV2_HEADER_SIZE;
102    if padding_len > 0 {
103        if padding_len > MAX_PADDING_LEN {
104            return Err(CarDecodeError::InvalidCarV1Header(format!(
105                "padding len too big {}",
106                padding_len
107            )));
108        }
109        let mut padding_buf = vec![0u8; padding_len];
110        r.read_exact(&mut padding_buf)?;
111    }
112
113    // Read inner CARv1 header
114    let header_v1 = read_carv1_header(r)?;
115
116    Ok((header_v2, header_v1))
117}
118
119#[cfg(test)]
120mod tests {
121
122    use std::io::Cursor;
123
124    use super::*;
125    use crate::{
126        carv1_header::CarV1Header,
127        carv2_header::{CARV2_PRAGMA, CARV2_PRAGMA_SIZE},
128    };
129
130    #[test]
131    fn read_carv1_header_v2_pragma() {
132        assert_eq!(
133            read_carv1_header(&mut Cursor::new(&CARV2_PRAGMA)).unwrap(),
134            (
135                CarV1Header {
136                    version: 2,
137                    roots: None
138                },
139                CARV2_PRAGMA_SIZE
140            )
141        )
142    }
143}