use std::collections::{HashMap, HashSet};
use std::convert::TryInto;
use super::bytes32::{hash_blob, hash_blobs, Bytes32};
#[derive(Debug)]
pub struct ReadCacheLookup {
root_hash: Bytes32,
read_stack: Vec<(Bytes32, Bytes32)>,
count: HashMap<Bytes32, u32>,
parent_lookup: HashMap<Bytes32, Vec<(Bytes32, u8)>>,
}
impl Default for ReadCacheLookup {
fn default() -> Self {
Self::new()
}
}
impl ReadCacheLookup {
pub fn new() -> Self {
let root_hash = hash_blob(&[1]);
let read_stack = vec![];
let mut count = HashMap::default();
count.insert(root_hash, 1);
let parent_lookup = HashMap::default();
Self {
root_hash,
read_stack,
count,
parent_lookup,
}
}
pub fn push(&mut self, id: Bytes32) {
let new_root_hash = hash_blobs(&[&[2], &id, &self.root_hash]);
self.read_stack.push((id, self.root_hash));
*self.count.entry(id).or_insert(0) += 1;
*self.count.entry(new_root_hash).or_insert(0) += 1;
let new_parent_to_old_root = (new_root_hash, 0);
self.parent_lookup
.entry(id)
.or_default()
.push(new_parent_to_old_root);
let new_parent_to_id = (new_root_hash, 1);
self.parent_lookup
.entry(self.root_hash)
.or_default()
.push(new_parent_to_id);
self.root_hash = new_root_hash;
}
fn pop(&mut self) -> (Bytes32, Bytes32) {
let item = self.read_stack.pop().expect("stack empty");
*self.count.entry(item.0).or_insert(0) -= 1;
*self.count.entry(self.root_hash).or_insert(0) -= 1;
self.root_hash = item.1;
item
}
pub fn pop2_and_cons(&mut self) {
let right = self.pop();
let left = self.pop();
*self.count.entry(left.0).or_insert(0) += 1;
*self.count.entry(right.0).or_insert(0) += 1;
let new_root_hash = hash_blobs(&[&[2], &left.0, &right.0]);
self.parent_lookup
.entry(left.0)
.or_default()
.push((new_root_hash, 0));
self.parent_lookup
.entry(right.0)
.or_default()
.push((new_root_hash, 1));
self.push(new_root_hash);
}
pub fn find_paths(&self, id: &Bytes32, serialized_length: u64) -> Vec<Vec<u8>> {
let mut seen_ids = HashSet::<&Bytes32>::default();
let mut possible_responses = vec![];
if serialized_length < 3 {
return possible_responses;
}
assert!(serialized_length > 2);
let max_bytes_for_path_encoding = serialized_length - 2; let max_path_length: usize = (max_bytes_for_path_encoding.saturating_mul(8) - 1)
.try_into()
.unwrap_or(usize::MAX);
seen_ids.insert(id);
let mut partial_paths = vec![(*id, vec![])];
loop {
if partial_paths.is_empty() {
break;
}
let mut new_partial_paths = vec![];
for (node, path) in partial_paths.iter_mut() {
if *node == self.root_hash {
possible_responses.push(reversed_path_to_vec_u8(path));
continue;
}
let parents = self.parent_lookup.get(node);
if let Some(items) = parents {
for (parent, direction) in items.iter() {
if *(self.count.get(parent).unwrap_or(&0)) > 0 && !seen_ids.contains(parent)
{
let mut new_path = path.clone();
new_path.push(*direction);
if new_path.len() > max_path_length {
return possible_responses;
}
new_partial_paths.push((*parent, new_path));
}
seen_ids.insert(parent);
}
}
}
if !possible_responses.is_empty() {
break;
}
partial_paths = new_partial_paths;
}
possible_responses
}
pub fn find_path(&self, id: &Bytes32, serialized_length: u64) -> Option<Vec<u8>> {
let mut paths = self.find_paths(id, serialized_length);
if !paths.is_empty() {
paths.sort();
paths.truncate(1);
paths.pop()
} else {
None
}
}
}
fn reversed_path_to_vec_u8(path: &[u8]) -> Vec<u8> {
let byte_count = (path.len() + 1 + 7) >> 3;
let mut v = vec![0; byte_count];
let mut index = byte_count - 1;
let mut mask: u8 = 1;
for p in path.iter().rev() {
if *p != 0 {
v[index] |= mask;
}
mask = {
if mask == 0x80 {
index -= 1;
1
} else {
mask + mask
}
};
}
v[index] |= mask;
v
}
#[test]
fn test_path_to_vec_u8() {
assert_eq!(reversed_path_to_vec_u8(&[]), vec!(0b1));
assert_eq!(reversed_path_to_vec_u8(&[0]), vec!(0b10));
assert_eq!(reversed_path_to_vec_u8(&[1]), vec!(0b11));
assert_eq!(reversed_path_to_vec_u8(&[0, 0]), vec!(0b100));
assert_eq!(reversed_path_to_vec_u8(&[0, 1]), vec!(0b101));
assert_eq!(reversed_path_to_vec_u8(&[1, 0]), vec!(0b110));
assert_eq!(reversed_path_to_vec_u8(&[1, 1]), vec!(0b111));
assert_eq!(reversed_path_to_vec_u8(&[1, 1, 1]), vec!(0b1111));
assert_eq!(reversed_path_to_vec_u8(&[0, 1, 1, 1]), vec!(0b10111));
assert_eq!(reversed_path_to_vec_u8(&[1, 0, 1, 1, 1]), vec!(0b110111));
assert_eq!(
reversed_path_to_vec_u8(&[1, 1, 0, 1, 1, 1]),
vec!(0b1110111)
);
assert_eq!(
reversed_path_to_vec_u8(&[0, 1, 1, 0, 1, 1, 1]),
vec!(0b10110111)
);
assert_eq!(
reversed_path_to_vec_u8(&[0, 0, 1, 1, 0, 1, 1, 1]),
vec!(0b1, 0b00110111)
);
assert_eq!(
reversed_path_to_vec_u8(&[1, 0, 0, 1, 1, 0, 1, 1, 1]),
vec!(0b11, 0b00110111)
);
}
#[test]
fn test_read_cache_lookup() {
let large_max = 30;
let mut rcl = ReadCacheLookup::new();
let hash_of_nil = hash_blob(&[1]);
assert_eq!(rcl.find_paths(&hash_of_nil, large_max), [[1]]);
assert_eq!(rcl.count.get(&hash_of_nil).unwrap(), &1);
let hash_of_1_atom = hash_blobs(&[&[1], &[1]]);
assert!(rcl.find_paths(&hash_of_1_atom, large_max).is_empty());
let hash_of_5_atom = hash_blobs(&[&[1], &[5]]);
rcl.push(hash_of_5_atom);
let hash_of_cons_5_nil = hash_blobs(&[&[2], &hash_of_5_atom, &hash_of_nil]);
assert_eq!(rcl.find_paths(&hash_of_cons_5_nil, large_max), [[1]]);
assert_eq!(rcl.find_paths(&hash_of_5_atom, large_max), [[2]]);
assert_eq!(rcl.find_paths(&hash_of_nil, large_max), [[3]]);
assert_eq!(rcl.count.get(&hash_of_cons_5_nil).unwrap(), &1);
assert_eq!(rcl.count.get(&hash_of_5_atom).unwrap(), &1);
assert_eq!(rcl.count.get(&hash_of_nil).unwrap(), &1);
assert!(rcl.find_paths(&hash_of_1_atom, large_max).is_empty());
let hash_of_9_atom = hash_blobs(&[&[1], &[9]]);
rcl.push(hash_of_9_atom);
let hash_of_cons_9_cons_5_nil = hash_blobs(&[&[2], &hash_of_9_atom, &hash_of_cons_5_nil]);
assert_eq!(rcl.find_paths(&hash_of_cons_9_cons_5_nil, large_max), [[1]]);
assert_eq!(rcl.find_paths(&hash_of_9_atom, large_max), [[2]]);
assert_eq!(rcl.find_paths(&hash_of_cons_5_nil, large_max), [[3]]);
assert_eq!(rcl.find_paths(&hash_of_5_atom, large_max), [[5]]);
assert_eq!(rcl.find_paths(&hash_of_nil, large_max), [[7]]);
assert_eq!(rcl.count.get(&hash_of_cons_9_cons_5_nil).unwrap(), &1);
assert_eq!(rcl.count.get(&hash_of_9_atom).unwrap(), &1);
assert_eq!(rcl.count.get(&hash_of_cons_5_nil).unwrap(), &1);
assert_eq!(rcl.count.get(&hash_of_5_atom).unwrap(), &1);
assert_eq!(rcl.count.get(&hash_of_nil).unwrap(), &1);
assert!(rcl.find_paths(&hash_of_1_atom, large_max).is_empty());
let hash_of_10_atom = hash_blobs(&[&[1], &[10]]);
rcl.push(hash_of_10_atom);
let hash_of_cons_10_cons_9_cons_5_nil =
hash_blobs(&[&[2], &hash_of_10_atom, &hash_of_cons_9_cons_5_nil]);
assert_eq!(
rcl.find_paths(&hash_of_cons_10_cons_9_cons_5_nil, large_max),
[[1]]
);
assert_eq!(rcl.find_paths(&hash_of_10_atom, large_max), [[2]]);
assert_eq!(rcl.find_paths(&hash_of_cons_9_cons_5_nil, large_max), [[3]]);
assert_eq!(rcl.find_paths(&hash_of_9_atom, large_max), [[5]]);
assert_eq!(rcl.find_paths(&hash_of_cons_5_nil, large_max), [[7]]);
assert_eq!(rcl.find_paths(&hash_of_5_atom, large_max), [[11]]);
assert_eq!(rcl.find_paths(&hash_of_nil, large_max), [[15]]);
assert_eq!(
rcl.count.get(&hash_of_cons_10_cons_9_cons_5_nil).unwrap(),
&1
);
assert_eq!(rcl.count.get(&hash_of_10_atom).unwrap(), &1);
assert_eq!(rcl.count.get(&hash_of_cons_9_cons_5_nil).unwrap(), &1);
assert_eq!(rcl.count.get(&hash_of_9_atom).unwrap(), &1);
assert_eq!(rcl.count.get(&hash_of_cons_5_nil).unwrap(), &1);
assert_eq!(rcl.count.get(&hash_of_5_atom).unwrap(), &1);
assert_eq!(rcl.count.get(&hash_of_nil).unwrap(), &1);
assert!(rcl.find_paths(&hash_of_1_atom, large_max).is_empty());
rcl.pop2_and_cons();
let hash_of_cons_9_10 = hash_blobs(&[&[2], &hash_of_9_atom, &hash_of_10_atom]);
let hash_of_cons_cons_9_10_cons_5_nil =
hash_blobs(&[&[2], &hash_of_cons_9_10, &hash_of_cons_5_nil]);
assert_eq!(
rcl.find_paths(&hash_of_cons_cons_9_10_cons_5_nil, large_max),
[[1]]
);
assert_eq!(rcl.find_paths(&hash_of_cons_9_10, large_max), [[2]]);
assert_eq!(rcl.find_paths(&hash_of_cons_5_nil, large_max), [[3]]);
assert_eq!(rcl.find_paths(&hash_of_9_atom, large_max), [[4]]);
assert_eq!(rcl.find_paths(&hash_of_10_atom, large_max), [[6]]);
assert_eq!(rcl.find_paths(&hash_of_5_atom, large_max), [[5]]);
assert_eq!(rcl.find_paths(&hash_of_nil, large_max), [[7]]);
assert!(rcl
.find_paths(&hash_of_cons_9_cons_5_nil, large_max)
.is_empty());
assert_eq!(
rcl.count.get(&hash_of_cons_cons_9_10_cons_5_nil).unwrap(),
&1
);
assert_eq!(rcl.count.get(&hash_of_cons_9_10).unwrap(), &1);
assert_eq!(rcl.count.get(&hash_of_cons_5_nil).unwrap(), &1);
assert_eq!(rcl.count.get(&hash_of_9_atom).unwrap(), &1);
assert_eq!(rcl.count.get(&hash_of_10_atom).unwrap(), &1);
assert_eq!(rcl.count.get(&hash_of_5_atom).unwrap(), &1);
assert_eq!(rcl.count.get(&hash_of_nil).unwrap(), &1);
assert_eq!(rcl.count.get(&hash_of_cons_9_cons_5_nil).unwrap(), &0);
assert!(rcl.find_paths(&hash_of_1_atom, large_max).is_empty());
assert!(rcl.count.get(&hash_of_1_atom).is_none());
}