-
Notifications
You must be signed in to change notification settings - Fork 142
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for HamtV0 from forest(commit hash: b622af5a6) #1808
Changes from 13 commits
45057b3
8e057e4
c623b6c
4af6feb
5ff361d
75da1de
d67134b
5ee32a7
7f40a6a
06bebb3
bc2b6b6
f818926
7aeeee9
bb3f0c1
b831c43
713099a
7825674
a34fcf6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,12 +23,15 @@ pub use forest_hash_utils::{BytesKey, Hash}; | |
use serde::{Deserialize, Serialize}; | ||
|
||
pub use self::error::Error; | ||
pub use self::hamt::Hamt; | ||
pub use self::hamt::{Hamt, Hamtv0}; | ||
pub use self::hash::*; | ||
pub use self::hash_algorithm::*; | ||
pub use self::pointer::version; | ||
|
||
/// Default bit width for indexing a hash at each depth level | ||
const DEFAULT_BIT_WIDTH: u32 = 8; | ||
/// Default bit width for indexing a hash at each depth level for Hamt v0 | ||
pub const DEFAULT_BIT_WIDTH_V0: u32 = 5; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's make this private unless we need it to be public for some reason. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've removed this field due to #1828 and filecoin-project/builtin-actors#1346. Basically, the user should just pass the bit-width they need and I'm going to deprecate the "default" methods as they're useless. As far as I can tell, the v0 bit-width was also 8 by default. We just set it to 5 everywhere. |
||
|
||
/// Configuration options for a HAMT instance. | ||
#[derive(Debug, Clone)] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,18 +15,37 @@ use super::node::Node; | |
use super::{Error, Hash, HashAlgorithm, KeyValuePair}; | ||
use crate::Config; | ||
|
||
pub mod version { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's make this crate-private if possible, or |
||
#[derive(PartialEq, Eq, Debug)] | ||
pub struct V0; | ||
#[derive(PartialEq, Eq, Debug)] | ||
pub struct V3; | ||
|
||
pub trait Version { | ||
const NUMBER: usize; | ||
} | ||
|
||
impl Version for V0 { | ||
const NUMBER: usize = 0; | ||
} | ||
|
||
impl Version for V3 { | ||
const NUMBER: usize = 3; | ||
} | ||
} | ||
|
||
/// Pointer to index values or a link to another child node. | ||
#[derive(Debug)] | ||
pub(crate) enum Pointer<K, V, H> { | ||
pub(crate) enum Pointer<K, V, Ver, H> { | ||
Values(Vec<KeyValuePair<K, V>>), | ||
Link { | ||
cid: Cid, | ||
cache: OnceCell<Box<Node<K, V, H>>>, | ||
cache: OnceCell<Box<Node<K, V, Ver, H>>>, | ||
}, | ||
Dirty(Box<Node<K, V, H>>), | ||
Dirty(Box<Node<K, V, Ver, H>>), | ||
} | ||
|
||
impl<K: PartialEq, V: PartialEq, H> PartialEq for Pointer<K, V, H> { | ||
impl<K: PartialEq, V: PartialEq, H, Ver> PartialEq for Pointer<K, V, H, Ver> { | ||
fn eq(&self, other: &Self) -> bool { | ||
match (self, other) { | ||
(Pointer::Values(a), Pointer::Values(b)) => a == b, | ||
|
@@ -37,25 +56,58 @@ impl<K: PartialEq, V: PartialEq, H> PartialEq for Pointer<K, V, H> { | |
} | ||
} | ||
|
||
mod pointer_v0 { | ||
use cid::Cid; | ||
use serde::Serialize; | ||
|
||
use crate::KeyValuePair; | ||
|
||
use super::Pointer; | ||
|
||
#[derive(Serialize)] | ||
pub(super) enum PointerSer<'a, K, V> { | ||
Vals(&'a [KeyValuePair<K, V>]), | ||
Link(&'a Cid), | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a bit confused at how this works. For v0, we want to serialize to a map of "0"/"1" to a cid/list of links. As far as I know, this here will serialize use "Vals" and "Link" as the keys? |
||
|
||
impl<'a, K, V, Ver, H> TryFrom<&'a Pointer<K, V, Ver, H>> for PointerSer<'a, K, V> { | ||
type Error = &'static str; | ||
|
||
fn try_from(pointer: &'a Pointer<K, V, Ver, H>) -> Result<Self, Self::Error> { | ||
match pointer { | ||
Pointer::Values(vals) => Ok(PointerSer::Vals(vals.as_ref())), | ||
Pointer::Link { cid, .. } => Ok(PointerSer::Link(cid)), | ||
Pointer::Dirty(_) => Err("Cannot serialize cached values"), | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// Serialize the Pointer like an untagged enum. | ||
impl<K, V, H> Serialize for Pointer<K, V, H> | ||
impl<K, V, Ver, H> Serialize for Pointer<K, V, Ver, H> | ||
where | ||
K: Serialize, | ||
V: Serialize, | ||
Ver: self::version::Version, | ||
{ | ||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||
where | ||
S: Serializer, | ||
{ | ||
match self { | ||
Pointer::Values(vals) => vals.serialize(serializer), | ||
Pointer::Link { cid, .. } => cid.serialize(serializer), | ||
Pointer::Dirty(_) => Err(ser::Error::custom("Cannot serialize cached values")), | ||
match Ver::NUMBER { | ||
0 => pointer_v0::PointerSer::try_from(self) | ||
.map_err(ser::Error::custom)? | ||
.serialize(serializer), | ||
_ => match self { | ||
Pointer::Values(vals) => vals.serialize(serializer), | ||
Pointer::Link { cid, .. } => cid.serialize(serializer), | ||
Pointer::Dirty(_) => Err(ser::Error::custom("Cannot serialize cached values")), | ||
}, | ||
} | ||
} | ||
} | ||
|
||
impl<K, V, H> TryFrom<Ipld> for Pointer<K, V, H> | ||
impl<K, V, Ver, H> TryFrom<Ipld> for Pointer<K, V, Ver, H> | ||
where | ||
K: DeserializeOwned, | ||
V: DeserializeOwned, | ||
|
@@ -81,26 +133,57 @@ where | |
} | ||
|
||
/// Deserialize the Pointer like an untagged enum. | ||
impl<'de, K, V, H> Deserialize<'de> for Pointer<K, V, H> | ||
impl<'de, K, V, Ver, H> Deserialize<'de> for Pointer<K, V, Ver, H> | ||
where | ||
K: DeserializeOwned, | ||
V: DeserializeOwned, | ||
Ver: self::version::Version, | ||
{ | ||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||
where | ||
D: Deserializer<'de>, | ||
{ | ||
Ipld::deserialize(deserializer).and_then(|ipld| ipld.try_into().map_err(de::Error::custom)) | ||
match Ver::NUMBER { | ||
0 => { | ||
let ipld = Ipld::deserialize(deserializer)?; | ||
let (_key, value) = match ipld { | ||
Ipld::Map(map) => map | ||
.into_iter() | ||
.next() | ||
.ok_or("Expected at least one element".to_string()), | ||
other => Err(format!("Expected `Ipld::Map`, got {:#?}", other)), | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should decode this as a tagged enum instead of using #[derive(Deserialize, Serialize)]
enum PointerDe<K, V> {
#[serde(name = "l")]
Link(Cid),
#[serde(name = "v")]
Values(KeyValuePair<K, V>),
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i had to change l and v to
But Hamtv0 tests in this repo fails with:
with the above changes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Huh. I thought it was k/v. I must be thinking of IPFS HAMTs.
Hm. Something funky is going on there. Can you push the code? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (or push it to a new branch) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've pushed the changes with failing hamtv0 tests. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was a sneaky bug. The Vals/Pointer variants were swapped. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah i see, interesting! thanks |
||
.map_err(de::Error::custom)?; | ||
match value { | ||
ipld_list @ Ipld::List(_) => { | ||
let values: Vec<KeyValuePair<K, V>> = | ||
Deserialize::deserialize(ipld_list).map_err(de::Error::custom)?; | ||
Ok(Self::Values(values)) | ||
} | ||
Ipld::Link(cid) => Ok(Self::Link { | ||
cid, | ||
cache: Default::default(), | ||
}), | ||
other => Err(format!( | ||
"Expected `Ipld::List` or `Ipld::Link`, got {:#?}", | ||
other | ||
)), | ||
} | ||
.map_err(de::Error::custom) | ||
} | ||
_ => Ipld::deserialize(deserializer) | ||
.and_then(|ipld| ipld.try_into().map_err(de::Error::custom)), | ||
} | ||
} | ||
} | ||
|
||
impl<K, V, H> Default for Pointer<K, V, H> { | ||
impl<K, V, H, Ver> Default for Pointer<K, V, H, Ver> { | ||
fn default() -> Self { | ||
Pointer::Values(Vec::new()) | ||
} | ||
} | ||
|
||
impl<K, V, H> Pointer<K, V, H> | ||
impl<K, V, Ver, H> Pointer<K, V, Ver, H> | ||
where | ||
K: Serialize + DeserializeOwned + Hash + PartialOrd, | ||
V: Serialize + DeserializeOwned, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to continue to take
H
as a type parameter to avoid breaking the API.