use crate::ipld::{BlockError, Ipld, IpldError};
use byteorder::{BigEndian, ByteOrder};
use cid::Cid;
use std::{
collections::BTreeMap,
convert::TryFrom,
io::{Read, Write},
};
use thiserror::Error;
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct DagCborCodec;
impl DagCborCodec {
pub fn encode(ipld: &Ipld) -> Result<Box<[u8]>, CborError> {
let mut bytes = Vec::new();
ipld.write_cbor(&mut bytes)?;
Ok(bytes.into_boxed_slice())
}
pub fn decode(mut data: &[u8]) -> Result<Ipld, CborError> {
Ipld::read_cbor(&mut data)
}
}
#[derive(Debug, Error)]
pub enum CborError {
#[error("Number larger than u64.")]
NumberOutOfRange,
#[error("Length out of range.")]
LengthOutOfRange,
#[error("Unexpected cbor code.")]
UnexpectedCode,
#[error("Unkown cbor tag.")]
UnknownTag,
#[error("Wrong key.")]
UnexpectedKey,
#[error("Unexpected end of file.")]
UnexpectedEof,
#[error("{0}")]
Io(#[from] std::io::Error),
#[error("{0}")]
Utf8(#[from] std::str::Utf8Error),
#[error("Invalid Cid prefix: {0}")]
InvalidCidPrefix(u8),
#[error("{0}")]
Cid(#[from] cid::Error),
#[error("{0}")]
Ipld(#[from] IpldError),
}
impl From<CborError> for BlockError {
fn from(err: CborError) -> Self {
Self::CodecError(err.into())
}
}
pub type CborResult<T> = Result<T, CborError>;
#[inline]
pub fn write_null<W: Write>(w: &mut W) -> CborResult<()> {
w.write_all(&[0xf6])?;
Ok(())
}
#[inline]
pub fn write_u8<W: Write>(w: &mut W, major: u8, value: u8) -> CborResult<()> {
if value <= 0x17 {
let buf = [major << 5 | value];
w.write_all(&buf)?;
} else {
let buf = [major << 5 | 24, value];
w.write_all(&buf)?;
}
Ok(())
}
#[inline]
pub fn write_u16<W: Write>(w: &mut W, major: u8, value: u16) -> CborResult<()> {
if value <= u16::from(u8::max_value()) {
write_u8(w, major, value as u8)?;
} else {
let mut buf = [major << 5 | 25, 0, 0];
BigEndian::write_u16(&mut buf[1..], value);
w.write_all(&buf)?;
}
Ok(())
}
#[inline]
pub fn write_u32<W: Write>(w: &mut W, major: u8, value: u32) -> CborResult<()> {
if value <= u32::from(u16::max_value()) {
write_u16(w, major, value as u16)?;
} else {
let mut buf = [major << 5 | 26, 0, 0, 0, 0];
BigEndian::write_u32(&mut buf[1..], value);
w.write_all(&buf)?;
}
Ok(())
}
#[inline]
pub fn write_u64<W: Write>(w: &mut W, major: u8, value: u64) -> CborResult<()> {
if value <= u64::from(u32::max_value()) {
write_u32(w, major, value as u32)?;
} else {
let mut buf = [major << 5 | 27, 0, 0, 0, 0, 0, 0, 0, 0];
BigEndian::write_u64(&mut buf[1..], value);
w.write_all(&buf)?;
}
Ok(())
}
#[inline]
pub fn write_tag<W: Write>(w: &mut W, tag: u64) -> CborResult<()> {
write_u64(w, 6, tag)
}
pub trait WriteCbor {
fn write_cbor<W: Write>(&self, w: &mut W) -> CborResult<()>;
}
impl WriteCbor for bool {
#[inline]
fn write_cbor<W: Write>(&self, w: &mut W) -> CborResult<()> {
let buf = if *self { [0xf5] } else { [0xf4] };
w.write_all(&buf)?;
Ok(())
}
}
impl WriteCbor for u8 {
#[inline]
fn write_cbor<W: Write>(&self, w: &mut W) -> CborResult<()> {
write_u8(w, 0, *self)
}
}
impl WriteCbor for u16 {
#[inline]
fn write_cbor<W: Write>(&self, w: &mut W) -> CborResult<()> {
write_u16(w, 0, *self)
}
}
impl WriteCbor for u32 {
#[inline]
fn write_cbor<W: Write>(&self, w: &mut W) -> CborResult<()> {
write_u32(w, 0, *self)
}
}
impl WriteCbor for u64 {
#[inline]
fn write_cbor<W: Write>(&self, w: &mut W) -> CborResult<()> {
write_u64(w, 0, *self)
}
}
impl WriteCbor for i8 {
#[inline]
fn write_cbor<W: Write>(&self, w: &mut W) -> CborResult<()> {
write_u8(w, 1, -(*self + 1) as u8)
}
}
impl WriteCbor for i16 {
#[inline]
fn write_cbor<W: Write>(&self, w: &mut W) -> CborResult<()> {
write_u16(w, 1, -(*self + 1) as u16)
}
}
impl WriteCbor for i32 {
#[inline]
fn write_cbor<W: Write>(&self, w: &mut W) -> CborResult<()> {
write_u32(w, 1, -(*self + 1) as u32)
}
}
impl WriteCbor for i64 {
#[inline]
fn write_cbor<W: Write>(&self, w: &mut W) -> CborResult<()> {
write_u64(w, 1, -(*self + 1) as u64)
}
}
impl WriteCbor for f32 {
#[inline]
#[allow(clippy::float_cmp)]
fn write_cbor<W: Write>(&self, w: &mut W) -> CborResult<()> {
if self.is_infinite() {
if self.is_sign_positive() {
w.write_all(&[0xf9, 0x7c, 0x00])?;
} else {
w.write_all(&[0xf9, 0xfc, 0x00])?;
}
} else if self.is_nan() {
w.write_all(&[0xf9, 0x7e, 0x00])?;
} else {
let mut buf = [0xfa, 0, 0, 0, 0];
BigEndian::write_f32(&mut buf[1..], *self);
w.write_all(&buf)?;
}
Ok(())
}
}
impl WriteCbor for f64 {
#[inline]
#[allow(clippy::float_cmp)]
fn write_cbor<W: Write>(&self, w: &mut W) -> CborResult<()> {
if !self.is_finite() || f64::from(*self as f32) == *self {
let value = *self as f32;
value.write_cbor(w)?;
} else {
let mut buf = [0xfb, 0, 0, 0, 0, 0, 0, 0, 0];
BigEndian::write_f64(&mut buf[1..], *self);
w.write_all(&buf)?;
}
Ok(())
}
}
impl WriteCbor for [u8] {
#[inline]
fn write_cbor<W: Write>(&self, w: &mut W) -> CborResult<()> {
write_u64(w, 2, self.len() as u64)?;
w.write_all(self)?;
Ok(())
}
}
impl WriteCbor for str {
#[inline]
fn write_cbor<W: Write>(&self, w: &mut W) -> CborResult<()> {
write_u64(w, 3, self.len() as u64)?;
w.write_all(self.as_bytes())?;
Ok(())
}
}
impl WriteCbor for String {
#[inline]
fn write_cbor<W: Write>(&self, w: &mut W) -> CborResult<()> {
self.as_str().write_cbor(w)
}
}
impl WriteCbor for i128 {
#[inline]
fn write_cbor<W: Write>(&self, w: &mut W) -> CborResult<()> {
if *self < 0 {
if -(*self + 1) > u64::max_value() as i128 {
return Err(CborError::NumberOutOfRange);
}
write_u64(w, 1, -(*self + 1) as u64)?;
} else {
if *self > u64::max_value() as i128 {
return Err(CborError::NumberOutOfRange);
}
write_u64(w, 0, *self as u64)?;
}
Ok(())
}
}
impl WriteCbor for Cid {
#[inline]
fn write_cbor<W: Write>(&self, w: &mut W) -> CborResult<()> {
write_tag(w, 42)?;
let bytes = self.to_bytes();
write_u64(w, 2, (bytes.len() + 1) as u64)?;
w.write_all(&[0])?;
w.write_all(&bytes)?;
Ok(())
}
}
impl<T: WriteCbor> WriteCbor for Option<T> {
#[inline]
fn write_cbor<W: Write>(&self, w: &mut W) -> CborResult<()> {
if let Some(value) = self {
value.write_cbor(w)?;
} else {
write_null(w)?;
}
Ok(())
}
}
impl<T: WriteCbor> WriteCbor for Vec<T> {
#[inline]
fn write_cbor<W: Write>(&self, w: &mut W) -> CborResult<()> {
write_u64(w, 4, self.len() as u64)?;
for value in self {
value.write_cbor(w)?;
}
Ok(())
}
}
impl<T: WriteCbor + 'static> WriteCbor for BTreeMap<String, T> {
#[inline]
fn write_cbor<W: Write>(&self, w: &mut W) -> CborResult<()> {
write_u64(w, 5, self.len() as u64)?;
for (k, v) in self {
k.write_cbor(w)?;
v.write_cbor(w)?;
}
Ok(())
}
}
impl WriteCbor for Ipld {
fn write_cbor<W: Write>(&self, w: &mut W) -> CborResult<()> {
match self {
Ipld::Null => write_null(w),
Ipld::Bool(b) => b.write_cbor(w),
Ipld::Integer(i) => i.write_cbor(w),
Ipld::Float(f) => f.write_cbor(w),
Ipld::Bytes(b) => b.as_slice().write_cbor(w),
Ipld::String(s) => s.as_str().write_cbor(w),
Ipld::List(l) => l.write_cbor(w),
Ipld::Map(m) => m.write_cbor(w),
Ipld::Link(c) => c.write_cbor(w),
}
}
}
#[inline]
pub fn read_u8<R: Read>(r: &mut R) -> CborResult<u8> {
let mut buf = [0; 1];
r.read_exact(&mut buf)?;
Ok(buf[0])
}
#[inline]
pub fn read_u16<R: Read>(r: &mut R) -> CborResult<u16> {
let mut buf = [0; 2];
r.read_exact(&mut buf)?;
Ok(BigEndian::read_u16(&buf))
}
#[inline]
pub fn read_u32<R: Read>(r: &mut R) -> CborResult<u32> {
let mut buf = [0; 4];
r.read_exact(&mut buf)?;
Ok(BigEndian::read_u32(&buf))
}
#[inline]
pub fn read_u64<R: Read>(r: &mut R) -> CborResult<u64> {
let mut buf = [0; 8];
r.read_exact(&mut buf)?;
Ok(BigEndian::read_u64(&buf))
}
#[inline]
pub fn read_f32<R: Read>(r: &mut R) -> CborResult<f32> {
let mut buf = [0; 4];
r.read_exact(&mut buf)?;
Ok(BigEndian::read_f32(&buf))
}
#[inline]
pub fn read_f64<R: Read>(r: &mut R) -> CborResult<f64> {
let mut buf = [0; 8];
r.read_exact(&mut buf)?;
Ok(BigEndian::read_f64(&buf))
}
#[inline]
pub fn read_bytes<R: Read>(r: &mut R, len: usize) -> CborResult<Vec<u8>> {
let mut buf = vec![0; len];
r.read_exact(&mut buf)?;
Ok(buf)
}
#[inline]
pub fn read_str<R: Read>(r: &mut R, len: usize) -> CborResult<String> {
let bytes = read_bytes(r, len)?;
let string = std::str::from_utf8(&bytes)?;
Ok(string.to_string())
}
#[inline]
pub fn read_list<R: Read, T: ReadCbor>(r: &mut R, len: usize) -> CborResult<Vec<T>> {
let mut list: Vec<T> = Vec::with_capacity(len);
for _ in 0..len {
list.push(T::read_cbor(r)?);
}
Ok(list)
}
#[inline]
pub fn read_map<R: Read, T: ReadCbor>(r: &mut R, len: usize) -> CborResult<BTreeMap<String, T>> {
let mut map: BTreeMap<String, T> = BTreeMap::new();
for _ in 0..len {
let key = String::read_cbor(r)?;
let value = T::read_cbor(r)?;
map.insert(key, value);
}
Ok(map)
}
#[inline]
pub fn read_link<R: Read>(r: &mut R) -> CborResult<Cid> {
let tag = read_u8(r)?;
if tag != 42 {
return Err(CborError::UnknownTag);
}
let ty = read_u8(r)?;
if ty != 0x58 {
return Err(CborError::UnknownTag);
}
let len = read_u8(r)?;
if len == 0 {
return Err(CborError::LengthOutOfRange);
}
let bytes = read_bytes(r, len as usize)?;
if bytes[0] != 0 {
return Err(CborError::InvalidCidPrefix(bytes[0]));
}
Ok(Cid::try_from(&bytes[1..])?)
}
pub trait ReadCbor: Sized {
fn try_read_cbor<R: Read>(r: &mut R, major: u8) -> CborResult<Option<Self>>;
#[inline]
fn read_cbor<R: Read>(r: &mut R) -> CborResult<Self> {
let major = read_u8(r)?;
if let Some(res) = Self::try_read_cbor(r, major)? {
Ok(res)
} else {
Err(CborError::UnexpectedCode)
}
}
}
impl ReadCbor for bool {
#[inline]
fn try_read_cbor<R: Read>(_: &mut R, major: u8) -> CborResult<Option<Self>> {
match major {
0xf4 => Ok(Some(false)),
0xf5 => Ok(Some(true)),
_ => Ok(None),
}
}
}
impl ReadCbor for u8 {
#[inline]
fn try_read_cbor<R: Read>(r: &mut R, major: u8) -> CborResult<Option<Self>> {
match major {
0x00..=0x17 => Ok(Some(major)),
0x18 => Ok(Some(read_u8(r)?)),
_ => Ok(None),
}
}
}
impl ReadCbor for u16 {
#[inline]
fn try_read_cbor<R: Read>(r: &mut R, major: u8) -> CborResult<Option<Self>> {
match major {
0x00..=0x17 => Ok(Some(major as u16)),
0x18 => Ok(Some(read_u8(r)? as u16)),
0x19 => Ok(Some(read_u16(r)?)),
_ => Ok(None),
}
}
}
impl ReadCbor for u32 {
#[inline]
fn try_read_cbor<R: Read>(r: &mut R, major: u8) -> CborResult<Option<Self>> {
match major {
0x00..=0x17 => Ok(Some(major as u32)),
0x18 => Ok(Some(read_u8(r)? as u32)),
0x19 => Ok(Some(read_u16(r)? as u32)),
0x1a => Ok(Some(read_u32(r)?)),
_ => Ok(None),
}
}
}
impl ReadCbor for u64 {
#[inline]
fn try_read_cbor<R: Read>(r: &mut R, major: u8) -> CborResult<Option<Self>> {
match major {
0x00..=0x17 => Ok(Some(major as u64)),
0x18 => Ok(Some(read_u8(r)? as u64)),
0x19 => Ok(Some(read_u16(r)? as u64)),
0x1a => Ok(Some(read_u32(r)? as u64)),
0x1b => Ok(Some(read_u64(r)?)),
_ => Ok(None),
}
}
}
impl ReadCbor for i8 {
#[inline]
fn try_read_cbor<R: Read>(r: &mut R, major: u8) -> CborResult<Option<Self>> {
match major {
0x20..=0x37 => Ok(Some(-1 - (major - 0x20) as i8)),
0x38 => Ok(Some(-1 - read_u8(r)? as i8)),
_ => Ok(None),
}
}
}
impl ReadCbor for i16 {
#[inline]
fn try_read_cbor<R: Read>(r: &mut R, major: u8) -> CborResult<Option<Self>> {
match major {
0x20..=0x37 => Ok(Some(-1 - (major - 0x20) as i16)),
0x38 => Ok(Some(-1 - read_u8(r)? as i16)),
0x39 => Ok(Some(-1 - read_u16(r)? as i16)),
_ => Ok(None),
}
}
}
impl ReadCbor for i32 {
#[inline]
fn try_read_cbor<R: Read>(r: &mut R, major: u8) -> CborResult<Option<Self>> {
match major {
0x20..=0x37 => Ok(Some(-1 - (major - 0x20) as i32)),
0x38 => Ok(Some(-1 - read_u8(r)? as i32)),
0x39 => Ok(Some(-1 - read_u16(r)? as i32)),
0x3a => Ok(Some(-1 - read_u32(r)? as i32)),
_ => Ok(None),
}
}
}
impl ReadCbor for i64 {
#[inline]
fn try_read_cbor<R: Read>(r: &mut R, major: u8) -> CborResult<Option<Self>> {
match major {
0x20..=0x37 => Ok(Some(-1 - (major - 0x20) as i64)),
0x38 => Ok(Some(-1 - read_u8(r)? as i64)),
0x39 => Ok(Some(-1 - read_u16(r)? as i64)),
0x3a => Ok(Some(-1 - read_u32(r)? as i64)),
0x3b => Ok(Some(-1 - read_u64(r)? as i64)),
_ => Ok(None),
}
}
}
impl ReadCbor for f32 {
#[inline]
fn try_read_cbor<R: Read>(r: &mut R, major: u8) -> CborResult<Option<Self>> {
match major {
0xfa => Ok(Some(read_f32(r)?)),
_ => Ok(None),
}
}
}
impl ReadCbor for f64 {
#[inline]
fn try_read_cbor<R: Read>(r: &mut R, major: u8) -> CborResult<Option<Self>> {
match major {
0xfa => Ok(Some(read_f32(r)? as f64)),
0xfb => Ok(Some(read_f64(r)?)),
_ => Ok(None),
}
}
}
impl ReadCbor for String {
#[inline]
fn try_read_cbor<R: Read>(r: &mut R, major: u8) -> CborResult<Option<Self>> {
let len = match major {
0x60..=0x77 => major as usize - 0x60,
0x78 => read_u8(r)? as usize,
0x79 => read_u16(r)? as usize,
0x7a => read_u32(r)? as usize,
0x7b => {
let len = read_u64(r)?;
if len > usize::max_value() as u64 {
return Err(CborError::LengthOutOfRange);
}
len as usize
}
_ => return Ok(None),
};
Ok(Some(read_str(r, len)?))
}
}
impl ReadCbor for Cid {
#[inline]
fn try_read_cbor<R: Read>(r: &mut R, major: u8) -> CborResult<Option<Self>> {
match major {
0xd8 => Ok(Some(read_link(r)?)),
_ => Ok(None),
}
}
}
impl ReadCbor for Box<[u8]> {
#[inline]
fn try_read_cbor<R: Read>(r: &mut R, major: u8) -> CborResult<Option<Self>> {
let len = match major {
0x40..=0x57 => major as usize - 0x40,
0x58 => read_u8(r)? as usize,
0x59 => read_u16(r)? as usize,
0x5a => read_u32(r)? as usize,
0x5b => {
let len = read_u64(r)?;
if len > usize::max_value() as u64 {
return Err(CborError::LengthOutOfRange);
}
len as usize
}
_ => return Ok(None),
};
Ok(Some(read_bytes(r, len)?.into_boxed_slice()))
}
}
impl<T: ReadCbor> ReadCbor for Option<T> {
#[inline]
fn try_read_cbor<R: Read>(r: &mut R, major: u8) -> CborResult<Option<Self>> {
match major {
0xf6 => Ok(Some(None)),
0xf7 => Ok(Some(None)),
_ => {
if let Some(res) = T::try_read_cbor(r, major)? {
Ok(Some(Some(res)))
} else {
Ok(None)
}
}
}
}
}
impl<T: ReadCbor> ReadCbor for Vec<T> {
#[inline]
fn try_read_cbor<R: Read>(r: &mut R, major: u8) -> CborResult<Option<Self>> {
let len = match major {
0x80..=0x97 => major as usize - 0x80,
0x98 => read_u8(r)? as usize,
0x99 => read_u16(r)? as usize,
0x9a => read_u32(r)? as usize,
0x9b => {
let len = read_u64(r)?;
if len > usize::max_value() as u64 {
return Err(CborError::LengthOutOfRange);
}
len as usize
}
_ => return Ok(None),
};
Ok(Some(read_list(r, len)?))
}
}
impl<T: ReadCbor> ReadCbor for BTreeMap<String, T> {
#[inline]
fn try_read_cbor<R: Read>(r: &mut R, major: u8) -> CborResult<Option<Self>> {
let len = match major {
0xa0..=0xb7 => major as usize - 0xa0,
0xb8 => read_u8(r)? as usize,
0xb9 => read_u16(r)? as usize,
0xba => read_u32(r)? as usize,
0xbb => {
let len = read_u64(r)?;
if len > usize::max_value() as u64 {
return Err(CborError::LengthOutOfRange);
}
len as usize
}
_ => return Ok(None),
};
Ok(Some(read_map(r, len)?))
}
}
impl ReadCbor for Ipld {
#[inline]
fn try_read_cbor<R: Read>(r: &mut R, major: u8) -> CborResult<Option<Self>> {
let ipld = match major {
0x00..=0x17 => Ipld::Integer(major as i128),
0x18 => Ipld::Integer(read_u8(r)? as i128),
0x19 => Ipld::Integer(read_u16(r)? as i128),
0x1a => Ipld::Integer(read_u32(r)? as i128),
0x1b => Ipld::Integer(read_u64(r)? as i128),
0x20..=0x37 => Ipld::Integer(-1 - (major - 0x20) as i128),
0x38 => Ipld::Integer(-1 - read_u8(r)? as i128),
0x39 => Ipld::Integer(-1 - read_u16(r)? as i128),
0x3a => Ipld::Integer(-1 - read_u32(r)? as i128),
0x3b => Ipld::Integer(-1 - read_u64(r)? as i128),
0x40..=0x57 => {
let len = major - 0x40;
let bytes = read_bytes(r, len as usize)?;
Ipld::Bytes(bytes)
}
0x58 => {
let len = read_u8(r)?;
let bytes = read_bytes(r, len as usize)?;
Ipld::Bytes(bytes)
}
0x59 => {
let len = read_u16(r)?;
let bytes = read_bytes(r, len as usize)?;
Ipld::Bytes(bytes)
}
0x5a => {
let len = read_u32(r)?;
let bytes = read_bytes(r, len as usize)?;
Ipld::Bytes(bytes)
}
0x5b => {
let len = read_u64(r)?;
if len > usize::max_value() as u64 {
return Err(CborError::LengthOutOfRange);
}
let bytes = read_bytes(r, len as usize)?;
Ipld::Bytes(bytes)
}
0x60..=0x77 => {
let len = major - 0x60;
let string = read_str(r, len as usize)?;
Ipld::String(string)
}
0x78 => {
let len = read_u8(r)?;
let string = read_str(r, len as usize)?;
Ipld::String(string)
}
0x79 => {
let len = read_u16(r)?;
let string = read_str(r, len as usize)?;
Ipld::String(string)
}
0x7a => {
let len = read_u32(r)?;
let string = read_str(r, len as usize)?;
Ipld::String(string)
}
0x7b => {
let len = read_u64(r)?;
if len > usize::max_value() as u64 {
return Err(CborError::LengthOutOfRange);
}
let string = read_str(r, len as usize)?;
Ipld::String(string)
}
0x80..=0x97 => {
let len = major - 0x80;
let list = read_list(r, len as usize)?;
Ipld::List(list)
}
0x98 => {
let len = read_u8(r)?;
let list = read_list(r, len as usize)?;
Ipld::List(list)
}
0x99 => {
let len = read_u16(r)?;
let list = read_list(r, len as usize)?;
Ipld::List(list)
}
0x9a => {
let len = read_u32(r)?;
let list = read_list(r, len as usize)?;
Ipld::List(list)
}
0x9b => {
let len = read_u64(r)?;
if len > usize::max_value() as u64 {
return Err(CborError::LengthOutOfRange);
}
let list = read_list(r, len as usize)?;
Ipld::List(list)
}
0xa0..=0xb7 => {
let len = major - 0xa0;
let map = read_map(r, len as usize)?;
Ipld::Map(map)
}
0xb8 => {
let len = read_u8(r)?;
let map = read_map(r, len as usize)?;
Ipld::Map(map)
}
0xb9 => {
let len = read_u16(r)?;
let map = read_map(r, len as usize)?;
Ipld::Map(map)
}
0xba => {
let len = read_u32(r)?;
let map = read_map(r, len as usize)?;
Ipld::Map(map)
}
0xbb => {
let len = read_u64(r)?;
if len > usize::max_value() as u64 {
return Err(CborError::LengthOutOfRange);
}
let map = read_map(r, len as usize)?;
Ipld::Map(map)
}
0xd8 => Ipld::Link(read_link(r)?),
0xf4 => Ipld::Bool(false),
0xf5 => Ipld::Bool(true),
0xf6 => Ipld::Null,
0xf7 => Ipld::Null,
0xfa => Ipld::Float(read_f32(r)? as f64),
0xfb => Ipld::Float(read_f64(r)?),
_ => return Ok(None),
};
Ok(Some(ipld))
}
}