Skip to main content

idakit_sys/
bridge_qvec.rs

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