From 986ba9c3c17ee1c7bb05ba9651fd0a6547cc2db6 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 4 Aug 2013 18:37:55 -0700 Subject: [PATCH] std: Update the c_str docs, and support CString not owning the pointer --- src/libstd/c_str.rs | 201 ++++++++++++++------------------------------ 1 file changed, 63 insertions(+), 138 deletions(-) diff --git a/src/libstd/c_str.rs b/src/libstd/c_str.rs index 29aa68b1533..312cfe54603 100644 --- a/src/libstd/c_str.rs +++ b/src/libstd/c_str.rs @@ -18,105 +18,77 @@ use ptr; use str::StrSlice; use vec::ImmutableVector; -/** - * The representation of a C String. - * - * This structure wraps a `*libc::c_char`, and will automatically free the - * memory it is pointing to when it goes out of scope. - */ +/// The representation of a C String. +/// +/// This structure wraps a `*libc::c_char`, and will automatically free the +/// memory it is pointing to when it goes out of scope. pub struct CString { priv buf: *libc::c_char, + priv owns_buffer_: bool, } impl<'self> CString { - /** - * Create a C String from a str. - */ - pub fn from_str(s: &str) -> CString { - s.to_c_str() + /// Create a C String from a pointer. + pub fn new(buf: *libc::c_char, owns_buffer: bool) -> CString { + CString { buf: buf, owns_buffer_: owns_buffer } } - /** - * Take the wrapped `*libc::c_char` from the `CString` wrapper. - * - * # Failure - * - * If the wrapper is empty. - */ - pub unsafe fn take(&mut self) -> *libc::c_char { - if self.buf.is_null() { - fail!("CString has no wrapped `*libc::c_char`"); - } - let buf = self.buf; - self.buf = ptr::null(); - buf + /// Unwraps the wrapped `*libc::c_char` from the `CString` wrapper. + pub unsafe fn unwrap(self) -> *libc::c_char { + let mut c_str = self; + c_str.owns_buffer_ = false; + c_str.buf } - /** - * Puts a `*libc::c_char` into the `CString` wrapper. - * - * # Failure - * - * If the `*libc::c_char` is null. - * If the wrapper is not empty. - */ - pub fn put_back(&mut self, buf: *libc::c_char) { - if buf.is_null() { - fail!("attempt to put a null pointer into a CString"); - } - if self.buf.is_not_null() { - fail!("CString already wraps a `*libc::c_char`"); - } - self.buf = buf; - } - - /** - * Calls a closure with a reference to the underlying `*libc::c_char`. - */ + /// Calls a closure with a reference to the underlying `*libc::c_char`. + /// + /// # Failure + /// + /// Fails if the CString is null. pub fn with_ref(&self, f: &fn(*libc::c_char) -> T) -> T { - if self.buf.is_null() { - fail!("CString already wraps a `*libc::c_char`"); - } + if self.buf.is_null() { fail!("CString is null!"); } f(self.buf) } - /** - * Calls a closure with a mutable reference to the underlying `*libc::c_char`. - */ + /// Calls a closure with a mutable reference to the underlying `*libc::c_char`. + /// + /// # Failure + /// + /// Fails if the CString is null. pub fn with_mut_ref(&mut self, f: &fn(*mut libc::c_char) -> T) -> T { - if self.buf.is_not_null() { - fail!("CString already wraps a `*libc::c_char`"); - } + if self.buf.is_null() { fail!("CString is null!"); } f(unsafe { cast::transmute(self.buf) }) } - /** - * Returns true if the CString does not wrap a `*libc::c_char`. - */ - pub fn is_empty(&self) -> bool { + /// Returns true if the CString is a null. + pub fn is_null(&self) -> bool { self.buf.is_null() } - /** - * Returns true if the CString wraps a `*libc::c_char`. - */ - pub fn is_not_empty(&self) -> bool { + /// Returns true if the CString is not null. + pub fn is_not_null(&self) -> bool { self.buf.is_not_null() } - /** - * Converts the CString into a `&[u8]` without copying. - */ + /// Returns whether or not the `CString` owns the buffer. + pub fn owns_buffer(&self) -> bool { + self.owns_buffer_ + } + + /// Converts the CString into a `&[u8]` without copying. + /// + /// # Failure + /// + /// Fails if the CString is null. pub fn as_bytes(&self) -> &'self [u8] { + if self.buf.is_null() { fail!("CString is null!"); } unsafe { let len = libc::strlen(self.buf) as uint; cast::transmute((self.buf, len + 1)) } } - /** - * Return a CString iterator. - */ + /// Return a CString iterator. fn iter(&self) -> CStringIterator<'self> { CStringIterator { ptr: self.buf, @@ -127,37 +99,28 @@ impl<'self> CString { impl Drop for CString { fn drop(&self) { - if self.buf.is_not_null() { + if self.owns_buffer_ && self.buf.is_not_null() { unsafe { libc::free(self.buf as *libc::c_void) - }; + } } } } -/** - * A generic trait for converting a value to a CString. - */ +/// A generic trait for converting a value to a CString. pub trait ToCStr { - /** - * Create a C String. - */ + /// Create a C String. fn to_c_str(&self) -> CString; } impl<'self> ToCStr for &'self str { - /** - * Create a C String from a `&str`. - */ + #[inline] fn to_c_str(&self) -> CString { self.as_bytes().to_c_str() } } impl<'self> ToCStr for &'self [u8] { - /** - * Create a C String from a `&[u8]`. - */ fn to_c_str(&self) -> CString { do self.as_imm_buf |self_buf, self_len| { unsafe { @@ -168,26 +131,22 @@ impl<'self> ToCStr for &'self [u8] { ptr::copy_memory(buf, self_buf, self_len); *ptr::mut_offset(buf, self_len as int) = 0; - CString { buf: buf as *libc::c_char } + + CString::new(buf as *libc::c_char, true) } } } } -/** - * External iterator for a CString's bytes. - * - * Use with the `std::iterator` module. - */ +/// External iterator for a CString's bytes. +/// +/// Use with the `std::iterator` module. pub struct CStringIterator<'self> { priv ptr: *libc::c_char, priv lifetime: &'self libc::c_char, // FIXME: #5922 } impl<'self> Iterator for CStringIterator<'self> { - /** - * Advance the iterator. - */ fn next(&mut self) -> Option { if self.ptr.is_null() { None @@ -226,66 +185,32 @@ mod tests { } #[test] - fn test_take() { - let mut c_str = "hello".to_c_str(); - unsafe { libc::free(c_str.take() as *libc::c_void) } - assert!(c_str.is_empty()); + fn test_is_null() { + let c_str = CString::new(ptr::null(), false); + assert!(c_str.is_null()); + assert!(!c_str.is_not_null()); } #[test] - fn test_take_and_put_back() { - let mut c_str = "hello".to_c_str(); - assert!(c_str.is_not_empty()); - - let buf = unsafe { c_str.take() }; - - assert!(c_str.is_empty()); - - c_str.put_back(buf); - - assert!(c_str.is_not_empty()); + fn test_unwrap() { + let c_str = "hello".to_c_str(); + unsafe { libc::free(c_str.unwrap() as *libc::c_void) } } #[test] - #[should_fail] - #[ignore(cfg(windows))] - fn test_take_empty_fail() { - let mut c_str = "hello".to_c_str(); - unsafe { - libc::free(c_str.take() as *libc::c_void); - c_str.take(); - } - } - - #[test] - #[should_fail] - #[ignore(cfg(windows))] - fn test_put_back_null_fail() { - let mut c_str = "hello".to_c_str(); - c_str.put_back(ptr::null()); - } - - #[test] - #[should_fail] - #[ignore(cfg(windows))] - fn test_put_back_full_fail() { - let mut c_str = "hello".to_c_str(); - c_str.put_back(0xdeadbeef as *libc::c_char); - } - - fn test_with() { + fn test_with_ref() { let c_str = "hello".to_c_str(); let len = unsafe { c_str.with_ref(|buf| libc::strlen(buf)) }; - assert!(c_str.is_not_empty()); + assert!(!c_str.is_null()); + assert!(c_str.is_not_null()); assert_eq!(len, 5); } #[test] #[should_fail] #[ignore(cfg(windows))] - fn test_with_empty_fail() { - let mut c_str = "hello".to_c_str(); - unsafe { libc::free(c_str.take() as *libc::c_void) } + fn test_with_ref_empty_fail() { + let c_str = CString::new(ptr::null(), false); c_str.with_ref(|_| ()); } }