Skip to main content

graphrecords_query/
execution.rs

1use crate::{
2    Arity, Bare, BareValueDomain, Definite, ElementShape, IndexDomain, Indexed, Multiple, Operand,
3    OrderState, QueryResult, Single, ValueDomain, operands::OperandHandle,
4};
5use elsa::FrozenMap;
6use graphrecords_core::GraphRecord;
7use std::{
8    any::Any,
9    hash::{Hash, Hasher},
10    ptr,
11    sync::Arc,
12};
13
14pub trait CacheableShape: ElementShape {
15    type CachedElement: 'static;
16
17    fn into_cached_element(element: Self::Element<'_>) -> Self::CachedElement;
18
19    fn from_cached_element(cached: &Self::CachedElement) -> Self::Element<'_>;
20}
21
22pub trait CacheableArity<S: CacheableShape>: Arity {
23    type Cached: 'static;
24
25    fn into_cached<'a>(values: Self::Container<'a, S::Element<'a>>) -> Self::Cached;
26
27    fn from_cached(cached: &Self::Cached) -> Self::Container<'_, S::Element<'_>>;
28}
29
30pub trait CacheableOperand: Operand {
31    type Cached: 'static;
32
33    fn into_cached(values: Self::ReturnValue<'_>) -> Self::Cached;
34
35    fn from_cached(cached: &Self::Cached) -> Self::ReturnValue<'_>;
36}
37
38impl<I: IndexDomain, V: ValueDomain> CacheableShape for Indexed<I, V> {
39    type CachedElement = (I::Owned, QueryResult<V::Owned>);
40
41    fn into_cached_element(element: Self::Element<'_>) -> Self::CachedElement {
42        let (index, outcome) = element;
43
44        (I::to_owned(&index), outcome.map(V::into_owned))
45    }
46
47    fn from_cached_element(cached: &Self::CachedElement) -> Self::Element<'_> {
48        let outcome = match &cached.1 {
49            Ok(value) => Ok(V::from_owned(value)),
50            Err(failure) => Err(failure.clone()),
51        };
52
53        (I::from_owned(&cached.0), outcome)
54    }
55}
56
57impl<V: BareValueDomain> CacheableShape for Bare<V> {
58    type CachedElement = QueryResult<V::Owned>;
59
60    fn into_cached_element(element: Self::Element<'_>) -> Self::CachedElement {
61        element.map(V::into_owned)
62    }
63
64    fn from_cached_element(cached: &Self::CachedElement) -> Self::Element<'_> {
65        match cached {
66            Ok(value) => Ok(V::from_owned(value)),
67            Err(failure) => Err(failure.clone()),
68        }
69    }
70}
71
72impl<S: CacheableShape> CacheableArity<S> for Definite {
73    type Cached = S::CachedElement;
74
75    fn into_cached<'a>(values: Self::Container<'a, S::Element<'a>>) -> Self::Cached {
76        S::into_cached_element(values)
77    }
78
79    fn from_cached(cached: &Self::Cached) -> Self::Container<'_, S::Element<'_>> {
80        S::from_cached_element(cached)
81    }
82}
83
84impl<S: CacheableShape> CacheableArity<S> for Single {
85    type Cached = Option<S::CachedElement>;
86
87    fn into_cached<'a>(values: Self::Container<'a, S::Element<'a>>) -> Self::Cached {
88        values.map(S::into_cached_element)
89    }
90
91    fn from_cached(cached: &Self::Cached) -> Self::Container<'_, S::Element<'_>> {
92        cached.as_ref().map(S::from_cached_element)
93    }
94}
95
96impl<S: CacheableShape, O: OrderState> CacheableArity<S> for Multiple<O> {
97    type Cached = Vec<S::CachedElement>;
98
99    fn into_cached<'a>(values: Self::Container<'a, S::Element<'a>>) -> Self::Cached {
100        values.map(S::into_cached_element).collect()
101    }
102
103    fn from_cached(cached: &Self::Cached) -> Self::Container<'_, S::Element<'_>> {
104        Box::new(cached.iter().map(S::from_cached_element))
105    }
106}
107
108impl<S: CacheableShape, C: CacheableArity<S>> CacheableOperand for OperandHandle<S, C> {
109    type Cached = C::Cached;
110
111    fn into_cached(values: Self::ReturnValue<'_>) -> Self::Cached {
112        C::into_cached(values)
113    }
114
115    fn from_cached(cached: &Self::Cached) -> Self::ReturnValue<'_> {
116        C::from_cached(cached)
117    }
118}
119
120#[derive(Clone)]
121pub(crate) struct CacheSlot(Arc<CacheSlotMarker>);
122
123struct CacheSlotMarker;
124
125impl CacheSlot {
126    #[must_use]
127    pub(crate) fn new() -> Self {
128        Self(Arc::new(CacheSlotMarker))
129    }
130}
131
132impl PartialEq for CacheSlot {
133    fn eq(&self, other: &Self) -> bool {
134        Arc::ptr_eq(&self.0, &other.0)
135    }
136}
137
138impl Eq for CacheSlot {}
139
140impl Hash for CacheSlot {
141    fn hash<H: Hasher>(&self, state: &mut H) {
142        ptr::hash(Arc::as_ptr(&self.0), state);
143    }
144}
145
146pub struct EvaluationCache<'a> {
147    graphrecord: &'a GraphRecord,
148    values: FrozenMap<CacheSlot, Box<dyn Any>>,
149}
150
151impl<'a> EvaluationCache<'a> {
152    #[must_use]
153    pub fn new(graphrecord: &'a GraphRecord) -> Self {
154        Self {
155            graphrecord,
156            values: FrozenMap::new(),
157        }
158    }
159
160    pub(crate) fn is_bound_to(&self, graphrecord: &GraphRecord) -> bool {
161        ptr::eq(self.graphrecord, graphrecord)
162    }
163
164    pub(crate) fn materialize<O: CacheableOperand>(
165        &'a self,
166        slot: &CacheSlot,
167        compute: impl FnOnce() -> QueryResult<O::ReturnValue<'a>>,
168    ) -> QueryResult<O::ReturnValue<'a>> {
169        let stored = if let Some(stored) = self.values.get(slot) {
170            stored
171        } else {
172            let computed = Box::new(compute().map(O::into_cached));
173            self.values.insert(slot.clone(), computed)
174        };
175
176        let stored = stored
177            .downcast_ref::<QueryResult<O::Cached>>()
178            .expect("Cache entry must match its slot's operand type");
179
180        match stored {
181            Ok(cached) => Ok(O::from_cached(cached)),
182            Err(failure) => Err(failure.clone()),
183        }
184    }
185}