1use crate::{ffi, 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 unsafe {
24 let cstr = CStr::from_ptr(ptr as *const _);
25 String::from_utf8_lossy(cstr.to_bytes()).into_owned()
26 }
27}
28
29pub(crate) unsafe fn raw_data(ptr: *const c_char, size: usize) -> Option<Vec<u8>> {
30 unsafe {
31 if ptr.is_null() {
32 None
33 } else {
34 let mut dst = vec![0; size];
35 ptr::copy_nonoverlapping(ptr as *const u8, dst.as_mut_ptr(), size);
36
37 Some(dst)
38 }
39 }
40}
41
42pub fn error_message(ptr: *const c_char) -> String {
43 unsafe {
44 let s = from_cstr(ptr);
45 ffi::rocksdb_free(ptr as *mut c_void);
46 s
47 }
48}
49
50pub fn opt_bytes_to_ptr<T: AsRef<[u8]> + ?Sized>(opt: Option<&T>) -> *const c_char {
56 match opt {
57 Some(v) => v.as_ref().as_ptr() as *const c_char,
58 None => ptr::null(),
59 }
60}
61
62pub(crate) fn to_cpath<P: AsRef<Path>>(path: P) -> Result<CString, Error> {
63 match CString::new(path.as_ref().to_string_lossy().as_bytes()) {
64 Ok(c) => Ok(c),
65 Err(e) => Err(Error::new(format!(
66 "Failed to convert path to CString: {e}"
67 ))),
68 }
69}
70
71macro_rules! ffi_try {
72 ( $($function:ident)::*() ) => {
73 ffi_try_impl!($($function)::*())
74 };
75
76 ( $($function:ident)::*( $arg1:expr $(, $arg:expr)* $(,)? ) ) => {
77 ffi_try_impl!($($function)::*($arg1 $(, $arg)* ,))
78 };
79}
80
81macro_rules! ffi_try_impl {
82 ( $($function:ident)::*( $($arg:expr,)*) ) => {{
83 let mut err: *mut ::libc::c_char = ::std::ptr::null_mut();
84 let result = $($function)::*($($arg,)* &mut err);
85 if !err.is_null() {
86 return Err(Error::new($crate::ffi_util::error_message(err)));
87 }
88 result
89 }};
90}
91
92pub trait CStrLike {
123 type Baked: std::ops::Deref<Target = CStr>;
124 type Error: std::fmt::Debug + std::fmt::Display;
125
126 fn bake(self) -> Result<Self::Baked, Self::Error>;
130
131 fn into_c_string(self) -> Result<CString, Self::Error>;
137}
138
139impl CStrLike for &str {
140 type Baked = CString;
141 type Error = std::ffi::NulError;
142
143 fn bake(self) -> Result<Self::Baked, Self::Error> {
144 CString::new(self)
145 }
146 fn into_c_string(self) -> Result<CString, Self::Error> {
147 CString::new(self)
148 }
149}
150
151impl CStrLike for &String {
154 type Baked = CString;
155 type Error = std::ffi::NulError;
156
157 fn bake(self) -> Result<Self::Baked, Self::Error> {
158 CString::new(self.as_bytes())
159 }
160 fn into_c_string(self) -> Result<CString, Self::Error> {
161 CString::new(self.as_bytes())
162 }
163}
164
165impl CStrLike for &CStr {
166 type Baked = Self;
167 type Error = std::convert::Infallible;
168
169 fn bake(self) -> Result<Self::Baked, Self::Error> {
170 Ok(self)
171 }
172 fn into_c_string(self) -> Result<CString, Self::Error> {
173 Ok(self.to_owned())
174 }
175}
176
177impl CStrLike for CString {
182 type Baked = CString;
183 type Error = std::convert::Infallible;
184
185 fn bake(self) -> Result<Self::Baked, Self::Error> {
186 Ok(self)
187 }
188 fn into_c_string(self) -> Result<CString, Self::Error> {
189 Ok(self)
190 }
191}
192
193impl<'a> CStrLike for &'a CString {
196 type Baked = &'a CStr;
197 type Error = std::convert::Infallible;
198
199 fn bake(self) -> Result<Self::Baked, Self::Error> {
200 Ok(self)
201 }
202 fn into_c_string(self) -> Result<CString, Self::Error> {
203 Ok(self.clone())
204 }
205}
206
207pub struct CSlice {
210 data: *const c_char,
211 len: size_t,
212}
213
214impl CSlice {
215 pub(crate) unsafe fn from_raw_parts(data: *const c_char, len: size_t) -> Self {
224 Self { data, len }
225 }
226}
227
228impl AsRef<[u8]> for CSlice {
229 fn as_ref(&self) -> &[u8] {
230 unsafe { std::slice::from_raw_parts(self.data as *const u8, self.len) }
231 }
232}
233
234impl Drop for CSlice {
235 fn drop(&mut self) {
236 unsafe {
237 ffi::rocksdb_free(self.data as *mut c_void);
238 }
239 }
240}
241
242#[test]
243fn test_c_str_like_bake() {
244 fn test<S: CStrLike>(value: S) -> Result<usize, S::Error> {
245 value
246 .bake()
247 .map(|value| unsafe { libc::strlen(value.as_ptr()) })
248 }
249
250 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());
257}
258
259#[test]
260fn test_c_str_like_into() {
261 fn test<S: CStrLike>(value: S) -> Result<CString, S::Error> {
262 value.into_c_string()
263 }
264
265 let want = CString::new("foo").unwrap();
266
267 assert_eq!(Ok(want.clone()), test("foo")); assert_eq!(Ok(want.clone()), test(&String::from("foo"))); assert_eq!(
270 Ok(want.clone()),
271 test(CString::new("foo").unwrap().as_ref())
272 ); 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());
277}