mvcc_cell 0.1.2

Software-transactional memory for Rust
Documentation
use std::collections::{BTreeMap,BTreeSet};
use std::fmt::Debug;
use parking_lot::Mutex;

#[derive(Debug)]
pub struct Histogram<K>(BTreeMap<K,usize>);

impl<K> Default for Histogram<K> {
    fn default()->Self { Histogram(BTreeMap::new()) }
}

#[allow(dead_code)]
impl<K:Ord + Debug + Copy> Histogram<K> {
    pub fn new()->Self { Self::default() }

    pub fn inc(&mut self, k:K) {
        *self.0.entry(k).or_default() += 1;
    }

    pub fn dec(&mut self, k:K) {
        use std::collections::btree_map::Entry::*;
        let Occupied(mut slot) = self.0.entry(k) else { panic!("Underflow, k={k:?}") };
        *slot.get_mut() -= 1;
        if *slot.get() == 0 { slot.remove(); }
    }

    pub fn iter(&self)->impl '_ + Iterator<Item=(K,usize)> {
        self.0.iter().map(|(&k,&v)| (k,v))
    }

    pub fn keys(&self)->impl '_ + Iterator<Item=K> {
        self.0.keys().copied()
    }
}

#[derive(Debug)]
pub struct TokenHist<K>(Mutex<Histogram<K>>);

impl<K> Default for TokenHist<K> {
    fn default()->Self { TokenHist(Mutex::default()) }
}

impl<K: Ord+Debug+Copy> TokenHist<K> {
    #[allow(dead_code)]
    pub fn new()->Self { Self::default() }

    pub fn make_token(&self, k:K)->Token<'_,K> {
        self.0.lock().inc(k);
        Token { k, hist: self }
    }

    #[allow(dead_code)]
    pub fn counts(&self)->BTreeMap<K,usize> {
        self.0.lock().0.clone()
    }

    pub fn keys(&self)->BTreeSet<K> {
        self.0.lock().keys().collect()
    }
}

pub struct Token<'hist, K:Ord+Debug+Copy> {
    hist: &'hist TokenHist<K>,
    k: K
}

impl<K: Ord+Debug+Copy> std::ops::Deref for Token<'_,K> {
    type Target=K;
    fn deref(&self)->&K { &self.k }
}

impl<K: Ord+Debug+Copy> Drop for Token<'_,K> {
    fn drop(&mut self) {
        self.hist.0.lock().dec(self.k);
    }
}

#[cfg(test)]
mod tests {
    use proptest::prelude::*;
    use super::*;

    proptest! {
        #[test] fn test_histogram(items in prop::collection::vec(0_usize..=0xF, 0..=0xFFF)) {
            let mut h: Histogram<usize> = Histogram::new();

            for &it in items.iter() { h.inc(it); }

            assert_eq!(h.iter().map(|(_,v)| v).sum::<usize>(), items.len());
            for (k,v) in h.iter() {
                assert_eq!(v, items.iter().filter(|&&it| it == k).count());
            }

            for &it in items.iter() { h.dec(it); }

            assert!(h.keys().next().is_none());
        }

        #[test] fn test_token_hist(items in prop::collection::vec(0_usize..=0xF, 0..=0xFFF)) {
            let h: TokenHist<usize> = TokenHist::new();
            let mut tokens: Vec<Token<'_, usize>> = vec![];

            for &it in items.iter() { tokens.push(h.make_token(it)); }

            assert_eq!(h.counts().into_iter().map(|(_,v)| v).sum::<usize>(), items.len());
            for (k,v) in h.counts() {
                assert_eq!(v, items.iter().filter(|&&it| it == k).count());
            }

            tokens.truncate(0);

            assert_eq!(h.keys().len(), 0);
        }
    }
}