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
//! FFI string utilities
//!
//! Helper functions for retrieving strings from C/Objective-C APIs
//! that use buffer-based string retrieval patterns.
use CStr;
/// Default buffer size for FFI string retrieval
pub const DEFAULT_BUFFER_SIZE: usize = 1024;
/// Smaller buffer size for short strings (e.g., device IDs, stream names)
pub const SMALL_BUFFER_SIZE: usize = 256;
/// Stack-allocate up to this many bytes — anything bigger falls back to a
/// heap `Vec`. 256 bytes covers every real call site (`SMALL_BUFFER_SIZE`,
/// audio device IDs, stream names, microphone IDs); the 1 KiB callers are
/// rare and currently absent from the codebase, so the heap fallback path
/// is essentially dead code today but kept for forward-compat with
/// future longer-string APIs.
const STACK_BUFFER_BYTES: usize = 256;
/// Retrieves a string from an FFI function that writes to a buffer.
///
/// This is a common pattern in Objective-C FFI where a function:
/// 1. Takes a buffer pointer and length
/// 2. Writes a null-terminated string to the buffer
/// 3. Returns a boolean indicating success
///
/// # Arguments
/// * `buffer_size` - Size of the buffer to allocate
/// * `ffi_call` - A closure that takes (`buffer_ptr`, `buffer_len`) and returns success bool
///
/// # Returns
/// * `Some(String)` if the FFI call succeeded and the string was valid UTF-8
/// * `None` if the FFI call failed or returned an empty string
///
/// # Safety
/// The caller must ensure the `ffi_call` closure does not write beyond the
/// provided `buffer_len`. This function defends against the closure writing
/// a non-NUL-terminated string by scanning the buffer up to its declared
/// length and treating the absence of a terminator as failure (returns
/// `None`) rather than reading past the buffer with `CStr::from_ptr`.
///
/// # Example
/// ```
/// use apple_cf::utils::ffi_string::ffi_string_from_buffer;
///
/// let result = unsafe {
/// ffi_string_from_buffer(64, |buf, len| {
/// // Simulate FFI call that writes "hello" to buffer
/// let src = b"hello\0";
/// if len >= src.len() as isize {
/// std::ptr::copy_nonoverlapping(src.as_ptr(), buf as *mut u8, src.len());
/// true
/// } else {
/// false
/// }
/// })
/// };
/// assert_eq!(result, Some("hello".to_string()));
/// ```
pub unsafe
/// Scan for the NUL terminator and decode the string portion.
/// Defensive: do NOT use `CStr::from_ptr` here. If the FFI closure
/// returned `true` but failed to write a NUL terminator, `CStr::from_ptr`
/// would read past the buffer until it found a zero byte — UB and a
/// potential information leak. Instead, scan only the buffer we
/// allocated and treat a missing terminator as failure.
/// Same as [`ffi_string_from_buffer`] but returns an empty string on failure
/// instead of `None`.
///
/// Useful when the API should always return a string, even if empty.
///
/// # Safety
/// The caller must ensure that the FFI call writes valid UTF-8 data to the buffer.
pub unsafe
/// Retrieves a string from an FFI function that returns an owned C string pointer.
///
/// This is more efficient than buffer-based retrieval as it avoids pre-allocation.
/// The FFI function allocates the string (via `strdup`) and this function takes
/// ownership and frees it.
///
/// # Arguments
/// * `ffi_call` - A closure that returns an owned C string pointer (or null)
///
/// # Returns
/// * `Some(String)` if the pointer was non-null and valid UTF-8
/// * `None` if the pointer was null
///
/// # Safety
/// The caller must ensure the returned pointer was allocated by Swift's `strdup`
/// or equivalent, and that `sc_free_string` properly frees it. The pointer is
/// freed via an RAII guard, so a panic in `to_string_lossy` (extremely rare —
/// only OOM) does not leak the Swift-allocated buffer.
pub unsafe
/// Same as [`ffi_string_owned`] but returns an empty string on failure.
///
/// # Safety
/// Same requirements as [`ffi_string_owned`].
pub unsafe