Skip to content

impl Clone for CString #11840

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 72 additions & 4 deletions src/libstd/c_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<CString> = 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)]
Expand Down