1use super::node::SimilaritySessionNode;
2use super::render::SimilarityRenderContext;
3use super::{
4 SimilarityApplyStatus, SimilarityEntityId, SimilarityEntityRef, SimilarityProviderId,
5 SimilarityResultId, SimilaritySessionCompletion,
6};
7use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
8use crate::settings::Settings;
9use crate::string::{BnString, IntoCStr};
10use binaryninjacore_sys::*;
11use std::ffi::{c_char, c_void};
12use std::marker::PhantomData;
13use std::rc::Rc;
14
15pub fn register_similarity_provider<C>(provider_ty: C) -> (&'static C, CoreSimilarityProviderType)
17where
18 C: SimilarityProviderType,
19{
20 let name = C::NAME.to_cstr();
21 let description = C::DESCRIPTION.to_cstr();
22 let leaked_provider: &'static C = Box::leak(Box::new(provider_ty));
24 let result = unsafe {
25 BNRegisterSimilarityProviderType(
26 name.as_ptr(),
27 description.as_ptr(),
28 &mut BNCustomSimilarityProviderType {
29 context: leaked_provider as *const C as *mut c_void,
30 create: Some(cb_create_provider::<C>),
31 getDefaultSettings: Some(cb_default_settings::<C>),
32 },
33 )
34 };
35 let core_provider_ty = unsafe { CoreSimilarityProviderType::from_raw(result) };
36 (leaked_provider, core_provider_ty)
37}
38
39pub trait SimilarityProviderType: Sync + 'static {
41 type SimilarityProvider: SimilarityProvider;
42
43 const NAME: &'static str;
45
46 const DESCRIPTION: &'static str;
48
49 fn create_provider(&self, settings: &Settings) -> Self::SimilarityProvider;
51
52 fn default_settings(&self) -> Option<Ref<Settings>>;
54}
55
56pub trait SimilarityProvider: Send + Sync + 'static {
61 fn update_settings(&self, _settings: &Settings) -> bool {
68 false
69 }
70
71 fn visit_node(
76 &self,
77 _node: &SimilaritySessionNode,
78 _results: &mut SimilarityProviderResults<'_>,
79 _completion: &SimilaritySessionCompletion,
80 ) -> bool {
81 true
82 }
83
84 fn visit_node_edge(
88 &self,
89 _from: &SimilaritySessionNode,
90 _to: &SimilaritySessionNode,
91 _results: &mut SimilarityProviderResults<'_>,
92 _completion: &SimilaritySessionCompletion,
93 ) -> bool {
94 true
95 }
96
97 fn result_name(
99 &self,
100 node: &SimilaritySessionNode,
101 entity: SimilarityEntityId,
102 result: SimilarityResultId,
103 ) -> Option<String>;
104
105 fn apply_result(
109 &self,
110 node: &SimilaritySessionNode,
111 entity: SimilarityEntityId,
112 result: SimilarityResultId,
113 ) -> SimilarityApplyStatus {
114 let Some(result) = node.result(result) else {
115 return SimilarityApplyStatus::SimilarityApplyFailed;
116 };
117 node.apply_target(entity, result.target)
118 }
119
120 fn render_result(
122 &self,
123 _node: &SimilaritySessionNode,
124 _entity: SimilarityEntityId,
125 _context: &SimilarityRenderContext,
126 _result: SimilarityResultId,
127 ) {
128 }
129}
130
131pub struct SimilarityProviderResults<'a> {
135 handle: *mut BNSimilarityProviderResults,
136 _lifetime: PhantomData<&'a mut BNSimilarityProviderResults>,
137 _not_send_or_sync: PhantomData<Rc<()>>,
138}
139
140impl<'a> SimilarityProviderResults<'a> {
141 unsafe fn from_raw(handle: *mut BNSimilarityProviderResults) -> Self {
142 Self {
143 handle,
144 _lifetime: PhantomData,
145 _not_send_or_sync: PhantomData,
146 }
147 }
148
149 pub fn add_result(
152 &mut self,
153 source: SimilarityEntityRef,
154 target: SimilarityEntityRef,
155 similarity: u8,
156 confidence: u8,
157 ) -> SimilarityResultId {
158 let source = BNSimilarityEntityRef::from(source);
159 let target = BNSimilarityEntityRef::from(target);
160 unsafe {
161 BNSimilarityProviderResultsAddResult(
162 self.handle,
163 &source,
164 &target,
165 similarity,
166 confidence,
167 )
168 .into()
169 }
170 }
171}
172
173pub struct CoreSimilarityProviderType {
175 pub(crate) handle: *mut BNSimilarityProviderType,
176}
177
178impl CoreSimilarityProviderType {
179 pub unsafe fn from_raw(handle: *mut BNSimilarityProviderType) -> Self {
180 Self { handle }
181 }
182
183 pub fn by_name(name: &str) -> Option<CoreSimilarityProviderType> {
185 let name = name.to_cstr();
186 let raw_type = unsafe { BNGetSimilarityProviderTypeByName(name.as_ptr()) };
187 match raw_type.is_null() {
188 true => None,
189 false => Some(unsafe { Self::from_raw(raw_type) }),
190 }
191 }
192
193 pub fn all() -> Array<CoreSimilarityProviderType> {
195 let mut count = 0;
196 let result = unsafe { BNGetSimilarityProviderTypeList(&mut count) };
197 unsafe { Array::new(result, count, ()) }
198 }
199
200 pub fn name(&self) -> String {
202 unsafe { BnString::into_string(BNSimilarityProviderTypeGetName(self.handle)) }
203 }
204
205 pub fn description(&self) -> String {
207 unsafe { BnString::into_string(BNSimilarityProviderTypeGetDescription(self.handle)) }
208 }
209
210 pub fn create_provider(&self, settings: &Settings) -> Option<Ref<CoreSimilarityProvider>> {
212 let provider_raw =
213 unsafe { BNSimilarityProviderTypeCreateProvider(self.handle, settings.handle) };
214 (!provider_raw.is_null())
215 .then(|| unsafe { CoreSimilarityProvider::ref_from_raw(provider_raw) })
216 }
217
218 pub fn default_settings(&self) -> Option<Ref<Settings>> {
220 let settings_raw = unsafe { BNSimilarityProviderTypeGetDefaultSettings(self.handle) };
221 (!settings_raw.is_null()).then(|| unsafe { Settings::ref_from_raw(settings_raw) })
222 }
223}
224
225impl CoreArrayProvider for CoreSimilarityProviderType {
226 type Raw = *mut BNSimilarityProviderType;
227 type Context = ();
228 type Wrapped<'a> = CoreSimilarityProviderType;
229}
230
231unsafe impl CoreArrayProviderInner for CoreSimilarityProviderType {
232 unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
233 BNFreeSimilarityProviderTypeList(raw)
234 }
235
236 unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
237 CoreSimilarityProviderType::from_raw(*raw)
238 }
239}
240
241pub struct CoreSimilarityProvider {
243 pub(crate) handle: *mut BNSimilarityProvider,
244}
245
246impl CoreSimilarityProvider {
247 pub unsafe fn from_raw(handle: *mut BNSimilarityProvider) -> Self {
248 Self { handle }
249 }
250
251 pub unsafe fn ref_from_raw(handle: *mut BNSimilarityProvider) -> Ref<Self> {
252 Ref::new(Self { handle })
253 }
254
255 pub fn id(&self) -> SimilarityProviderId {
257 unsafe { BNSimilarityProviderGetId(self.handle) }.into()
258 }
259
260 pub fn create<C: SimilarityProvider>(
262 ty: &CoreSimilarityProviderType,
263 provider: C,
264 ) -> Ref<CoreSimilarityProvider> {
265 let provider = Box::into_raw(Box::new(provider));
266 let mut callbacks = BNCustomSimilarityProvider {
267 context: provider.cast(),
268 externalRefTaken: None,
269 externalRefReleased: None,
270 updateSettings: Some(cb_update_provider_settings::<C>),
271 visitNode: Some(cb_visit_node::<C>),
272 visitNodeEdge: Some(cb_visit_node_edge::<C>),
273 getName: Some(cb_provider_get_name::<C>),
274 apply: Some(cb_provider_apply::<C>),
275 render: Some(cb_provider_render::<C>),
276 free: Some(cb_provider_free::<C>),
277 };
278
279 let raw_provider = unsafe { BNCreateCustomSimilarityProvider(ty.handle, &mut callbacks) };
280 unsafe { CoreSimilarityProvider::ref_from_raw(raw_provider) }
281 }
282
283 pub fn provider_type(&self) -> CoreSimilarityProviderType {
285 let handle = unsafe { BNSimilarityProviderGetType(self.handle) };
286 unsafe { CoreSimilarityProviderType::from_raw(handle) }
287 }
288
289 pub fn visit_node(
291 &self,
292 node: &SimilaritySessionNode,
293 completion: &SimilaritySessionCompletion,
294 ) {
295 unsafe { BNSimilarityProviderVisitNode(self.handle, node.handle, completion.handle) }
296 }
297
298 pub fn visit_node_edge(
300 &self,
301 from: &SimilaritySessionNode,
302 to: &SimilaritySessionNode,
303 completion: &SimilaritySessionCompletion,
304 ) {
305 unsafe {
306 BNSimilarityProviderVisitNodeEdge(
307 self.handle,
308 from.handle,
309 to.handle,
310 completion.handle,
311 )
312 }
313 }
314
315 pub fn perform_visit_node(
317 &self,
318 node: &SimilaritySessionNode,
319 results: &mut SimilarityProviderResults<'_>,
320 completion: &SimilaritySessionCompletion,
321 ) -> bool {
322 unsafe {
323 BNSimilarityProviderPerformVisitNode(
324 self.handle,
325 node.handle,
326 results.handle,
327 completion.handle,
328 )
329 }
330 }
331
332 pub fn perform_visit_node_edge(
334 &self,
335 from: &SimilaritySessionNode,
336 to: &SimilaritySessionNode,
337 results: &mut SimilarityProviderResults<'_>,
338 completion: &SimilaritySessionCompletion,
339 ) -> bool {
340 unsafe {
341 BNSimilarityProviderPerformVisitNodeEdge(
342 self.handle,
343 from.handle,
344 to.handle,
345 results.handle,
346 completion.handle,
347 )
348 }
349 }
350
351 pub fn result_name(
353 &self,
354 node: &SimilaritySessionNode,
355 entity: SimilarityEntityId,
356 result: SimilarityResultId,
357 ) -> Option<String> {
358 let name_raw = unsafe {
359 BNSimilarityProviderGetName(self.handle, node.handle, entity.into(), result.into())
360 };
361 if name_raw.is_null() {
362 return None;
363 }
364 Some(unsafe { BnString::into_string(name_raw) })
365 }
366
367 pub fn apply_result(
369 &self,
370 node: &SimilaritySessionNode,
371 entity: SimilarityEntityId,
372 result: SimilarityResultId,
373 ) -> SimilarityApplyStatus {
374 unsafe { BNSimilarityProviderApply(self.handle, node.handle, entity.into(), result.into()) }
375 }
376
377 pub fn render_result(
379 &self,
380 node: &SimilaritySessionNode,
381 entity: SimilarityEntityId,
382 context: &SimilarityRenderContext,
383 result: SimilarityResultId,
384 ) {
385 unsafe {
386 BNSimilarityProviderRender(
387 self.handle,
388 node.handle,
389 entity.into(),
390 context.handle,
391 result.into(),
392 )
393 }
394 }
395}
396
397unsafe impl Send for CoreSimilarityProvider {}
398unsafe impl Sync for CoreSimilarityProvider {}
399
400impl ToOwned for CoreSimilarityProvider {
401 type Owned = Ref<Self>;
402
403 fn to_owned(&self) -> Self::Owned {
404 unsafe { RefCountable::inc_ref(self) }
405 }
406}
407
408unsafe impl RefCountable for CoreSimilarityProvider {
409 unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
410 Ref::new(Self {
411 handle: BNNewSimilarityProviderReference(handle.handle),
412 })
413 }
414
415 unsafe fn dec_ref(handle: &Self) {
416 BNFreeSimilarityProvider(handle.handle);
417 }
418}
419
420impl CoreArrayProvider for CoreSimilarityProvider {
421 type Raw = *mut BNSimilarityProvider;
422 type Context = ();
423 type Wrapped<'a> = Guard<'a, Self>;
424}
425
426unsafe impl CoreArrayProviderInner for CoreSimilarityProvider {
427 unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
428 BNFreeSimilarityProviderList(raw, count)
429 }
430
431 unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
432 Guard::new(Self::from_raw(*raw), context)
433 }
434}
435
436#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
438pub struct SimilarityResult {
439 pub provider_id: SimilarityProviderId,
441 pub similarity: u8,
443 pub confidence: u8,
445 pub target: SimilarityEntityRef,
449}
450
451impl From<BNSimilarityResult> for SimilarityResult {
452 fn from(value: BNSimilarityResult) -> Self {
453 Self {
454 provider_id: value.providerId.into(),
455 similarity: value.similarity,
456 confidence: value.confidence,
457 target: value.target.into(),
458 }
459 }
460}
461
462impl From<SimilarityResult> for BNSimilarityResult {
463 fn from(value: SimilarityResult) -> Self {
464 Self {
465 providerId: value.provider_id.into(),
466 similarity: value.similarity,
467 confidence: value.confidence,
468 target: value.target.into(),
469 }
470 }
471}
472
473unsafe extern "C" fn cb_create_provider<C: SimilarityProviderType>(
474 ctxt: *mut c_void,
475 settings: *mut BNSettings,
476) -> *mut BNSimilarityProvider {
477 ffi_wrap!("SimilarityProviderType::create_provider", unsafe {
478 let ctxt: &C = &*(ctxt as *const C);
479 let settings = Settings::from_raw(settings);
480 let provider = ctxt.create_provider(&settings);
481 let core_type = CoreSimilarityProviderType::by_name(C::NAME).unwrap();
482 let core_provider = CoreSimilarityProvider::create(&core_type, provider);
483 Ref::into_raw(core_provider).handle
484 })
485}
486
487unsafe extern "C" fn cb_default_settings<C: SimilarityProviderType>(
488 ctxt: *mut c_void,
489) -> *mut BNSettings {
490 ffi_wrap!("SimilarityProviderType::default_settings", unsafe {
491 let ctxt: &C = &*(ctxt as *const C);
492 ctxt.default_settings()
493 .map(|settings| Ref::into_raw(settings).handle)
494 .unwrap_or(std::ptr::null_mut())
495 })
496}
497
498unsafe extern "C" fn cb_update_provider_settings<C: SimilarityProvider>(
499 ctxt: *mut c_void,
500 settings: *mut BNSettings,
501) -> bool {
502 ffi_wrap!("SimilarityProvider::update_settings", unsafe {
503 let ctxt: &C = &*(ctxt as *const C);
504 let settings = Settings::from_raw(settings);
505 ctxt.update_settings(&settings)
506 })
507}
508
509unsafe extern "C" fn cb_visit_node<C: SimilarityProvider>(
510 ctxt: *mut c_void,
511 node: *mut BNSimilaritySessionNode,
512 results: *mut BNSimilarityProviderResults,
513 completion: *mut BNSimilaritySessionCompletion,
514) -> bool {
515 ffi_wrap!("SimilarityProvider::visit_node", unsafe {
516 let ctxt: &C = &*(ctxt as *const C);
517 let node = SimilaritySessionNode::from_raw(node);
518 let mut results = SimilarityProviderResults::from_raw(results);
519 let completion = SimilaritySessionCompletion::from_raw(completion);
520 ctxt.visit_node(&node, &mut results, &completion)
521 })
522}
523
524unsafe extern "C" fn cb_visit_node_edge<C: SimilarityProvider>(
525 ctxt: *mut c_void,
526 from: *mut BNSimilaritySessionNode,
527 to: *mut BNSimilaritySessionNode,
528 results: *mut BNSimilarityProviderResults,
529 completion: *mut BNSimilaritySessionCompletion,
530) -> bool {
531 ffi_wrap!("SimilarityProvider::visit_node_edge", unsafe {
532 let ctxt: &C = &*(ctxt as *const C);
533 let from = SimilaritySessionNode::from_raw(from);
534 let to = SimilaritySessionNode::from_raw(to);
535 let mut results = SimilarityProviderResults::from_raw(results);
536 let completion = SimilaritySessionCompletion::from_raw(completion);
537 ctxt.visit_node_edge(&from, &to, &mut results, &completion)
538 })
539}
540
541unsafe extern "C" fn cb_provider_get_name<C: SimilarityProvider>(
542 ctxt: *mut c_void,
543 node: *mut BNSimilaritySessionNode,
544 entity: BNSimilarityEntityId,
545 result: BNSimilarityResultId,
546) -> *mut c_char {
547 ffi_wrap!("SimilarityProvider::result_name", unsafe {
548 let ctxt: &C = &*(ctxt as *const C);
549 let node = SimilaritySessionNode::from_raw(node);
550 let Some(name) = ctxt.result_name(&node, entity.into(), result.into()) else {
551 return std::ptr::null_mut();
552 };
553 BnString::into_raw(BnString::new(name))
554 })
555}
556
557unsafe extern "C" fn cb_provider_apply<C: SimilarityProvider>(
558 ctxt: *mut c_void,
559 node: *mut BNSimilaritySessionNode,
560 entity: BNSimilarityEntityId,
561 result: BNSimilarityResultId,
562) -> BNSimilarityApplyStatus {
563 ffi_wrap!("SimilarityProvider::apply_result", unsafe {
564 let ctxt: &C = &*(ctxt as *const C);
565 let node = SimilaritySessionNode::from_raw(node);
566 ctxt.apply_result(&node, entity.into(), result.into())
567 })
568}
569
570unsafe extern "C" fn cb_provider_render<C: SimilarityProvider>(
571 ctxt: *mut c_void,
572 node: *mut BNSimilaritySessionNode,
573 entity: BNSimilarityEntityId,
574 context: *mut BNSimilarityRenderContext,
575 result: BNSimilarityResultId,
576) {
577 ffi_wrap!("SimilarityProvider::render_result", unsafe {
578 let ctxt: &C = &*(ctxt as *const C);
579 let node = SimilaritySessionNode::from_raw(node);
580 let context = SimilarityRenderContext::from_raw(context);
581 ctxt.render_result(&node, entity.into(), &context, result.into());
582 })
583}
584
585unsafe extern "C" fn cb_provider_free<C: SimilarityProvider>(ctxt: *mut c_void) {
586 ffi_wrap!("SimilarityProvider::free", unsafe {
587 let _ = Box::from_raw(ctxt as *mut C);
588 })
589}