use std::io::{self, Read};
use crate::cbor::{Cid, Decoder, Value};
use crate::car::{Block, CarError};
const MAX_HEADER_SIZE: u64 = 1 << 20;
const MAX_BLOCK_SIZE: u64 = 128 << 20;
pub struct Reader<R: Read> {
reader: R,
roots: Vec<Cid>,
}
impl<R: Read> Reader<R> {
pub fn new(mut reader: R) -> Result<Self, CarError> {
let header_len = read_varint(&mut reader)?;
if header_len > MAX_HEADER_SIZE {
return Err(CarError::InvalidHeader(format!(
"header length {header_len} exceeds maximum of {MAX_HEADER_SIZE}"
)));
}
let header_len_usize = header_len as usize;
let mut header_buf = vec![0u8; header_len_usize];
reader.read_exact(&mut header_buf).map_err(|e| {
if e.kind() == io::ErrorKind::UnexpectedEof {
CarError::InvalidHeader("truncated header".into())
} else {
CarError::Io(e)
}
})?;
let mut dec = Decoder::new(&header_buf);
let val = dec.decode()?;
let entries = match val {
Value::Map(entries) => entries,
_ => return Err(CarError::InvalidHeader("header must be a CBOR map".into())),
};
let mut version: Option<u64> = None;
let mut roots: Option<Vec<Cid>> = None;
for (key, value) in entries {
match key {
"version" => {
let v = match value {
Value::Unsigned(n) => n,
_ => {
return Err(CarError::InvalidHeader(
"version must be an integer".into(),
));
}
};
version = Some(v);
}
"roots" => {
let items = match value {
Value::Array(items) => items,
_ => return Err(CarError::InvalidHeader("roots must be an array".into())),
};
let mut cids = Vec::with_capacity(items.len());
for item in items {
match item {
Value::Cid(c) => cids.push(c),
_ => {
return Err(CarError::InvalidHeader(
"roots must contain CIDs".into(),
));
}
}
}
roots = Some(cids);
}
_ => {
}
}
}
let version =
version.ok_or_else(|| CarError::InvalidHeader("missing 'version' field".into()))?;
if version != 1 {
return Err(CarError::InvalidHeader(format!(
"unsupported version {version}, expected 1"
)));
}
let roots = roots.ok_or_else(|| CarError::InvalidHeader("missing 'roots' field".into()))?;
Ok(Reader { reader, roots })
}
pub fn roots(&self) -> &[Cid] {
&self.roots
}
pub fn next_block(&mut self) -> Result<Option<Block>, CarError> {
let mut block = Block::default();
match self.next_block_into(&mut block)? {
true => Ok(Some(block)),
false => Ok(None),
}
}
pub fn next_block_into(&mut self, block: &mut Block) -> Result<bool, CarError> {
let block_len = match read_varint_eof(&mut self.reader)? {
Some(v) => v,
None => return Ok(false),
};
if block_len == 0 {
return Err(CarError::InvalidBlock("zero-length block".into()));
}
if block_len > MAX_BLOCK_SIZE {
return Err(CarError::InvalidBlock(format!(
"block length {block_len} exceeds maximum of {MAX_BLOCK_SIZE}"
)));
}
let block_len_usize = block_len as usize;
if block_len_usize < 36 {
return Err(CarError::InvalidBlock(
"block too short to contain CID".into(),
));
}
let mut cid_buf = [0u8; 36];
self.reader.read_exact(&mut cid_buf).map_err(|e| {
if e.kind() == io::ErrorKind::UnexpectedEof {
CarError::InvalidBlock("truncated block data".into())
} else {
CarError::Io(e)
}
})?;
block.cid = Cid::from_bytes(&cid_buf)?;
let data_len = block_len_usize - 36;
block.data.resize(data_len, 0);
self.reader.read_exact(&mut block.data).map_err(|e| {
if e.kind() == io::ErrorKind::UnexpectedEof {
CarError::InvalidBlock("truncated block data".into())
} else {
CarError::Io(e)
}
})?;
Ok(true)
}
}
fn read_varint<R: Read>(reader: &mut R) -> Result<u64, CarError> {
match read_varint_eof(reader)? {
Some(v) => Ok(v),
None => Err(CarError::InvalidHeader(
"unexpected EOF reading varint".into(),
)),
}
}
fn read_varint_eof<R: Read>(reader: &mut R) -> Result<Option<u64>, CarError> {
let mut buf = [0u8; 1];
match reader.read(&mut buf) {
Ok(0) => return Ok(None),
Ok(_) => {}
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => return Ok(None),
Err(e) => return Err(CarError::Io(e)),
}
let first = buf[0];
if first & 0x80 == 0 {
return Ok(Some(first as u64));
}
let mut value: u64 = (first & 0x7F) as u64;
let mut shift = 7u32;
for _ in 1..10 {
match reader.read(&mut buf) {
Ok(0) => return Err(CarError::InvalidBlock("truncated varint".into())),
Ok(_) => {}
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => {
return Err(CarError::InvalidBlock("truncated varint".into()));
}
Err(e) => return Err(CarError::Io(e)),
}
let byte = buf[0];
value |= ((byte & 0x7F) as u64) << shift;
if byte & 0x80 == 0 {
return Ok(Some(value));
}
shift += 7;
if shift >= 64 {
return Err(CarError::InvalidBlock("varint too long".into()));
}
}
Err(CarError::InvalidBlock("varint too long".into()))
}