Created
September 4, 2020 13:03
-
-
Save rklaehn/62e21237e7ef8d868cfe655ad6209f28 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//! 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