Skip to main content

oxidd_test_utils/
edge.rs

1//! Simple dummy edge implementation based on [`Arc`]
2//!
3//! The implementation is very limited but perfectly fine to test e.g. an apply
4//! cache.
5
6use std::cmp::Ordering;
7use std::collections::HashSet;
8use std::hash::{Hash, Hasher};
9use std::ops::Range;
10use std::sync::Arc;
11
12use oxidd_core::error::DuplicateVarName;
13use oxidd_core::util::{AllocResult, Borrowed, DropWith};
14use oxidd_core::{
15    DiagramRules, Edge, HasWorkers, InnerNode, LevelNo, LevelView, Manager, Node, NodeID,
16    ReducedOrNew, VarNo,
17};
18
19/// Simple dummy edge implementation based on [`Arc`]
20///
21/// The implementation is very limited but perfectly fine to test e.g. an apply
22/// cache.
23#[derive(Debug)]
24pub struct DummyEdge(Arc<()>);
25
26impl PartialEq for DummyEdge {
27    fn eq(&self, other: &Self) -> bool {
28        Arc::ptr_eq(&self.0, &other.0)
29    }
30}
31impl Eq for DummyEdge {}
32impl PartialOrd for DummyEdge {
33    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
34        Some(self.cmp(other))
35    }
36}
37impl Ord for DummyEdge {
38    fn cmp(&self, other: &Self) -> Ordering {
39        Arc::as_ptr(&self.0).cmp(&Arc::as_ptr(&other.0))
40    }
41}
42impl Hash for DummyEdge {
43    fn hash<H: Hasher>(&self, state: &mut H) {
44        Arc::as_ptr(&self.0).hash(state);
45    }
46}
47
48impl Drop for DummyEdge {
49    fn drop(&mut self) {
50        eprintln!(
51            "Edges must not be dropped. Use Manager::drop_edge(). Backtrace:\n{}",
52            std::backtrace::Backtrace::capture()
53        );
54    }
55}
56
57impl DummyEdge {
58    /// Create a new `DummyEdge`
59    pub fn new() -> Self {
60        DummyEdge(Arc::new(()))
61    }
62
63    /// Get the node's reference count (note: `Node::ref_count()` is
64    /// unimplemented)
65    pub fn ref_count(&self) -> usize {
66        Arc::strong_count(&self.0)
67    }
68}
69impl Default for DummyEdge {
70    fn default() -> Self {
71        Self::new()
72    }
73}
74
75impl Edge for DummyEdge {
76    type Tag = ();
77
78    fn borrowed(&self) -> Borrowed<'_, Self> {
79        let ptr = Arc::as_ptr(&self.0);
80        Borrowed::new(DummyEdge(unsafe { Arc::from_raw(ptr) }))
81    }
82    fn with_tag(&self, _tag: ()) -> Borrowed<'_, Self> {
83        let ptr = Arc::as_ptr(&self.0);
84        Borrowed::new(DummyEdge(unsafe { Arc::from_raw(ptr) }))
85    }
86    fn with_tag_owned(self, _tag: ()) -> Self {
87        self
88    }
89    fn tag(&self) -> Self::Tag {}
90
91    fn node_id(&self) -> NodeID {
92        Arc::as_ptr(&self.0) as usize
93    }
94}
95
96/// Dummy manager that does not actually manage anything. It is only useful to
97/// clone and drop edges.
98pub struct DummyManager;
99
100/// Dummy diagram rules
101pub struct DummyRules;
102impl DiagramRules<DummyEdge, DummyNode, ()> for DummyRules {
103    type Cofactors<'a> = std::iter::Empty<Borrowed<'a, DummyEdge>>;
104
105    fn reduce<M>(
106        _manager: &M,
107        _level: LevelNo,
108        _children: impl IntoIterator<Item = DummyEdge>,
109    ) -> ReducedOrNew<DummyEdge, DummyNode>
110    where
111        M: Manager<Edge = DummyEdge, InnerNode = DummyNode>,
112    {
113        ReducedOrNew::New(DummyNode, ())
114    }
115
116    fn cofactors(_tag: (), _node: &DummyNode) -> Self::Cofactors<'_> {
117        std::iter::empty()
118    }
119}
120
121unsafe impl Manager for DummyManager {
122    type Edge = DummyEdge;
123    type EdgeTag = ();
124    type InnerNode = DummyNode;
125    type Terminal = ();
126    type TerminalRef<'a> = &'a ();
127    type Rules = DummyRules;
128    type TerminalIterator<'a>
129        = std::iter::Empty<DummyEdge>
130    where
131        Self: 'a;
132    type NodeSet = HashSet<NodeID>;
133    type LevelView<'a>
134        = DummyLevelView
135    where
136        Self: 'a;
137    type LevelIterator<'a>
138        = std::iter::Empty<DummyLevelView>
139    where
140        Self: 'a;
141
142    fn get_node(&self, _edge: &Self::Edge) -> Node<'_, Self> {
143        Node::Inner(&DummyNode)
144    }
145
146    fn clone_edge(&self, edge: &Self::Edge) -> Self::Edge {
147        DummyEdge(edge.0.clone())
148    }
149
150    fn drop_edge(&self, edge: Self::Edge) {
151        // Move the inner arc out. We need to use `std::ptr::read` since
152        // `DummyEdge` implements `Drop` (to print an error).
153        let arc = unsafe { std::ptr::read(&edge.0) };
154        std::mem::forget(edge);
155        drop(arc);
156    }
157
158    fn try_remove_node(&self, edge: Self::Edge, _level: LevelNo) -> bool {
159        // Move the inner arc out. We need to use `std::ptr::read` since
160        // `DummyEdge` implements `Drop` (to print an error).
161        let arc = unsafe { std::ptr::read(&edge.0) };
162        std::mem::forget(edge);
163        Arc::into_inner(arc).is_some()
164    }
165
166    fn num_inner_nodes(&self) -> usize {
167        0
168    }
169
170    fn num_levels(&self) -> LevelNo {
171        0
172    }
173
174    fn num_named_vars(&self) -> VarNo {
175        0
176    }
177
178    fn add_vars(&mut self, _additional: VarNo) -> Range<VarNo> {
179        unimplemented!()
180    }
181
182    fn add_named_vars<S: Into<String>>(
183        &mut self,
184        _names: impl IntoIterator<Item = S>,
185    ) -> Result<Range<VarNo>, DuplicateVarName> {
186        unimplemented!()
187    }
188
189    fn var_name(&self, _var: VarNo) -> &str {
190        panic!("out of range")
191    }
192
193    fn set_var_name(
194        &mut self,
195        _var: VarNo,
196        _name: impl Into<String>,
197    ) -> Result<(), DuplicateVarName> {
198        panic!("out of range")
199    }
200
201    fn name_to_var(&self, _name: impl AsRef<str>) -> Option<VarNo> {
202        None
203    }
204
205    fn var_to_level(&self, _var: VarNo) -> LevelNo {
206        panic!("out of range")
207    }
208
209    fn level_to_var(&self, _level: LevelNo) -> VarNo {
210        panic!("out of range")
211    }
212
213    fn level(&self, _no: LevelNo) -> Self::LevelView<'_> {
214        panic!("out of range")
215    }
216
217    unsafe fn level_unchecked(&self, _no: LevelNo) -> Self::LevelView<'_> {
218        panic!("out of range")
219    }
220
221    fn levels(&self) -> Self::LevelIterator<'_> {
222        std::iter::empty()
223    }
224
225    fn get_terminal(&self, _terminal: Self::Terminal) -> AllocResult<Self::Edge> {
226        unimplemented!()
227    }
228
229    fn num_terminals(&self) -> usize {
230        0
231    }
232
233    fn terminals(&self) -> Self::TerminalIterator<'_> {
234        std::iter::empty()
235    }
236
237    fn gc(&self) -> usize {
238        0
239    }
240
241    fn reorder<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
242        f(self)
243    }
244
245    fn gc_count(&self) -> u64 {
246        0
247    }
248
249    fn reorder_count(&self) -> u64 {
250        0
251    }
252}
253
254impl HasWorkers for DummyManager {
255    type WorkerPool = crate::Workers;
256
257    fn workers(&self) -> &Self::WorkerPool {
258        &crate::Workers
259    }
260}
261
262/// Dummy level view (not constructible)
263pub struct DummyLevelView;
264
265unsafe impl LevelView<DummyEdge, DummyNode> for DummyLevelView {
266    type Iterator<'a>
267        = std::iter::Empty<&'a DummyEdge>
268    where
269        Self: 'a,
270        DummyEdge: 'a;
271
272    type Taken = Self;
273
274    fn len(&self) -> usize {
275        unreachable!()
276    }
277
278    fn level_no(&self) -> LevelNo {
279        unreachable!()
280    }
281
282    fn reserve(&mut self, _additional: usize) {
283        unreachable!()
284    }
285
286    fn get(&self, _node: &DummyNode) -> Option<&DummyEdge> {
287        unreachable!()
288    }
289
290    fn insert(&mut self, _edge: DummyEdge) -> bool {
291        unreachable!()
292    }
293
294    unsafe fn insert_unchecked(&mut self, _edge: DummyEdge) -> bool {
295        unreachable!()
296    }
297
298    fn get_or_insert(&mut self, _node: DummyNode) -> AllocResult<DummyEdge> {
299        unreachable!()
300    }
301
302    unsafe fn get_or_insert_unchecked(&mut self, _node: DummyNode) -> AllocResult<DummyEdge> {
303        unreachable!()
304    }
305
306    fn gc(&mut self) {
307        unreachable!()
308    }
309
310    fn remove(&mut self, _node: &DummyNode) -> bool {
311        unreachable!()
312    }
313
314    unsafe fn swap(&mut self, _other: &mut Self) {
315        unreachable!()
316    }
317
318    fn iter(&self) -> Self::Iterator<'_> {
319        unreachable!()
320    }
321
322    fn take(&mut self) -> Option<Self::Taken> {
323        unreachable!()
324    }
325}
326
327/// Dummy node
328#[derive(PartialEq, Eq, Hash, Debug)]
329pub struct DummyNode;
330
331impl DropWith<DummyEdge> for DummyNode {
332    fn drop_with(self, _drop_edge: impl Fn(DummyEdge)) {
333        unimplemented!()
334    }
335}
336
337impl InnerNode<DummyEdge> for DummyNode {
338    const ARITY: usize = 0;
339
340    type ChildrenIter<'a>
341        = std::iter::Empty<Borrowed<'a, DummyEdge>>
342    where
343        Self: 'a;
344
345    fn new(_level: LevelNo, _children: impl IntoIterator<Item = DummyEdge>) -> Self {
346        unimplemented!()
347    }
348
349    fn check_level(&self, _check: impl FnOnce(LevelNo) -> bool) -> bool {
350        true
351    }
352    fn assert_level_matches(&self, _level: LevelNo) {}
353
354    fn children(&self) -> Self::ChildrenIter<'_> {
355        std::iter::empty()
356    }
357
358    fn child(&self, _n: usize) -> Borrowed<'_, DummyEdge> {
359        unimplemented!()
360    }
361
362    unsafe fn set_child(&self, _n: usize, _child: DummyEdge) -> DummyEdge {
363        unimplemented!()
364    }
365
366    fn ref_count(&self) -> usize {
367        unimplemented!()
368    }
369}
370
371/// Assert that the reference counts of edges match
372///
373/// # Example
374///
375/// ```
376/// # use oxidd_core::{Edge, Manager};
377/// # use oxidd_test_utils::assert_ref_counts;
378/// # use oxidd_test_utils::edge::{DummyEdge, DummyManager};
379/// let e1 = DummyEdge::new();
380/// let e2 = DummyManager.clone_edge(&e1);
381/// let e3 = DummyEdge::new();
382/// assert_ref_counts!(e1, e2 = 2; e3 = 1);
383/// # DummyManager.drop_edge(e1);
384/// # DummyManager.drop_edge(e2);
385/// # DummyManager.drop_edge(e3);
386/// ```
387#[macro_export]
388macro_rules! assert_ref_counts {
389    ($edge:ident = $count:literal) => {
390        assert_eq!($edge.ref_count(), $count);
391    };
392    ($edge:ident, $($edges:ident),+ = $count:literal) => {
393        assert_ref_counts!($edge = $count);
394        assert_ref_counts!($($edges),+ = $count);
395    };
396    // spell-checker:ignore edgess
397    ($($edges:ident),+ = $count:literal; $($($edgess:ident),+ = $counts:literal);+) => {
398        assert_ref_counts!($($edges),+ = $count);
399        assert_ref_counts!($($($edgess),+ = $counts);+);
400    };
401}