forest/cid_collections/
mod.rs1pub mod hash_map;
4pub mod hash_set;
5mod small_cid_vec;
6pub use hash_map::CidHashMap;
7pub use hash_set::{CidHashSet, CidHashSetLike, FileBackedCidHashSet};
8use imp::{CidV1DagCborBlake2b256, Uncompactable};
9pub use small_cid_vec::{SmallCid, SmallCidNonEmptyVec};
10
11#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
33enum MaybeCompactedCid {
34 Compact(CidV1DagCborBlake2b256),
35 Uncompactable(Uncompactable),
37}
38
39mod imp {
41 use super::*;
42 use crate::utils::multihash::prelude::*;
43 use cid::{Cid, multihash::Multihash};
44 use get_size2::GetSize;
45 #[cfg(test)]
46 use {crate::utils::db::CborStoreExt as _, quickcheck::Arbitrary};
47
48 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd, GetSize)]
49 #[repr(transparent)]
50 pub struct CidV1DagCborBlake2b256 {
51 digest: [u8; Self::WIDTH],
52 }
53
54 impl CidV1DagCborBlake2b256 {
55 const WIDTH: usize = 32;
56
57 pub fn digest(&self) -> &[u8; Self::WIDTH] {
58 &self.digest
59 }
60 }
61
62 #[cfg(test)]
63 impl Arbitrary for CidV1DagCborBlake2b256 {
64 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
65 Self {
66 digest: std::array::from_fn(|_ix| u8::arbitrary(g)),
67 }
68 }
69 }
70
71 #[test]
72 fn width() {
73 assert_eq!(
74 MultihashCode::Blake2b256.digest(&[]).size() as usize,
75 CidV1DagCborBlake2b256::WIDTH,
76 );
77 }
78
79 impl TryFrom<Cid> for CidV1DagCborBlake2b256 {
80 type Error = &'static str;
81
82 fn try_from(value: Cid) -> Result<Self, Self::Error> {
83 if value.version() == cid::Version::V1
84 && value.codec() == fvm_ipld_encoding::DAG_CBOR
85 && let Ok(small_hash) = value.hash().resize()
86 {
87 let (code, digest, size) = small_hash.into_inner();
88 if code == u64::from(MultihashCode::Blake2b256) && size as usize == Self::WIDTH {
89 return Ok(Self { digest });
90 }
91 }
92 Err("cannot be compacted")
93 }
94 }
95
96 impl From<CidV1DagCborBlake2b256> for Cid {
97 fn from(value: CidV1DagCborBlake2b256) -> Self {
98 let CidV1DagCborBlake2b256 { digest } = value;
99 Cid::new_v1(
100 fvm_ipld_encoding::DAG_CBOR,
101 Multihash::wrap(MultihashCode::Blake2b256.into(), digest.as_slice())
102 .expect("could not round-trip compacted CID"),
103 )
104 }
105 }
106
107 #[derive(
108 Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd, GetSize, derive_more::Deref,
109 )]
110 #[repr(transparent)]
111 pub struct Uncompactable {
112 #[get_size(ignore)]
113 inner: Cid,
114 }
115
116 impl From<Uncompactable> for Cid {
119 fn from(value: Uncompactable) -> Self {
120 value.inner
121 }
122 }
123
124 impl From<Cid> for MaybeCompactedCid {
125 fn from(value: Cid) -> Self {
126 match value.try_into() {
127 Ok(compact) => Self::Compact(compact),
128 Err(_) => Self::Uncompactable(Uncompactable { inner: value }),
129 }
130 }
131 }
132
133 impl From<MaybeCompactedCid> for Cid {
134 fn from(value: MaybeCompactedCid) -> Self {
135 match value {
136 MaybeCompactedCid::Compact(compact) => compact.into(),
137 MaybeCompactedCid::Uncompactable(Uncompactable { inner }) => inner,
138 }
139 }
140 }
141
142 #[test]
143 fn compactable() {
144 let cid = Cid::new(
145 cid::Version::V1,
146 fvm_ipld_encoding::DAG_CBOR,
147 MultihashCode::Blake2b256.digest("blake".as_bytes()),
148 )
149 .unwrap();
150 assert!(matches!(cid.into(), MaybeCompactedCid::Compact(_)));
151 }
152
153 #[test]
154 fn default() {
155 let cid = crate::db::MemoryDB::default()
156 .put_cbor_default(&())
157 .unwrap();
158 assert!(
159 matches!(cid.into(), MaybeCompactedCid::Compact(_)),
160 "the default encoding is no longer v1+dagcbor+blake2b.
161 consider adding the new default CID type to [`MaybeCompactCid`]"
162 );
163 }
164
165 #[test]
166 fn uncompactable_get_size() {
167 let i = Uncompactable {
168 inner: Cid::default(),
169 };
170 assert_eq!(i.get_size(), std::mem::size_of_val(&i.inner));
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177 use cid::Cid;
178 use quickcheck::Arbitrary;
179
180 impl Arbitrary for MaybeCompactedCid {
181 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
182 let compact = MaybeCompactedCid::Compact(CidV1DagCborBlake2b256::arbitrary(g));
184 let maybe_compact = Self::from(Cid::arbitrary(g));
185 *g.choose(&[compact, maybe_compact]).unwrap()
186 }
187 }
188
189 #[quickcheck_macros::quickcheck]
190 fn cid_via_maybe_compacted_cid(before: Cid) {
191 let via = MaybeCompactedCid::from(before);
192 let after = Cid::from(via);
193 assert_eq!(before, after);
194 }
195}