use super::{VectorTree, WriteOptions};
use motion_core::sync::MutexExt;
use motion_core::{Node, ValidationError};
use std::{
collections::{BTreeMap, BTreeSet},
sync::{Arc, Mutex},
};
const MAX_SLOTS: usize = 8;
pub struct CompileCache<K, V> {
key: Arc<K>,
slots: Arc<Mutex<BTreeMap<u64, V>>>,
}
impl<K, V> Clone for CompileCache<K, V> {
fn clone(&self) -> Self {
Self {
key: self.key.clone(),
slots: self.slots.clone(),
}
}
}
impl<K: std::fmt::Debug, V> std::fmt::Debug for CompileCache<K, V> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("CompileCache")
.field("key", &self.key)
.finish_non_exhaustive()
}
}
impl<K, V: Clone> CompileCache<K, V> {
pub fn new(key: K) -> Self {
Self {
key: Arc::new(key),
slots: Arc::default(),
}
}
pub fn key(&self) -> &K {
&self.key
}
pub fn shares(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.slots, &other.slots)
}
pub fn is_compiled(&self) -> bool {
!self.slots.lock_unpoisoned().is_empty()
}
pub fn get_or_compile(&self, variant: u64, compile: impl FnOnce(&K) -> V) -> V {
let mut slots = self.slots.lock_unpoisoned();
if let Some(result) = slots.get(&variant) {
return result.clone();
}
let result = compile(&self.key);
if slots.len() >= MAX_SLOTS {
slots.clear();
}
slots.insert(variant, result.clone());
result
}
}
impl<K: Clone + PartialEq, V: Clone> CompileCache<K, V> {
pub fn map_key(&self, change: impl FnOnce(&mut K)) -> Self {
let mut key = (*self.key).clone();
change(&mut key);
if key == *self.key {
self.clone()
} else {
Self::new(key)
}
}
}
type Modify = Arc<dyn Fn(Node) -> Node + Send + Sync>;
#[derive(Clone)]
pub struct VectorSelection {
addresses: Arc<BTreeSet<Vec<usize>>>,
modify: Modify,
}
impl VectorSelection {
pub fn new(
addresses: impl IntoIterator<Item = Vec<usize>>,
modify: impl Fn(Node) -> Node + Send + Sync + 'static,
) -> Self {
Self {
addresses: Arc::new(addresses.into_iter().collect()),
modify: Arc::new(modify),
}
}
pub fn shared(addresses: impl IntoIterator<Item = Vec<usize>>, modify: &Modify) -> Self {
Self {
addresses: Arc::new(addresses.into_iter().collect()),
modify: modify.clone(),
}
}
}
impl VectorTree {
pub fn reveal(
&self,
selections: &[VectorSelection],
write: Option<f64>,
) -> Result<Node, ValidationError> {
let map = |address: &[usize], mut node: Node| {
for selection in selections {
if selection.addresses.contains(address) {
node = (selection.modify)(node);
}
}
node
};
match write {
Some(progress) => self.write_mapped(progress, WriteOptions::default(), &map),
None => Ok(self.node_mapped(&map)),
}
}
}