Skip to main content

binaryninja/similarity/
graph.rs

1use super::node::SimilaritySessionNode;
2use super::SimilaritySessionNodeId;
3use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
4use binaryninjacore_sys::*;
5use std::ffi::c_void;
6
7/// Receives notifications after nodes or edges are added to or removed from a session graph.
8pub trait SimilaritySessionGraphReceiver: Send + Sync + 'static {
9    fn on_graph_changed(&self);
10}
11
12/// A core-backed session graph receiver.
13pub struct CoreSimilaritySessionGraphReceiver {
14    pub(crate) handle: *mut BNSimilaritySessionGraphReceiver,
15}
16
17impl CoreSimilaritySessionGraphReceiver {
18    pub unsafe fn from_raw(handle: *mut BNSimilaritySessionGraphReceiver) -> Self {
19        Self { handle }
20    }
21
22    pub unsafe fn ref_from_raw(handle: *mut BNSimilaritySessionGraphReceiver) -> Ref<Self> {
23        Ref::new(Self { handle })
24    }
25
26    pub fn create<C: SimilaritySessionGraphReceiver>(
27        receiver: C,
28    ) -> Ref<CoreSimilaritySessionGraphReceiver> {
29        let receiver = Box::into_raw(Box::new(receiver));
30        let mut callbacks = BNCustomSimilaritySessionGraphReceiver {
31            context: receiver.cast(),
32            externalRefTaken: None,
33            externalRefReleased: None,
34            onGraphChanged: Some(cb_on_graph_changed::<C>),
35            free: Some(cb_receiver_free::<C>),
36        };
37        let raw_receiver = unsafe { BNCreateCustomSimilaritySessionGraphReceiver(&mut callbacks) };
38        unsafe { CoreSimilaritySessionGraphReceiver::ref_from_raw(raw_receiver) }
39    }
40}
41
42impl SimilaritySessionGraphReceiver for CoreSimilaritySessionGraphReceiver {
43    fn on_graph_changed(&self) {
44        unsafe { BNSimilaritySessionGraphReceiverNotifyGraphChanged(self.handle) }
45    }
46}
47
48unsafe impl Send for CoreSimilaritySessionGraphReceiver {}
49unsafe impl Sync for CoreSimilaritySessionGraphReceiver {}
50
51impl ToOwned for CoreSimilaritySessionGraphReceiver {
52    type Owned = Ref<Self>;
53
54    fn to_owned(&self) -> Self::Owned {
55        unsafe { RefCountable::inc_ref(self) }
56    }
57}
58
59unsafe impl RefCountable for CoreSimilaritySessionGraphReceiver {
60    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
61        Ref::new(Self {
62            handle: BNNewSimilaritySessionGraphReceiverReference(handle.handle),
63        })
64    }
65
66    unsafe fn dec_ref(handle: &Self) {
67        BNFreeSimilaritySessionGraphReceiver(handle.handle);
68    }
69}
70
71impl CoreArrayProvider for CoreSimilaritySessionGraphReceiver {
72    type Raw = *mut BNSimilaritySessionGraphReceiver;
73    type Context = ();
74    type Wrapped<'a> = Guard<'a, Self>;
75}
76
77unsafe impl CoreArrayProviderInner for CoreSimilaritySessionGraphReceiver {
78    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
79        BNFreeSimilaritySessionGraphReceiverList(raw, count)
80    }
81
82    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
83        Guard::new(Self::from_raw(*raw), context)
84    }
85}
86
87unsafe extern "C" fn cb_on_graph_changed<C: SimilaritySessionGraphReceiver>(ctxt: *mut c_void) {
88    ffi_wrap!("SimilaritySessionGraphReceiver::on_graph_changed", unsafe {
89        let ctxt: &C = &*(ctxt as *const C);
90        ctxt.on_graph_changed();
91    })
92}
93
94unsafe extern "C" fn cb_receiver_free<C: SimilaritySessionGraphReceiver>(ctxt: *mut c_void) {
95    ffi_wrap!("SimilaritySessionGraphReceiver::free", unsafe {
96        let _ = Box::from_raw(ctxt as *mut C);
97    })
98}
99
100/// A graph that controls node processing order and cannot contain cycles.
101///
102/// Nodes and edges cannot be changed during a run.
103pub struct SimilaritySessionGraph {
104    pub(crate) handle: *mut BNSimilaritySessionGraph,
105}
106
107impl SimilaritySessionGraph {
108    pub unsafe fn ref_from_raw(handle: *mut BNSimilaritySessionGraph) -> Ref<Self> {
109        Ref::new(Self { handle })
110    }
111
112    /// Adds a node, moving it from its current graph if necessary.
113    ///
114    /// If either graph is running, the node is unchanged.
115    pub fn add_node(&self, node: &SimilaritySessionNode) {
116        unsafe { BNSimilaritySessionGraphAddNode(self.handle, node.handle) }
117    }
118
119    /// Removes a node and its edges from the graph.
120    pub fn remove_node(&self, node: &SimilaritySessionNode) {
121        unsafe { BNSimilaritySessionGraphRemoveNode(self.handle, node.handle) }
122    }
123
124    /// Returns a node by ID.
125    pub fn node(&self, id: SimilaritySessionNodeId) -> Option<Ref<SimilaritySessionNode>> {
126        let handle = unsafe { BNSimilaritySessionGraphGetNode(self.handle, id.into()) };
127        match handle.is_null() {
128            true => None,
129            false => Some(unsafe { SimilaritySessionNode::ref_from_raw(handle) }),
130        }
131    }
132
133    /// Returns all nodes in the graph.
134    pub fn nodes(&self) -> Array<SimilaritySessionNode> {
135        let mut count = 0;
136        let result = unsafe { BNSimilaritySessionGraphGetNodes(self.handle, &mut count) };
137        unsafe { Array::new(result, count, ()) }
138    }
139
140    /// Returns whether an edge can be added without creating a cycle.
141    pub fn is_valid_edge(&self, from: &SimilaritySessionNode, to: &SimilaritySessionNode) -> bool {
142        unsafe { BNSimilaritySessionGraphIsValidEdge(self.handle, from.handle, to.handle) }
143    }
144
145    /// Adds an edge if both nodes are present and it would not create a cycle.
146    pub fn add_edge(&self, from: &SimilaritySessionNode, to: &SimilaritySessionNode) -> bool {
147        unsafe { BNSimilaritySessionGraphAddEdge(self.handle, from.handle, to.handle) }
148    }
149
150    /// Removes an edge from the graph.
151    pub fn remove_edge(&self, from: &SimilaritySessionNode, to: &SimilaritySessionNode) -> bool {
152        unsafe { BNSimilaritySessionGraphRemoveEdge(self.handle, from.handle, to.handle) }
153    }
154
155    /// Adds a graph-change receiver.
156    pub fn add_receiver(&self, receiver: &CoreSimilaritySessionGraphReceiver) {
157        unsafe { BNSimilaritySessionGraphAddReceiver(self.handle, receiver.handle) }
158    }
159
160    /// Removes a graph-change receiver.
161    pub fn remove_receiver(&self, receiver: &CoreSimilaritySessionGraphReceiver) {
162        unsafe { BNSimilaritySessionGraphRemoveReceiver(self.handle, receiver.handle) }
163    }
164
165    /// Returns the graph-change receivers registered with this graph.
166    pub fn receivers(&self) -> Array<CoreSimilaritySessionGraphReceiver> {
167        let mut count = 0;
168        let result = unsafe { BNSimilaritySessionGraphGetReceivers(self.handle, &mut count) };
169        unsafe { Array::new(result, count, ()) }
170    }
171
172    /// Returns groups of nodes in processing order. Nodes in the same group may run in parallel.
173    pub fn schedule(&self) -> Vec<Vec<Ref<SimilaritySessionNode>>> {
174        let mut level_count = 0;
175        let mut node_counts_ptr: *mut usize = std::ptr::null_mut();
176        let raw_schedule = unsafe {
177            BNSimilaritySessionGraphGetSchedule(self.handle, &mut node_counts_ptr, &mut level_count)
178        };
179
180        let mut result = Vec::with_capacity(level_count);
181        unsafe {
182            let raw_levels = std::slice::from_raw_parts(raw_schedule, level_count);
183            let node_counts = std::slice::from_raw_parts(node_counts_ptr, level_count);
184            for (&raw_level, &count) in raw_levels.iter().zip(node_counts) {
185                let level = std::slice::from_raw_parts(raw_level, count)
186                    .iter()
187                    .map(|&node| {
188                        SimilaritySessionNode::ref_from_raw(BNNewSimilaritySessionNodeReference(
189                            node,
190                        ))
191                    })
192                    .collect();
193                result.push(level);
194            }
195            BNFreeSimilaritySessionNodeSchedule(raw_schedule, node_counts_ptr, level_count);
196        }
197        result
198    }
199}
200
201impl ToOwned for SimilaritySessionGraph {
202    type Owned = Ref<Self>;
203
204    fn to_owned(&self) -> Self::Owned {
205        unsafe { RefCountable::inc_ref(self) }
206    }
207}
208
209unsafe impl RefCountable for SimilaritySessionGraph {
210    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
211        Ref::new(Self {
212            handle: BNNewSimilaritySessionGraphReference(handle.handle),
213        })
214    }
215    unsafe fn dec_ref(handle: &Self) {
216        BNFreeSimilaritySessionGraph(handle.handle);
217    }
218}