pub mod hash_map;
pub mod hash_set;
mod small_cid_vec;
pub use hash_map::CidHashMap;
pub use hash_set::CidHashSet;
use imp::{CidV1DagCborBlake2b256, Uncompactable};
pub use small_cid_vec::SmallCidNonEmptyVec;
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
enum MaybeCompactedCid {
Compact(CidV1DagCborBlake2b256),
Uncompactable(Uncompactable),
}
mod imp {
use super::MaybeCompactedCid;
use cid::{
multihash::{self, Multihash},
Cid,
};
#[cfg(test)]
use {
crate::utils::db::CborStoreExt as _, multihash::MultihashDigest as _, quickcheck::Arbitrary,
};
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
#[repr(transparent)]
pub struct CidV1DagCborBlake2b256 {
digest: [u8; Self::WIDTH],
}
impl CidV1DagCborBlake2b256 {
const WIDTH: usize = 32;
}
#[cfg(test)]
impl Arbitrary for CidV1DagCborBlake2b256 {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
Self {
digest: std::array::from_fn(|_ix| u8::arbitrary(g)),
}
}
}
#[test]
fn width() {
assert_eq!(
multihash::Code::Blake2b256.digest(&[]).size() as usize,
CidV1DagCborBlake2b256::WIDTH,
);
}
impl TryFrom<Cid> for CidV1DagCborBlake2b256 {
type Error = &'static str;
fn try_from(value: Cid) -> Result<Self, Self::Error> {
if value.version() == cid::Version::V1 && value.codec() == fvm_ipld_encoding::DAG_CBOR {
if let Ok(small_hash) = value.hash().resize() {
let (code, digest, size) = small_hash.into_inner();
if code == u64::from(multihash::Code::Blake2b256)
&& size as usize == Self::WIDTH
{
return Ok(Self { digest });
}
}
}
Err("cannot be compacted")
}
}
impl From<CidV1DagCborBlake2b256> for Cid {
fn from(value: CidV1DagCborBlake2b256) -> Self {
let CidV1DagCborBlake2b256 { digest } = value;
Cid::new_v1(
fvm_ipld_encoding::DAG_CBOR,
Multihash::wrap(multihash::Code::Blake2b256.into(), digest.as_slice())
.expect("could not round-trip compacted CID"),
)
}
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
#[repr(transparent)]
pub struct Uncompactable {
inner: Cid,
}
impl From<Uncompactable> for Cid {
fn from(value: Uncompactable) -> Self {
value.inner
}
}
impl From<Cid> for MaybeCompactedCid {
fn from(value: Cid) -> Self {
match value.try_into() {
Ok(compact) => Self::Compact(compact),
Err(_) => Self::Uncompactable(Uncompactable { inner: value }),
}
}
}
impl From<MaybeCompactedCid> for Cid {
fn from(value: MaybeCompactedCid) -> Self {
match value {
MaybeCompactedCid::Compact(compact) => compact.into(),
MaybeCompactedCid::Uncompactable(Uncompactable { inner }) => inner,
}
}
}
#[test]
fn compactable() {
let cid = Cid::new(
cid::Version::V1,
fvm_ipld_encoding::DAG_CBOR,
multihash::Code::Blake2b256.digest("blake".as_bytes()),
)
.unwrap();
assert!(matches!(cid.into(), MaybeCompactedCid::Compact(_)));
}
#[test]
fn default() {
let cid = crate::db::MemoryDB::default()
.put_cbor_default(&())
.unwrap();
assert!(
matches!(cid.into(), MaybeCompactedCid::Compact(_)),
"the default encoding is no longer v1+dagcbor+blake2b.
consider adding the new default CID type to [`MaybeCompactCid`]"
);
}
}
#[cfg(test)]
mod tests {
use super::*;
use cid::Cid;
use quickcheck::{quickcheck, Arbitrary};
impl Arbitrary for MaybeCompactedCid {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let compact = MaybeCompactedCid::Compact(CidV1DagCborBlake2b256::arbitrary(g));
let maybe_compact = Self::from(Cid::arbitrary(g));
*g.choose(&[compact, maybe_compact]).unwrap()
}
}
quickcheck! {
fn cid_via_maybe_compacted_cid(before: Cid) -> () {
let via = MaybeCompactedCid::from(before);
let after = Cid::from(via);
assert_eq!(before, after);
}
}
}