electrs_rocksdb/
ffi_util.rs1use crate::Error;
17use libc::{self, c_char, c_void};
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: {}",
58 e,
59 ))),
60 }
61}
62
63macro_rules! ffi_try {
64 ( $($function:ident)::*() ) => {
65 ffi_try_impl!($($function)::*())
66 };
67
68 ( $($function:ident)::*( $arg1:expr $(, $arg:expr)* $(,)? ) ) => {
69 ffi_try_impl!($($function)::*($arg1 $(, $arg)* ,))
70 };
71}
72
73macro_rules! ffi_try_impl {
74 ( $($function:ident)::*( $($arg:expr,)*) ) => {{
75 let mut err: *mut ::libc::c_char = ::std::ptr::null_mut();
76 let result = $($function)::*($($arg,)* &mut err);
77 if !err.is_null() {
78 return Err(Error::new($crate::ffi_util::error_message(err)));
79 }
80 result
81 }};
82}
83
84pub trait CStrLike {
115 type Baked: std::ops::Deref<Target = CStr>;
116 type Error: std::fmt::Debug + std::fmt::Display;
117
118 fn bake(self) -> Result<Self::Baked, Self::Error>;
122
123 fn into_c_string(self) -> Result<CString, Self::Error>;
129}
130
131impl CStrLike for &str {
132 type Baked = CString;
133 type Error = std::ffi::NulError;
134
135 fn bake(self) -> Result<Self::Baked, Self::Error> {
136 CString::new(self)
137 }
138 fn into_c_string(self) -> Result<CString, Self::Error> {
139 CString::new(self)
140 }
141}
142
143impl CStrLike for &String {
146 type Baked = CString;
147 type Error = std::ffi::NulError;
148
149 fn bake(self) -> Result<Self::Baked, Self::Error> {
150 CString::new(self.as_bytes())
151 }
152 fn into_c_string(self) -> Result<CString, Self::Error> {
153 CString::new(self.as_bytes())
154 }
155}
156
157impl CStrLike for &CStr {
158 type Baked = Self;
159 type Error = std::convert::Infallible;
160
161 fn bake(self) -> Result<Self::Baked, Self::Error> {
162 Ok(self)
163 }
164 fn into_c_string(self) -> Result<CString, Self::Error> {
165 Ok(self.to_owned())
166 }
167}
168
169impl CStrLike for CString {
174 type Baked = CString;
175 type Error = std::convert::Infallible;
176
177 fn bake(self) -> Result<Self::Baked, Self::Error> {
178 Ok(self)
179 }
180 fn into_c_string(self) -> Result<CString, Self::Error> {
181 Ok(self)
182 }
183}
184
185impl<'a> CStrLike for &'a CString {
188 type Baked = &'a CStr;
189 type Error = std::convert::Infallible;
190
191 fn bake(self) -> Result<Self::Baked, Self::Error> {
192 Ok(self)
193 }
194 fn into_c_string(self) -> Result<CString, Self::Error> {
195 Ok(self.clone())
196 }
197}
198
199#[test]
200fn test_c_str_like_bake() {
201 fn test<S: CStrLike>(value: S) -> Result<usize, S::Error> {
202 value
203 .bake()
204 .map(|value| unsafe { libc::strlen(value.as_ptr()) })
205 }
206
207 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());
214}
215
216#[test]
217fn test_c_str_like_into() {
218 fn test<S: CStrLike>(value: S) -> Result<CString, S::Error> {
219 value.into_c_string()
220 }
221
222 let want = CString::new("foo").unwrap();
223
224 assert_eq!(Ok(want.clone()), test("foo")); assert_eq!(Ok(want.clone()), test(&String::from("foo"))); assert_eq!(
227 Ok(want.clone()),
228 test(CString::new("foo").unwrap().as_ref())
229 ); 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());
234}