use alloc::{collections::BTreeMap, vec::Vec};
use core::{cmp, fmt, iter, ops};
use hashbrown::HashMap;
#[derive(Clone)]
pub struct StorageDiff {
btree: BTreeMap<Vec<u8>, bool>,
hashmap: HashMap<Vec<u8>, Option<Vec<u8>>, fnv::FnvBuildHasher>,
}
impl StorageDiff {
pub fn empty() -> Self {
Self {
btree: BTreeMap::default(),
hashmap: HashMap::with_capacity_and_hasher(0, Default::default()),
}
}
pub fn clear(&mut self) {
self.hashmap.clear();
self.btree.clear();
}
pub fn diff_insert(
&mut self,
key: impl Into<Vec<u8>>,
value: impl Into<Vec<u8>>,
) -> Option<Option<Vec<u8>>> {
let key = key.into();
let previous = self.hashmap.insert(key.clone(), Some(value.into()));
match &previous {
Some(Some(_)) => {
debug_assert_eq!(self.btree.get(&key), Some(&true));
}
None | Some(None) => {
self.btree.insert(key, true);
}
}
previous
}
pub fn diff_insert_erase(&mut self, key: impl Into<Vec<u8>>) -> Option<Option<Vec<u8>>> {
let key = key.into();
let previous = self.hashmap.insert(key.clone(), None);
match &previous {
Some(None) => {
debug_assert_eq!(self.btree.get(&key), Some(&false));
}
None | Some(Some(_)) => {
self.btree.insert(key, false);
}
}
previous
}
pub fn diff_remove(&mut self, key: impl AsRef<[u8]>) -> Option<Option<Vec<u8>>> {
let previous = self.hashmap.remove(key.as_ref());
if let Some(_previous) = &previous {
let _in_btree = self.btree.remove(key.as_ref());
debug_assert_eq!(_in_btree, Some(_previous.is_some()));
}
previous
}
pub fn diff_get(&self, key: &[u8]) -> Option<Option<&[u8]>> {
self.hashmap.get(key).map(|v| v.as_ref().map(|v| &v[..]))
}
pub fn diff_iter_unordered(
&self,
) -> impl ExactSizeIterator<Item = (&[u8], Option<&[u8]>)> + Clone {
self.hashmap
.iter()
.map(|(k, v)| (&k[..], v.as_ref().map(|v| &v[..])))
}
pub fn diff_into_iter_unordered(
self,
) -> impl ExactSizeIterator<Item = (Vec<u8>, Option<Vec<u8>>)> {
self.hashmap.into_iter()
}
pub fn storage_get<'a, 'b>(
&'a self,
key: &'b [u8],
or_parent: impl FnOnce() -> Option<&'a [u8]>,
) -> Option<&'a [u8]> {
self.hashmap
.get(key)
.map_or_else(or_parent, |opt| opt.as_ref().map(|v| &v[..]))
}
pub fn storage_next_key<'a, 'b>(
&'a self,
key: &'b [u8],
in_parent_next_key: Option<&'a [u8]>,
) -> StorageNextKey<'a> {
if let Some(in_parent_next_key) = in_parent_next_key {
assert!(in_parent_next_key > key);
}
let in_diff = self
.btree
.range::<[u8], _>((ops::Bound::Excluded(key), ops::Bound::Unbounded))
.next();
match (in_parent_next_key, in_diff) {
(Some(a), Some((b, true))) if a <= &b[..] => StorageNextKey::Found(Some(a)),
(Some(a), Some((b, false))) if a < &b[..] => StorageNextKey::Found(Some(a)),
(Some(a), Some((b, false))) => {
debug_assert!(a >= &b[..]);
debug_assert_ne!(&b[..], key);
StorageNextKey::NextOf(b)
}
(Some(a), Some((b, true))) => {
debug_assert!(a >= &b[..]);
StorageNextKey::Found(Some(&b[..]))
}
(Some(a), None) => StorageNextKey::Found(Some(a)),
(None, Some((b, true))) => StorageNextKey::Found(Some(&b[..])),
(None, Some((b, false))) => {
debug_assert!(&b[..] > key);
let found = self
.btree
.range::<[u8], _>((ops::Bound::Excluded(&b[..]), ops::Bound::Unbounded))
.find(|(_, value)| **value)
.map(|(key, _)| &key[..]);
StorageNextKey::Found(found)
}
(None, None) => StorageNextKey::Found(None),
}
}
pub fn storage_prefix_keys_ordered<'a>(
&'a self, prefix: &'a [u8],
in_parent_ordered: impl Iterator<Item = impl AsRef<[u8]> + 'a> + 'a,
) -> impl Iterator<Item = impl AsRef<[u8]> + 'a> + 'a {
let mut in_finalized_filtered = in_parent_ordered
.filter(|k| !self.btree.contains_key(k.as_ref()))
.peekable();
let mut diff_inserted = self
.btree
.range::<[u8], _>((ops::Bound::Included(prefix), ops::Bound::Unbounded))
.take_while(|(k, _)| k.starts_with(prefix))
.filter(|(_, v)| **v)
.map(|(k, _)| &k[..])
.peekable();
iter::from_fn(
move || match (in_finalized_filtered.peek(), diff_inserted.peek()) {
(Some(_), None) => in_finalized_filtered.next().map(either::Left),
(Some(a), Some(b)) if a.as_ref() < *b => {
in_finalized_filtered.next().map(either::Left)
}
(Some(a), Some(b)) => {
debug_assert_ne!(a.as_ref(), *b);
diff_inserted.next().map(either::Right)
}
(None, Some(_)) => diff_inserted.next().map(either::Right),
(None, None) => None,
},
)
}
pub fn merge(&mut self, other: &StorageDiff) {
for (key, value) in &other.hashmap {
self.hashmap.insert(key.clone(), value.clone());
self.btree.insert(key.clone(), value.is_some());
}
}
}
impl fmt::Debug for StorageDiff {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.hashmap, f)
}
}
impl cmp::PartialEq for StorageDiff {
fn eq(&self, other: &Self) -> bool {
self.hashmap == other.hashmap
}
}
impl cmp::Eq for StorageDiff {}
impl Default for StorageDiff {
fn default() -> Self {
StorageDiff::empty()
}
}
impl FromIterator<(Vec<u8>, Option<Vec<u8>>)> for StorageDiff {
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = (Vec<u8>, Option<Vec<u8>>)>,
{
let hashmap = iter
.into_iter()
.collect::<HashMap<Vec<u8>, Option<Vec<u8>>, fnv::FnvBuildHasher>>();
let btree = hashmap
.iter()
.map(|(k, v)| (k.clone(), v.is_some()))
.collect();
Self { btree, hashmap }
}
}
pub enum StorageNextKey<'a> {
Found(Option<&'a [u8]>),
NextOf(&'a [u8]),
}