Skip to main content

oxidd_manager_index/node/
fixed_arity.rs

1use std::cell::UnsafeCell;
2use std::hash::Hash;
3use std::hash::Hasher;
4use std::mem::MaybeUninit;
5use std::sync::atomic::AtomicU32;
6use std::sync::atomic::Ordering::{self, Relaxed, Release};
7
8use oxidd_core::AtomicLevelNo;
9use oxidd_core::Edge;
10use oxidd_core::HasLevel;
11use oxidd_core::InnerNode;
12use oxidd_core::LevelNo;
13use oxidd_core::Tag;
14use oxidd_core::util::Borrowed;
15use oxidd_core::util::BorrowedEdgeIter;
16use oxidd_core::util::DropWith;
17
18use crate::manager;
19use crate::manager::InnerNodeCons;
20
21use super::NodeBase;
22
23pub struct NodeWithLevel<'id, ET, const ARITY: usize> {
24    rc: AtomicU32,
25    level: AtomicLevelNo,
26    children: UnsafeCell<[manager::Edge<'id, Self, ET>; ARITY]>,
27}
28
29impl<'id, ET: Tag, const ARITY: usize> NodeWithLevel<'id, ET, ARITY> {
30    const UNINIT_EDGE: MaybeUninit<manager::Edge<'id, Self, ET>> = MaybeUninit::uninit();
31}
32
33impl<ET: Tag, const ARITY: usize> PartialEq for NodeWithLevel<'_, ET, ARITY> {
34    #[inline(always)]
35    fn eq(&self, other: &Self) -> bool {
36        // SAFETY: we have shared access to the node
37        unsafe { *self.children.get() == *other.children.get() }
38    }
39}
40impl<ET: Tag, const ARITY: usize> Eq for NodeWithLevel<'_, ET, ARITY> {}
41
42impl<ET: Tag, const ARITY: usize> Hash for NodeWithLevel<'_, ET, ARITY> {
43    #[inline(always)]
44    fn hash<H: Hasher>(&self, state: &mut H) {
45        // SAFETY: we have shared access to the node
46        unsafe { &*self.children.get() }.hash(state);
47    }
48}
49
50// SAFETY:
51// - `InnerNode::new()` initializes the reference counter to 2
52// - `Self::retain()` increments the reference counter by 1 with `Relaxed` order
53// - `Self::release()` decrements the reference counter by 1 with `Release`
54//   order
55// - No other functions modify the reference counter.
56// - `Self::load_rc()` loads the reference counter with the given `order`
57unsafe impl<ET: Tag, const ARITY: usize> NodeBase for NodeWithLevel<'_, ET, ARITY> {
58    #[inline(always)]
59    fn retain(&self) {
60        if self.rc.fetch_add(1, Relaxed) > (u32::MAX >> 1) {
61            std::process::abort();
62        }
63    }
64
65    #[inline(always)]
66    unsafe fn release(&self) -> usize {
67        self.rc.fetch_sub(1, Release) as usize
68    }
69
70    #[inline(always)]
71    fn load_rc(&self, order: Ordering) -> usize {
72        self.rc.load(order) as usize
73    }
74
75    #[inline(always)]
76    fn needs_drop() -> bool {
77        false
78    }
79}
80
81impl<'id, ET: Tag, const ARITY: usize> DropWith<manager::Edge<'id, Self, ET>>
82    for NodeWithLevel<'id, ET, ARITY>
83{
84    #[inline]
85    fn drop_with(self, drop_edge: impl Fn(manager::Edge<'id, Self, ET>)) {
86        for c in self.children.into_inner() {
87            drop_edge(c);
88        }
89    }
90}
91
92impl<'id, ET: Tag, const ARITY: usize> InnerNode<manager::Edge<'id, Self, ET>>
93    for NodeWithLevel<'id, ET, ARITY>
94{
95    const ARITY: usize = ARITY;
96
97    type ChildrenIter<'a>
98        = BorrowedEdgeIter<
99        'a,
100        manager::Edge<'id, Self, ET>,
101        std::slice::Iter<'a, manager::Edge<'id, Self, ET>>,
102    >
103    where
104        Self: 'a;
105
106    #[inline(always)]
107    fn new(
108        level: LevelNo,
109        children: impl IntoIterator<Item = manager::Edge<'id, Self, ET>>,
110    ) -> Self {
111        let mut it = children.into_iter();
112        let mut children = [Self::UNINIT_EDGE; ARITY];
113
114        for slot in &mut children {
115            slot.write(it.next().unwrap());
116        }
117        debug_assert!(it.next().is_none());
118
119        // SAFETY:
120        // - all elements are initialized
121        // - we effectively move out of `children`; the old `children` are not dropped
122        //   since they are `MaybeUninit`
123        //
124        // TODO: replace this by `MaybeUninit::transpose()` /
125        // `MaybeUninit::array_assume_init()` once stable
126        let children = unsafe {
127            std::ptr::read((&raw const children).cast::<[manager::Edge<'id, Self, ET>; ARITY]>())
128        };
129
130        Self {
131            rc: AtomicU32::new(2),
132            level: AtomicLevelNo::new(level),
133            children: UnsafeCell::new(children),
134        }
135    }
136
137    #[inline(always)]
138    fn check_level(&self, check: impl FnOnce(LevelNo) -> bool) -> bool {
139        check(self.level.load(Relaxed))
140    }
141    #[inline(always)]
142    #[track_caller]
143    fn assert_level_matches(&self, level: LevelNo) {
144        assert_eq!(
145            self.level.load(Relaxed),
146            level,
147            "the level number does not match"
148        );
149    }
150
151    #[inline(always)]
152    fn children(&self) -> Self::ChildrenIter<'_> {
153        // SAFETY: we have shared access to the node
154        BorrowedEdgeIter::from(unsafe { &*self.children.get() }.iter())
155    }
156
157    #[inline(always)]
158    fn child(&self, n: usize) -> Borrowed<'_, manager::Edge<'id, Self, ET>> {
159        // SAFETY: we have shared access to the node
160        let children = unsafe { &*self.children.get() };
161        children[n].borrowed()
162    }
163
164    #[inline(always)]
165    unsafe fn set_child(
166        &self,
167        n: usize,
168        child: manager::Edge<'id, Self, ET>,
169    ) -> manager::Edge<'id, Self, ET> {
170        // SAFETY: we have exclusive access to the node and no child is
171        // referenced
172        let children = unsafe { &mut *self.children.get() };
173        std::mem::replace(&mut children[n], child)
174    }
175
176    #[inline(always)]
177    fn ref_count(&self) -> usize {
178        // Subtract 1 for the reference in the unique table
179        (self.rc.load(Relaxed) - 1) as usize
180    }
181}
182
183unsafe impl<ET, const ARITY: usize> HasLevel for NodeWithLevel<'_, ET, ARITY> {
184    #[inline(always)]
185    fn level(&self) -> LevelNo {
186        self.level.load(Relaxed)
187    }
188
189    #[inline(always)]
190    unsafe fn set_level(&self, level: LevelNo) {
191        self.level.store(level, Relaxed);
192    }
193}
194
195unsafe impl<ET: Send + Sync, const ARITY: usize> Send for NodeWithLevel<'_, ET, ARITY> {}
196unsafe impl<ET: Send + Sync, const ARITY: usize> Sync for NodeWithLevel<'_, ET, ARITY> {}
197
198pub struct NodeWithLevelCons<const ARITY: usize>;
199impl<ET: Tag + Send + Sync, const ARITY: usize> InnerNodeCons<ET> for NodeWithLevelCons<ARITY> {
200    type T<'id> = NodeWithLevel<'id, ET, ARITY>;
201}