binaryninja/
string.rs

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