After some discussion with PVH I realise that the repo structure in the last reorg was very rust-centric. In an attempt to put each language on a level footing move the rust code and project files into ./rust
137 lines
3.9 KiB
Rust
137 lines
3.9 KiB
Rust
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()
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
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 }
|
|
}
|
|
}
|