diff --git a/src/libstd/c_str.rs b/src/libstd/c_str.rs index b7374b6f15d08..7e79ad97df083 100644 --- a/src/libstd/c_str.rs +++ b/src/libstd/c_str.rs @@ -68,6 +68,8 @@ use iter::{Iterator, range}; use libc; use kinds::marker; use ops::Drop; +use cmp::Eq; +use clone::Clone; use option::{Option, Some, None}; use ptr::RawPtr; use ptr; @@ -76,6 +78,7 @@ use str; use vec::{CloneableVector, ImmutableVector, MutableVector}; use vec; use unstable::intrinsics; +use rt::global_heap::malloc_raw; /// Resolution options for the `null_byte` condition pub enum NullByteResolution { @@ -99,6 +102,36 @@ pub struct CString { priv owns_buffer_: bool, } +impl Clone for CString { + /// Clone this CString into a new, uniquely owned CString. For safety + /// reasons, this is always a deep clone, rather than the usual shallow + /// clone. + fn clone(&self) -> CString { + if self.buf.is_null() { + CString { buf: self.buf, owns_buffer_: self.owns_buffer_ } + } else { + let len = self.len() + 1; + let buf = unsafe { malloc_raw(len) } as *mut libc::c_char; + unsafe { ptr::copy_nonoverlapping_memory(buf, self.buf, len); } + CString { buf: buf as *libc::c_char, owns_buffer_: true } + } + } +} + +impl Eq for CString { + fn eq(&self, other: &CString) -> bool { + if self.buf as uint == other.buf as uint { + true + } else if self.buf.is_null() || other.buf.is_null() { + false + } else { + unsafe { + libc::strcmp(self.buf, other.buf) == 0 + } + } + } +} + impl CString { /// Create a C String from a pointer. pub unsafe fn new(buf: *libc::c_char, owns_buffer: bool) -> CString { @@ -287,10 +320,7 @@ impl<'a> ToCStr for &'a [u8] { unsafe fn to_c_str_unchecked(&self) -> CString { let self_len = self.len(); - let buf = libc::malloc(self_len as libc::size_t + 1) as *mut u8; - if buf.is_null() { - fail!("failed to allocate memory!"); - } + let buf = malloc_raw(self_len + 1); ptr::copy_memory(buf, self.as_ptr(), self_len); *ptr::mut_offset(buf, self_len as int) = 0; @@ -598,6 +628,44 @@ mod tests { let c_str = unsafe { CString::new(ptr::null(), false) }; c_str.iter(); } + + #[test] + fn test_clone() { + let a = "hello".to_c_str(); + let b = a.clone(); + assert!(a == b); + } + + #[test] + fn test_clone_noleak() { + fn foo(f: |c: &CString|) { + let s = ~"test"; + let c = s.to_c_str(); + // give the closure a non-owned CString + let mut c_ = c.with_ref(|c| unsafe { CString::new(c, false) } ); + f(&c_); + // muck with the buffer for later printing + c_.with_mut_ref(|c| unsafe { *c = 'X' as libc::c_char } ); + } + + let mut c_: Option = None; + foo(|c| { + c_ = Some(c.clone()); + c.clone(); + // force a copy, reading the memory + c.as_bytes().to_owned(); + }); + let c_ = c_.unwrap(); + // force a copy, reading the memory + c_.as_bytes().to_owned(); + } + + #[test] + fn test_clone_eq_null() { + let x = unsafe { CString::new(ptr::null(), false) }; + let y = x.clone(); + assert!(x == y); + } } #[cfg(test)]