|
@@ -0,0 +1,101 @@
|
|
|
+use std::ffi::CString;
|
|
|
+use std::mem::forget;
|
|
|
+use libc;
|
|
|
+
|
|
|
+/// Compatibility wrapper for strings allocated in Rust and passed to C.
|
|
|
+///
|
|
|
+/// Rust doesn't ensure the safety of freeing memory across an FFI boundary, so
|
|
|
+/// we need to take special care to ensure we're not accidentally calling
|
|
|
+/// `tor_free`() on any string allocated in Rust. To more easily differentiate
|
|
|
+/// between strings that possibly (if Rust support is enabled) were allocated
|
|
|
+/// in Rust, C has the `rust_str_t` helper type. The equivalent on the Rust
|
|
|
+/// side is `RustString`.
|
|
|
+///
|
|
|
+/// Note: This type must not be used for strings allocated in C.
|
|
|
+#[repr(C)]
|
|
|
+#[derive(Debug)]
|
|
|
+pub struct RustString(*mut libc::c_char);
|
|
|
+
|
|
|
+impl RustString {
|
|
|
+ /// Returns a pointer to the underlying NUL-terminated byte array.
|
|
|
+ ///
|
|
|
+ /// Note that this function is not typically useful for Rust callers,
|
|
|
+ /// except in a direct FFI context.
|
|
|
+ ///
|
|
|
+ /// # Examples
|
|
|
+ /// ```
|
|
|
+ /// # use tor_util::RustString;
|
|
|
+ /// use std::ffi::CString;
|
|
|
+ ///
|
|
|
+ /// let r = RustString::from(CString::new("asdf").unwrap());
|
|
|
+ /// let c_str = r.as_ptr();
|
|
|
+ /// assert_eq!(b'a', unsafe { *c_str as u8});
|
|
|
+ /// ```
|
|
|
+ pub fn as_ptr(&self) -> *const libc::c_char {
|
|
|
+ self.0 as *const libc::c_char
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl From<CString> for RustString {
|
|
|
+ /// Constructs a new `RustString`
|
|
|
+ ///
|
|
|
+ /// # Examples
|
|
|
+ /// ```
|
|
|
+ /// # use tor_util::RustString;
|
|
|
+ /// use std::ffi::CString;
|
|
|
+ ///
|
|
|
+ /// let r = RustString::from(CString::new("asdf").unwrap());
|
|
|
+ /// ```
|
|
|
+ fn from(str: CString) -> RustString {
|
|
|
+ RustString(str.into_raw())
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl Into<CString> for RustString {
|
|
|
+ /// Reconstructs a `CString` from this `RustString`.
|
|
|
+ ///
|
|
|
+ /// Useful to take ownership back from a `RustString` that was given to C
|
|
|
+ /// code.
|
|
|
+ ///
|
|
|
+ /// # Examples
|
|
|
+ /// ```
|
|
|
+ /// # use tor_util::RustString;
|
|
|
+ /// use std::ffi::CString;
|
|
|
+ ///
|
|
|
+ /// let cs = CString::new("asdf").unwrap();
|
|
|
+ /// let r = RustString::from(cs.clone());
|
|
|
+ /// let cs2 = r.into();
|
|
|
+ /// assert_eq!(cs, cs2);
|
|
|
+ /// ```
|
|
|
+ fn into(self) -> CString {
|
|
|
+ // Calling from_raw is always OK here: We only construct self using
|
|
|
+ // valid CStrings and don't expose anything that could mutate it
|
|
|
+ let ret = unsafe { CString::from_raw(self.0) };
|
|
|
+ forget(self);
|
|
|
+ ret
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl Drop for RustString {
|
|
|
+ fn drop(&mut self) {
|
|
|
+ // Don't use into() here, because we would need to move out of
|
|
|
+ // self. Same safety consideration. Immediately drop the created
|
|
|
+ // CString, which takes care of freeing the wrapped string.
|
|
|
+ unsafe { CString::from_raw(self.0) };
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#[cfg(test)]
|
|
|
+mod test {
|
|
|
+ use std::mem;
|
|
|
+ use super::*;
|
|
|
+
|
|
|
+ use libc;
|
|
|
+
|
|
|
+ /// Ensures we're not adding overhead by using RustString.
|
|
|
+ #[test]
|
|
|
+ fn size_of() {
|
|
|
+ assert_eq!(mem::size_of::<*mut libc::c_char>(),
|
|
|
+ mem::size_of::<RustString>())
|
|
|
+ }
|
|
|
+}
|