WIP: remove a *bunch* of allocations and clones in hash_prospective_leaf
This commit is contained in:
parent
81da8e9bca
commit
124807bf8b
1 changed files with 82 additions and 16 deletions
|
@ -3,6 +3,7 @@ use alloc::{
|
||||||
string::ToString,
|
string::ToString,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
|
use core::iter;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
EmptySubtreeRoots, Felt, InnerNode, InnerNodeInfo, LeafIndex, MerkleError, MerklePath,
|
EmptySubtreeRoots, Felt, InnerNode, InnerNodeInfo, LeafIndex, MerkleError, MerklePath,
|
||||||
|
@ -287,25 +288,90 @@ impl SparseMerkleTree<SMT_DEPTH> for Smt {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash_prospective_leaf(&self, key: &RpoDigest, value: &Word) -> RpoDigest {
|
fn hash_prospective_leaf(&self, key: &RpoDigest, value: &Word) -> RpoDigest {
|
||||||
// If this key already has a value, then the hash will be based off a
|
// This function combines logic from SmtLeaf::insert() and SmtLeaf::hash() to determine what
|
||||||
// prospective mutation on the leaf.
|
// the hash of a leaf would be with the `(key, value)` pair inserted into it, without simply
|
||||||
let leaf_index: LeafIndex<SMT_DEPTH> = Self::key_to_leaf_index(&key);
|
// cloning the leaf which could be expensive for some leaves, and is easily avoidable when
|
||||||
|
// we can combine the insertion and hashing operations.
|
||||||
|
let new_pair = (*key, *value);
|
||||||
|
let is_removal: bool = *value == EMPTY_WORD;
|
||||||
|
|
||||||
|
let leaf_index: LeafIndex<SMT_DEPTH> = Self::key_to_leaf_index(key);
|
||||||
match self.leaves.get(&leaf_index.value()) {
|
match self.leaves.get(&leaf_index.value()) {
|
||||||
Some(existing_leaf) => {
|
// If this key doesn't have a value, our job is very simple.
|
||||||
if value == &Self::EMPTY_VALUE {
|
None => SmtLeaf::Single(new_pair).hash(),
|
||||||
// A leaf with an empty value is conceptually a removal the
|
|
||||||
// value in that leaf with this key.
|
// If this key already has a value, then the hash will be based off a prospective
|
||||||
// TODO: avoid cloning the leaf.
|
// mutation on the leaf.
|
||||||
let mut cloned = existing_leaf.clone();
|
Some(existing_leaf) => match existing_leaf {
|
||||||
cloned.remove(*key);
|
// Inserting an empty value into an empty leaf or a single leaf both do the same
|
||||||
return cloned.hash();
|
// thing.
|
||||||
|
SmtLeaf::Empty(_) | SmtLeaf::Single(_) if is_removal => {
|
||||||
|
SmtLeaf::new_empty(key.into()).hash()
|
||||||
|
},
|
||||||
|
|
||||||
|
SmtLeaf::Empty(_) => SmtLeaf::Single(new_pair).hash(),
|
||||||
|
|
||||||
|
SmtLeaf::Single(pair) => {
|
||||||
|
if pair.0 == *key {
|
||||||
|
SmtLeaf::Single(new_pair).hash()
|
||||||
|
} else {
|
||||||
|
// Inserting a non-empty value into a new key would change this to a
|
||||||
|
// multi-leaf.
|
||||||
|
// TODO: mini-optimization: use an array with each key's and value's Felts
|
||||||
|
// flattened inline to avoid the Vec allocation.
|
||||||
|
let elements: Vec<Felt> = [*pair, new_pair]
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(leaf::kv_to_elements)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Rpo256::hash_elements(&elements)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
SmtLeaf::Multiple(pairs) => {
|
||||||
|
match pairs.binary_search_by(|&(cur_key, _)| leaf::cmp_keys(cur_key, *key)) {
|
||||||
|
Ok(pos) => {
|
||||||
|
if is_removal && pairs.len() == 2 {
|
||||||
|
// This removal would convert this Multi into a Single, so we can
|
||||||
|
// just stop here.
|
||||||
|
return SmtLeaf::Single(pairs[0]).hash();
|
||||||
|
}
|
||||||
|
|
||||||
|
let (before_pos, rest) = pairs.split_at(pos);
|
||||||
|
let with_pos_removed = rest.iter().copied().skip(1);
|
||||||
|
let middle = iter::once(new_pair).filter(|_| !is_removal);
|
||||||
|
let elements: Vec<Felt> = before_pos
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.chain(middle)
|
||||||
|
.chain(with_pos_removed)
|
||||||
|
.flat_map(leaf::kv_to_elements)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Rpo256::hash_elements(&elements)
|
||||||
|
}
|
||||||
|
Err(pos_for_insert) => {
|
||||||
|
if is_removal {
|
||||||
|
// The only values are at other keys, so we just hash the leaf
|
||||||
|
// as-is.
|
||||||
|
return existing_leaf.hash();
|
||||||
|
}
|
||||||
|
|
||||||
|
let (before_pos, rest) = pairs.split_at(pos_for_insert);
|
||||||
|
let middle = iter::once(new_pair);
|
||||||
|
let elements: Vec<Felt> = before_pos
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.chain(middle)
|
||||||
|
.chain(rest.iter().copied())
|
||||||
|
.flat_map(leaf::kv_to_elements)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Rpo256::hash_elements(&elements)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// TODO: avoid cloning the leaf.
|
|
||||||
let mut cloned = existing_leaf.clone();
|
|
||||||
cloned.insert(*key, *value);
|
|
||||||
cloned.hash()
|
|
||||||
},
|
},
|
||||||
None => SmtLeaf::new_single(*key, *value).hash(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue