diff --git a/src/libstd/c_str.rs b/src/libstd/c_str.rs index 51b70a07be8c6..a2842efbf8a04 100644 --- a/src/libstd/c_str.rs +++ b/src/libstd/c_str.rs @@ -15,6 +15,7 @@ use ops::Drop; use option::{Option, Some, None}; use ptr::RawPtr; use ptr; +use str; use str::StrSlice; use vec::{ImmutableVector, CopyableVector}; use container::Container; @@ -97,15 +98,25 @@ impl CString { /// # Failure /// /// Fails if the CString is null. + #[inline] pub fn as_bytes<'a>(&'a self) -> &'a [u8] { - #[fixed_stack_segment]; #[inline(never)]; if self.buf.is_null() { fail!("CString is null!"); } unsafe { - let len = libc::strlen(self.buf) as uint; + let len = ptr::position(self.buf, |c| *c == 0); cast::transmute((self.buf, len + 1)) } } + /// Converts the CString into a `&str` without copying. + /// Returns None if the CString is not UTF-8 or is null. + #[inline] + pub fn as_str<'a>(&'a self) -> Option<&'a str> { + if self.buf.is_null() { return None; } + let buf = self.as_bytes(); + let buf = buf.slice_to(buf.len()-1); // chop off the trailing NUL + str::from_utf8_slice_opt(buf) + } + /// Return a CString iterator. pub fn iter<'a>(&'a self) -> CStringIterator<'a> { CStringIterator { @@ -238,7 +249,7 @@ mod tests { use option::{Some, None}; #[test] - fn test_to_c_str() { + fn test_str_to_c_str() { do "".to_c_str().with_ref |buf| { unsafe { assert_eq!(*ptr::offset(buf, 0), 0); @@ -257,6 +268,37 @@ mod tests { } } + #[test] + fn test_vec_to_c_str() { + let b: &[u8] = []; + do b.to_c_str().with_ref |buf| { + unsafe { + assert_eq!(*ptr::offset(buf, 0), 0); + } + } + + do bytes!("hello").to_c_str().with_ref |buf| { + unsafe { + assert_eq!(*ptr::offset(buf, 0), 'h' as libc::c_char); + assert_eq!(*ptr::offset(buf, 1), 'e' as libc::c_char); + assert_eq!(*ptr::offset(buf, 2), 'l' as libc::c_char); + assert_eq!(*ptr::offset(buf, 3), 'l' as libc::c_char); + assert_eq!(*ptr::offset(buf, 4), 'o' as libc::c_char); + assert_eq!(*ptr::offset(buf, 5), 0); + } + } + + do bytes!("foo", 0xff).to_c_str().with_ref |buf| { + unsafe { + assert_eq!(*ptr::offset(buf, 0), 'f' as libc::c_char); + assert_eq!(*ptr::offset(buf, 1), 'o' as libc::c_char); + assert_eq!(*ptr::offset(buf, 2), 'o' as libc::c_char); + assert_eq!(*ptr::offset(buf, 3), 0xff); + assert_eq!(*ptr::offset(buf, 4), 0); + } + } + } + #[test] fn test_is_null() { let c_str = unsafe { CString::new(ptr::null(), false) }; @@ -349,4 +391,33 @@ mod tests { } } } + + #[test] + fn test_as_bytes() { + let c_str = "hello".to_c_str(); + assert_eq!(c_str.as_bytes(), bytes!("hello", 0)); + let c_str = "".to_c_str(); + assert_eq!(c_str.as_bytes(), bytes!(0)); + let c_str = bytes!("foo", 0xff).to_c_str(); + assert_eq!(c_str.as_bytes(), bytes!("foo", 0xff, 0)); + } + + #[test] + #[should_fail] + fn test_as_bytes_fail() { + let c_str = unsafe { CString::new(ptr::null(), false) }; + c_str.as_bytes(); + } + + #[test] + fn test_as_str() { + let c_str = "hello".to_c_str(); + assert_eq!(c_str.as_str(), Some("hello")); + let c_str = "".to_c_str(); + assert_eq!(c_str.as_str(), Some("")); + let c_str = bytes!("foo", 0xff).to_c_str(); + assert_eq!(c_str.as_str(), None); + let c_str = unsafe { CString::new(ptr::null(), false) }; + assert_eq!(c_str.as_str(), None); + } }