binaryninja/
string.rs1// Copyright 2021-2025 Vector 35 Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! String wrappers for core-owned strings and strings being passed to the core
16
17use binaryninjacore_sys::*;
18use std::borrow::Cow;
19use std::ffi::{c_char, CStr, CString};
20use std::fmt;
21use std::hash::{Hash, Hasher};
22use std::mem;
23use std::ops::Deref;
24use std::path::{Path, PathBuf};
25
26use crate::rc::*;
27use crate::type_archive::TypeArchiveSnapshotId;
28use crate::types::QualifiedName;
29
30// TODO: Remove or refactor this.
31pub(crate) fn raw_to_string(ptr: *const c_char) -> Option<String> {
32 if ptr.is_null() {
33 None
34 } else {
35 Some(unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() })
36 }
37}
38
39pub fn strings_to_string_list<I, S>(strings: I) -> *mut *mut c_char
40where
41 I: IntoIterator<Item = S>,
42 // TODO make `S: BnStrCompatible,`
43 S: AsRef<str>,
44{
45 use binaryninjacore_sys::BNAllocStringList;
46 let bn_str_list = strings
47 .into_iter()
48 .map(|s| BnString::new(s.as_ref()))
49 .collect::<Vec<_>>();
50 let mut raw_str_list = bn_str_list.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
51 unsafe { BNAllocStringList(raw_str_list.as_mut_ptr(), raw_str_list.len()) }
52}
53
54/// A nul-terminated C string allocated by the core.
55///
56/// Received from a variety of core function calls, and must be used when giving strings to the
57/// core from many core-invoked callbacks, or otherwise passing ownership of the string to the core.
58///
59/// These are strings we're responsible for freeing, such as strings allocated by the core and
60/// given to us through the API and then forgotten about by the core.
61///
62/// When passing to the core, make sure to use [`BnString::to_cstr`] and [`CStr::as_ptr`].
63///
64/// When giving ownership to the core, make sure to prevent dropping by calling [`BnString::into_raw`].
65#[repr(transparent)]
66pub struct BnString {
67 raw: *mut c_char,
68}
69
70impl BnString {
71 pub fn new(s: impl IntoCStr) -> Self {
72 let raw = s.to_cstr();
73 unsafe { Self::from_raw(BNAllocString(raw.as_ptr())) }
74 }
75
76 /// Take an owned core string and convert it to [`String`].
77 ///
78 /// This expects the passed raw string to be owned, as in, freed by us.
79 pub unsafe fn into_string(raw: *mut c_char) -> String {
80 Self::from_raw(raw).to_string_lossy().to_string()
81 }
82
83 /// Construct a BnString from an owned const char* allocated by [`BNAllocString`].
84 pub(crate) unsafe fn from_raw(raw: *mut c_char) -> Self {
85 Self { raw }
86 }
87
88 /// Free a raw string allocated by [`BNAllocString`].
89 pub unsafe fn free_raw(raw: *mut c_char) {
90 if !raw.is_null() {
91 BNFreeString(raw);
92 }
93 }
94
95 /// Consumes the `BnString`, returning a raw pointer to the string.
96 ///
97 /// After calling this function, the caller is responsible for the
98 /// memory previously managed by the `BnString`.
99 ///
100 /// This is typically used to pass a string back through the core where the core is expected to free.
101 pub fn into_raw(value: Self) -> *mut c_char {
102 let res = value.raw;
103 // we're surrendering ownership over the *mut c_char to
104 // the core, so ensure we don't free it
105 mem::forget(value);
106 res
107 }
108}
109
110impl Drop for BnString {
111 fn drop(&mut self) {
112 unsafe { BnString::free_raw(self.raw) };
113 }
114}
115
116impl Clone for BnString {
117 fn clone(&self) -> Self {
118 unsafe {
119 Self {
120 raw: BNAllocString(self.raw),
121 }
122 }
123 }
124}
125
126impl Deref for BnString {
127 type Target = CStr;
128
129 fn deref(&self) -> &CStr {
130 unsafe { CStr::from_ptr(self.raw) }
131 }
132}
133
134impl From<String> for BnString {
135 fn from(s: String) -> Self {
136 Self::new(s)
137 }
138}
139
140impl From<&str> for BnString {
141 fn from(s: &str) -> Self {
142 Self::new(s)
143 }
144}
145
146impl AsRef<[u8]> for BnString {
147 fn as_ref(&self) -> &[u8] {
148 self.to_bytes_with_nul()
149 }
150}
151
152impl Hash for BnString {
153 fn hash<H: Hasher>(&self, state: &mut H) {
154 self.raw.hash(state)
155 }
156}
157
158impl PartialEq for BnString {
159 fn eq(&self, other: &Self) -> bool {
160 self.deref() == other.deref()
161 }
162}
163
164impl Eq for BnString {}
165
166impl fmt::Debug for BnString {
167 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
168 self.to_string_lossy().fmt(f)
169 }
170}
171
172impl CoreArrayProvider for BnString {
173 type Raw = *mut c_char;
174 type Context = ();
175 type Wrapped<'a> = &'a str;
176}
177
178unsafe impl CoreArrayProviderInner for BnString {
179 unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
180 BNFreeStringList(raw, count);
181 }
182
183 unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
184 CStr::from_ptr(*raw).to_str().unwrap()
185 }
186}
187
188pub trait IntoCStr {
189 type Result: Deref<Target = CStr>;
190
191 fn to_cstr(self) -> Self::Result;
192}
193
194impl IntoCStr for &CStr {
195 type Result = Self;
196
197 fn to_cstr(self) -> Self::Result {
198 self
199 }
200}
201
202impl IntoCStr for BnString {
203 type Result = Self;
204
205 fn to_cstr(self) -> Self::Result {
206 self
207 }
208}
209
210impl IntoCStr for &BnString {
211 type Result = BnString;
212
213 fn to_cstr(self) -> Self::Result {
214 self.clone()
215 }
216}
217
218impl IntoCStr for CString {
219 type Result = Self;
220
221 fn to_cstr(self) -> Self::Result {
222 self
223 }
224}
225
226impl IntoCStr for &str {
227 type Result = CString;
228
229 fn to_cstr(self) -> Self::Result {
230 CString::new(self).expect("can't pass strings with internal nul bytes to core!")
231 }
232}
233
234impl IntoCStr for String {
235 type Result = CString;
236
237 fn to_cstr(self) -> Self::Result {
238 CString::new(self).expect("can't pass strings with internal nul bytes to core!")
239 }
240}
241
242impl IntoCStr for &String {
243 type Result = CString;
244
245 fn to_cstr(self) -> Self::Result {
246 self.clone().to_cstr()
247 }
248}
249
250impl<'a> IntoCStr for &'a Cow<'a, str> {
251 type Result = CString;
252
253 fn to_cstr(self) -> Self::Result {
254 self.to_string().to_cstr()
255 }
256}
257
258impl IntoCStr for Cow<'_, str> {
259 type Result = CString;
260
261 fn to_cstr(self) -> Self::Result {
262 self.to_string().to_cstr()
263 }
264}
265
266impl IntoCStr for &QualifiedName {
267 type Result = CString;
268
269 fn to_cstr(self) -> Self::Result {
270 self.to_string().to_cstr()
271 }
272}
273
274impl IntoCStr for PathBuf {
275 type Result = CString;
276
277 fn to_cstr(self) -> Self::Result {
278 self.as_path().to_cstr()
279 }
280}
281
282impl IntoCStr for &Path {
283 type Result = CString;
284
285 fn to_cstr(self) -> Self::Result {
286 CString::new(self.as_os_str().as_encoded_bytes())
287 .expect("can't pass paths with internal nul bytes to core!")
288 }
289}
290
291impl IntoCStr for TypeArchiveSnapshotId {
292 type Result = CString;
293
294 fn to_cstr(self) -> Self::Result {
295 self.to_string().to_cstr()
296 }
297}
298
299pub trait IntoJson {
300 type Output: IntoCStr;
301
302 fn get_json_string(self) -> Result<Self::Output, ()>;
303}
304
305impl<S: IntoCStr> IntoJson for S {
306 type Output = S;
307
308 fn get_json_string(self) -> Result<Self::Output, ()> {
309 Ok(self)
310 }
311}