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                // TODO: Add a print_all_types that accepts a set of type containers.
342                data.handle,
343                padding_cols as c_int,
344                escaping,
345                &mut result,
346            )
347        };
348        for raw_name in raw_names {
349            QualifiedName::free_raw(raw_name);
350        }
351        for raw_type in raw_types {
352            let _ = unsafe { Type::ref_from_raw(raw_type) };
353        }
354        success.then(|| unsafe {
355            assert!(!result.is_null());
356            BnString::from_raw(result)
357        })
358    }
359}
360
361impl Default for CoreTypePrinter {
362    fn default() -> Self {
363        // TODO: Remove this entirely, there is no "default", its view specific lets not make this some defined behavior.
364        let default_settings = crate::settings::Settings::new();
365        let name = default_settings.get_string("analysis.types.printerName");
366        Self::printer_by_name(&name).unwrap()
367    }
368}
369
370impl CoreArrayProvider for CoreTypePrinter {
371    type Raw = *mut BNTypePrinter;
372    type Context = ();
373    type Wrapped<'a> = Self;
374}
375
376unsafe impl CoreArrayProviderInner for CoreTypePrinter {
377    unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
378        BNFreeTypePrinterList(raw)
379    }
380
381    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
382        // TODO: Because handle is a NonNull we should prob make Self::Raw that as well...
383        let handle = NonNull::new(*raw).unwrap();
384        CoreTypePrinter::from_raw(handle)
385    }
386}
387
388pub trait TypePrinter {
389    /// Generate a single-line text representation of a type, Returns a List
390    /// of text tokens representing the type.
391    ///
392    /// * `type_` - Type to print
393    /// * `platform` - Platform responsible for this type
394    /// * `name` - Name of the type
395    /// * `base_confidence` - Confidence to use for tokens created for this type
396    /// * `escaping` - Style of escaping literals which may not be parsable
397    fn get_type_tokens<T: Into<QualifiedName>>(
398        &self,
399        type_: Ref<Type>,
400        platform: Option<Ref<Platform>>,
401        name: T,
402        base_confidence: u8,
403        escaping: TokenEscapingType,
404    ) -> Option<Vec<InstructionTextToken>>;
405
406    /// In a single-line text representation of a type, generate the tokens that
407    /// should be printed before the type's name. Returns a list of text tokens
408    /// representing the type
409    ///
410    /// * `type_` - Type to print
411    /// * `platform` - Platform responsible for this type
412    /// * `base_confidence` - Confidence to use for tokens created for this type
413    /// * `parent_type` - Type of the parent of this type, or None
414    /// * `escaping` - Style of escaping literals which may not be parsable
415    fn get_type_tokens_before_name(
416        &self,
417        type_: Ref<Type>,
418        platform: Option<Ref<Platform>>,
419        base_confidence: u8,
420        parent_type: Option<Ref<Type>>,
421        escaping: TokenEscapingType,
422    ) -> Option<Vec<InstructionTextToken>>;
423
424    /// In a single-line text representation of a type, generate the tokens
425    /// that should be printed after the type's name. Returns a list of text
426    /// tokens representing the type
427    ///
428    /// * `type_` - Type to print
429    /// * `platform` - Platform responsible for this type
430    /// * `base_confidence` - Confidence to use for tokens created for this type
431    /// * `parent_type` - Type of the parent of this type, or None
432    /// * `escaping` - Style of escaping literals which may not be parsable
433    fn get_type_tokens_after_name(
434        &self,
435        type_: Ref<Type>,
436        platform: Option<Ref<Platform>>,
437        base_confidence: u8,
438        parent_type: Option<Ref<Type>>,
439        escaping: TokenEscapingType,
440    ) -> Option<Vec<InstructionTextToken>>;
441
442    /// Generate a single-line text representation of a type. Returns a string
443    /// representing the type
444    ///
445    /// * `type_` - Type to print
446    /// * `platform` - Platform responsible for this type
447    /// * `name` - Name of the type
448    /// * `escaping` - Style of escaping literals which may not be parsable
449    fn get_type_string<T: Into<QualifiedName>>(
450        &self,
451        type_: Ref<Type>,
452        platform: Option<Ref<Platform>>,
453        name: T,
454        escaping: TokenEscapingType,
455    ) -> Option<String>;
456
457    /// In a single-line text representation of a type, generate the string that
458    /// should be printed before the type's name. Returns a string representing
459    /// the type
460    ///
461    /// * `type_` - Type to print
462    /// * `platform` - Platform responsible for this type
463    /// * `escaping` - Style of escaping literals which may not be parsable
464    fn get_type_string_before_name(
465        &self,
466        type_: Ref<Type>,
467        platform: Option<Ref<Platform>>,
468        escaping: TokenEscapingType,
469    ) -> Option<String>;
470
471    /// In a single-line text representation of a type, generate the string that
472    /// should be printed after the type's name. Returns a string representing
473    /// the type
474    ///
475    /// * `type_` - Type to print
476    /// * `platform` - Platform responsible for this type
477    /// * `escaping` - Style of escaping literals which may not be parsable
478    fn get_type_string_after_name(
479        &self,
480        type_: Ref<Type>,
481        platform: Option<Ref<Platform>>,
482        escaping: TokenEscapingType,
483    ) -> Option<String>;
484
485    /// Generate a multi-line representation of a type. Returns a list of type
486    /// definition lines
487    ///
488    /// * `type_` - Type to print
489    /// * `types` - Type Container containing the type and dependencies
490    /// * `name` - Name of the type
491    /// * `padding_cols` - Maximum number of bytes represented by each padding line
492    /// * `collapsed` - Whether to collapse structure/enum blocks
493    /// * `escaping` - Style of escaping literals which may not be parsable
494    fn get_type_lines<T: Into<QualifiedName>>(
495        &self,
496        type_: Ref<Type>,
497        types: &TypeContainer,
498        name: T,
499        padding_cols: isize,
500        collapsed: bool,
501        escaping: TokenEscapingType,
502    ) -> Option<Vec<TypeDefinitionLine>>;
503
504    /// Print all types to a single big string, including headers, sections,
505    /// etc.
506    ///
507    /// * `types` - All types to print
508    /// * `data` - Binary View in which all the types are defined
509    /// * `padding_cols` - Maximum number of bytes represented by each padding line
510    /// * `escaping` - Style of escaping literals which may not be parsable
511    fn print_all_types(
512        &self,
513        names: Vec<QualifiedName>,
514        types: Vec<Ref<Type>>,
515        data: Ref<BinaryView>,
516        padding_cols: isize,
517        escaping: TokenEscapingType,
518    ) -> Option<String>;
519}
520
521// TODO: This needs an extreme amount of documentation...
522#[derive(Clone)]
523pub struct TypeDefinitionLine {
524    pub line_type: TypeDefinitionLineType,
525    pub tokens: Vec<InstructionTextToken>,
526    pub ty: Ref<Type>,
527    pub parent_type: Option<Ref<Type>>,
528    // TODO: Document what the root type is.
529    pub root_type: Option<Ref<Type>>,
530    pub root_type_name: Option<String>,
531    // TODO: Document the base type, and why its a ntr instead of type + name like root type
532    pub base_type: Option<Ref<NamedTypeReference>>,
533    // TODO: These can also be optional?
534    pub base_offset: u64,
535    pub offset: u64,
536    pub field_index: usize,
537}
538
539impl TypeDefinitionLine {
540    pub(crate) fn from_raw(value: &BNTypeDefinitionLine) -> Self {
541        Self {
542            line_type: value.lineType,
543            tokens: {
544                let raw_tokens = unsafe { std::slice::from_raw_parts(value.tokens, value.count) };
545                raw_tokens
546                    .iter()
547                    .map(InstructionTextToken::from_raw)
548                    .collect()
549            },
550            ty: unsafe { Type::from_raw(value.type_).to_owned() },
551            parent_type: match value.parentType.is_null() {
552                false => Some(unsafe { Type::from_raw(value.parentType).to_owned() }),
553                true => None,
554            },
555            root_type: match value.rootType.is_null() {
556                false => Some(unsafe { Type::from_raw(value.rootType).to_owned() }),
557                true => None,
558            },
559            root_type_name: match value.rootTypeName.is_null() {
560                false => Some(raw_to_string(value.rootTypeName).unwrap()),
561                true => None,
562            },
563            base_type: match value.baseType.is_null() {
564                false => Some(unsafe { NamedTypeReference::from_raw(value.baseType).to_owned() }),
565                true => None,
566            },
567            base_offset: value.baseOffset,
568            offset: value.offset,
569            field_index: value.fieldIndex,
570        }
571    }
572
573    /// The raw value must have been allocated by rust. See [`Self::free_owned_raw`] for details.
574    pub(crate) fn from_owned_raw(value: BNTypeDefinitionLine) -> Self {
575        let owned = Self::from_raw(&value);
576        Self::free_owned_raw(value);
577        owned
578    }
579
580    pub(crate) fn into_raw(value: Self) -> BNTypeDefinitionLine {
581        // NOTE: This is leaking [BNInstructionTextToken::text], [BNInstructionTextToken::typeNames].
582        let tokens: Box<[BNInstructionTextToken]> = value
583            .tokens
584            .into_iter()
585            .map(InstructionTextToken::into_raw)
586            .collect();
587        BNTypeDefinitionLine {
588            lineType: value.line_type,
589            count: tokens.len(),
590            // NOTE: This is leaking tokens. Must free with `cb_free_lines`.
591            tokens: Box::leak(tokens).as_mut_ptr(),
592            // NOTE: This is leaking a ref to ty. Must free with `cb_free_lines`.
593            type_: unsafe { Ref::into_raw(value.ty) }.handle,
594            // NOTE: This is leaking a ref to parent_type. Must free with `cb_free_lines`.
595            parentType: value
596                .parent_type
597                .map(|t| unsafe { Ref::into_raw(t) }.handle)
598                .unwrap_or(std::ptr::null_mut()),
599            // NOTE: This is leaking a ref to root_type. Must free with `cb_free_lines`.
600            rootType: value
601                .root_type
602                .map(|t| unsafe { Ref::into_raw(t) }.handle)
603                .unwrap_or(std::ptr::null_mut()),
604            // NOTE: This is leaking root_type_name. Must free with `cb_free_lines`.
605            rootTypeName: value
606                .root_type_name
607                .map(|s| BnString::into_raw(BnString::new(s)))
608                .unwrap_or(std::ptr::null_mut()),
609            // NOTE: This is leaking a ref to base_type. Must free with `cb_free_lines`.
610            baseType: value
611                .base_type
612                .map(|t| unsafe { Ref::into_raw(t) }.handle)
613                .unwrap_or(std::ptr::null_mut()),
614            baseOffset: value.base_offset,
615            offset: value.offset,
616            fieldIndex: value.field_index,
617        }
618    }
619
620    /// This is unique from the typical `from_raw` as the allocation of InstructionTextToken requires it be from rust, hence the "owned" free.
621    pub(crate) fn free_owned_raw(raw: BNTypeDefinitionLine) {
622        if !raw.tokens.is_null() {
623            let tokens = std::ptr::slice_from_raw_parts_mut(raw.tokens, raw.count);
624            // SAFETY: raw.tokens must have been allocated by rust.
625            let boxed_tokens = unsafe { Box::from_raw(tokens) };
626            for token in boxed_tokens {
627                InstructionTextToken::free_raw(token);
628            }
629        }
630        if !raw.type_.is_null() {
631            // SAFETY: raw.type_ must have been ref incremented in conjunction with this free
632            let _ = unsafe { Type::ref_from_raw(raw.type_) };
633        }
634        if !raw.parentType.is_null() {
635            // SAFETY: raw.parentType must have been ref incremented in conjunction with this free
636            let _ = unsafe { Type::ref_from_raw(raw.parentType) };
637        }
638        if !raw.rootType.is_null() {
639            // SAFETY: raw.rootType must have been ref incremented in conjunction with this free
640            let _ = unsafe { Type::ref_from_raw(raw.rootType) };
641        }
642        if !raw.rootTypeName.is_null() {
643            // SAFETY: raw.rootTypeName must have been ref incremented in conjunction with this free
644            let _ = unsafe { BnString::from_raw(raw.rootTypeName) };
645        }
646        if !raw.baseType.is_null() {
647            // SAFETY: raw.baseType must have been ref incremented in conjunction with this free
648            let _ = unsafe { NamedTypeReference::ref_from_raw(raw.baseType) };
649        }
650    }
651}
652
653impl CoreArrayProvider for TypeDefinitionLine {
654    type Raw = BNTypeDefinitionLine;
655    type Context = ();
656    type Wrapped<'a> = Self;
657}
658
659unsafe impl CoreArrayProviderInner for TypeDefinitionLine {
660    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
661        unsafe { BNFreeTypeDefinitionLineList(raw, count) };
662    }
663
664    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
665        Self::from_raw(raw)
666    }
667}
668
669unsafe extern "C" fn cb_get_type_tokens<T: TypePrinter>(
670    ctxt: *mut ::std::os::raw::c_void,
671    type_: *mut BNType,
672    platform: *mut BNPlatform,
673    name: *mut BNQualifiedName,
674    base_confidence: u8,
675    escaping: BNTokenEscapingType,
676    result: *mut *mut BNInstructionTextToken,
677    result_count: *mut usize,
678) -> bool {
679    let ctxt: &mut T = &mut *(ctxt as *mut T);
680    // NOTE: The caller is responsible for freeing name.
681    let qualified_name = QualifiedName::from_raw(&*name);
682    let inner_result = ctxt.get_type_tokens(
683        unsafe { Type::ref_from_raw(type_) },
684        match platform.is_null() {
685            false => Some(Platform::ref_from_raw(platform)),
686            true => None,
687        },
688        qualified_name,
689        base_confidence,
690        escaping,
691    );
692    if let Some(inner_result) = inner_result {
693        let raw_text_tokens: Box<[BNInstructionTextToken]> = inner_result
694            .into_iter()
695            .map(InstructionTextToken::into_raw)
696            .collect();
697        *result_count = raw_text_tokens.len();
698        // NOTE: Dropped by the cb_free_tokens
699        *result = Box::leak(raw_text_tokens).as_mut_ptr();
700        true
701    } else {
702        *result = std::ptr::null_mut();
703        *result_count = 0;
704        false
705    }
706}
707
708unsafe extern "C" fn cb_get_type_tokens_before_name<T: TypePrinter>(
709    ctxt: *mut ::std::os::raw::c_void,
710    type_: *mut BNType,
711    platform: *mut BNPlatform,
712    base_confidence: u8,
713    parent_type: *mut BNType,
714    escaping: BNTokenEscapingType,
715    result: *mut *mut BNInstructionTextToken,
716    result_count: *mut usize,
717) -> bool {
718    let ctxt: &mut T = &mut *(ctxt as *mut T);
719    let inner_result = ctxt.get_type_tokens_before_name(
720        Type::ref_from_raw(type_),
721        match platform.is_null() {
722            false => Some(Platform::ref_from_raw(platform)),
723            true => None,
724        },
725        base_confidence,
726        match parent_type.is_null() {
727            false => Some(Type::ref_from_raw(parent_type)),
728            true => None,
729        },
730        escaping,
731    );
732    if let Some(inner_result) = inner_result {
733        let raw_text_tokens: Box<[BNInstructionTextToken]> = inner_result
734            .into_iter()
735            .map(InstructionTextToken::into_raw)
736            .collect();
737        *result_count = raw_text_tokens.len();
738        // NOTE: Dropped by the cb_free_tokens
739        *result = Box::leak(raw_text_tokens).as_mut_ptr();
740        true
741    } else {
742        *result = std::ptr::null_mut();
743        *result_count = 0;
744        false
745    }
746}
747
748unsafe extern "C" fn cb_get_type_tokens_after_name<T: TypePrinter>(
749    ctxt: *mut ::std::os::raw::c_void,
750    type_: *mut BNType,
751    platform: *mut BNPlatform,
752    base_confidence: u8,
753    parent_type: *mut BNType,
754    escaping: BNTokenEscapingType,
755    result: *mut *mut BNInstructionTextToken,
756    result_count: *mut usize,
757) -> bool {
758    let ctxt: &mut T = &mut *(ctxt as *mut T);
759    let inner_result = ctxt.get_type_tokens_after_name(
760        Type::ref_from_raw(type_),
761        match platform.is_null() {
762            false => Some(Platform::ref_from_raw(platform)),
763            true => None,
764        },
765        base_confidence,
766        match parent_type.is_null() {
767            false => Some(Type::ref_from_raw(parent_type)),
768            true => None,
769        },
770        escaping,
771    );
772    if let Some(inner_result) = inner_result {
773        let raw_text_tokens: Box<[BNInstructionTextToken]> = inner_result
774            .into_iter()
775            .map(InstructionTextToken::into_raw)
776            .collect();
777        *result_count = raw_text_tokens.len();
778        // NOTE: Dropped by the cb_free_tokens
779        *result = Box::leak(raw_text_tokens).as_mut_ptr();
780        true
781    } else {
782        *result = std::ptr::null_mut();
783        *result_count = 0;
784        false
785    }
786}
787
788unsafe extern "C" fn cb_get_type_string<T: TypePrinter>(
789    ctxt: *mut ::std::os::raw::c_void,
790    type_: *mut BNType,
791    platform: *mut BNPlatform,
792    name: *mut BNQualifiedName,
793    escaping: BNTokenEscapingType,
794    result: *mut *mut ::std::os::raw::c_char,
795) -> bool {
796    let ctxt: &mut T = &mut *(ctxt as *mut T);
797    // NOTE: The caller is responsible for freeing name.
798    let qualified_name = QualifiedName::from_raw(&*name);
799    let inner_result = ctxt.get_type_string(
800        Type::ref_from_raw(type_),
801        match platform.is_null() {
802            false => Some(Platform::ref_from_raw(platform)),
803            true => None,
804        },
805        qualified_name,
806        escaping,
807    );
808    if let Some(inner_result) = inner_result {
809        let raw_string = BnString::new(inner_result);
810        // NOTE: Dropped by `cb_free_string`
811        *result = BnString::into_raw(raw_string);
812        true
813    } else {
814        *result = std::ptr::null_mut();
815        false
816    }
817}
818
819unsafe extern "C" fn cb_get_type_string_before_name<T: TypePrinter>(
820    ctxt: *mut ::std::os::raw::c_void,
821    type_: *mut BNType,
822    platform: *mut BNPlatform,
823    escaping: BNTokenEscapingType,
824    result: *mut *mut ::std::os::raw::c_char,
825) -> bool {
826    let ctxt: &mut T = &mut *(ctxt as *mut T);
827    let inner_result = ctxt.get_type_string_before_name(
828        Type::ref_from_raw(type_),
829        match platform.is_null() {
830            false => Some(Platform::ref_from_raw(platform)),
831            true => None,
832        },
833        escaping,
834    );
835    if let Some(inner_result) = inner_result {
836        // NOTE: Dropped by `cb_free_string`
837        let raw_string = BnString::new(inner_result);
838        *result = BnString::into_raw(raw_string);
839        true
840    } else {
841        *result = std::ptr::null_mut();
842        false
843    }
844}
845
846unsafe extern "C" fn cb_get_type_string_after_name<T: TypePrinter>(
847    ctxt: *mut ::std::os::raw::c_void,
848    type_: *mut BNType,
849    platform: *mut BNPlatform,
850    escaping: BNTokenEscapingType,
851    result: *mut *mut ::std::os::raw::c_char,
852) -> bool {
853    let ctxt: &mut T = &mut *(ctxt as *mut T);
854    let inner_result = ctxt.get_type_string_after_name(
855        Type::ref_from_raw(type_),
856        match platform.is_null() {
857            false => Some(Platform::ref_from_raw(platform)),
858            true => None,
859        },
860        escaping,
861    );
862    if let Some(inner_result) = inner_result {
863        let raw_string = BnString::new(inner_result);
864        // NOTE: Dropped by `cb_free_string`
865        *result = BnString::into_raw(raw_string);
866        true
867    } else {
868        *result = std::ptr::null_mut();
869        false
870    }
871}
872
873unsafe extern "C" fn cb_get_type_lines<T: TypePrinter>(
874    ctxt: *mut ::std::os::raw::c_void,
875    type_: *mut BNType,
876    types: *mut BNTypeContainer,
877    name: *mut BNQualifiedName,
878    padding_cols: ::std::os::raw::c_int,
879    collapsed: bool,
880    escaping: BNTokenEscapingType,
881    result: *mut *mut BNTypeDefinitionLine,
882    result_count: *mut usize,
883) -> bool {
884    let ctxt: &mut T = &mut *(ctxt as *mut T);
885    // NOTE: The caller is responsible for freeing name.
886    let qualified_name = QualifiedName::from_raw(&*name);
887    let types_ptr = NonNull::new(types).unwrap();
888    let types = TypeContainer::from_raw(types_ptr);
889    let inner_result = ctxt.get_type_lines(
890        Type::ref_from_raw(type_),
891        &types,
892        qualified_name,
893        padding_cols as isize,
894        collapsed,
895        escaping,
896    );
897    if let Some(inner_result) = inner_result {
898        let boxed_raw_lines: Box<[_]> = inner_result
899            .into_iter()
900            .map(TypeDefinitionLine::into_raw)
901            .collect();
902        *result_count = boxed_raw_lines.len();
903        // NOTE: Dropped by `cb_free_lines`
904        *result = Box::leak(boxed_raw_lines).as_mut_ptr();
905        true
906    } else {
907        *result = std::ptr::null_mut();
908        *result_count = 0;
909        false
910    }
911}
912
913unsafe extern "C" fn cb_print_all_types<T: TypePrinter>(
914    ctxt: *mut ::std::os::raw::c_void,
915    names: *mut BNQualifiedName,
916    types: *mut *mut BNType,
917    type_count: usize,
918    data: *mut BNBinaryView,
919    padding_cols: ::std::os::raw::c_int,
920    escaping: BNTokenEscapingType,
921    result: *mut *mut ::std::os::raw::c_char,
922) -> bool {
923    let ctxt: &mut T = &mut *(ctxt as *mut T);
924    let raw_names = std::slice::from_raw_parts(names, type_count);
925    // NOTE: The caller is responsible for freeing raw_names.
926    let names: Vec<_> = raw_names.iter().map(QualifiedName::from_raw).collect();
927    let raw_types = std::slice::from_raw_parts(types, type_count);
928    // NOTE: The caller is responsible for freeing raw_types.
929    let types: Vec<_> = raw_types.iter().map(|&t| Type::ref_from_raw(t)).collect();
930    let inner_result = ctxt.print_all_types(
931        names,
932        types,
933        BinaryView::ref_from_raw(data),
934        padding_cols as isize,
935        escaping,
936    );
937    if let Some(inner_result) = inner_result {
938        let raw_string = BnString::new(inner_result);
939        // NOTE: Dropped by `cb_free_string`
940        *result = BnString::into_raw(raw_string);
941        true
942    } else {
943        *result = std::ptr::null_mut();
944        false
945    }
946}
947
948unsafe extern "C" fn cb_free_string(_ctxt: *mut c_void, string: *mut c_char) {
949    // SAFETY: The returned string is just BnString
950    BnString::free_raw(string);
951}
952
953unsafe extern "C" fn cb_free_tokens(
954    _ctxt: *mut ::std::os::raw::c_void,
955    tokens: *mut BNInstructionTextToken,
956    count: usize,
957) {
958    let tokens = std::ptr::slice_from_raw_parts_mut(tokens, count);
959    // SAFETY: tokens must have been allocated by rust.
960    let boxed_tokens = Box::from_raw(tokens);
961    for token in boxed_tokens {
962        InstructionTextToken::free_raw(token);
963    }
964}
965
966unsafe extern "C" fn cb_free_lines(
967    _ctxt: *mut ::std::os::raw::c_void,
968    lines: *mut BNTypeDefinitionLine,
969    count: usize,
970) {
971    let lines = std::ptr::slice_from_raw_parts_mut(lines, count);
972    // SAFETY: lines must have been allocated by rust.
973    let boxes_lines = Box::from_raw(lines);
974    for line in boxes_lines {
975        TypeDefinitionLine::free_owned_raw(line);
976    }
977}