|
|
|
@ -80,3 +80,155 @@ macro_rules! ffi_try_impl { |
|
|
|
|
result |
|
|
|
|
}}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Value which can be converted into a C string.
|
|
|
|
|
///
|
|
|
|
|
/// The trait is used as argument to functions which wish to accept either
|
|
|
|
|
/// [`&str`] or [`&CStr`] arguments while internally need to interact with
|
|
|
|
|
/// C APIs. Accepting [`&str`] may be more convenient for users but requires
|
|
|
|
|
/// conversion into [`CString`] internally which requires allocation. With this
|
|
|
|
|
/// trait, latency-conscious users may choose to prepare `CStr` in advance and
|
|
|
|
|
/// then pass it directly without having to incur the conversion cost.
|
|
|
|
|
///
|
|
|
|
|
/// To use the trait, function should accept `impl CStrLike` and after baking
|
|
|
|
|
/// the argument (with [`CStrLike::bake`] method) it can use it as a `&CStr`
|
|
|
|
|
/// (since the baked result dereferences into `CStr`).
|
|
|
|
|
///
|
|
|
|
|
/// # Example
|
|
|
|
|
///
|
|
|
|
|
/// ```
|
|
|
|
|
/// use std::ffi::{CStr, CString};
|
|
|
|
|
/// use rocksdb::CStrLike;
|
|
|
|
|
///
|
|
|
|
|
/// fn strlen(arg: impl CStrLike) -> std::result::Result<usize, String> {
|
|
|
|
|
/// let baked = arg.bake().map_err(|err| err.to_string())?;
|
|
|
|
|
/// Ok(unsafe { libc::strlen(baked.as_ptr()) })
|
|
|
|
|
/// }
|
|
|
|
|
///
|
|
|
|
|
/// const FOO: &str = "foo";
|
|
|
|
|
/// const BAR: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"bar\0") };
|
|
|
|
|
///
|
|
|
|
|
/// assert_eq!(Ok(3), strlen(FOO));
|
|
|
|
|
/// assert_eq!(Ok(3), strlen(BAR));
|
|
|
|
|
/// ```
|
|
|
|
|
pub trait CStrLike { |
|
|
|
|
type Baked: std::ops::Deref<Target = CStr>; |
|
|
|
|
type Error: std::fmt::Debug + std::fmt::Display; |
|
|
|
|
|
|
|
|
|
/// Bakes self into value which can be freely converted into [`&CStr`].
|
|
|
|
|
///
|
|
|
|
|
/// This may require allocation and may fail if `self` has invalid value.
|
|
|
|
|
fn bake(self) -> Result<Self::Baked, Self::Error>; |
|
|
|
|
|
|
|
|
|
/// Consumers and converts value into an owned [`CString`].
|
|
|
|
|
///
|
|
|
|
|
/// If `Self` is already a `CString` simply returns it; if it’s a reference
|
|
|
|
|
/// to a `CString` then the value is cloned. In other cases this may
|
|
|
|
|
/// require allocation and may fail if `self` has invalid value.
|
|
|
|
|
fn into_c_string(self) -> Result<CString, Self::Error>; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl CStrLike for &str { |
|
|
|
|
type Baked = CString; |
|
|
|
|
type Error = std::ffi::NulError; |
|
|
|
|
|
|
|
|
|
fn bake(self) -> Result<Self::Baked, Self::Error> { |
|
|
|
|
CString::new(self) |
|
|
|
|
} |
|
|
|
|
fn into_c_string(self) -> Result<CString, Self::Error> { |
|
|
|
|
CString::new(self) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// This is redundant for the most part and exists so that `foo(&string)` (where
|
|
|
|
|
// `string: String` works just as if `foo` took `arg: &str` argument.
|
|
|
|
|
impl CStrLike for &String { |
|
|
|
|
type Baked = CString; |
|
|
|
|
type Error = std::ffi::NulError; |
|
|
|
|
|
|
|
|
|
fn bake(self) -> Result<Self::Baked, Self::Error> { |
|
|
|
|
CString::new(self.as_bytes()) |
|
|
|
|
} |
|
|
|
|
fn into_c_string(self) -> Result<CString, Self::Error> { |
|
|
|
|
CString::new(self.as_bytes()) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl CStrLike for &CStr { |
|
|
|
|
type Baked = Self; |
|
|
|
|
type Error = std::convert::Infallible; |
|
|
|
|
|
|
|
|
|
fn bake(self) -> Result<Self::Baked, Self::Error> { |
|
|
|
|
Ok(self) |
|
|
|
|
} |
|
|
|
|
fn into_c_string(self) -> Result<CString, Self::Error> { |
|
|
|
|
Ok(self.to_owned()) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// This exists so that if caller constructs a `CString` they can pass it into
|
|
|
|
|
// the function accepting `CStrLike` argument. Some of such functions may take
|
|
|
|
|
// the argument whereas otherwise they would need to allocated a new owned
|
|
|
|
|
// object.
|
|
|
|
|
impl CStrLike for CString { |
|
|
|
|
type Baked = CString; |
|
|
|
|
type Error = std::convert::Infallible; |
|
|
|
|
|
|
|
|
|
fn bake(self) -> Result<Self::Baked, Self::Error> { |
|
|
|
|
Ok(self) |
|
|
|
|
} |
|
|
|
|
fn into_c_string(self) -> Result<CString, Self::Error> { |
|
|
|
|
Ok(self) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// This is redundant for the most part and exists so that `foo(&cstring)` (where
|
|
|
|
|
// `string: CString` works just as if `foo` took `arg: &CStr` argument.
|
|
|
|
|
impl<'a> CStrLike for &'a CString { |
|
|
|
|
type Baked = &'a CStr; |
|
|
|
|
type Error = std::convert::Infallible; |
|
|
|
|
|
|
|
|
|
fn bake(self) -> Result<Self::Baked, Self::Error> { |
|
|
|
|
Ok(self) |
|
|
|
|
} |
|
|
|
|
fn into_c_string(self) -> Result<CString, Self::Error> { |
|
|
|
|
Ok(self.clone()) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn test_c_str_like_bake() { |
|
|
|
|
fn test<S: CStrLike>(value: S) -> Result<usize, S::Error> { |
|
|
|
|
value |
|
|
|
|
.bake() |
|
|
|
|
.map(|value| unsafe { libc::strlen(value.as_ptr()) }) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
assert_eq!(Ok(3), test("foo")); // &str
|
|
|
|
|
assert_eq!(Ok(3), test(&String::from("foo"))); // String
|
|
|
|
|
assert_eq!(Ok(3), test(CString::new("foo").unwrap().as_ref())); // &CStr
|
|
|
|
|
assert_eq!(Ok(3), test(&CString::new("foo").unwrap())); // &CString
|
|
|
|
|
assert_eq!(Ok(3), test(CString::new("foo").unwrap())); // CString
|
|
|
|
|
|
|
|
|
|
assert_eq!(3, test("foo\0bar").err().unwrap().nul_position()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn test_c_str_like_into() { |
|
|
|
|
fn test<S: CStrLike>(value: S) -> Result<CString, S::Error> { |
|
|
|
|
value.into_c_string() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let want = CString::new("foo").unwrap(); |
|
|
|
|
|
|
|
|
|
assert_eq!(Ok(want.clone()), test("foo")); // &str
|
|
|
|
|
assert_eq!(Ok(want.clone()), test(&String::from("foo"))); // &String
|
|
|
|
|
assert_eq!( |
|
|
|
|
Ok(want.clone()), |
|
|
|
|
test(CString::new("foo").unwrap().as_ref()) |
|
|
|
|
); // &CStr
|
|
|
|
|
assert_eq!(Ok(want.clone()), test(&CString::new("foo").unwrap())); // &CString
|
|
|
|
|
assert_eq!(Ok(want), test(CString::new("foo").unwrap())); // CString
|
|
|
|
|
|
|
|
|
|
assert_eq!(3, test("foo\0bar").err().unwrap().nul_position()); |
|
|
|
|
} |
|
|
|
|