neco-car 0.1.1

necosystems series CAR v1 parser and writer for content-addressable archives
Documentation
use neco_cbor::CborValue;
use neco_cid::Cid;

use crate::error::CarError;

pub fn write_v1(roots: &[Cid], blocks: &[(Cid, &[u8])]) -> Result<Vec<u8>, CarError> {
    let root_values: Vec<CborValue> = roots
        .iter()
        .map(|cid| {
            let cid_bytes = cid.to_bytes();
            let mut payload = Vec::with_capacity(1 + cid_bytes.len());
            payload.push(0x00);
            payload.extend_from_slice(&cid_bytes);
            CborValue::Tag(42, Box::new(CborValue::Bytes(payload)))
        })
        .collect();

    let header = CborValue::Map(vec![
        (
            CborValue::Text("roots".into()),
            CborValue::Array(root_values),
        ),
        (CborValue::Text("version".into()), CborValue::Unsigned(1)),
    ]);

    let header_bytes = neco_cbor::encode_dag(&header).map_err(CarError::HeaderEncode)?;

    let mut output = Vec::new();
    encode_varint(header_bytes.len() as u64, &mut output);
    output.extend_from_slice(&header_bytes);

    for (cid, data) in blocks {
        let cid_bytes = cid.to_bytes();
        let section_len = cid_bytes.len() + data.len();
        encode_varint(section_len as u64, &mut output);
        output.extend_from_slice(&cid_bytes);
        output.extend_from_slice(data);
    }

    Ok(output)
}

fn encode_varint(mut value: u64, out: &mut Vec<u8>) {
    loop {
        let lower = (value & 0x7f) as u8;
        value >>= 7;
        if value == 0 {
            out.push(lower);
            return;
        }
        out.push(lower | 0x80);
    }
}