1use super::cell_impl::VirtualCellWrapper;
2use super::{Cell, CellDescriptor, CellImpl, CellInner, DynCell, HashBytes};
3
4#[derive(Debug, Clone, Copy, Eq, PartialEq)]
6pub enum UsageTreeMode {
7 OnLoad,
9 OnDataAccess,
11}
12
13pub struct UsageTree {
15 state: SharedState,
16}
17
18impl UsageTree {
19 pub fn new(mode: UsageTreeMode) -> Self {
21 Self {
22 state: UsageTreeState::new(mode),
23 }
24 }
25
26 pub fn with_mode_and_capacity(mode: UsageTreeMode, capacity: usize) -> Self {
29 Self {
30 state: UsageTreeState::with_mode_and_capacity(mode, capacity),
31 }
32 }
33
34 pub fn track(&self, cell: &Cell) -> Cell {
37 if self.state.mode == UsageTreeMode::OnLoad {
38 self.state.insert(cell);
39 }
40 self.state.wrap(cell.clone())
41 }
42
43 pub fn contains(&self, repr_hash: &HashBytes) -> bool {
46 self.state.contains(repr_hash)
47 }
48
49 pub fn with_subtrees(self) -> UsageTreeWithSubtrees {
51 UsageTreeWithSubtrees {
52 state: self.state,
53 subtrees: Default::default(),
54 }
55 }
56
57 pub fn is_empty(&self) -> bool {
59 self.state.is_empty()
60 }
61
62 pub fn len(&self) -> usize {
64 self.state.len()
65 }
66}
67
68pub struct UsageTreeWithSubtrees {
70 state: SharedState,
71 subtrees: ahash::HashSet<HashBytes>,
72}
73
74impl UsageTreeWithSubtrees {
75 pub fn track(&self, cell: &Cell) -> Cell {
78 if self.state.mode == UsageTreeMode::OnLoad {
79 self.state.insert(cell);
80 }
81 self.state.wrap(cell.clone())
82 }
83
84 pub fn contains_direct(&self, repr_hash: &HashBytes) -> bool {
87 self.state.as_ref().contains(repr_hash)
88 }
89
90 pub fn contains_subtree(&self, repr_hash: &HashBytes) -> bool {
93 self.subtrees.contains(repr_hash)
94 }
95
96 pub fn add_subtree(&mut self, root: &DynCell) -> bool {
99 self.subtrees.insert(*root.repr_hash())
100 }
101}
102
103#[cfg(not(feature = "sync"))]
104use self::rc::{SharedState, UsageCell, UsageTreeState};
105#[cfg(feature = "sync")]
106use self::sync::{SharedState, UsageCell, UsageTreeState};
107
108impl CellImpl for UsageCell {
109 #[inline]
110 fn untrack(self: CellInner<Self>) -> Cell {
111 UsageCell::untrack_impl(self)
112 }
113
114 fn descriptor(&self) -> CellDescriptor {
115 self.cell.descriptor()
116 }
117
118 fn data(&self) -> &[u8] {
119 if self.should_insert() {
120 if let Some(usage_tree) = self.usage_tree.upgrade() {
121 usage_tree.insert(&self.cell);
122 } else {
123 self.set_orphaned();
124 }
125 self.set_inserted();
126 }
127 self.cell.data()
128 }
129
130 fn bit_len(&self) -> u16 {
131 self.cell.bit_len()
132 }
133
134 fn reference(&self, index: u8) -> Option<&DynCell> {
135 if self.is_orphaned() {
136 self.cell.reference(index)
137 } else {
138 Some(self.load_reference(index)?.as_ref())
139 }
140 }
141
142 fn reference_cloned(&self, index: u8) -> Option<Cell> {
143 if self.is_orphaned() {
144 self.cell.reference_cloned(index)
145 } else {
146 let cell = self.load_reference(index)?.clone();
147
148 #[cfg(not(feature = "sync"))]
149 {
150 Some(Cell::from(cell as std::rc::Rc<DynCell>))
151 }
152
153 #[cfg(feature = "sync")]
154 {
155 Some(Cell::from(cell as std::sync::Arc<DynCell>))
156 }
157 }
158 }
159
160 fn reference_repr_hash(&self, index: u8) -> Option<HashBytes> {
161 self.cell.reference_repr_hash(index)
162 }
163
164 fn virtualize(&self) -> &DynCell {
165 VirtualCellWrapper::wrap(self)
166 }
167
168 fn hash(&self, level: u8) -> &HashBytes {
169 self.cell.hash(level)
170 }
171
172 fn depth(&self, level: u8) -> u16 {
173 self.cell.depth(level)
174 }
175}
176
177#[cfg(not(feature = "sync"))]
178mod rc {
179 use std::rc::{Rc, Weak};
180
181 use super::UsageTreeMode;
182 use crate::cell::{Cell, DynCell, HashBytes};
183
184 pub type SharedState = Rc<UsageTreeState>;
185
186 type VisitedCells = std::cell::RefCell<ahash::HashSet<HashBytes>>;
187
188 pub struct UsageTreeState {
189 pub mode: UsageTreeMode,
190 pub visited: VisitedCells,
191 }
192
193 impl UsageTreeState {
194 pub fn new(mode: UsageTreeMode) -> SharedState {
195 Rc::new(Self {
196 mode,
197 visited: Default::default(),
198 })
199 }
200
201 pub fn with_mode_and_capacity(mode: UsageTreeMode, capacity: usize) -> SharedState {
202 Rc::new(Self {
203 mode,
204 visited: std::cell::RefCell::new(ahash::HashSet::with_capacity_and_hasher(
205 capacity,
206 Default::default(),
207 )),
208 })
209 }
210
211 pub fn wrap(self: &SharedState, cell: Cell) -> Cell {
212 Cell::from(Rc::new(UsageCell::new(cell, Rc::downgrade(self), self.mode)) as Rc<DynCell>)
213 }
214
215 #[inline]
216 pub fn insert(&self, cell: &Cell) {
217 self.visited.borrow_mut().insert(*cell.repr_hash());
218 }
219
220 #[inline]
221 pub fn contains(&self, repr_hash: &HashBytes) -> bool {
222 self.visited.borrow().contains(repr_hash)
223 }
224
225 #[inline]
226 pub fn is_empty(&self) -> bool {
227 self.visited.borrow().is_empty()
228 }
229
230 #[inline]
231 pub fn len(&self) -> usize {
232 self.visited.borrow().len()
233 }
234 }
235
236 pub struct UsageCell {
237 pub cell: Cell,
238 pub usage_tree: Weak<UsageTreeState>,
239 pub children: std::cell::UnsafeCell<[Option<Rc<Self>>; 4]>,
240 pub inserted: std::cell::Cell<bool>,
241 pub orphaned: std::cell::Cell<bool>,
242 pub mode: UsageTreeMode,
243 }
244
245 impl UsageCell {
246 pub fn new(cell: Cell, usage_tree: Weak<UsageTreeState>, mode: UsageTreeMode) -> Self {
247 Self {
248 cell,
249 usage_tree,
250 children: Default::default(),
251 inserted: std::cell::Cell::new(mode == UsageTreeMode::OnLoad),
252 orphaned: std::cell::Cell::new(false),
253 mode,
254 }
255 }
256
257 pub fn untrack_impl(self: Rc<Self>) -> Cell {
258 self.cell.clone()
259 }
260
261 #[inline]
262 pub fn is_orphaned(&self) -> bool {
263 self.orphaned.get()
264 }
265
266 #[inline]
267 pub fn set_orphaned(&self) {
268 self.orphaned.set(true);
269 }
270
271 #[inline]
272 pub fn should_insert(&self) -> bool {
273 !self.inserted.get()
274 }
275
276 #[inline]
277 pub fn set_inserted(&self) {
278 self.inserted.set(true);
279 }
280
281 pub fn load_reference(&self, index: u8) -> Option<&Rc<Self>> {
282 if index < 4 {
283 let children = unsafe { &mut *self.children.get() };
284 Some(match &mut children[index as usize] {
285 Some(value) => value,
286 slot @ None => {
287 let child = self.cell.as_ref().reference_cloned(index)?;
288 if self.mode == UsageTreeMode::OnLoad {
289 if let Some(usage_tree) = self.usage_tree.upgrade() {
290 usage_tree.insert(&child);
291 } else {
292 self.set_orphaned();
293 }
294 }
295
296 slot.insert(Rc::new(UsageCell::new(
297 child,
298 self.usage_tree.clone(),
299 self.mode,
300 )))
301 }
302 })
303 } else {
304 None
305 }
306 }
307 }
308}
309
310#[cfg(feature = "sync")]
311mod sync {
312 use std::cell::UnsafeCell;
313 use std::sync::atomic::{AtomicBool, Ordering};
314 use std::sync::{Arc, Once, Weak};
315
316 use super::UsageTreeMode;
317 use crate::cell::{Cell, DynCell, HashBytes};
318
319 pub type SharedState = Arc<UsageTreeState>;
320
321 type VisitedCells = scc::HashSet<HashBytes, ahash::RandomState>;
322
323 pub struct UsageTreeState {
324 pub mode: UsageTreeMode,
325 pub visited: VisitedCells,
326 }
327
328 impl UsageTreeState {
329 pub fn new(mode: UsageTreeMode) -> SharedState {
330 Arc::new(Self {
331 mode,
332 visited: Default::default(),
333 })
334 }
335
336 pub fn with_mode_and_capacity(mode: UsageTreeMode, capacity: usize) -> SharedState {
337 Arc::new(Self {
338 mode,
339 visited: VisitedCells::with_capacity_and_hasher(capacity, Default::default()),
340 })
341 }
342
343 pub fn wrap(self: &SharedState, cell: Cell) -> Cell {
344 Cell::from(
345 Arc::new(UsageCell::new(cell, Arc::downgrade(self), self.mode)) as Arc<DynCell>,
346 )
347 }
348
349 #[inline]
350 pub fn insert(&self, cell: &Cell) {
351 _ = self.visited.insert(*cell.repr_hash());
352 }
353
354 #[inline]
355 pub fn contains(&self, repr_hash: &HashBytes) -> bool {
356 self.visited.contains(repr_hash)
357 }
358
359 #[inline]
360 pub fn is_empty(&self) -> bool {
361 self.visited.is_empty()
362 }
363
364 #[inline]
365 pub fn len(&self) -> usize {
366 self.visited.len()
367 }
368 }
369
370 pub struct UsageCell {
371 pub cell: Cell,
372 pub usage_tree: Weak<UsageTreeState>,
373 pub reference_states: [Once; 4],
375 pub reference_data: [UnsafeCell<Option<Arc<Self>>>; 4],
376 pub inserted: AtomicBool,
377 pub orphaned: AtomicBool,
378 pub mode: UsageTreeMode,
379 }
380
381 impl UsageCell {
382 pub fn new(cell: Cell, usage_tree: Weak<UsageTreeState>, mode: UsageTreeMode) -> Self {
383 Self {
384 cell: Cell::untrack(cell),
386 usage_tree,
387 reference_states: [(); 4].map(|_| Once::new()),
388 reference_data: [(); 4].map(|_| UnsafeCell::new(None)),
389 inserted: AtomicBool::new(mode == UsageTreeMode::OnLoad),
390 orphaned: AtomicBool::new(false),
391 mode,
392 }
393 }
394
395 pub fn untrack_impl(self: Arc<Self>) -> Cell {
396 match Arc::try_unwrap(self) {
397 Ok(inner) => inner.cell,
398 Err(this) => this.cell.clone(),
399 }
400 }
401
402 #[inline]
403 pub fn is_orphaned(&self) -> bool {
404 self.orphaned.load(Ordering::Acquire)
405 }
406
407 #[inline]
408 pub fn set_orphaned(&self) {
409 self.orphaned.store(true, Ordering::Release);
410 }
411
412 #[inline]
413 pub fn should_insert(&self) -> bool {
414 !self.inserted.load(Ordering::Acquire)
415 }
416
417 #[inline]
418 pub fn set_inserted(&self) {
419 self.inserted.store(true, Ordering::Release);
420 }
421
422 pub fn load_reference(&self, index: u8) -> Option<&Arc<Self>> {
423 if index < 4 {
424 let mut should_insert = false;
425 self.reference_states[index as usize].call_once_force(|_| {
426 let Some(child) = self.cell.as_ref().reference_cloned(index) else {
427 return;
429 };
430
431 should_insert = self.mode == UsageTreeMode::OnLoad;
432
433 unsafe {
435 *self.reference_data[index as usize].get() = Some(Arc::new(Self::new(
436 child,
437 self.usage_tree.clone(),
438 self.mode,
439 )));
440 };
441 });
442
443 let child = unsafe { &*self.reference_data[index as usize].get().cast_const() };
445 if crate::util::unlikely(should_insert)
446 && let Some(child) = child
447 {
448 if let Some(usage_tree) = self.usage_tree.upgrade() {
449 usage_tree.insert(&child.cell);
450 } else {
451 self.set_orphaned();
452 }
453 }
454
455 child.as_ref()
456 } else {
457 None
458 }
459 }
460 }
461
462 unsafe impl Send for UsageCell {}
464 unsafe impl Sync for UsageCell {}
465}