1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
// -------------------------------------------------------------------------------------------------
// Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// -------------------------------------------------------------------------------------------------
//! Utilities for transferring heap-allocated Rust `Vec<T>` values across an FFI boundary.
//!
//! The primary abstraction offered by this module is `CVec`, a C-compatible struct that stores
//! a raw pointer (`ptr`) together with the vector’s logical `len` and `cap`. By moving the
//! allocation metadata into a plain `repr(C)` type we allow the memory created by Rust to be
//! owned, inspected, and ultimately freed by foreign code (or vice-versa) without introducing
//! undefined behaviour.
//!
//! Only a very small API surface is exposed to C:
//!
//! * `cvec_new` – create an empty `CVec` sentinel that can be returned to foreign code.
//!
//! De-allocation is intentionally **not** provided via a generic helper. Instead each FFI module
//! must expose its own *type-specific* `vec_*_drop` function which reconstructs the original
//! `Vec<T>` with [`Vec::from_raw_parts`] and allows it to drop. This avoids the size-mismatch risk
//! that a one-size-fits-all `cvec_drop` had in the past.
//!
//! All other manipulation happens on the Rust side before relinquishing ownership. This keeps the
//! rules for memory safety straightforward: foreign callers must treat the memory region pointed
//! to by `ptr` as **opaque** and interact with it solely through the functions provided here.
use ;
use crateabort_on_panic;
/// `CVec` is a C compatible struct that stores an opaque pointer to a block of
/// memory, its length and the capacity of the vector it was allocated from.
///
/// # Safety
///
/// Changing the values here may lead to undefined behavior when the memory is dropped.
// SAFETY: CVec is marked as Send to satisfy PyO3's PyCapsule requirements, which need
// to transfer ownership across the Python/Rust boundary. However, CVec contains raw
// pointers and is only safe to use in single-threaded contexts or with external
// synchronization guarantees.
//
// The Send impl is required for:
// 1. PyO3's PyCapsule::new_with_destructor which has a Send bound
// 2. Transferring CVec ownership to Python (which runs on a single GIL-protected thread)
//
// IMPORTANT: Do not send CVec instances across threads without ensuring:
// - The underlying data type T is itself Send + Sync
// - Proper external synchronization (e.g., mutex) protects concurrent access
// - The CVec is consumed on the same thread where it will be reconstructed
//
// In practice, CVec usage in this codebase is confined to the Python FFI boundary
// where the Python GIL provides the necessary synchronization.
unsafe
/// Consumes and leaks the Vec, returning a mutable pointer to the contents as
/// a [`CVec`]. The memory has been leaked and now exists for the lifetime of the
/// program unless dropped manually.
/// Note: drop the memory by reconstructing the vec using `from_raw_parts` method
/// as shown in the test below.
////////////////////////////////////////////////////////////////////////////////
// C API
////////////////////////////////////////////////////////////////////////////////
/// Construct a new *empty* [`CVec`] value for use as initialiser/sentinel in foreign code.
pub extern "C"