binaryninja/
string.rs

1// 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}