Skip to main content

idakit_sys/
bridge_qvec.rs

1//! `cxx` bindings for IDA's own generic container `qvector<T>` (`idakit_cxx::*vec_*`).
2//!
3//! `cxx` has no generic-template support and its built-in `CxxVector<T>` is `std::vector`-only,
4//! so it cannot match `qvector`'s `{ T* array; size_t n; size_t alloc; }` ABI. This module
5//! applies the KDAB cxx-qt per-instantiation recipe: each concrete `qvector<T>` is bound as its
6//! own `Opaque` [`cxx::ExternType`] (one distinct `type_id!` per instantiation), and the
7//! mechanical per-`T` boilerplate is emitted by the [`qvec_opaque!`] macro. Two instantiations
8//! prove the recipe from scalar `T` to a `Trivial`-struct `T`:
9//!
10//! - [`IntVec`] binds the SDK's `intvec_t` (`qvector<int>`), sourced from a flow-chart block's
11//!   successor edge list.
12//! - [`RangeVec`] binds the SDK's `rangevec_t` (`struct : qvector<range_t>`), built from a
13//!   function's chunk ranges, with [`RangeT`](crate::bridge_gen::RangeT) (the `Trivial` ExternType
14//!   from the generated [`bridge_gen`](crate::bridge_gen)) as its element.
15//!
16//! Both `intvec_t` and `rangevec_t` are real global SDK names, so `#[cxx_name]` names them
17//! directly with no C++ `using` alias (angle-bracketed `#[cxx_name = "qvector<int>"]` does not
18//! parse in `type_id!`/`cxx_name`, which require an identifier path).
19//!
20//! Each instantiation is read two ways, contrasting cost:
21//!
22//! - **Copy** (`intvec_copy`): one linear copy into an owned [`Vec`], no lifetime tie. The safe
23//!   default when the data outlives the container.
24//! - **Zero-copy** (`intvec_slice`, `rangevec_slice`): a `&[T]` reconstructed on the C++ side
25//!   from the container's `{array, n}` and returned as `rust::Slice`, borrowing the live
26//!   backing store with no allocation and no copy. Sound only because the returned slice's
27//!   lifetime is tied to the borrowed container (`&'a V -> &'a [T]`), the access is read-only
28//!   (`alloc`/`qalloc`/`qfree` are never touched), and the `{array, n}` layout is fixed by the
29//!   ABI-per-minor pin. The bodies are hand-written in `facade/qvec_cxx.cc`.
30
31/// Emit the per-instantiation `Opaque` handle for one `qvector<T>`: a zero-sized `!Unpin`
32/// mirror struct plus its [`cxx::ExternType`] impl bound to the SDK type named by `$cxx`.
33///
34/// This is the reusable core of the recipe: the ExternType boilerplate that differs only by
35/// `(RustName, "cxx_name")` per instantiation. The `#[cxx::bridge]` fn declarations and the
36/// `impl UniquePtr<T> {}` container glue cannot be folded in here, because `cxx_build` parses
37/// the source file textually (with `syn`) and cannot expand a `macro_rules!`, so anything it
38/// must see, the bridge module itself, has to be written literally below.
39macro_rules! qvec_opaque {
40    ($rust:ident, $cxx:literal) => {
41        #[doc = concat!("An `Opaque` `cxx` binding of the SDK container `", $cxx, "`.")]
42        ///
43        /// Never held by value in Rust; only borrowed (`&Self`) or owned behind
44        /// [`UniquePtr`](cxx::UniquePtr). The opaque body mirrors `cxx`'s own generated
45        /// representation, so the type is zero-sized and `!Unpin`.
46        #[repr(C)]
47        pub struct $rust {
48            _private: cxx::private::Opaque,
49        }
50
51        // SAFETY: the type id names the real global SDK type; Opaque is correct because a
52        // qvector owns a heap buffer (nontrivial destructor), so it may only cross the bridge
53        // behind a reference or UniquePtr, never by value.
54        unsafe impl cxx::ExternType for $rust {
55            type Id = cxx::type_id!($cxx);
56            type Kind = cxx::kind::Opaque;
57        }
58    };
59}
60
61qvec_opaque!(IntVec, "intvec_t");
62qvec_opaque!(RangeVec, "rangevec_t");
63
64#[cxx::bridge(namespace = "idakit_cxx")]
65mod ffi {
66    unsafe extern "C++" {
67        include!("qvec_cxx.h");
68
69        /// The SDK's `range_t`, shared from the generated [`bridge_gen`](crate::bridge_gen) so it
70        /// can be a zero-copy [`RangeVec`] slice element (one ExternType across both bridges).
71        #[namespace = ""]
72        #[cxx_name = "range_t"]
73        type RangeT = crate::bridge_gen::RangeT;
74
75        /// The SDK's `qflow_chart_t`, shared from the generated [`bridge_gen`](crate::bridge_gen);
76        /// the source of the borrowed [`IntVec`].
77        #[namespace = ""]
78        #[cxx_name = "qflow_chart_t"]
79        type FlowChart = crate::bridge_gen::FlowChart;
80
81        /// `qvector<int>`, bound by [`super::IntVec`]'s macro-generated `ExternType` impl.
82        #[namespace = ""]
83        #[cxx_name = "intvec_t"]
84        type IntVec = super::IntVec;
85
86        /// `qvector<range_t>`, bound by [`super::RangeVec`]'s macro-generated `ExternType` impl.
87        #[namespace = ""]
88        #[cxx_name = "rangevec_t"]
89        type RangeVec = super::RangeVec;
90
91        /// Borrow block `n`'s successor edge list (`qbasic_block_t::succ`, an `intvec_t`) out of
92        /// the live flow chart; `Err` when `n` is out of range. The returned `&IntVec` borrows
93        /// `fc`, so it cannot outlive it.
94        fn cfg_succ_vec<'a>(fc: &'a FlowChart, n: usize) -> Result<&'a IntVec>;
95
96        /// Element count of an `intvec_t`, via its `qvector::size()`.
97        fn intvec_len(v: &IntVec) -> usize;
98        /// Copy an `intvec_t` into an owned [`Vec<i32>`] (the allocation/copy path).
99        fn intvec_copy(v: &IntVec) -> Vec<i32>;
100        /// Borrow an `intvec_t`'s backing store as `&[i32]` (the zero-copy path). The slice
101        /// borrows `v`, so it cannot outlive it.
102        fn intvec_slice<'a>(v: &'a IntVec) -> &'a [i32];
103
104        /// Build a `rangevec_t` of every chunk (entry plus tails) of the function at `ea`, owned
105        /// by a [`UniquePtr`](cxx::UniquePtr); `Err` when no function is there.
106        fn rangevec_build_chunks(ea: u64) -> Result<UniquePtr<RangeVec>>;
107        /// Element count of a `rangevec_t`, via its inherited `qvector::size()`.
108        fn rangevec_len(v: &RangeVec) -> usize;
109        /// Borrow a `rangevec_t`'s backing store as `&[RangeT]` (the zero-copy path). The slice
110        /// borrows `v`, so it cannot outlive it.
111        fn rangevec_slice<'a>(v: &'a RangeVec) -> &'a [RangeT];
112    }
113
114    // Force cxx to emit the UniquePtr<RangeVec> glue. A hand-written ExternType is not declared
115    // by an in-bridge `type X;`, so this empty impl is what triggers its container support.
116    impl UniquePtr<RangeVec> {}
117}
118
119pub use ffi::{
120    cfg_succ_vec, intvec_copy, intvec_len, intvec_slice, rangevec_build_chunks, rangevec_len,
121    rangevec_slice,
122};