binaryninja/types/
printer.rs

1#![allow(unused)]
2
3use crate::binary_view::BinaryView;
4use crate::disassembly::InstructionTextToken;
5use crate::platform::Platform;
6use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
7use crate::string::{raw_to_string, BnString, IntoCStr};
8use crate::types::{NamedTypeReference, QualifiedName, QualifiedNameAndType, Type, TypeContainer};
9use binaryninjacore_sys::*;
10use std::ffi::{c_char, c_int, c_void};
11use std::ptr::NonNull;
12
13pub type TokenEscapingType = BNTokenEscapingType;
14pub type TypeDefinitionLineType = BNTypeDefinitionLineType;
15
16/// Register a custom parser with the API
17pub fn register_type_printer<T: TypePrinter>(
18    name: &str,
19    parser: T,
20) -> (&'static mut T, CoreTypePrinter) {
21    let parser = Box::leak(Box::new(parser));
22    let mut callback = BNTypePrinterCallbacks {
23        context: parser as *mut _ as *mut c_void,
24        getTypeTokens: Some(cb_get_type_tokens::<T>),
25        getTypeTokensBeforeName: Some(cb_get_type_tokens_before_name::<T>),
26        getTypeTokensAfterName: Some(cb_get_type_tokens_after_name::<T>),
27        getTypeString: Some(cb_get_type_string::<T>),
28        getTypeStringBeforeName: Some(cb_get_type_string_before_name::<T>),
29        getTypeStringAfterName: Some(cb_get_type_string_after_name::<T>),
30        getTypeLines: Some(cb_get_type_lines::<T>),
31        printAllTypes: Some(cb_print_all_types::<T>),
32        freeTokens: Some(cb_free_tokens),
33        freeString: Some(cb_free_string),
34        freeLines: Some(cb_free_lines),
35    };
36    let raw_name = name.to_cstr();
37    let result = unsafe { BNRegisterTypePrinter(raw_name.as_ptr(), &mut callback) };
38    let core = unsafe { CoreTypePrinter::from_raw(NonNull::new(result).unwrap()) };
39    (parser, core)
40}
41
42#[repr(transparent)]
43pub struct CoreTypePrinter {
44    pub(crate) handle: NonNull<BNTypePrinter>,
45}
46
47impl CoreTypePrinter {
48    pub(crate) unsafe fn from_raw(handle: NonNull<BNTypePrinter>) -> CoreTypePrinter {
49        Self { handle }
50    }
51
52    pub fn printers() -> Array<CoreTypePrinter> {
53        let mut count = 0;
54        let result = unsafe { BNGetTypePrinterList(&mut count) };
55        assert!(!result.is_null());
56        unsafe { Array::new(result, count, ()) }
57    }
58
59    pub fn printer_by_name(name: &str) -> Option<CoreTypePrinter> {
60        let name_raw = name.to_cstr();
61        let result = unsafe { BNGetTypePrinterByName(name_raw.as_ptr()) };
62        NonNull::new(result).map(|x| unsafe { Self::from_raw(x) })
63    }
64
65    pub fn name(&self) -> String {
66        let result = unsafe { BNGetTypePrinterName(self.handle.as_ptr()) };
67        assert!(!result.is_null());
68        unsafe { BnString::into_string(result) }
69    }
70
71    pub fn get_type_tokens<T: Into<QualifiedName>>(
72        &self,
73        type_: &Type,
74        platform: &Platform,
75        name: T,
76        base_confidence: u8,
77        escaping: TokenEscapingType,
78    ) -> Option<Array<InstructionTextToken>> {
79        let mut result_count = 0;
80        let mut result = std::ptr::null_mut();
81        let mut raw_name = QualifiedName::into_raw(name.into());
82        let success = unsafe {
83            BNGetTypePrinterTypeTokens(
84                self.handle.as_ptr(),
85                type_.handle,
86                platform.handle,
87                &mut raw_name,
88                base_confidence,
89                escaping,
90                &mut result,
91                &mut result_count,
92            )
93        };
94        QualifiedName::free_raw(raw_name);
95        success.then(|| {
96            assert!(!result.is_null());
97            unsafe { Array::new(result, result_count, ()) }
98        })
99    }
100
101    pub fn get_type_tokens_before_name(
102        &self,
103        type_: &Type,
104        platform: &Platform,
105        base_confidence: u8,
106        parent_type: &Type,
107        escaping: TokenEscapingType,
108    ) -> Option<Array<InstructionTextToken>> {
109        let mut result_count = 0;
110        let mut result = std::ptr::null_mut();
111        let success = unsafe {
112            BNGetTypePrinterTypeTokensBeforeName(
113                self.handle.as_ptr(),
114                type_.handle,
115                platform.handle,
116                base_confidence,
117                parent_type.handle,
118                escaping,
119                &mut result,
120                &mut result_count,
121            )
122        };
123        success.then(|| {
124            assert!(!result.is_null());
125            unsafe { Array::new(result, result_count, ()) }
126        })
127    }
128
129    pub fn get_type_tokens_after_name(
130        &self,
131        type_: &Type,
132        platform: &Platform,
133        base_confidence: u8,
134        parent_type: &Type,
135        escaping: TokenEscapingType,
136    ) -> Option<Array<InstructionTextToken>> {
137        let mut result_count = 0;
138        let mut result = std::ptr::null_mut();
139        let success = unsafe {
140            BNGetTypePrinterTypeTokensAfterName(
141                self.handle.as_ptr(),
142                type_.handle,
143                platform.handle,
144                base_confidence,
145                parent_type.handle,
146                escaping,
147                &mut result,
148                &mut result_count,
149            )
150        };
151        success.then(|| {
152            assert!(!result.is_null());
153            unsafe { Array::new(result, result_count, ()) }
154        })
155    }
156
157    pub fn get_type_string<T: Into<QualifiedName>>(
158        &self,
159        type_: &Type,
160        platform: &Platform,
161        name: T,
162        escaping: TokenEscapingType,
163    ) -> Option<BnString> {
164        let mut result = std::ptr::null_mut();
165        let mut raw_name = QualifiedName::into_raw(name.into());
166        let success = unsafe {
167            BNGetTypePrinterTypeString(
168                self.handle.as_ptr(),
169                type_.handle,
170                platform.handle,
171                &mut raw_name,
172                escaping,
173                &mut result,
174            )
175        };
176        QualifiedName::free_raw(raw_name);
177        success.then(|| unsafe {
178            assert!(!result.is_null());
179            BnString::from_raw(result)
180        })
181    }
182
183    pub fn get_type_string_before_name(
184        &self,
185        type_: &Type,
186        platform: &Platform,
187        escaping: BNTokenEscapingType,
188    ) -> Option<BnString> {
189        let mut result = std::ptr::null_mut();
190        let success = unsafe {
191            BNGetTypePrinterTypeStringAfterName(
192                self.handle.as_ptr(),
193                type_.handle,
194                platform.handle,
195                escaping,
196                &mut result,
197            )
198        };
199        success.then(|| unsafe {
200            assert!(!result.is_null());
201            BnString::from_raw(result)
202        })
203    }
204
205    pub fn get_type_string_after_name(
206        &self,
207        type_: &Type,
208        platform: &Platform,
209        escaping: TokenEscapingType,
210    ) -> Option<BnString> {
211        let mut result = std::ptr::null_mut();
212        let success = unsafe {
213            BNGetTypePrinterTypeStringBeforeName(
214                self.handle.as_ptr(),
215                type_.handle,
216                platform.handle,
217                escaping,
218                &mut result,
219            )
220        };
221        success.then(|| unsafe {
222            assert!(!result.is_null());
223            BnString::from_raw(result)
224        })
225    }
226
227    pub fn get_type_lines<T: Into<QualifiedName>>(
228        &self,
229        type_: &Type,
230        types: &TypeContainer,
231        name: T,
232        padding_cols: isize,
233        collapsed: bool,
234        escaping: TokenEscapingType,
235    ) -> Option<Array<TypeDefinitionLine>> {
236        let mut result_count = 0;
237        let mut result = std::ptr::null_mut();
238        let mut raw_name = QualifiedName::into_raw(name.into());
239        let success = unsafe {
240            BNGetTypePrinterTypeLines(
241                self.handle.as_ptr(),
242                type_.handle,
243                types.handle.as_ptr(),
244                &mut raw_name,
245                padding_cols as c_int,
246                collapsed,
247                escaping,
248                &mut result,
249                &mut result_count,
250            )
251        };
252        QualifiedName::free_raw(raw_name);
253        success.then(|| {
254            assert!(!result.is_null());
255            unsafe { Array::<TypeDefinitionLine>::new(result, result_count, ()) }
256        })
257    }
258
259    /// Print all types to a single big string, including headers, sections, etc
260    ///
261    /// * `types` - All types to print
262    /// * `data` - Binary View in which all the types are defined
263    /// * `padding_cols` - Maximum number of bytes represented by each padding line
264    /// * `escaping` - Style of escaping literals which may not be parsable
265    pub fn default_print_all_types<T, I>(
266        &self,
267        types: T,
268        data: &BinaryView,
269        padding_cols: isize,
270        escaping: TokenEscapingType,
271    ) -> Option<BnString>
272    where
273        T: Iterator<Item = I>,
274        I: Into<QualifiedNameAndType>,
275    {
276        let mut result = std::ptr::null_mut();
277        let (mut raw_names, mut raw_types): (Vec<BNQualifiedName>, Vec<_>) = types
278            .map(|t| {
279                let t = t.into();
280                // Leak both to the core and then free afterwards.
281                (
282                    QualifiedName::into_raw(t.name),
283                    unsafe { Ref::into_raw(t.ty) }.handle,
284                )
285            })
286            .unzip();
287        let success = unsafe {
288            BNTypePrinterDefaultPrintAllTypes(
289                self.handle.as_ptr(),
290                raw_names.as_mut_ptr(),
291                raw_types.as_mut_ptr(),
292                raw_types.len(),
293                data.handle,
294                padding_cols as c_int,
295                escaping,
296                &mut result,
297            )
298        };
299        for raw_name in raw_names {
300            QualifiedName::free_raw(raw_name);
301        }
302        for raw_type in raw_types {
303            let _ = unsafe { Type::ref_from_raw(raw_type) };
304        }
305        success.then(|| unsafe {
306            assert!(!result.is_null());
307            BnString::from_raw(result)
308        })
309    }
310
311    pub fn print_all_types<T, I>(
312        &self,
313        types: T,
314        data: &BinaryView,
315        padding_cols: isize,
316        escaping: TokenEscapingType,
317    ) -> Option<BnString>
318    where
319        T: IntoIterator<Item = I>,
320        I: Into<QualifiedNameAndType>,
321    {
322        let mut result = std::ptr::null_mut();
323        // TODO: I dislike how this iter unzip looks like... but its how to avoid allocating again...
324        let (mut raw_names, mut raw_types): (Vec<BNQualifiedName>, Vec<_>) = types
325            .into_iter()
326            .map(|t| {
327                let t = t.into();
328                // Leak both to the core and then free afterwards.
329                (
330                    QualifiedName::into_raw(t.name),
331                    unsafe { Ref::into_raw(t.ty) }.handle,
332                )
333            })
334            .unzip();
335        let success = unsafe {
336            BNTypePrinterPrintAllTypes(
337                self.handle.as_ptr(),
338                raw_names.as_mut_ptr(),
339                raw_types.as_mut_ptr(),
340                raw_types.len(),
341                data.handle,
342                padding_cols as c_int,
343                escaping,
344                &mut result,
345            )
346        };
347        for raw_name in raw_names {
348            QualifiedName::free_raw(raw_name);
349        }
350        for raw_type in raw_types {
351            let _ = unsafe { Type::ref_from_raw(raw_type) };
352        }
353        success.then(|| unsafe {
354            assert!(!result.is_null());
355            BnString::from_raw(result)
356        })
357    }
358}
359
360impl Default for CoreTypePrinter {
361    fn default() -> Self {
362        // TODO: Remove this entirely, there is no "default", its view specific lets not make this some defined behavior.
363        let default_settings = crate::settings::Settings::new();
364        let name = default_settings.get_string("analysis.types.printerName");
365        Self::printer_by_name(&name).unwrap()
366    }
367}
368
369impl CoreArrayProvider for CoreTypePrinter {
370    type Raw = *mut BNTypePrinter;
371    type Context = ();
372    type Wrapped<'a> = Self;
373}
374
375unsafe impl CoreArrayProviderInner for CoreTypePrinter {
376    unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
377        BNFreeTypePrinterList(raw)
378    }
379
380    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
381        // TODO: Because handle is a NonNull we should prob make Self::Raw that as well...
382        let handle = NonNull::new(*raw).unwrap();
383        CoreTypePrinter::from_raw(handle)
384    }
385}
386
387pub trait TypePrinter {
388    /// Generate a single-line text representation of a type, Returns a List
389    /// of text tokens representing the type.
390    ///
391    /// * `type_` - Type to print
392    /// * `platform` - Platform responsible for this type
393    /// * `name` - Name of the type
394    /// * `base_confidence` - Confidence to use for tokens created for this type
395    /// * `escaping` - Style of escaping literals which may not be parsable
396    fn get_type_tokens<T: Into<QualifiedName>>(
397        &self,
398        type_: Ref<Type>,
399        platform: Option<Ref<Platform>>,
400        name: T,
401        base_confidence: u8,
402        escaping: TokenEscapingType,
403    ) -> Option<Vec<InstructionTextToken>>;
404
405    /// In a single-line text representation of a type, generate the tokens that
406    /// should be printed before the type's name. Returns a list of text tokens
407    /// representing the type
408    ///
409    /// * `type_` - Type to print
410    /// * `platform` - Platform responsible for this type
411    /// * `base_confidence` - Confidence to use for tokens created for this type
412    /// * `parent_type` - Type of the parent of this type, or None
413    /// * `escaping` - Style of escaping literals which may not be parsable
414    fn get_type_tokens_before_name(
415        &self,
416        type_: Ref<Type>,
417        platform: Option<Ref<Platform>>,
418        base_confidence: u8,
419        parent_type: Option<Ref<Type>>,
420        escaping: TokenEscapingType,
421    ) -> Option<Vec<InstructionTextToken>>;
422
423    /// In a single-line text representation of a type, generate the tokens
424    /// that should be printed after the type's name. Returns a list of text
425    /// tokens representing the type
426    ///
427    /// * `type_` - Type to print
428    /// * `platform` - Platform responsible for this type
429    /// * `base_confidence` - Confidence to use for tokens created for this type
430    /// * `parent_type` - Type of the parent of this type, or None
431    /// * `escaping` - Style of escaping literals which may not be parsable
432    fn get_type_tokens_after_name(
433        &self,
434        type_: Ref<Type>,
435        platform: Option<Ref<Platform>>,
436        base_confidence: u8,
437        parent_type: Option<Ref<Type>>,
438        escaping: TokenEscapingType,
439    ) -> Option<Vec<InstructionTextToken>>;
440
441    /// Generate a single-line text representation of a type. Returns a string
442    /// representing the type
443    ///
444    /// * `type_` - Type to print
445    /// * `platform` - Platform responsible for this type
446    /// * `name` - Name of the type
447    /// * `escaping` - Style of escaping literals which may not be parsable
448    fn get_type_string<T: Into<QualifiedName>>(
449        &self,
450        type_: Ref<Type>,
451        platform: Option<Ref<Platform>>,
452        name: T,
453        escaping: TokenEscapingType,
454    ) -> Option<String>;
455
456    /// In a single-line text representation of a type, generate the string that
457    /// should be printed before the type's name. Returns a string representing
458    /// the type
459    ///
460    /// * `type_` - Type to print
461    /// * `platform` - Platform responsible for this type
462    /// * `escaping` - Style of escaping literals which may not be parsable
463    fn get_type_string_before_name(
464        &self,
465        type_: Ref<Type>,
466        platform: Option<Ref<Platform>>,
467        escaping: TokenEscapingType,
468    ) -> Option<String>;
469
470    /// In a single-line text representation of a type, generate the string that
471    /// should be printed after the type's name. Returns a string representing
472    /// the type
473    ///
474    /// * `type_` - Type to print
475    /// * `platform` - Platform responsible for this type
476    /// * `escaping` - Style of escaping literals which may not be parsable
477    fn get_type_string_after_name(
478        &self,
479        type_: Ref<Type>,
480        platform: Option<Ref<Platform>>,
481        escaping: TokenEscapingType,
482    ) -> Option<String>;
483
484    /// Generate a multi-line representation of a type. Returns a list of type
485    /// definition lines
486    ///
487    /// * `type_` - Type to print
488    /// * `types` - Type Container containing the type and dependencies
489    /// * `name` - Name of the type
490    /// * `padding_cols` - Maximum number of bytes represented by each padding line
491    /// * `collapsed` - Whether to collapse structure/enum blocks
492    /// * `escaping` - Style of escaping literals which may not be parsable
493    fn get_type_lines<T: Into<QualifiedName>>(
494        &self,
495        type_: Ref<Type>,
496        types: &TypeContainer,
497        name: T,
498        padding_cols: isize,
499        collapsed: bool,
500        escaping: TokenEscapingType,
501    ) -> Option<Vec<TypeDefinitionLine>>;
502
503    /// Print all types to a single big string, including headers, sections,
504    /// etc.
505    ///
506    /// * `types` - All types to print
507    /// * `data` - Binary View in which all the types are defined
508    /// * `padding_cols` - Maximum number of bytes represented by each padding line
509    /// * `escaping` - Style of escaping literals which may not be parsable
510    fn print_all_types(
511        &self,
512        names: Vec<QualifiedName>,
513        types: Vec<Ref<Type>>,
514        data: Ref<BinaryView>,
515        padding_cols: isize,
516        escaping: TokenEscapingType,
517    ) -> Option<String>;
518}
519
520// TODO: This needs an extreme amount of documentation...
521#[derive(Clone)]
522pub struct TypeDefinitionLine {
523    pub line_type: TypeDefinitionLineType,
524    pub tokens: Vec<InstructionTextToken>,
525    pub ty: Ref<Type>,
526    pub parent_type: Option<Ref<Type>>,
527    // TODO: Document what the root type is.
528    pub root_type: Option<Ref<Type>>,
529    pub root_type_name: Option<String>,
530    // TODO: Document the base type, and why its a ntr instead of type + name like root type
531    pub base_type: Option<Ref<NamedTypeReference>>,
532    // TODO: These can also be optional?
533    pub base_offset: u64,
534    pub offset: u64,
535    pub field_index: usize,
536}
537
538impl TypeDefinitionLine {
539    pub(crate) fn from_raw(value: &BNTypeDefinitionLine) -> Self {
540        Self {
541            line_type: value.lineType,
542            tokens: {
543                let raw_tokens = unsafe { std::slice::from_raw_parts(value.tokens, value.count) };
544                raw_tokens
545                    .iter()
546                    .map(InstructionTextToken::from_raw)
547                    .collect()
548            },
549            ty: unsafe { Type::from_raw(value.type_).to_owned() },
550            parent_type: match value.parentType.is_null() {
551                false => Some(unsafe { Type::from_raw(value.parentType).to_owned() }),
552                true => None,
553            },
554            root_type: match value.rootType.is_null() {
555                false => Some(unsafe { Type::from_raw(value.rootType).to_owned() }),
556                true => None,
557            },
558            root_type_name: match value.rootTypeName.is_null() {
559                false => Some(raw_to_string(value.rootTypeName).unwrap()),
560                true => None,
561            },
562            base_type: match value.baseType.is_null() {
563                false => Some(unsafe { NamedTypeReference::from_raw(value.baseType).to_owned() }),
564                true => None,
565            },
566            base_offset: value.baseOffset,
567            offset: value.offset,
568            field_index: value.fieldIndex,
569        }
570    }
571
572    /// The raw value must have been allocated by rust. See [`Self::free_owned_raw`] for details.
573    pub(crate) fn from_owned_raw(value: BNTypeDefinitionLine) -> Self {
574        let owned = Self::from_raw(&value);
575        Self::free_owned_raw(value);
576        owned
577    }
578
579    pub(crate) fn into_raw(value: Self) -> BNTypeDefinitionLine {
580        // NOTE: This is leaking [BNInstructionTextToken::text], [BNInstructionTextToken::typeNames].
581        let tokens: Box<[BNInstructionTextToken]> = value
582            .tokens
583            .into_iter()
584            .map(InstructionTextToken::into_raw)
585            .collect();
586        BNTypeDefinitionLine {
587            lineType: value.line_type,
588            count: tokens.len(),
589            // NOTE: This is leaking tokens. Must free with `cb_free_lines`.
590            tokens: Box::leak(tokens).as_mut_ptr(),
591            // NOTE: This is leaking a ref to ty. Must free with `cb_free_lines`.
592            type_: unsafe { Ref::into_raw(value.ty) }.handle,
593            // NOTE: This is leaking a ref to parent_type. Must free with `cb_free_lines`.
594            parentType: value
595                .parent_type
596                .map(|t| unsafe { Ref::into_raw(t) }.handle)
597                .unwrap_or(std::ptr::null_mut()),
598            // NOTE: This is leaking a ref to root_type. Must free with `cb_free_lines`.
599            rootType: value
600                .root_type
601                .map(|t| unsafe { Ref::into_raw(t) }.handle)
602                .unwrap_or(std::ptr::null_mut()),
603            // NOTE: This is leaking root_type_name. Must free with `cb_free_lines`.
604            rootTypeName: value
605                .root_type_name
606                .map(|s| BnString::into_raw(BnString::new(s)))
607                .unwrap_or(std::ptr::null_mut()),
608            // NOTE: This is leaking a ref to base_type. Must free with `cb_free_lines`.
609            baseType: value
610                .base_type
611                .map(|t| unsafe { Ref::into_raw(t) }.handle)
612                .unwrap_or(std::ptr::null_mut()),
613            baseOffset: value.base_offset,
614            offset: value.offset,
615            fieldIndex: value.field_index,
616        }
617    }
618
619    /// This is unique from the typical `from_raw` as the allocation of InstructionTextToken requires it be from rust, hence the "owned" free.
620    pub(crate) fn free_owned_raw(raw: BNTypeDefinitionLine) {
621        if !raw.tokens.is_null() {
622            let tokens = std::ptr::slice_from_raw_parts_mut(raw.tokens, raw.count);
623            // SAFETY: raw.tokens must have been allocated by rust.
624            let boxed_tokens = unsafe { Box::from_raw(tokens) };
625            for token in boxed_tokens {
626                InstructionTextToken::free_raw(token);
627            }
628        }
629        if !raw.type_.is_null() {
630            // SAFETY: raw.type_ must have been ref incremented in conjunction with this free
631            let _ = unsafe { Type::ref_from_raw(raw.type_) };
632        }
633        if !raw.parentType.is_null() {
634            // SAFETY: raw.parentType must have been ref incremented in conjunction with this free
635            let _ = unsafe { Type::ref_from_raw(raw.parentType) };
636        }
637        if !raw.rootType.is_null() {
638            // SAFETY: raw.rootType must have been ref incremented in conjunction with this free
639            let _ = unsafe { Type::ref_from_raw(raw.rootType) };
640        }
641        if !raw.rootTypeName.is_null() {
642            // SAFETY: raw.rootTypeName must have been ref incremented in conjunction with this free
643            let _ = unsafe { BnString::from_raw(raw.rootTypeName) };
644        }
645        if !raw.baseType.is_null() {
646            // SAFETY: raw.baseType must have been ref incremented in conjunction with this free
647            let _ = unsafe { NamedTypeReference::ref_from_raw(raw.baseType) };
648        }
649    }
650}
651
652impl CoreArrayProvider for TypeDefinitionLine {
653    type Raw = BNTypeDefinitionLine;
654    type Context = ();
655    type Wrapped<'a> = Self;
656}
657
658unsafe impl CoreArrayProviderInner for TypeDefinitionLine {
659    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
660        unsafe { BNFreeTypeDefinitionLineList(raw, count) };
661    }
662
663    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
664        Self::from_raw(raw)
665    }
666}
667
668unsafe extern "C" fn cb_get_type_tokens<T: TypePrinter>(
669    ctxt: *mut ::std::os::raw::c_void,
670    type_: *mut BNType,
671    platform: *mut BNPlatform,
672    name: *mut BNQualifiedName,
673    base_confidence: u8,
674    escaping: BNTokenEscapingType,
675    result: *mut *mut BNInstructionTextToken,
676    result_count: *mut usize,
677) -> bool {
678    let ctxt: &mut T = &mut *(ctxt as *mut T);
679    // NOTE: The caller is responsible for freeing name.
680    let qualified_name = QualifiedName::from_raw(&*name);
681    let inner_result = ctxt.get_type_tokens(
682        unsafe { Type::ref_from_raw(type_) },
683        match platform.is_null() {
684            false => Some(Platform::ref_from_raw(platform)),
685            true => None,
686        },
687        qualified_name,
688        base_confidence,
689        escaping,
690    );
691    if let Some(inner_result) = inner_result {
692        let raw_text_tokens: Box<[BNInstructionTextToken]> = inner_result
693            .into_iter()
694            .map(InstructionTextToken::into_raw)
695            .collect();
696        *result_count = raw_text_tokens.len();
697        // NOTE: Dropped by the cb_free_tokens
698        *result = Box::leak(raw_text_tokens).as_mut_ptr();
699        true
700    } else {
701        *result = std::ptr::null_mut();
702        *result_count = 0;
703        false
704    }
705}
706
707unsafe extern "C" fn cb_get_type_tokens_before_name<T: TypePrinter>(
708    ctxt: *mut ::std::os::raw::c_void,
709    type_: *mut BNType,
710    platform: *mut BNPlatform,
711    base_confidence: u8,
712    parent_type: *mut BNType,
713    escaping: BNTokenEscapingType,
714    result: *mut *mut BNInstructionTextToken,
715    result_count: *mut usize,
716) -> bool {
717    let ctxt: &mut T = &mut *(ctxt as *mut T);
718    let inner_result = ctxt.get_type_tokens_before_name(
719        Type::ref_from_raw(type_),
720        match platform.is_null() {
721            false => Some(Platform::ref_from_raw(platform)),
722            true => None,
723        },
724        base_confidence,
725        match parent_type.is_null() {
726            false => Some(Type::ref_from_raw(parent_type)),
727            true => None,
728        },
729        escaping,
730    );
731    if let Some(inner_result) = inner_result {
732        let raw_text_tokens: Box<[BNInstructionTextToken]> = inner_result
733            .into_iter()
734            .map(InstructionTextToken::into_raw)
735            .collect();
736        *result_count = raw_text_tokens.len();
737        // NOTE: Dropped by the cb_free_tokens
738        *result = Box::leak(raw_text_tokens).as_mut_ptr();
739        true
740    } else {
741        *result = std::ptr::null_mut();
742        *result_count = 0;
743        false
744    }
745}
746
747unsafe extern "C" fn cb_get_type_tokens_after_name<T: TypePrinter>(
748    ctxt: *mut ::std::os::raw::c_void,
749    type_: *mut BNType,
750    platform: *mut BNPlatform,
751    base_confidence: u8,
752    parent_type: *mut BNType,
753    escaping: BNTokenEscapingType,
754    result: *mut *mut BNInstructionTextToken,
755    result_count: *mut usize,
756) -> bool {
757    let ctxt: &mut T = &mut *(ctxt as *mut T);
758    let inner_result = ctxt.get_type_tokens_after_name(
759        Type::ref_from_raw(type_),
760        match platform.is_null() {
761            false => Some(Platform::ref_from_raw(platform)),
762            true => None,
763        },
764        base_confidence,
765        match parent_type.is_null() {
766            false => Some(Type::ref_from_raw(parent_type)),
767            true => None,
768        },
769        escaping,
770    );
771    if let Some(inner_result) = inner_result {
772        let raw_text_tokens: Box<[BNInstructionTextToken]> = inner_result
773            .into_iter()
774            .map(InstructionTextToken::into_raw)
775            .collect();
776        *result_count = raw_text_tokens.len();
777        // NOTE: Dropped by the cb_free_tokens
778        *result = Box::leak(raw_text_tokens).as_mut_ptr();
779        true
780    } else {
781        *result = std::ptr::null_mut();
782        *result_count = 0;
783        false
784    }
785}
786
787unsafe extern "C" fn cb_get_type_string<T: TypePrinter>(
788    ctxt: *mut ::std::os::raw::c_void,
789    type_: *mut BNType,
790    platform: *mut BNPlatform,
791    name: *mut BNQualifiedName,
792    escaping: BNTokenEscapingType,
793    result: *mut *mut ::std::os::raw::c_char,
794) -> bool {
795    let ctxt: &mut T = &mut *(ctxt as *mut T);
796    // NOTE: The caller is responsible for freeing name.
797    let qualified_name = QualifiedName::from_raw(&*name);
798    let inner_result = ctxt.get_type_string(
799        Type::ref_from_raw(type_),
800        match platform.is_null() {
801            false => Some(Platform::ref_from_raw(platform)),
802            true => None,
803        },
804        qualified_name,
805        escaping,
806    );
807    if let Some(inner_result) = inner_result {
808        let raw_string = BnString::new(inner_result);
809        // NOTE: Dropped by `cb_free_string`
810        *result = BnString::into_raw(raw_string);
811        true
812    } else {
813        *result = std::ptr::null_mut();
814        false
815    }
816}
817
818unsafe extern "C" fn cb_get_type_string_before_name<T: TypePrinter>(
819    ctxt: *mut ::std::os::raw::c_void,
820    type_: *mut BNType,
821    platform: *mut BNPlatform,
822    escaping: BNTokenEscapingType,
823    result: *mut *mut ::std::os::raw::c_char,
824) -> bool {
825    let ctxt: &mut T = &mut *(ctxt as *mut T);
826    let inner_result = ctxt.get_type_string_before_name(
827        Type::ref_from_raw(type_),
828        match platform.is_null() {
829            false => Some(Platform::ref_from_raw(platform)),
830            true => None,
831        },
832        escaping,
833    );
834    if let Some(inner_result) = inner_result {
835        // NOTE: Dropped by `cb_free_string`
836        let raw_string = BnString::new(inner_result);
837        *result = BnString::into_raw(raw_string);
838        true
839    } else {
840        *result = std::ptr::null_mut();
841        false
842    }
843}
844
845unsafe extern "C" fn cb_get_type_string_after_name<T: TypePrinter>(
846    ctxt: *mut ::std::os::raw::c_void,
847    type_: *mut BNType,
848    platform: *mut BNPlatform,
849    escaping: BNTokenEscapingType,
850    result: *mut *mut ::std::os::raw::c_char,
851) -> bool {
852    let ctxt: &mut T = &mut *(ctxt as *mut T);
853    let inner_result = ctxt.get_type_string_after_name(
854        Type::ref_from_raw(type_),
855        match platform.is_null() {
856            false => Some(Platform::ref_from_raw(platform)),
857            true => None,
858        },
859        escaping,
860    );
861    if let Some(inner_result) = inner_result {
862        let raw_string = BnString::new(inner_result);
863        // NOTE: Dropped by `cb_free_string`
864        *result = BnString::into_raw(raw_string);
865        true
866    } else {
867        *result = std::ptr::null_mut();
868        false
869    }
870}
871
872unsafe extern "C" fn cb_get_type_lines<T: TypePrinter>(
873    ctxt: *mut ::std::os::raw::c_void,
874    type_: *mut BNType,
875    types: *mut BNTypeContainer,
876    name: *mut BNQualifiedName,
877    padding_cols: ::std::os::raw::c_int,
878    collapsed: bool,
879    escaping: BNTokenEscapingType,
880    result: *mut *mut BNTypeDefinitionLine,
881    result_count: *mut usize,
882) -> bool {
883    let ctxt: &mut T = &mut *(ctxt as *mut T);
884    // NOTE: The caller is responsible for freeing name.
885    let qualified_name = QualifiedName::from_raw(&*name);
886    let types_ptr = NonNull::new(types).unwrap();
887    let types = TypeContainer::from_raw(types_ptr);
888    let inner_result = ctxt.get_type_lines(
889        Type::ref_from_raw(type_),
890        &types,
891        qualified_name,
892        padding_cols as isize,
893        collapsed,
894        escaping,
895    );
896    if let Some(inner_result) = inner_result {
897        let boxed_raw_lines: Box<[_]> = inner_result
898            .into_iter()
899            .map(TypeDefinitionLine::into_raw)
900            .collect();
901        *result_count = boxed_raw_lines.len();
902        // NOTE: Dropped by `cb_free_lines`
903        *result = Box::leak(boxed_raw_lines).as_mut_ptr();
904        true
905    } else {
906        *result = std::ptr::null_mut();
907        *result_count = 0;
908        false
909    }
910}
911
912unsafe extern "C" fn cb_print_all_types<T: TypePrinter>(
913    ctxt: *mut ::std::os::raw::c_void,
914    names: *mut BNQualifiedName,
915    types: *mut *mut BNType,
916    type_count: usize,
917    data: *mut BNBinaryView,
918    padding_cols: ::std::os::raw::c_int,
919    escaping: BNTokenEscapingType,
920    result: *mut *mut ::std::os::raw::c_char,
921) -> bool {
922    let ctxt: &mut T = &mut *(ctxt as *mut T);
923    let raw_names = std::slice::from_raw_parts(names, type_count);
924    // NOTE: The caller is responsible for freeing raw_names.
925    let names: Vec<_> = raw_names.iter().map(QualifiedName::from_raw).collect();
926    let raw_types = std::slice::from_raw_parts(types, type_count);
927    // NOTE: The caller is responsible for freeing raw_types.
928    let types: Vec<_> = raw_types.iter().map(|&t| Type::ref_from_raw(t)).collect();
929    let inner_result = ctxt.print_all_types(
930        names,
931        types,
932        BinaryView::ref_from_raw(data),
933        padding_cols as isize,
934        escaping,
935    );
936    if let Some(inner_result) = inner_result {
937        let raw_string = BnString::new(inner_result);
938        // NOTE: Dropped by `cb_free_string`
939        *result = BnString::into_raw(raw_string);
940        true
941    } else {
942        *result = std::ptr::null_mut();
943        false
944    }
945}
946
947unsafe extern "C" fn cb_free_string(_ctxt: *mut c_void, string: *mut c_char) {
948    // SAFETY: The returned string is just BnString
949    BnString::free_raw(string);
950}
951
952unsafe extern "C" fn cb_free_tokens(
953    _ctxt: *mut ::std::os::raw::c_void,
954    tokens: *mut BNInstructionTextToken,
955    count: usize,
956) {
957    let tokens = std::ptr::slice_from_raw_parts_mut(tokens, count);
958    // SAFETY: tokens must have been allocated by rust.
959    let boxed_tokens = Box::from_raw(tokens);
960    for token in boxed_tokens {
961        InstructionTextToken::free_raw(token);
962    }
963}
964
965unsafe extern "C" fn cb_free_lines(
966    _ctxt: *mut ::std::os::raw::c_void,
967    lines: *mut BNTypeDefinitionLine,
968    count: usize,
969) {
970    let lines = std::ptr::slice_from_raw_parts_mut(lines, count);
971    // SAFETY: lines must have been allocated by rust.
972    let boxes_lines = Box::from_raw(lines);
973    for line in boxes_lines {
974        TypeDefinitionLine::free_owned_raw(line);
975    }
976}