1use crate::{ffi, Error};
17use libc::{self, c_char, c_void, size_t};
18use std::ffi::{CStr, CString};
19#[cfg(unix)]
20use std::os::unix::ffi::OsStrExt;
21use std::path::Path;
22use std::ptr;
23
24pub(crate) unsafe fn from_cstr(ptr: *const c_char) -> String {
25 unsafe {
26 let cstr = CStr::from_ptr(ptr as *const _);
27 String::from_utf8_lossy(cstr.to_bytes()).into_owned()
28 }
29}
30
31pub(crate) unsafe fn raw_data(ptr: *const c_char, size: usize) -> Option<Vec<u8>> {
32 if ptr.is_null() {
33 None
34 } else {
35 Some(std::slice::from_raw_parts(ptr as *const u8, size).to_vec())
37 }
38}
39
40pub fn error_message(ptr: *const c_char) -> String {
41 unsafe {
42 let s = from_cstr(ptr);
43 ffi::rocksdb_free(ptr as *mut c_void);
44 s
45 }
46}
47
48pub fn opt_bytes_to_ptr<T: AsRef<[u8]> + ?Sized>(opt: Option<&T>) -> *const c_char {
54 match opt {
55 Some(v) => v.as_ref().as_ptr() as *const c_char,
56 None => ptr::null(),
57 }
58}
59
60#[cfg(unix)]
61pub(crate) fn to_cpath<P: AsRef<Path>>(path: P) -> Result<CString, Error> {
62 CString::new(path.as_ref().as_os_str().as_bytes())
63 .map_err(|e| Error::new(format!("Failed to convert path to CString: {e}")))
64}
65
66#[cfg(not(unix))]
67pub(crate) fn to_cpath<P: AsRef<Path>>(path: P) -> Result<CString, Error> {
68 match CString::new(path.as_ref().to_string_lossy().as_bytes()) {
69 Ok(c) => Ok(c),
70 Err(e) => Err(Error::new(format!(
71 "Failed to convert path to CString: {e}"
72 ))),
73 }
74}
75
76macro_rules! ffi_try {
77 ( $($function:ident)::*() ) => {
78 ffi_try_impl!($($function)::*())
79 };
80
81 ( $($function:ident)::*( $arg1:expr $(, $arg:expr)* $(,)? ) ) => {
82 ffi_try_impl!($($function)::*($arg1 $(, $arg)* ,))
83 };
84}
85
86macro_rules! ffi_try_impl {
87 ( $($function:ident)::*( $($arg:expr,)*) ) => {{
88 let mut err: *mut ::libc::c_char = ::std::ptr::null_mut();
89 let result = $($function)::*($($arg,)* &mut err);
90 if !err.is_null() {
91 return Err(Error::new($crate::ffi_util::error_message(err)));
92 }
93 result
94 }};
95}
96
97pub trait CStrLike {
128 type Baked: std::ops::Deref<Target = CStr>;
129 type Error: std::fmt::Debug + std::fmt::Display;
130
131 fn bake(self) -> Result<Self::Baked, Self::Error>;
135
136 fn into_c_string(self) -> Result<CString, Self::Error>;
142}
143
144impl CStrLike for &str {
145 type Baked = CString;
146 type Error = std::ffi::NulError;
147
148 fn bake(self) -> Result<Self::Baked, Self::Error> {
149 CString::new(self)
150 }
151 fn into_c_string(self) -> Result<CString, Self::Error> {
152 CString::new(self)
153 }
154}
155
156impl CStrLike for &String {
159 type Baked = CString;
160 type Error = std::ffi::NulError;
161
162 fn bake(self) -> Result<Self::Baked, Self::Error> {
163 CString::new(self.as_bytes())
164 }
165 fn into_c_string(self) -> Result<CString, Self::Error> {
166 CString::new(self.as_bytes())
167 }
168}
169
170impl CStrLike for &CStr {
171 type Baked = Self;
172 type Error = std::convert::Infallible;
173
174 fn bake(self) -> Result<Self::Baked, Self::Error> {
175 Ok(self)
176 }
177 fn into_c_string(self) -> Result<CString, Self::Error> {
178 Ok(self.to_owned())
179 }
180}
181
182impl CStrLike for CString {
187 type Baked = CString;
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)
195 }
196}
197
198impl<'a> CStrLike for &'a CString {
201 type Baked = &'a CStr;
202 type Error = std::convert::Infallible;
203
204 fn bake(self) -> Result<Self::Baked, Self::Error> {
205 Ok(self)
206 }
207 fn into_c_string(self) -> Result<CString, Self::Error> {
208 Ok(self.clone())
209 }
210}
211
212pub struct CSlice {
215 data: *const c_char,
216 len: size_t,
217}
218
219impl CSlice {
220 pub(crate) unsafe fn from_raw_parts(data: *const c_char, len: size_t) -> Self {
229 Self { data, len }
230 }
231}
232
233impl AsRef<[u8]> for CSlice {
234 fn as_ref(&self) -> &[u8] {
235 unsafe { std::slice::from_raw_parts(self.data as *const u8, self.len) }
236 }
237}
238
239impl Drop for CSlice {
240 fn drop(&mut self) {
241 unsafe {
242 ffi::rocksdb_free(self.data as *mut c_void);
243 }
244 }
245}
246
247#[test]
248fn test_c_str_like_bake() {
249 fn test<S: CStrLike>(value: S) -> Result<usize, S::Error> {
250 value
251 .bake()
252 .map(|value| unsafe { libc::strlen(value.as_ptr()) })
253 }
254
255 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());
262}
263
264#[test]
265fn test_c_str_like_into() {
266 fn test<S: CStrLike>(value: S) -> Result<CString, S::Error> {
267 value.into_c_string()
268 }
269
270 let want = CString::new("foo").unwrap();
271
272 assert_eq!(Ok(want.clone()), test("foo")); assert_eq!(Ok(want.clone()), test(&String::from("foo"))); assert_eq!(
275 Ok(want.clone()),
276 test(CString::new("foo").unwrap().as_ref())
277 ); 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());
282}