Skip to content

Instantly share code, notes, and snippets.

@rklaehn
Created September 4, 2020 13:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rklaehn/62e21237e7ef8d868cfe655ad6209f28 to your computer and use it in GitHub Desktop.
Save rklaehn/62e21237e7ef8d868cfe655ad6209f28 to your computer and use it in GitHub Desktop.
//! Decoding of dag-cbor blobs
//!
//! So far this is just used for figuring out links from a block for pinning
use cbor_event::{de::Deserializer, Len, Special, Type};
use cid::Cid;
use derive_more::{Display, From};
use std::{collections::BTreeSet, convert::TryFrom, error, io::Cursor};
const CBOR_TAG_LINK: u64 = 42;
#[derive(Debug, Display, From)]
pub(crate) enum CborError {
/// Error while parsing CBOR using the cbor_event crate
CborEvent(cbor_event::Error),
/// Error parsing a CID
Cid(cid::Error),
}
impl error::Error for CborError {}
pub(crate) fn extract_cbor_links(data: &[u8], cids: &mut BTreeSet<Cid>) -> Result<(), CborError> {
let mut raw = Deserializer::from(Cursor::new(data));
extract_links(&mut raw, cids).map(|_| ())
}
fn extract_links(raw: &mut Deserializer<Cursor<&[u8]>>, cids: &mut BTreeSet<Cid>) -> Result<bool, CborError> {
// The is_break value is used to signal that we read the special end marker `Break` that is used
// to signal the end of an `Indefinite` length repeating structure like an `Array` or `Map` that
// doesn't have the length encoded up front.
let is_break = match raw.cbor_type()? {
Type::UnsignedInteger => {
raw.unsigned_integer()?;
false
}
Type::NegativeInteger => {
raw.negative_integer()?;
false
}
Type::Bytes => {
raw.bytes()?;
false
}
Type::Text => {
raw.text()?;
false
}
Type::Array => {
if let Len::Len(n) = raw.array()? {
for _ in 0..n {
extract_links(raw, cids)?;
}
} else {
// This is an `Array` of `Indefinite` length. Continue until we it a `Break`.
while !extract_links(raw, cids)? {}
}
false
}
Type::Map => {
if let Len::Len(n) = raw.map()? {
for _ in 0..n {
extract_links(raw, cids)?;
extract_links(raw, cids)?;
}
} else {
// This is a `Map` of `Indefinite` length. Continue until we hit a `Break`.
while !(extract_links(raw, cids)? | extract_links(raw, cids)?) {}
}
false
}
Type::Tag => {
let tag = raw.tag()?;
if tag == CBOR_TAG_LINK {
let bytes = raw.bytes()?;
// We need to drop the first byte, since it's just a 0 padding
let cid = Cid::try_from(&bytes[1..])?;
cids.insert(cid);
} else {
// This was not our link `Tag`, so just extract the value after and ignore it.
extract_links(raw, cids)?;
}
false
}
Type::Special => {
// We don't care about any other `Special` value except `Break` that signals the end of
// an `Indefinite` length `Array` or `Map`.
if let Special::Break = raw.special()? {
true
} else {
false
}
}
};
Ok(is_break)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment