1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
use itertools::Itertools;
use std::collections::HashMap;
use std::hash::Hash;
use std::ops::Index;
#[derive(Debug, Clone)]
pub(crate) struct IndexedCache<T> {
pub(crate) cache: Vec<T>,
lookup: HashMap<T, usize>,
}
impl<T> PartialEq for IndexedCache<T>
where
T: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.cache == other.cache
}
}
impl<T> IndexedCache<T>
where
T: Clone + Eq + Hash + Ord,
{
pub(crate) fn new() -> Self {
IndexedCache {
cache: Default::default(),
lookup: Default::default(),
}
}
pub(crate) fn cache(&mut self, item: T) -> usize {
if let Some(n) = self.lookup.get(&item) {
*n
} else {
let n = self.cache.len();
self.cache.push(item.clone());
self.lookup.insert(item, n);
n
}
}
pub(crate) fn lookup(&self, item: &T) -> Option<usize> {
self.lookup.get(item).cloned()
}
pub(crate) fn len(&self) -> usize {
self.cache.len()
}
pub(crate) fn get(&self, index: usize) -> &T {
&self.cache[index]
}
pub(crate) fn safe_get(&self, index: usize) -> Option<&T> {
self.cache.get(index)
}
/// Remove the last inserted entry into this cache.
/// This is safe to do as it does not require reshuffling other entries.
///
/// # Panics
///
/// Panics on an empty cache.
pub(crate) fn remove_last(&mut self) -> T {
let last = self.cache.len() - 1;
let t = self.cache.remove(last);
self.lookup.remove(&t);
t
}
pub(crate) fn sorted(&self) -> IndexedCache<T> {
let mut sorted = Self::new();
self.cache.iter().sorted().cloned().for_each(|item| {
let n = sorted.cache.len();
sorted.cache.push(item.clone());
sorted.lookup.insert(item, n);
});
sorted
}
/// Create a vector from positions in this index to positions in an equivalent sorted index
///
/// This is useful primarily when encoding an `IndexedCache<ActorId>` in the document format.
/// In this case we encode the actors in sorted order in the document and all ops reference the
/// offset into this sorted actor array. But the `IndexedCache<ActorId>` we have in the
/// application does not contain actors in sorted order because we add them as we encounter
/// them, so we must map from the actor IDs in the application to the actor IDs in the document
/// format
///
/// # Examples
///
/// ```rust,ignore
/// let idx: IndexedCache<String> = IndexedCache::new();
/// let first_idx = idx.cache("b"); // first_idx is `0`
/// let second_idx = idx.cache("a"); // second_idx i `1`
/// let encoded = idx.encode_index();
/// // first_idx (0) maps to `1` whilst second_idx (1) maps to `0` because "a" < "b"
/// assert_eq!(encoded, vec![1,0])
/// ```
pub(crate) fn encode_index(&self) -> Vec<usize> {
let sorted: Vec<_> = self.cache.iter().sorted().cloned().collect();
self.cache
.iter()
.map(|a| sorted.iter().position(|r| r == a).unwrap())
.collect()
}
}
impl<T> IntoIterator for IndexedCache<T> {
type Item = T;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.cache.into_iter()
}
}
impl<T> Index<usize> for IndexedCache<T> {
type Output = T;
fn index(&self, i: usize) -> &T {
&self.cache[i]
}
}
impl<A: Hash + Eq + Clone> FromIterator<A> for IndexedCache<A> {
fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
let mut cache = Vec::new();
let mut lookup = HashMap::new();
for (index, elem) in iter.into_iter().enumerate() {
cache.push(elem.clone());
lookup.insert(elem, index);
}
Self { cache, lookup }
}
}