1use crate::Error;
17use libc::{self, c_char, c_void, size_t};
18use std::ffi::{CStr, CString};
19use std::path::Path;
20use std::ptr;
21
22pub(crate) unsafe fn from_cstr(ptr: *const c_char) -> String {
23 let cstr = CStr::from_ptr(ptr as *const _);
24 String::from_utf8_lossy(cstr.to_bytes()).into_owned()
25}
26
27pub(crate) unsafe fn raw_data(ptr: *const c_char, size: usize) -> Option<Vec<u8>> {
28 if ptr.is_null() {
29 None
30 } else {
31 let mut dst = vec![0; size];
32 ptr::copy_nonoverlapping(ptr as *const u8, dst.as_mut_ptr(), size);
33
34 Some(dst)
35 }
36}
37
38pub fn error_message(ptr: *const c_char) -> String {
39 unsafe {
40 let s = from_cstr(ptr);
41 libc::free(ptr as *mut c_void);
42 s
43 }
44}
45
46pub fn opt_bytes_to_ptr<T: AsRef<[u8]>>(opt: Option<T>) -> *const c_char {
47 match opt {
48 Some(v) => v.as_ref().as_ptr() as *const c_char,
49 None => ptr::null(),
50 }
51}
52
53pub(crate) fn to_cpath<P: AsRef<Path>>(path: P) -> Result<CString, Error> {
54 match CString::new(path.as_ref().to_string_lossy().as_bytes()) {
55 Ok(c) => Ok(c),
56 Err(e) => Err(Error::new(format!(
57 "Failed to convert path to CString: {e}"
58 ))),
59 }
60}
61
62macro_rules! ffi_try {
63 ( $($function:ident)::*() ) => {
64 ffi_try_impl!($($function)::*())
65 };
66
67 ( $($function:ident)::*( $arg1:expr $(, $arg:expr)* $(,)? ) ) => {
68 ffi_try_impl!($($function)::*($arg1 $(, $arg)* ,))
69 };
70}
71
72macro_rules! ffi_try_impl {
73 ( $($function:ident)::*( $($arg:expr,)*) ) => {{
74 let mut err: *mut ::libc::c_char = ::std::ptr::null_mut();
75 let result = $($function)::*($($arg,)* &mut err);
76 if !err.is_null() {
77 return Err(Error::new($crate::ffi_util::error_message(err)));
78 }
79 result
80 }};
81}
82
83pub trait CStrLike {
114 type Baked: std::ops::Deref<Target = CStr>;
115 type Error: std::fmt::Debug + std::fmt::Display;
116
117 fn bake(self) -> Result<Self::Baked, Self::Error>;
121
122 fn into_c_string(self) -> Result<CString, Self::Error>;
128}
129
130impl CStrLike for &str {
131 type Baked = CString;
132 type Error = std::ffi::NulError;
133
134 fn bake(self) -> Result<Self::Baked, Self::Error> {
135 CString::new(self)
136 }
137 fn into_c_string(self) -> Result<CString, Self::Error> {
138 CString::new(self)
139 }
140}
141
142impl CStrLike for &String {
145 type Baked = CString;
146 type Error = std::ffi::NulError;
147
148 fn bake(self) -> Result<Self::Baked, Self::Error> {
149 CString::new(self.as_bytes())
150 }
151 fn into_c_string(self) -> Result<CString, Self::Error> {
152 CString::new(self.as_bytes())
153 }
154}
155
156impl CStrLike for &CStr {
157 type Baked = Self;
158 type Error = std::convert::Infallible;
159
160 fn bake(self) -> Result<Self::Baked, Self::Error> {
161 Ok(self)
162 }
163 fn into_c_string(self) -> Result<CString, Self::Error> {
164 Ok(self.to_owned())
165 }
166}
167
168impl CStrLike for CString {
173 type Baked = CString;
174 type Error = std::convert::Infallible;
175
176 fn bake(self) -> Result<Self::Baked, Self::Error> {
177 Ok(self)
178 }
179 fn into_c_string(self) -> Result<CString, Self::Error> {
180 Ok(self)
181 }
182}
183
184impl<'a> CStrLike for &'a CString {
187 type Baked = &'a CStr;
188 type Error = std::convert::Infallible;
189
190 fn bake(self) -> Result<Self::Baked, Self::Error> {
191 Ok(self)
192 }
193 fn into_c_string(self) -> Result<CString, Self::Error> {
194 Ok(self.clone())
195 }
196}
197
198pub struct CSlice {
201 data: *const c_char,
202 len: size_t,
203}
204
205impl CSlice {
206 pub(crate) unsafe fn from_raw_parts(data: *const c_char, len: size_t) -> Self {
214 Self { data, len }
215 }
216}
217
218impl AsRef<[u8]> for CSlice {
219 fn as_ref(&self) -> &[u8] {
220 unsafe { std::slice::from_raw_parts(self.data as *const u8, self.len) }
221 }
222}
223
224impl Drop for CSlice {
225 fn drop(&mut self) {
226 unsafe {
227 libc::free(self.data as *mut c_void);
228 }
229 }
230}
231
232#[test]
233fn test_c_str_like_bake() {
234 fn test<S: CStrLike>(value: S) -> Result<usize, S::Error> {
235 value
236 .bake()
237 .map(|value| unsafe { libc::strlen(value.as_ptr()) })
238 }
239
240 assert_eq!(Ok(3), test("foo")); assert_eq!(Ok(3), test(&String::from("foo"))); assert_eq!(Ok(3), test(CString::new("foo").unwrap().as_ref())); assert_eq!(Ok(3), test(&CString::new("foo").unwrap())); assert_eq!(Ok(3), test(CString::new("foo").unwrap())); assert_eq!(3, test("foo\0bar").err().unwrap().nul_position());
247}
248
249#[test]
250fn test_c_str_like_into() {
251 fn test<S: CStrLike>(value: S) -> Result<CString, S::Error> {
252 value.into_c_string()
253 }
254
255 let want = CString::new("foo").unwrap();
256
257 assert_eq!(Ok(want.clone()), test("foo")); assert_eq!(Ok(want.clone()), test(&String::from("foo"))); assert_eq!(
260 Ok(want.clone()),
261 test(CString::new("foo").unwrap().as_ref())
262 ); assert_eq!(Ok(want.clone()), test(&CString::new("foo").unwrap())); assert_eq!(Ok(want), test(CString::new("foo").unwrap())); assert_eq!(3, test("foo\0bar").err().unwrap().nul_position());
267}