1use std::any::Any;
29use std::cell::{Cell, RefCell};
30use std::num::NonZeroUsize;
31use std::rc::{Rc, Weak};
32
33use smallvec::SmallVec;
34
35use crate::config::Cost;
36use crate::meter::{Space, Time, ZombieOps};
37use crate::record::Replayer;
38use crate::tock::Tock;
39use crate::union_find::UF;
40
41pub type TockVec = SmallVec<[Tock; 4]>;
45
46pub type ValueVec = SmallVec<[Option<Rc<dyn EZombieNode>>; 2]>;
50
51pub type ContextRef = Rc<dyn ContextNode>;
53
54pub type ContextWeakRef = Weak<dyn ContextNode>;
56
57pub trait EZombieNode: Any {
59 fn created_time(&self) -> Tock;
60 fn size(&self) -> usize;
61 fn as_any(&self) -> &dyn Any;
62 fn get_value_ptr(&self) -> *const dyn Any;
63 fn set_context(&self, ctx: ContextWeakRef);
64 fn get_context(&self) -> Option<ContextRef>;
65 fn clone_context_weak(&self) -> Option<ContextWeakRef>;
68 fn notify_accessed(&self);
69}
70
71pub struct ZombieNode<T: ZombieOps> {
82 pub created_time: Tock,
83 pub value: T,
84 context: std::cell::Cell<Option<ContextWeakRef>>,
86}
87
88impl<T: 'static + Clone + ZombieOps> ZombieNode<T> {
89 #[inline]
90 pub fn new(created_time: Tock, value: T) -> Rc<Self> {
91 Rc::new(Self {
92 created_time,
93 value,
94 context: std::cell::Cell::new(None),
95 })
96 }
97
98 #[inline]
99 pub fn get(&self) -> &T {
100 self.notify_accessed();
101 &self.value
102 }
103}
104
105impl<T: 'static + ZombieOps> EZombieNode for ZombieNode<T> {
106 fn created_time(&self) -> Tock {
107 self.created_time
108 }
109
110 fn size(&self) -> usize {
111 self.value.get_size()
113 }
114
115 fn as_any(&self) -> &dyn Any {
116 self
117 }
118
119 fn get_value_ptr(&self) -> *const dyn Any {
120 &self.value as &dyn Any as *const dyn Any
121 }
122
123 fn set_context(&self, ctx: ContextWeakRef) {
124 self.context.set(Some(ctx));
125 }
126
127 fn get_context(&self) -> Option<ContextRef> {
128 let weak = self.context.take();
130 let result = weak.as_ref().and_then(|w| w.upgrade());
131 self.context.set(weak);
132 result
133 }
134
135 fn clone_context_weak(&self) -> Option<ContextWeakRef> {
136 let weak = self.context.take();
138 let result = weak.clone();
139 self.context.set(weak);
140 result
141 }
142
143 fn notify_accessed(&self) {
144 if let Some(ctx) = self.get_context() {
145 ctx.accessed();
146 }
147 }
148}
149
150pub trait ContextNode {
155 fn start_tock(&self) -> Tock;
156 fn end_tock(&self) -> Tock;
157 fn evictable(&self) -> bool;
158 fn evict(&self);
159 fn evict_at(&self, idx: usize);
160 fn accessed(&self);
161 fn get_replayer(&self) -> Option<Rc<Replayer>>;
162 fn get_value_at(&self, idx: usize) -> Option<Rc<dyn EZombieNode>>;
163 fn as_any(&self) -> &dyn Any;
165 fn get_forward_uf(&self) -> Option<UF> {
167 None
168 }
169}
170
171impl std::fmt::Debug for dyn ContextNode {
172 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
173 write!(f, "ContextNode(start={}, end={})", self.start_tock(), self.end_tock())
174 }
175}
176
177pub struct RootContext {
179 start: Tock,
180 end: RefCell<Tock>,
181 values: RefCell<ValueVec>,
182 space_taken: Cell<Space>,
184 replayer: Option<Rc<Replayer>>,
185}
186
187impl std::fmt::Debug for RootContext {
188 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189 f.debug_struct("RootContext")
190 .field("start", &self.start)
191 .field("end", &*self.end.borrow())
192 .field("values_count", &self.values.borrow().len())
193 .finish()
194 }
195}
196
197impl RootContext {
198 pub fn new(start: Tock) -> Rc<Self> {
199 Rc::new(Self {
200 start,
201 end: RefCell::new(start),
202 values: RefCell::new(ValueVec::new()),
203 space_taken: Cell::new(Space::ZERO),
204 replayer: None,
205 })
206 }
207
208 pub fn new_with_values(
210 start: Tock,
211 values: ValueVec,
212 space: Space,
213 replayer: Option<Rc<Replayer>>,
214 ) -> Rc<Self> {
215 let end = Tock(start.0 + values.len() as i64 + 1);
216 let ctx = Rc::new(Self {
217 start,
218 end: RefCell::new(end),
219 values: RefCell::new(values),
220 space_taken: Cell::new(space),
221 replayer,
222 });
223
224 let weak_ctx: ContextWeakRef = Rc::downgrade(&ctx) as ContextWeakRef;
226 for value in ctx.values.borrow().iter().flatten() {
227 value.set_context(weak_ctx.clone());
228 }
229
230 ctx
231 }
232
233 pub fn push(&self, node: Rc<dyn EZombieNode>) {
235 self.space_taken.set(self.space_taken.get() + Space::from_bytes(node.size()));
236 self.values.borrow_mut().push(Some(node));
237 *self.end.borrow_mut() = self.start + self.values.borrow().len() + 1;
238 }
239}
240
241impl ContextNode for RootContext {
242 fn start_tock(&self) -> Tock {
243 self.start
244 }
245
246 fn end_tock(&self) -> Tock {
247 *self.end.borrow()
248 }
249
250 fn evictable(&self) -> bool {
251 false
252 }
253
254 fn evict(&self) {
255 }
257
258 fn evict_at(&self, _idx: usize) {
259 }
261
262 fn accessed(&self) {
263 }
265
266 fn get_replayer(&self) -> Option<Rc<Replayer>> {
267 self.replayer.clone()
268 }
269
270 fn get_value_at(&self, idx: usize) -> Option<Rc<dyn EZombieNode>> {
271 self.values.borrow().get(idx).cloned().flatten()
272 }
273
274 fn as_any(&self) -> &dyn Any {
275 self
276 }
277}
278
279pub struct FullContext {
281 start: Tock,
282 end: Tock,
283 values: RefCell<ValueVec>,
284 space_taken: Cell<Space>,
286 time_taken: Time,
287 last_accessed_ns: Cell<u64>,
290 replayer: Rc<Replayer>,
291 dependencies: TockVec,
292
293 pool_index: Cell<Option<NonZeroUsize>>,
299 forward_uf: UF,
300 backward_uf: UF,
301 }
304
305impl std::fmt::Debug for FullContext {
306 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
307 f.debug_struct("FullContext")
308 .field("start", &self.start)
309 .field("end", &self.end)
310 .field("values_count", &self.values.borrow().len())
311 .finish()
312 }
313}
314
315impl FullContext {
316 pub fn new(
323 start: Tock,
324 end: Tock,
325 values: ValueVec,
326 space: Space,
327 time_taken: Time,
328 replayer: Rc<Replayer>,
329 dependencies: TockVec,
330 ) -> Rc<Self> {
331 let ctx = Rc::new(Self {
332 start,
333 end,
334 values: RefCell::new(values),
335 space_taken: Cell::new(space),
336 time_taken,
337 last_accessed_ns: Cell::new(0),
338 replayer,
339 dependencies,
340 pool_index: Cell::new(None),
341 forward_uf: UF::new(Time::ZERO),
342 backward_uf: UF::new(Time::ZERO),
343 });
344
345 let weak_ctx: ContextWeakRef = Rc::downgrade(&ctx) as ContextWeakRef;
347 for value in ctx.values.borrow().iter().flatten() {
348 value.set_context(weak_ctx.clone());
349 }
350
351 ctx
352 }
353
354 pub fn set_pool_index(&self, idx: usize) {
355 self.pool_index.set(NonZeroUsize::new(idx + 1));
357 }
358
359 pub fn clear_pool_index(&self) {
360 self.pool_index.set(None);
361 }
362
363 pub fn pool_index(&self) -> Option<usize> {
364 self.pool_index.get().map(|n| n.get() - 1)
366 }
367
368 pub fn time_cost(&self) -> Time {
370 self.time_taken + self.forward_uf.value() + self.backward_uf.value()
371 }
372
373 pub fn cost(&self, metric: fn(Time, Time, Space) -> Cost) -> Cost {
375 metric(self.time_taken, self.time_cost(), self.space_taken.get())
376 }
377
378 pub fn space(&self) -> Space {
379 self.space_taken.get()
380 }
381
382 pub fn dependencies(&self) -> &[Tock] {
383 &self.dependencies
384 }
385
386 pub fn time_taken(&self) -> Time {
388 self.time_taken
389 }
390
391 pub fn backward_uf(&self) -> &UF {
393 &self.backward_uf
394 }
395
396 pub fn forward_uf(&self) -> &UF {
398 &self.forward_uf
399 }
400}
401
402impl ContextNode for FullContext {
403 fn start_tock(&self) -> Tock {
404 self.start
405 }
406
407 fn end_tock(&self) -> Tock {
408 self.end
409 }
410
411 fn evictable(&self) -> bool {
412 true
413 }
414
415 fn evict(&self) {
416 for v in self.values.borrow_mut().iter_mut() {
418 *v = None;
419 }
420 self.space_taken.set(Space::ZERO);
421
422 let cost = UF::new(self.time_taken);
429 self.forward_uf.merge(&cost);
430 self.backward_uf.merge(&cost);
431 }
432
433 fn evict_at(&self, idx: usize) {
434 let mut values = self.values.borrow_mut();
435 if let Some(node) = values.get_mut(idx).and_then(|slot| slot.take()) {
436 self.space_taken.set(self.space_taken.get() - Space::from_bytes(node.size()));
437 }
438 }
439
440 fn accessed(&self) {
441 use std::time::{SystemTime, UNIX_EPOCH};
444 let now_ns = SystemTime::now()
445 .duration_since(UNIX_EPOCH)
446 .map(|d| d.as_nanos() as u64)
447 .unwrap_or(0);
448 self.last_accessed_ns.set(now_ns);
449 }
450
451 fn get_replayer(&self) -> Option<Rc<Replayer>> {
452 Some(self.replayer.clone())
453 }
454
455 fn get_value_at(&self, idx: usize) -> Option<Rc<dyn EZombieNode>> {
456 self.values.borrow().get(idx).cloned().flatten()
457 }
458
459 fn as_any(&self) -> &dyn Any {
460 self
461 }
462
463 fn get_forward_uf(&self) -> Option<UF> {
464 Some(self.forward_uf)
465 }
466}
467
468#[inline]
508pub fn downcast_zombie<T: 'static + ZombieOps>(node: &Rc<dyn EZombieNode>) -> Option<Rc<ZombieNode<T>>> {
509 let any_ref = node.as_any();
511 if !any_ref.is::<ZombieNode<T>>() {
512 return None;
513 }
514
515 let cloned = node.clone();
523 let raw: *const dyn EZombieNode = Rc::into_raw(cloned);
524 let data_ptr = raw as *const ZombieNode<T>;
525 Some(unsafe { Rc::from_raw(data_ptr) })
526}
527
528#[cfg(test)]
529mod tests {
530 use super::*;
531
532 #[test]
533 fn test_root_context() {
534 let ctx = RootContext::new(Tock(0));
535 assert!(!ctx.evictable());
536 assert_eq!(ctx.start_tock(), Tock(0));
537 }
538
539 #[test]
540 fn test_zombie_node_context_link() {
541 let ctx = RootContext::new(Tock(0));
542 let node = ZombieNode::new(Tock(1), 42);
543
544 let weak_ctx: ContextWeakRef = Rc::downgrade(&ctx) as ContextWeakRef;
546 node.set_context(weak_ctx);
547
548 assert!(node.get_context().is_some());
550 }
551
552 #[test]
553 fn test_downcast_zombie() {
554 let node: Rc<dyn EZombieNode> = ZombieNode::new(Tock(1), 42i32);
555
556 let typed = downcast_zombie::<i32>(&node);
558 assert!(typed.is_some());
559 assert_eq!(typed.unwrap().value, 42);
560
561 let wrong = downcast_zombie::<String>(&node);
563 assert!(wrong.is_none());
564 }
565}