#![deny(missing_docs)]
#![deny(warnings)]
extern crate bit_set;
extern crate either;
extern crate parking_lot;
extern crate take_mut;
use std::num::NonZeroUsize;
use std::ops::DerefMut;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use bit_set::BitSet;
use either::Either;
use parking_lot::Mutex;
pub trait Calc {
type Value;
fn add_dep(&mut self, seen: &mut BitSet, dep: NonZeroUsize);
fn eval(&mut self, dirty: &mut BitSet) -> (NonZeroUsize, Self::Value);
}
struct Counter(AtomicUsize);
impl Counter {
pub fn new(first_value: NonZeroUsize) -> Self {
Counter(AtomicUsize::new(first_value.get()))
}
pub fn next(&self) -> NonZeroUsize {
let next = self.0.fetch_add(1, Ordering::SeqCst);
unsafe { NonZeroUsize::new_unchecked(next) }
}
}
struct GraphInner {
dirty: Mutex<BitSet>,
next_id: Counter,
}
const CONST_VERSION: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(1) };
const FIRST_VERSION: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(2) };
const FIRST_ID: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(1) };
#[derive(Clone)]
pub struct Node<C> {
calc: C,
graph: Option<Arc<GraphInner>>,
}
pub fn const_<T>(value: T) -> Node<Const<T>> {
Node {
calc: Const(value),
graph: None,
}
}
pub fn lazy<T, F: FnOnce() -> T>(f: F) -> Node<Lazy<T, F>> {
Node {
calc: Lazy(Either::Right(f)),
graph: None,
}
}
fn with_graph<T>(graph: &Option<Arc<GraphInner>>, f: impl FnOnce(&mut BitSet) -> T) -> T {
let mut dirty = graph.as_ref().map(|graph| graph.dirty.lock());
let dirty = dirty.as_mut().map(DerefMut::deref_mut);
let mut no_dirty = BitSet::new();
f(dirty.unwrap_or(&mut no_dirty))
}
impl<C: Calc> Node<C>
where
C::Value: Clone,
{
pub fn get_mut(&mut self) -> C::Value {
let calc = &mut self.calc;
with_graph(&self.graph, move |dirty| calc.eval(dirty).1)
}
}
pub type SharedNode<C> = Node<Arc<Mutex<C>>>;
impl<C: Calc> Node<C> {
pub fn shared(self) -> SharedNode<C> {
let calc = Arc::new(Mutex::new(self.calc));
Node {
calc,
graph: self.graph,
}
}
}
pub type BoxNode<T> = Node<Box<Calc<Value = T> + Send>>;
impl<C: Calc + Send + 'static> Node<C> {
pub fn boxed(self) -> BoxNode<C::Value> {
let calc: Box<Calc<Value = C::Value> + Send> = Box::new(self.calc);
Node {
calc,
graph: self.graph,
}
}
}
impl<C: Calc> SharedNode<C> {
pub fn get(&self) -> C::Value {
with_graph(&self.graph, move |dirty| self.calc.lock().eval(dirty).1)
}
}
impl<T: Clone> Node<Const<T>> {
pub fn get(&self) -> T {
self.calc.0.clone()
}
}
impl<T: Clone> Node<Source<T>> {
pub fn get(&self) -> T {
self.calc.inner.lock().value.clone()
}
}
impl<T> Node<Source<T>> {
pub fn update(&self, updater: impl FnOnce(&mut T) -> bool) {
let mut dirty = self.graph.as_ref().unwrap().dirty.lock();
let mut inner = self.calc.inner.lock();
if !updater(&mut inner.value) {
return;
}
inner.version = self.calc.next_version.next();
dirty.union_with(&inner.deps);
}
pub fn set(&self, value: T) {
self.update(move |value_cell| {
*value_cell = value;
true
})
}
}
fn alloc_id(graph: &Arc<GraphInner>) -> NonZeroUsize {
let id = graph.next_id.next();
graph.dirty.lock().insert(id.get());
id
}
struct SourceInner<T> {
version: NonZeroUsize,
value: T,
deps: BitSet,
}
#[derive(Clone)]
pub struct Source<T> {
inner: Arc<Mutex<SourceInner<T>>>,
next_version: Arc<Counter>,
}
impl<T: Clone> Calc for Source<T> {
type Value = T;
fn add_dep(&mut self, _seen: &mut BitSet, dep: NonZeroUsize) {
self.inner.lock().deps.insert(dep.get());
}
fn eval(&mut self, _dirty: &mut BitSet) -> (NonZeroUsize, T) {
let inner = self.inner.lock();
(inner.version, inner.value.clone())
}
}
pub struct Const<T>(T);
impl<T: Clone> Calc for Const<T> {
type Value = T;
fn add_dep(&mut self, _seen: &mut BitSet, _dep: NonZeroUsize) {}
fn eval(&mut self, _dirty: &mut BitSet) -> (NonZeroUsize, T) {
(CONST_VERSION, self.0.clone())
}
}
pub struct Lazy<T, F>(Either<T, F>);
impl<T: Clone, F: FnOnce() -> T> Calc for Lazy<T, F> {
type Value = T;
fn add_dep(&mut self, _seen: &mut BitSet, _dep: NonZeroUsize) {}
fn eval(&mut self, _dirty: &mut BitSet) -> (NonZeroUsize, T) {
take_mut::take(&mut self.0, |value_or_f| match value_or_f {
Either::Left(value) => Either::Left(value),
Either::Right(f) => Either::Left(f()),
});
match &self.0 {
Either::Left(value) => (CONST_VERSION, value.clone()),
Either::Right(_) => unreachable!(),
}
}
}
pub struct Inspect<C, F> {
f: F,
last_version: usize,
prec: C,
}
impl<C: Calc, F: FnMut(&C::Value)> Calc for Inspect<C, F> {
type Value = C::Value;
fn add_dep(&mut self, seen: &mut BitSet<u32>, dep: NonZeroUsize) {
self.prec.add_dep(seen, dep)
}
fn eval(&mut self, dirty: &mut BitSet<u32>) -> (NonZeroUsize, C::Value) {
let (version, value) = self.prec.eval(dirty);
if version.get() > self.last_version {
self.last_version = version.get();
(self.f)(&value);
}
(version, value)
}
}
impl<C: Calc> Node<C> {
pub fn inspect<F: FnMut(&C::Value)>(self, f: F) -> Node<Inspect<C, F>> {
Node {
calc: Inspect {
f,
last_version: 0,
prec: self.calc,
},
graph: self.graph,
}
}
}
fn eval_func<A, T: Clone + PartialEq>(
dirty: &mut BitSet,
id: Option<NonZeroUsize>,
value_cell: &mut Option<(NonZeroUsize, T)>,
f1: impl FnOnce(&mut BitSet) -> (NonZeroUsize, A),
f2: impl FnOnce(A) -> T,
) -> (NonZeroUsize, T) {
if let Some(id) = id {
let id = id.get();
if dirty.contains(id) {
dirty.remove(id);
} else {
let (version, value) = value_cell.as_ref().unwrap();
return (*version, value.clone());
}
} else if let Some((version, value)) = value_cell.as_ref() {
return (*version, value.clone());
}
let (prec_version, precs) = f1(dirty);
if let Some((version, value)) = value_cell {
if prec_version > *version {
let new_value = f2(precs);
if new_value != *value {
*version = prec_version;
*value = new_value.clone();
return (prec_version, new_value);
}
}
(*version, value.clone())
} else {
let value = f2(precs);
*value_cell = Some((prec_version, value.clone()));
(prec_version, value)
}
}
fn eval_update<A, T: Clone>(
dirty: &mut BitSet,
id: Option<NonZeroUsize>,
version_cell: &mut Option<NonZeroUsize>,
value_cell: &mut T,
f1: impl FnOnce(&mut BitSet) -> (NonZeroUsize, A),
f2: impl FnOnce(&mut T, A) -> bool,
) -> (NonZeroUsize, T) {
if let Some(id) = id {
let id = id.get();
if dirty.contains(id) {
dirty.remove(id);
} else {
let version = version_cell.as_ref().unwrap();
return (*version, value_cell.clone());
}
} else if let Some(version) = version_cell.as_ref() {
return (*version, value_cell.clone());
}
let (prec_version, precs) = f1(dirty);
if let Some(version) = version_cell {
if prec_version > *version {
if f2(value_cell, precs) {
*version = prec_version;
return (prec_version, value_cell.clone());
}
}
(*version, value_cell.clone())
} else {
f2(value_cell, precs);
*version_cell = Some(prec_version);
(prec_version, value_cell.clone())
}
}
impl<C: Calc> Calc for Arc<Mutex<C>> {
type Value = C::Value;
fn add_dep(&mut self, seen: &mut BitSet, dep: NonZeroUsize) {
self.lock().add_dep(seen, dep)
}
fn eval(&mut self, dirty: &mut BitSet) -> (NonZeroUsize, C::Value) {
self.lock().eval(dirty)
}
}
impl<T> Calc for Box<Calc<Value = T> + Send> {
type Value = T;
fn add_dep(&mut self, seen: &mut BitSet, dep: NonZeroUsize) {
(**self).add_dep(seen, dep)
}
fn eval(&mut self, dirty: &mut BitSet) -> (NonZeroUsize, T) {
(**self).eval(dirty)
}
}
#[derive(Clone)]
pub struct Graph {
inner: Arc<GraphInner>,
next_version: Arc<Counter>,
}
impl Graph {
pub fn new() -> Self {
Graph {
inner: Arc::new(GraphInner {
dirty: Mutex::new(BitSet::new()),
next_id: Counter::new(FIRST_ID),
}),
next_version: Arc::new(Counter::new(FIRST_VERSION)),
}
}
pub fn source<T>(&self, value: T) -> Node<Source<T>> {
let version = self.next_version.next();
let inner = SourceInner {
deps: BitSet::new(),
version,
value,
};
let calc = Source {
inner: Arc::new(Mutex::new(inner)),
next_version: self.next_version.clone(),
};
Node {
calc,
graph: Some(self.inner.clone()),
}
}
}
include!(concat!(env!("OUT_DIR"), "/funcs.rs"));
#[test]
fn test_nodes_are_send_sync() {
fn assert_send_sync<T: Send + Sync>(value: T) -> T {
value
}
let graph = assert_send_sync(Graph::new());
let c = const_("const");
let l = lazy(|| "lazy");
let s = assert_send_sync(graph.source("source".to_owned()));
let mut m = assert_send_sync(Node::zip3(c, l, s.clone(), |a, b, c| {
format!("{a} {b} {c}", a = a, b = b, c = c)
}));
assert_eq!("const lazy source", m.get_mut());
s.update(|text| {
*text += "2";
true
});
assert_eq!("const lazy source2", m.get_mut());
}
#[test]
fn test_source() {
let graph = Graph::new();
let source = graph.source(1);
assert_eq!(1, source.get());
source.set(2);
assert_eq!(2, source.get());
}
#[test]
fn test_const() {
let c = const_("hello");
assert_eq!("hello", c.get());
}
#[test]
fn test_lazy() {
let mut lazy1 = lazy(|| "hello");
let _lazy2 = lazy(|| unreachable!());
assert_eq!("hello", lazy1.get_mut());
}
#[test]
fn test_inspect() {
let graph = Graph::new();
let source = graph.source(1);
let inspect_count = AtomicUsize::new(0);
let mut map = source.clone().map(|n| n * n).inspect(|_| {
inspect_count.fetch_add(1, Ordering::SeqCst);
});
assert_eq!(0, inspect_count.load(Ordering::SeqCst));
assert_eq!(1, map.get_mut());
assert_eq!(1, inspect_count.load(Ordering::SeqCst));
source.set(2);
assert_eq!(1, inspect_count.load(Ordering::SeqCst));
assert_eq!(4, map.get_mut());
assert_eq!(2, inspect_count.load(Ordering::SeqCst));
source.set(2);
assert_eq!(2, inspect_count.load(Ordering::SeqCst));
assert_eq!(4, map.get_mut());
assert_eq!(2, inspect_count.load(Ordering::SeqCst));
}
#[test]
fn test_map() {
let graph = Graph::new();
let source = graph.source(1);
let c = const_(2);
let map1 = source.clone().zip(c, |n, c| n * c);
let mut map2 = map1.map(|m| -m);
assert_eq!(-2, map2.get_mut());
source.set(2);
assert_eq!(-4, map2.get_mut());
}
#[test]
fn test_map_cache() {
let graph = Graph::new();
let source = graph.source("hello");
let c = const_::<usize>(1);
let calc_count1 = AtomicUsize::new(0);
let calc_count2 = AtomicUsize::new(0);
let calc_count3 = AtomicUsize::new(0);
let calc_counts = || {
(
calc_count1.load(Ordering::SeqCst),
calc_count2.load(Ordering::SeqCst),
calc_count3.load(Ordering::SeqCst),
)
};
let map1 = source
.clone()
.map(|s| {
calc_count1.fetch_add(1, Ordering::SeqCst);
s.len()
})
.shared();
let map2 = Node::zip(source.clone(), c, |s, c| {
calc_count2.fetch_add(1, Ordering::SeqCst);
s.as_bytes()[c] as usize
});
let mut map3 = Node::zip3(map1.clone(), map2, map1, |x, y, z| {
calc_count3.fetch_add(1, Ordering::SeqCst);
x + y + z
});
assert_eq!((0, 0, 0), calc_counts());
assert_eq!(111, map3.get_mut());
assert_eq!((1, 1, 1), calc_counts());
source.set("jello");
assert_eq!(111, map3.get_mut());
assert_eq!((2, 2, 1), calc_counts());
source.set("jollo");
assert_eq!(121, map3.get_mut());
assert_eq!((3, 3, 2), calc_counts());
}
#[test]
fn test_map_lazy() {
let graph = Graph::new();
let source = graph.source(1);
let _map = source.clone().map(|_| unreachable!());
assert_eq!(1, source.get());
source.set(2);
assert_eq!(2, source.get());
}