diff options
Diffstat (limited to 'src/rust/tor_util/rust_string.rs')
-rw-r--r-- | src/rust/tor_util/rust_string.rs | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/src/rust/tor_util/rust_string.rs b/src/rust/tor_util/rust_string.rs new file mode 100644 index 0000000000..46ec3fd7a8 --- /dev/null +++ b/src/rust/tor_util/rust_string.rs @@ -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>()) + } +} |