graphrecords_query/
execution.rs1use 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}