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};