mycroft_support/
aggregator.rs

1//! Defines types and utility wrappers for merging multiple pieces of data together
2use std::hash::Hash;
3use std::rc::Rc;
4use std::marker::PhantomData;
5use storage;
6
7/// The base aggregation trait. It is intentionally not parameterized over type so that Tuple
8/// operation remains fully generic.
9pub trait Aggregator {
10    /// This is an associative, commutative function over points in some dataspace.
11    /// Accepting more than two points is a hack to allow allocation skipping
12    fn aggregate(&self, &[usize]) -> usize;
13    /// Clone, jammed into the trait to avoid multitrait problems
14    fn agg_clone(&self) -> Box<Aggregator>;
15}
16
17/// Func is an aggregator which doesn't need a data backing, for use with small types
18#[derive(Clone)]
19pub struct Func<F, T> {
20    f: Rc<F>,
21    phantom: PhantomData<T>,
22}
23
24impl<T, F: Fn(&[T]) -> T> Func<F, T> {
25    /// Create a new small-typed aggregator
26    pub fn new(f: F) -> Self {
27        Self {
28            f: Rc::new(f),
29            phantom: PhantomData,
30        }
31    }
32}
33
34macro_rules! castable_aggregator {
35    ($ty:ty) => {
36impl<F: Fn(&[$ty]) -> $ty + 'static> Aggregator for Func<F, $ty> {
37    fn aggregate(&self, big_ks: &[usize]) -> usize {
38        let ks: Vec<_> = big_ks.iter().map(|x| *x as $ty).collect();
39        (self.f)(&ks) as usize
40    }
41    fn agg_clone(&self) -> Box<Aggregator> {
42        Box::new(Self { f: self.f.clone(), phantom: PhantomData})
43    }
44}
45}
46}
47
48impl<F: Fn(&[bool]) -> bool + 'static> Aggregator for Func<F, bool> {
49    fn aggregate(&self, big_ks: &[usize]) -> usize {
50        let ks: Vec<_> = big_ks.iter().map(|x| *x == 1).collect();
51        (self.f)(&ks) as usize
52    }
53    fn agg_clone(&self) -> Box<Aggregator> {
54        Box::new(Self {
55            f: self.f.clone(),
56            phantom: PhantomData,
57        })
58    }
59}
60
61castable_aggregator!(u8);
62castable_aggregator!(u16);
63castable_aggregator!(u32);
64castable_aggregator!(u64);
65castable_aggregator!(usize);
66castable_aggregator!(i8);
67castable_aggregator!(i16);
68castable_aggregator!(i32);
69castable_aggregator!(i64);
70castable_aggregator!(isize);
71
72/// FuncData is a convenient way to box up a &[T]->T style function as one that acts on
73/// coordinates instead by wrapping it with a handle to the relevant Data.
74#[derive(Clone)]
75pub struct FuncData<T, F> {
76    f: Rc<F>,
77    data: storage::Data<T>,
78}
79
80impl<T, F: Fn(&[&T]) -> T> FuncData<T, F> {
81    /// Creates a new `FuncData`, from the actual function you want to use plus the dataspace it
82    /// operates on.
83    pub fn new(f: F, data: storage::Data<T>) -> Self {
84        Self {
85            f: Rc::new(f),
86            data: data,
87        }
88    }
89}
90
91impl<T: Hash + PartialEq + 'static, F: Fn(&[&T]) -> T + 'static> Aggregator for FuncData<T, F> {
92    fn aggregate(&self, ks: &[usize]) -> usize {
93        let v = {
94            let vs: Vec<_> = ks.iter().map(|k| &self.data[*k]).collect();
95            (self.f)(&vs)
96        };
97        unsafe {
98            self.data.read_exit(ks.len());
99        }
100        self.data.insert(v)
101    }
102    fn agg_clone(&self) -> Box<Aggregator> {
103        Box::new(Self {
104            f: self.f.clone(),
105            data: self.data.clone(),
106        })
107    }
108}