Skip to content

Commit

Permalink
Add support for HamtV0 from forest(commit hash: b622af5a6) (filecoin-…
Browse files Browse the repository at this point in the history
  • Loading branch information
creativcoder authored Aug 2, 2023
1 parent ac2b9b4 commit 0f247eb
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 37 deletions.
22 changes: 16 additions & 6 deletions ipld/hamt/src/hamt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ use serde::{Serialize, Serializer};

use crate::hash_bits::HashBits;
use crate::node::Node;
use crate::{Config, Error, Hash, HashAlgorithm, Sha256};
use crate::pointer::version::Version;
use crate::{pointer::version, Config, Error, Hash, HashAlgorithm, Sha256};

/// Implementation of the HAMT data structure for IPLD.
///
Expand All @@ -33,21 +34,27 @@ use crate::{Config, Error, Hash, HashAlgorithm, Sha256};
/// assert_eq!(map.get::<_>(&1).unwrap(), None);
/// let cid = map.flush().unwrap();
/// ```
pub type Hamt<BS, V, K = BytesKey, H = Sha256> = HamtImpl<BS, V, version::V3, K, H>;
/// Legacy amt V0
pub type Hamtv0<BS, V, K = BytesKey, H = Sha256> = HamtImpl<BS, V, version::V0, K, H>;

#[derive(Debug)]
pub struct Hamt<BS, V, K = BytesKey, H = Sha256> {
root: Node<K, V, H>,
#[doc(hidden)]
pub struct HamtImpl<BS, V, Ver, K = BytesKey, H = Sha256> {
root: Node<K, V, Ver, H>,
store: BS,
conf: Config,
hash: PhantomData<H>,
/// Remember the last flushed CID until it changes.
flushed_cid: Option<Cid>,
}

impl<BS, V, K, H> Serialize for Hamt<BS, V, K, H>
impl<BS, V, Ver, K, H> Serialize for HamtImpl<BS, V, Ver, K, H>
where
K: Serialize,
V: Serialize,
H: HashAlgorithm,
Ver: Version,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
Expand All @@ -57,17 +64,20 @@ where
}
}

impl<K: PartialEq, V: PartialEq, S: Blockstore, H: HashAlgorithm> PartialEq for Hamt<S, V, K, H> {
impl<K: PartialEq, V: PartialEq, S: Blockstore, H: HashAlgorithm, Ver> PartialEq
for HamtImpl<S, V, Ver, K, H>
{
fn eq(&self, other: &Self) -> bool {
self.root == other.root
}
}

impl<BS, V, K, H> Hamt<BS, V, K, H>
impl<BS, V, Ver, K, H> HamtImpl<BS, V, Ver, K, H>
where
K: Hash + Eq + PartialOrd + Serialize + DeserializeOwned,
V: Serialize + DeserializeOwned,
BS: Blockstore,
Ver: Version,
H: HashAlgorithm,
{
pub fn new(store: BS) -> Self {
Expand Down
2 changes: 1 addition & 1 deletion ipld/hamt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ 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::*;

Expand Down
34 changes: 19 additions & 15 deletions ipld/hamt/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,28 @@ use super::bitfield::Bitfield;
use super::hash_bits::HashBits;
use super::pointer::Pointer;
use super::{Error, Hash, HashAlgorithm, KeyValuePair};
use crate::pointer::version::Version;
use crate::Config;

/// Node in Hamt tree which contains bitfield of set indexes and pointers to nodes
#[derive(Debug)]
pub(crate) struct Node<K, V, H> {
pub(crate) struct Node<K, V, Ver, H> {
pub(crate) bitfield: Bitfield,
pub(crate) pointers: Vec<Pointer<K, V, H>>,
pub(crate) pointers: Vec<Pointer<K, V, Ver, H>>,
hash: PhantomData<H>,
}

impl<K: PartialEq, V: PartialEq, H> PartialEq for Node<K, V, H> {
impl<K: PartialEq, V: PartialEq, H, Ver> PartialEq for Node<K, V, H, Ver> {
fn eq(&self, other: &Self) -> bool {
(self.bitfield == other.bitfield) && (self.pointers == other.pointers)
}
}

impl<K, V, H> Serialize for Node<K, V, H>
impl<K, V, Ver, H> Serialize for Node<K, V, Ver, H>
where
K: Serialize,
V: Serialize,
Ver: self::Version,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
Expand All @@ -46,10 +48,11 @@ where
}
}

impl<'de, K, V, H> Deserialize<'de> for Node<K, V, H>
impl<'de, K, V, Ver, H> Deserialize<'de> for Node<K, V, Ver, H>
where
K: DeserializeOwned,
V: DeserializeOwned,
Ver: Version,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
Expand All @@ -64,7 +67,7 @@ where
}
}

impl<K, V, H> Default for Node<K, V, H> {
impl<K, V, H, Ver> Default for Node<K, V, H, Ver> {
fn default() -> Self {
Node {
bitfield: Bitfield::zero(),
Expand All @@ -74,11 +77,12 @@ impl<K, V, H> Default for Node<K, V, H> {
}
}

impl<K, V, H> Node<K, V, H>
impl<K, V, Ver, H> Node<K, V, Ver, H>
where
K: Hash + Eq + PartialOrd + Serialize + DeserializeOwned,
H: HashAlgorithm,
V: Serialize + DeserializeOwned,
Ver: Version,
{
pub fn set<S: Blockstore>(
&mut self,
Expand Down Expand Up @@ -318,7 +322,7 @@ where
// Link node is cached
cached_node
} else {
let node: Box<Node<K, V, H>> = if let Some(node) = store.get_cbor(cid)? {
let node: Box<Node<K, V, Ver, H>> = if let Some(node) = store.get_cbor(cid)? {
node
} else {
#[cfg(not(feature = "ignore-dead-links"))]
Expand Down Expand Up @@ -367,7 +371,7 @@ where
self.insert_child(idx, key, value);
} else {
// Need to insert some empty nodes reserved for links.
let mut sub = Node::<K, V, H>::default();
let mut sub = Node::<K, V, Ver, H>::default();
sub.modify_value(hashed_key, conf, depth + 1, key, value, store, overwrite)?;
self.insert_child_dirty(idx, Box::new(sub));
}
Expand Down Expand Up @@ -433,7 +437,7 @@ where
});

let consumed = hashed_key.consumed;
let mut sub = Node::<K, V, H>::default();
let mut sub = Node::<K, V, Ver, H>::default();
let modified = sub.modify_value(
hashed_key,
conf,
Expand Down Expand Up @@ -568,7 +572,7 @@ where
Ok(())
}

fn rm_child(&mut self, i: usize, idx: u32) -> Pointer<K, V, H> {
fn rm_child(&mut self, i: usize, idx: u32) -> Pointer<K, V, Ver, H> {
self.bitfield.clear_bit(idx);
self.pointers.remove(i)
}
Expand All @@ -579,7 +583,7 @@ where
self.pointers.insert(i, Pointer::from_key_value(key, value))
}

fn insert_child_dirty(&mut self, idx: u32, node: Box<Node<K, V, H>>) {
fn insert_child_dirty(&mut self, idx: u32, node: Box<Node<K, V, Ver, H>>) {
let i = self.index_for_bit_pos(idx);
self.bitfield.set_bit(idx);
self.pointers.insert(i, Pointer::Dirty(node))
Expand All @@ -591,19 +595,19 @@ where
mask.and(&self.bitfield).count_ones()
}

fn get_child_mut(&mut self, i: usize) -> &mut Pointer<K, V, H> {
fn get_child_mut(&mut self, i: usize) -> &mut Pointer<K, V, Ver, H> {
&mut self.pointers[i]
}

fn get_child(&self, i: usize) -> &Pointer<K, V, H> {
fn get_child(&self, i: usize) -> &Pointer<K, V, Ver, H> {
&self.pointers[i]
}

/// Clean after delete to retrieve canonical form.
///
/// Returns true if the child pointer is completely empty and can be removed,
/// which can happen if we artificially inserted nodes during insertion.
fn clean(child: &mut Pointer<K, V, H>, conf: &Config, depth: u32) -> Result<bool, Error> {
fn clean(child: &mut Pointer<K, V, Ver, H>, conf: &Config, depth: u32) -> Result<bool, Error> {
match child.clean(conf, depth) {
Ok(()) => Ok(false),
Err(Error::ZeroPointers) if depth < conf.min_data_depth => Ok(true),
Expand Down
112 changes: 98 additions & 14 deletions ipld/hamt/src/pointer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,38 @@ use super::node::Node;
use super::{Error, Hash, HashAlgorithm, KeyValuePair};
use crate::Config;

#[doc(hidden)]
pub mod version {
#[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,
Expand All @@ -37,25 +57,80 @@ impl<K: PartialEq, V: PartialEq, H> PartialEq for Pointer<K, V, H> {
}
}

mod pointer_v0 {
use cid::Cid;
use serde::{Deserialize, Serialize};

use crate::KeyValuePair;

use super::Pointer;

#[derive(Serialize)]
pub(super) enum PointerSer<'a, K, V> {
#[serde(rename = "0")]
Link(&'a Cid),
#[serde(rename = "1")]
Vals(&'a [KeyValuePair<K, V>]),
}

#[derive(Deserialize, Serialize)]
pub(super) enum PointerDe<K, V> {
#[serde(rename = "0")]
Link(Cid),
#[serde(rename = "1")]
Vals(Vec<KeyValuePair<K, V>>),
}

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"),
}
}
}

impl<K, V, Ver, H> From<PointerDe<K, V>> for Pointer<K, V, Ver, H> {
fn from(pointer: PointerDe<K, V>) -> Self {
match pointer {
PointerDe::Link(cid) => Pointer::Link {
cid,
cache: Default::default(),
},
PointerDe::Vals(vals) => Pointer::Values(vals),
}
}
}
}

/// 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,
Expand All @@ -81,26 +156,35 @@ 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 pointer_de: pointer_v0::PointerDe<K, V> =
Deserialize::deserialize(deserializer)?;
Ok(Pointer::from(pointer_de))
}
_ => 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,
Expand Down
Loading

0 comments on commit 0f247eb

Please sign in to comment.