diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2014-11-25 13:28:35 -0800 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2015-01-05 08:00:13 -0800 |
| commit | ec7a50d20dff416d9fec837a6492dfe244f5f3ab (patch) | |
| tree | 559c2e05abb4f51d6309a1af3f4e1953a6627e3d /src/libstd | |
| parent | 1f732ef53d54ccfc3e7728390ffbcea8a696ecee (diff) | |
| download | rust-ec7a50d20dff416d9fec837a6492dfe244f5f3ab.tar.gz rust-ec7a50d20dff416d9fec837a6492dfe244f5f3ab.zip | |
std: Redesign c_str and c_vec
This commit is an implementation of [RFC 494][rfc] which removes the entire `std::c_vec` module and redesigns the `std::c_str` module as `std::ffi`. [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0494-c_str-and-c_vec-stability.md The interface of the new `CString` is outlined in the linked RFC, the primary changes being: * The `ToCStr` trait is gone, meaning the `with_c_str` and `to_c_str` methods are now gone. These two methods are replaced with a `CString::from_slice` method. * The `CString` type is now just a wrapper around `Vec<u8>` with a static guarantee that there is a trailing nul byte with no internal nul bytes. This means that `CString` now implements `Deref<Target = [c_char]>`, which is where it gains most of its methods from. A few helper methods are added to acquire a slice of `u8` instead of `c_char`, as well as including a slice with the trailing nul byte if necessary. * All usage of non-owned `CString` values is now done via two functions inside of `std::ffi`, called `c_str_to_bytes` and `c_str_to_bytes_with_nul`. These functions are now the one method used to convert a `*const c_char` to a Rust slice of `u8`. Many more details, including newly deprecated methods, can be found linked in the RFC. This is a: [breaking-change] Closes #20444
Diffstat (limited to 'src/libstd')
30 files changed, 463 insertions, 1379 deletions
diff --git a/src/libstd/c_str.rs b/src/libstd/c_str.rs deleted file mode 100644 index 9c96a9cac78..00000000000 --- a/src/libstd/c_str.rs +++ /dev/null @@ -1,857 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! C-string manipulation and management -//! -//! This modules provides the basic methods for creating and manipulating -//! null-terminated strings for use with FFI calls (back to C). Most C APIs require -//! that the string being passed to them is null-terminated, and by default rust's -//! string types are *not* null terminated. -//! -//! The other problem with translating Rust strings to C strings is that Rust -//! strings can validly contain a null-byte in the middle of the string (0 is a -//! valid Unicode codepoint). This means that not all Rust strings can actually be -//! translated to C strings. -//! -//! # Creation of a C string -//! -//! A C string is managed through the `CString` type defined in this module. It -//! "owns" the internal buffer of characters and will automatically deallocate the -//! buffer when the string is dropped. The `ToCStr` trait is implemented for `&str` -//! and `&[u8]`, but the conversions can fail due to some of the limitations -//! explained above. -//! -//! This also means that currently whenever a C string is created, an allocation -//! must be performed to place the data elsewhere (the lifetime of the C string is -//! not tied to the lifetime of the original string/data buffer). If C strings are -//! heavily used in applications, then caching may be advisable to prevent -//! unnecessary amounts of allocations. -//! -//! Be carefull to remember that the memory is managed by C allocator API and not -//! by Rust allocator API. -//! That means that the CString pointers should be freed with C allocator API -//! if you intend to do that on your own, as the behaviour if you free them with -//! Rust's allocator API is not well defined -//! -//! An example of creating and using a C string would be: -//! -//! ```rust -//! extern crate libc; -//! -//! use std::c_str::ToCStr; -//! -//! extern { -//! fn puts(s: *const libc::c_char); -//! } -//! -//! fn main() { -//! let my_string = "Hello, world!"; -//! -//! // Allocate the C string with an explicit local that owns the string. The -//! // `c_buffer` pointer will be deallocated when `my_c_string` goes out of scope. -//! let my_c_string = my_string.to_c_str(); -//! unsafe { -//! puts(my_c_string.as_ptr()); -//! } -//! -//! // Don't save/return the pointer to the C string, the `c_buffer` will be -//! // deallocated when this block returns! -//! my_string.with_c_str(|c_buffer| { -//! unsafe { puts(c_buffer); } -//! }); -//! } -//! ``` - -use core::prelude::*; -use libc; - -use cmp::Ordering; -use fmt; -use hash; -use mem; -use ptr; -use slice::{self, IntSliceExt}; -use str; -use string::String; -use core::kinds::marker; - -/// 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. -#[allow(missing_copy_implementations)] -pub struct CString { - buf: *const libc::c_char, - owns_buffer_: bool, -} - -unsafe impl Send for CString { } -unsafe impl Sync for CString { } - -impl Clone for CString { - /// Clone this CString into a new, uniquely owned CString. For safety - /// reasons, this is always a deep clone with the memory allocated - /// with C's allocator API, rather than the usual shallow clone. - fn clone(&self) -> CString { - let len = self.len() + 1; - let buf = unsafe { libc::malloc(len as libc::size_t) } as *mut libc::c_char; - if buf.is_null() { ::alloc::oom() } - unsafe { ptr::copy_nonoverlapping_memory(buf, self.buf, len); } - CString { buf: buf as *const libc::c_char, owns_buffer_: true } - } -} - -impl PartialEq for CString { - fn eq(&self, other: &CString) -> bool { - // Check if the two strings share the same buffer - if self.buf as uint == other.buf as uint { - true - } else { - unsafe { - libc::strcmp(self.buf, other.buf) == 0 - } - } - } -} - -impl PartialOrd for CString { - #[inline] - fn partial_cmp(&self, other: &CString) -> Option<Ordering> { - self.as_bytes().partial_cmp(other.as_bytes()) - } -} - -impl Eq for CString {} - -impl<S: hash::Writer> hash::Hash<S> for CString { - #[inline] - fn hash(&self, state: &mut S) { - self.as_bytes().hash(state) - } -} - -impl CString { - /// Create a C String from a pointer, with memory managed by C's allocator - /// API, so avoid calling it with a pointer to memory managed by Rust's - /// allocator API, as the behaviour would not be well defined. - /// - ///# Panics - /// - /// Panics if `buf` is null - pub unsafe fn new(buf: *const libc::c_char, owns_buffer: bool) -> CString { - assert!(!buf.is_null()); - CString { buf: buf, owns_buffer_: owns_buffer } - } - - /// Return a pointer to the NUL-terminated string data. - /// - /// `.as_ptr` returns an internal pointer into the `CString`, and - /// may be invalidated when the `CString` falls out of scope (the - /// destructor will run, freeing the allocation if there is - /// one). - /// - /// ```rust - /// use std::c_str::ToCStr; - /// - /// let foo = "some string"; - /// - /// // right - /// let x = foo.to_c_str(); - /// let p = x.as_ptr(); - /// - /// // wrong (the CString will be freed, invalidating `p`) - /// let p = foo.to_c_str().as_ptr(); - /// ``` - /// - /// # Example - /// - /// ```rust - /// extern crate libc; - /// - /// use std::c_str::ToCStr; - /// - /// fn main() { - /// let c_str = "foo bar".to_c_str(); - /// unsafe { - /// libc::puts(c_str.as_ptr()); - /// } - /// } - /// ``` - pub fn as_ptr(&self) -> *const libc::c_char { - self.buf - } - - /// Return a mutable pointer to the NUL-terminated string data. - /// - /// `.as_mut_ptr` returns an internal pointer into the `CString`, and - /// may be invalidated when the `CString` falls out of scope (the - /// destructor will run, freeing the allocation if there is - /// one). - /// - /// ```rust - /// use std::c_str::ToCStr; - /// - /// let foo = "some string"; - /// - /// // right - /// let mut x = foo.to_c_str(); - /// let p = x.as_mut_ptr(); - /// - /// // wrong (the CString will be freed, invalidating `p`) - /// let p = foo.to_c_str().as_mut_ptr(); - /// ``` - pub fn as_mut_ptr(&mut self) -> *mut libc::c_char { - self.buf as *mut _ - } - - /// 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. - /// Includes the terminating NUL byte. - #[inline] - pub fn as_bytes<'a>(&'a self) -> &'a [u8] { - unsafe { - slice::from_raw_buf(&self.buf, self.len() + 1).as_unsigned() - } - } - - /// Converts the CString into a `&[u8]` without copying. - /// Does not include the terminating NUL byte. - #[inline] - pub fn as_bytes_no_nul<'a>(&'a self) -> &'a [u8] { - unsafe { - slice::from_raw_buf(&self.buf, self.len()).as_unsigned() - } - } - - /// Converts the CString into a `&str` without copying. - /// Returns None if the CString is not UTF-8. - #[inline] - pub fn as_str<'a>(&'a self) -> Option<&'a str> { - let buf = self.as_bytes_no_nul(); - str::from_utf8(buf).ok() - } - - /// Return a CString iterator. - pub fn iter<'a>(&'a self) -> CChars<'a> { - CChars { - ptr: self.buf, - marker: marker::ContravariantLifetime, - } - } - - /// Unwraps the wrapped `*libc::c_char` from the `CString` wrapper. - /// - /// Any ownership of the buffer by the `CString` wrapper is - /// forgotten, meaning that the backing allocation of this - /// `CString` is not automatically freed if it owns the - /// allocation. In this case, a user of `.unwrap()` should ensure - /// the allocation is freed, to avoid leaking memory. You should - /// use libc's memory allocator in this case. - /// - /// Prefer `.as_ptr()` when just retrieving a pointer to the - /// string data, as that does not relinquish ownership. - pub unsafe fn into_inner(mut self) -> *const libc::c_char { - self.owns_buffer_ = false; - self.buf - } - - /// Return the number of bytes in the CString (not including the NUL - /// terminator). - #[inline] - pub fn len(&self) -> uint { - unsafe { libc::strlen(self.buf) as uint } - } - - /// Returns if there are no bytes in this string - #[inline] - pub fn is_empty(&self) -> bool { self.len() == 0 } -} - -impl Drop for CString { - fn drop(&mut self) { - if self.owns_buffer_ { - unsafe { - libc::free(self.buf as *mut libc::c_void) - } - } - } -} - -impl fmt::Show for CString { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - String::from_utf8_lossy(self.as_bytes_no_nul()).fmt(f) - } -} - -/// A generic trait for converting a value to a CString. -pub trait ToCStr for Sized? { - /// Copy the receiver into a CString. - /// - /// # Panics - /// - /// Panics the task if the receiver has an interior null. - fn to_c_str(&self) -> CString; - - /// Unsafe variant of `to_c_str()` that doesn't check for nulls. - unsafe fn to_c_str_unchecked(&self) -> CString; - - /// Work with a temporary CString constructed from the receiver. - /// The provided `*libc::c_char` will be freed immediately upon return. - /// - /// # Example - /// - /// ```rust - /// extern crate libc; - /// - /// use std::c_str::ToCStr; - /// - /// fn main() { - /// let s = "PATH".with_c_str(|path| unsafe { - /// libc::getenv(path) - /// }); - /// } - /// ``` - /// - /// # Panics - /// - /// Panics the task if the receiver has an interior null. - #[inline] - fn with_c_str<T, F>(&self, f: F) -> T where - F: FnOnce(*const libc::c_char) -> T, - { - let c_str = self.to_c_str(); - f(c_str.as_ptr()) - } - - /// Unsafe variant of `with_c_str()` that doesn't check for nulls. - #[inline] - unsafe fn with_c_str_unchecked<T, F>(&self, f: F) -> T where - F: FnOnce(*const libc::c_char) -> T, - { - let c_str = self.to_c_str_unchecked(); - f(c_str.as_ptr()) - } -} - -impl ToCStr for str { - #[inline] - fn to_c_str(&self) -> CString { - self.as_bytes().to_c_str() - } - - #[inline] - unsafe fn to_c_str_unchecked(&self) -> CString { - self.as_bytes().to_c_str_unchecked() - } - - #[inline] - fn with_c_str<T, F>(&self, f: F) -> T where - F: FnOnce(*const libc::c_char) -> T, - { - self.as_bytes().with_c_str(f) - } - - #[inline] - unsafe fn with_c_str_unchecked<T, F>(&self, f: F) -> T where - F: FnOnce(*const libc::c_char) -> T, - { - self.as_bytes().with_c_str_unchecked(f) - } -} - -impl ToCStr for String { - #[inline] - fn to_c_str(&self) -> CString { - self.as_bytes().to_c_str() - } - - #[inline] - unsafe fn to_c_str_unchecked(&self) -> CString { - self.as_bytes().to_c_str_unchecked() - } - - #[inline] - fn with_c_str<T, F>(&self, f: F) -> T where - F: FnOnce(*const libc::c_char) -> T, - { - self.as_bytes().with_c_str(f) - } - - #[inline] - unsafe fn with_c_str_unchecked<T, F>(&self, f: F) -> T where - F: FnOnce(*const libc::c_char) -> T, - { - self.as_bytes().with_c_str_unchecked(f) - } -} - -// The length of the stack allocated buffer for `vec.with_c_str()` -const BUF_LEN: uint = 128; - -impl ToCStr for [u8] { - fn to_c_str(&self) -> CString { - let mut cs = unsafe { self.to_c_str_unchecked() }; - check_for_null(self, cs.as_mut_ptr()); - cs - } - - 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() { ::alloc::oom() } - - ptr::copy_memory(buf, self.as_ptr(), self_len); - *buf.offset(self_len as int) = 0; - - CString::new(buf as *const libc::c_char, true) - } - - fn with_c_str<T, F>(&self, f: F) -> T where - F: FnOnce(*const libc::c_char) -> T, - { - unsafe { with_c_str(self, true, f) } - } - - unsafe fn with_c_str_unchecked<T, F>(&self, f: F) -> T where - F: FnOnce(*const libc::c_char) -> T, - { - with_c_str(self, false, f) - } -} - -impl<'a, Sized? T: ToCStr> ToCStr for &'a T { - #[inline] - fn to_c_str(&self) -> CString { - (**self).to_c_str() - } - - #[inline] - unsafe fn to_c_str_unchecked(&self) -> CString { - (**self).to_c_str_unchecked() - } - - #[inline] - fn with_c_str<T, F>(&self, f: F) -> T where - F: FnOnce(*const libc::c_char) -> T, - { - (**self).with_c_str(f) - } - - #[inline] - unsafe fn with_c_str_unchecked<T, F>(&self, f: F) -> T where - F: FnOnce(*const libc::c_char) -> T, - { - (**self).with_c_str_unchecked(f) - } -} - -// Unsafe function that handles possibly copying the &[u8] into a stack array. -unsafe fn with_c_str<T, F>(v: &[u8], checked: bool, f: F) -> T where - F: FnOnce(*const libc::c_char) -> T, -{ - let c_str = if v.len() < BUF_LEN { - let mut buf: [u8; BUF_LEN] = mem::uninitialized(); - slice::bytes::copy_memory(&mut buf, v); - buf[v.len()] = 0; - - let buf = buf.as_mut_ptr(); - if checked { - check_for_null(v, buf as *mut libc::c_char); - } - - return f(buf as *const libc::c_char) - } else if checked { - v.to_c_str() - } else { - v.to_c_str_unchecked() - }; - - f(c_str.as_ptr()) -} - -#[inline] -fn check_for_null(v: &[u8], buf: *mut libc::c_char) { - for i in range(0, v.len()) { - unsafe { - let p = buf.offset(i as int); - assert!(*p != 0); - } - } -} - -/// External iterator for a CString's bytes. -/// -/// Use with the `std::iter` module. -#[allow(raw_pointer_deriving)] -#[derive(Clone)] -pub struct CChars<'a> { - ptr: *const libc::c_char, - marker: marker::ContravariantLifetime<'a>, -} - -impl<'a> Iterator for CChars<'a> { - type Item = libc::c_char; - - fn next(&mut self) -> Option<libc::c_char> { - let ch = unsafe { *self.ptr }; - if ch == 0 { - None - } else { - self.ptr = unsafe { self.ptr.offset(1) }; - Some(ch) - } - } -} - -/// Parses a C "multistring", eg windows env values or -/// the req->ptr result in a uv_fs_readdir() call. -/// -/// Optionally, a `count` can be passed in, limiting the -/// parsing to only being done `count`-times. -/// -/// The specified closure is invoked with each string that -/// is found, and the number of strings found is returned. -pub unsafe fn from_c_multistring<F>(buf: *const libc::c_char, - count: Option<uint>, - mut f: F) - -> uint where - F: FnMut(&CString), -{ - - let mut curr_ptr: uint = buf as uint; - let mut ctr = 0; - let (limited_count, limit) = match count { - Some(limit) => (true, limit), - None => (false, 0) - }; - while ((limited_count && ctr < limit) || !limited_count) - && *(curr_ptr as *const libc::c_char) != 0 as libc::c_char { - let cstr = CString::new(curr_ptr as *const libc::c_char, false); - f(&cstr); - curr_ptr += cstr.len() + 1; - ctr += 1; - } - return ctr; -} - -#[cfg(test)] -mod tests { - use prelude::v1::*; - use super::*; - use ptr; - use thread::Thread; - use libc; - - #[test] - fn test_str_multistring_parsing() { - unsafe { - let input = b"zero\0one\0\0"; - let ptr = input.as_ptr(); - let expected = ["zero", "one"]; - let mut it = expected.iter(); - let result = from_c_multistring(ptr as *const libc::c_char, None, |c| { - let cbytes = c.as_bytes_no_nul(); - assert_eq!(cbytes, it.next().unwrap().as_bytes()); - }); - assert_eq!(result, 2); - assert!(it.next().is_none()); - } - } - - #[test] - fn test_str_to_c_str() { - let c_str = "".to_c_str(); - unsafe { - assert_eq!(*c_str.as_ptr().offset(0), 0); - } - - let c_str = "hello".to_c_str(); - let buf = c_str.as_ptr(); - unsafe { - assert_eq!(*buf.offset(0), 'h' as libc::c_char); - assert_eq!(*buf.offset(1), 'e' as libc::c_char); - assert_eq!(*buf.offset(2), 'l' as libc::c_char); - assert_eq!(*buf.offset(3), 'l' as libc::c_char); - assert_eq!(*buf.offset(4), 'o' as libc::c_char); - assert_eq!(*buf.offset(5), 0); - } - } - - #[test] - fn test_vec_to_c_str() { - let b: &[u8] = &[]; - let c_str = b.to_c_str(); - unsafe { - assert_eq!(*c_str.as_ptr().offset(0), 0); - } - - let c_str = b"hello".to_c_str(); - let buf = c_str.as_ptr(); - unsafe { - assert_eq!(*buf.offset(0), 'h' as libc::c_char); - assert_eq!(*buf.offset(1), 'e' as libc::c_char); - assert_eq!(*buf.offset(2), 'l' as libc::c_char); - assert_eq!(*buf.offset(3), 'l' as libc::c_char); - assert_eq!(*buf.offset(4), 'o' as libc::c_char); - assert_eq!(*buf.offset(5), 0); - } - - let c_str = b"foo\xFF".to_c_str(); - let buf = c_str.as_ptr(); - unsafe { - assert_eq!(*buf.offset(0), 'f' as libc::c_char); - assert_eq!(*buf.offset(1), 'o' as libc::c_char); - assert_eq!(*buf.offset(2), 'o' as libc::c_char); - assert_eq!(*buf.offset(3), 0xffu8 as libc::c_char); - assert_eq!(*buf.offset(4), 0); - } - } - - #[test] - fn test_unwrap() { - let c_str = "hello".to_c_str(); - unsafe { libc::free(c_str.into_inner() as *mut libc::c_void) } - } - - #[test] - fn test_as_ptr() { - let c_str = "hello".to_c_str(); - let len = unsafe { libc::strlen(c_str.as_ptr()) }; - assert_eq!(len, 5); - } - - #[test] - fn test_iterator() { - let c_str = "".to_c_str(); - let mut iter = c_str.iter(); - assert_eq!(iter.next(), None); - - let c_str = "hello".to_c_str(); - let mut iter = c_str.iter(); - assert_eq!(iter.next(), Some('h' as libc::c_char)); - assert_eq!(iter.next(), Some('e' as libc::c_char)); - assert_eq!(iter.next(), Some('l' as libc::c_char)); - assert_eq!(iter.next(), Some('l' as libc::c_char)); - assert_eq!(iter.next(), Some('o' as libc::c_char)); - assert_eq!(iter.next(), None); - } - - #[test] - fn test_to_c_str_fail() { - assert!(Thread::spawn(move|| { "he\x00llo".to_c_str() }).join().is_err()); - } - - #[test] - fn test_to_c_str_unchecked() { - unsafe { - let c_string = "he\x00llo".to_c_str_unchecked(); - let buf = c_string.as_ptr(); - assert_eq!(*buf.offset(0), 'h' as libc::c_char); - assert_eq!(*buf.offset(1), 'e' as libc::c_char); - assert_eq!(*buf.offset(2), 0); - assert_eq!(*buf.offset(3), 'l' as libc::c_char); - assert_eq!(*buf.offset(4), 'l' as libc::c_char); - assert_eq!(*buf.offset(5), 'o' as libc::c_char); - assert_eq!(*buf.offset(6), 0); - } - } - - #[test] - fn test_as_bytes() { - let c_str = "hello".to_c_str(); - assert_eq!(c_str.as_bytes(), b"hello\0"); - let c_str = "".to_c_str(); - assert_eq!(c_str.as_bytes(), b"\0"); - let c_str = b"foo\xFF".to_c_str(); - assert_eq!(c_str.as_bytes(), b"foo\xFF\0"); - } - - #[test] - fn test_as_bytes_no_nul() { - let c_str = "hello".to_c_str(); - assert_eq!(c_str.as_bytes_no_nul(), b"hello"); - let c_str = "".to_c_str(); - let exp: &[u8] = &[]; - assert_eq!(c_str.as_bytes_no_nul(), exp); - let c_str = b"foo\xFF".to_c_str(); - assert_eq!(c_str.as_bytes_no_nul(), b"foo\xFF"); - } - - #[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 = b"foo\xFF".to_c_str(); - assert_eq!(c_str.as_str(), None); - } - - #[test] - #[should_fail] - fn test_new_fail() { - let _c_str = unsafe { CString::new(ptr::null(), false) }; - } - - #[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>(f: F) where F: FnOnce(&CString) { - let s = "test".to_string(); - let c = s.to_c_str(); - // give the closure a non-owned CString - let mut c_ = unsafe { CString::new(c.as_ptr(), false) }; - f(&c_); - // muck with the buffer for later printing - unsafe { *c_.as_mut_ptr() = '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_vec(); - }); - let c_ = c_.unwrap(); - // force a copy, reading the memory - c_.as_bytes().to_vec(); - } -} - -#[cfg(test)] -mod bench { - extern crate test; - - use prelude::v1::*; - use self::test::Bencher; - use libc; - use c_str::ToCStr; - - #[inline] - fn check(s: &str, c_str: *const libc::c_char) { - let s_buf = s.as_ptr(); - for i in range(0, s.len()) { - unsafe { - assert_eq!( - *s_buf.offset(i as int) as libc::c_char, - *c_str.offset(i as int)); - } - } - } - - static S_SHORT: &'static str = "Mary"; - static S_MEDIUM: &'static str = "Mary had a little lamb"; - static S_LONG: &'static str = "\ - Mary had a little lamb, Little lamb - Mary had a little lamb, Little lamb - Mary had a little lamb, Little lamb - Mary had a little lamb, Little lamb - Mary had a little lamb, Little lamb - Mary had a little lamb, Little lamb"; - - fn bench_to_string(b: &mut Bencher, s: &str) { - b.iter(|| { - let c_str = s.to_c_str(); - check(s, c_str.as_ptr()); - }) - } - - #[bench] - fn bench_to_c_str_short(b: &mut Bencher) { - bench_to_string(b, S_SHORT) - } - - #[bench] - fn bench_to_c_str_medium(b: &mut Bencher) { - bench_to_string(b, S_MEDIUM) - } - - #[bench] - fn bench_to_c_str_long(b: &mut Bencher) { - bench_to_string(b, S_LONG) - } - - fn bench_to_c_str_unchecked(b: &mut Bencher, s: &str) { - b.iter(|| { - let c_str = unsafe { s.to_c_str_unchecked() }; - check(s, c_str.as_ptr()) - }) - } - - #[bench] - fn bench_to_c_str_unchecked_short(b: &mut Bencher) { - bench_to_c_str_unchecked(b, S_SHORT) - } - - #[bench] - fn bench_to_c_str_unchecked_medium(b: &mut Bencher) { - bench_to_c_str_unchecked(b, S_MEDIUM) - } - - #[bench] - fn bench_to_c_str_unchecked_long(b: &mut Bencher) { - bench_to_c_str_unchecked(b, S_LONG) - } - - fn bench_with_c_str(b: &mut Bencher, s: &str) { - b.iter(|| { - s.with_c_str(|c_str_buf| check(s, c_str_buf)) - }) - } - - #[bench] - fn bench_with_c_str_short(b: &mut Bencher) { - bench_with_c_str(b, S_SHORT) - } - - #[bench] - fn bench_with_c_str_medium(b: &mut Bencher) { - bench_with_c_str(b, S_MEDIUM) - } - - #[bench] - fn bench_with_c_str_long(b: &mut Bencher) { - bench_with_c_str(b, S_LONG) - } - - fn bench_with_c_str_unchecked(b: &mut Bencher, s: &str) { - b.iter(|| { - unsafe { - s.with_c_str_unchecked(|c_str_buf| check(s, c_str_buf)) - } - }) - } - - #[bench] - fn bench_with_c_str_unchecked_short(b: &mut Bencher) { - bench_with_c_str_unchecked(b, S_SHORT) - } - - #[bench] - fn bench_with_c_str_unchecked_medium(b: &mut Bencher) { - bench_with_c_str_unchecked(b, S_MEDIUM) - } - - #[bench] - fn bench_with_c_str_unchecked_long(b: &mut Bencher) { - bench_with_c_str_unchecked(b, S_LONG) - } -} diff --git a/src/libstd/c_vec.rs b/src/libstd/c_vec.rs deleted file mode 100644 index 4a20208f31a..00000000000 --- a/src/libstd/c_vec.rs +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Library to interface with chunks of memory allocated in C. -//! -//! It is often desirable to safely interface with memory allocated from C, -//! encapsulating the unsafety into allocation and destruction time. Indeed, -//! allocating memory externally is currently the only way to give Rust shared -//! mut state with C programs that keep their own references; vectors are -//! unsuitable because they could be reallocated or moved at any time, and -//! importing C memory into a vector takes a one-time snapshot of the memory. -//! -//! This module simplifies the usage of such external blocks of memory. Memory -//! is encapsulated into an opaque object after creation; the lifecycle of the -//! memory can be optionally managed by Rust, if an appropriate destructor -//! closure is provided. Safety is ensured by bounds-checking accesses, which -//! are marshalled through get and set functions. -//! -//! There are three unsafe functions: the two constructors, and the -//! unwrap method. The constructors are unsafe for the -//! obvious reason (they act on a pointer that cannot be checked inside the -//! method), but `unwrap()` is somewhat more subtle in its unsafety. -//! It returns the contained pointer, but at the same time destroys the CVec -//! without running its destructor. This can be used to pass memory back to -//! C, but care must be taken that the ownership of underlying resources are -//! handled correctly, i.e. that allocated memory is eventually freed -//! if necessary. - -#![experimental] - -use kinds::Send; -use mem; -use ops::{Drop, FnOnce}; -use option::Option; -use option::Option::{Some, None}; -use ptr::PtrExt; -use ptr; -use raw; -use slice::AsSlice; -use thunk::{Thunk}; - -/// The type representing a foreign chunk of memory -pub struct CVec<T> { - base: *mut T, - len: uint, - dtor: Option<Thunk>, -} - -#[unsafe_destructor] -impl<T> Drop for CVec<T> { - fn drop(&mut self) { - match self.dtor.take() { - None => (), - Some(f) => f.invoke(()) - } - } -} - -impl<T> CVec<T> { - /// Create a `CVec` from a raw pointer to a buffer with a given length. - /// - /// Panics if the given pointer is null. The returned vector will not attempt - /// to deallocate the vector when dropped. - /// - /// # Arguments - /// - /// * base - A raw pointer to a buffer - /// * len - The number of elements in the buffer - pub unsafe fn new(base: *mut T, len: uint) -> CVec<T> { - assert!(base != ptr::null_mut()); - CVec { - base: base, - len: len, - dtor: None, - } - } - - /// Create a `CVec` from a foreign buffer, with a given length, - /// and a function to run upon destruction. - /// - /// Panics if the given pointer is null. - /// - /// # Arguments - /// - /// * base - A foreign pointer to a buffer - /// * len - The number of elements in the buffer - /// * dtor - A fn to run when the value is destructed, useful - /// for freeing the buffer, etc. - pub unsafe fn new_with_dtor<F>(base: *mut T, - len: uint, - dtor: F) - -> CVec<T> - where F : FnOnce(), F : Send - { - assert!(base != ptr::null_mut()); - let dtor: Thunk = Thunk::new(dtor); - CVec { - base: base, - len: len, - dtor: Some(dtor) - } - } - - /// View the stored data as a mutable slice. - pub fn as_mut_slice<'a>(&'a mut self) -> &'a mut [T] { - unsafe { - mem::transmute(raw::Slice { data: self.base as *const T, len: self.len }) - } - } - - /// Retrieves an element at a given index, returning `None` if the requested - /// index is greater than the length of the vector. - pub fn get<'a>(&'a self, ofs: uint) -> Option<&'a T> { - if ofs < self.len { - Some(unsafe { &*self.base.offset(ofs as int) }) - } else { - None - } - } - - /// Retrieves a mutable element at a given index, returning `None` if the - /// requested index is greater than the length of the vector. - pub fn get_mut<'a>(&'a mut self, ofs: uint) -> Option<&'a mut T> { - if ofs < self.len { - Some(unsafe { &mut *self.base.offset(ofs as int) }) - } else { - None - } - } - - /// Unwrap the pointer without running the destructor - /// - /// This method retrieves the underlying pointer, and in the process - /// destroys the CVec but without running the destructor. A use case - /// would be transferring ownership of the buffer to a C function, as - /// in this case you would not want to run the destructor. - /// - /// Note that if you want to access the underlying pointer without - /// cancelling the destructor, you can simply call `transmute` on the return - /// value of `get(0)`. - pub unsafe fn into_inner(mut self) -> *mut T { - self.dtor = None; - self.base - } - - /// Returns the number of items in this vector. - pub fn len(&self) -> uint { self.len } - - /// Returns whether this vector is empty. - pub fn is_empty(&self) -> bool { self.len() == 0 } -} - -impl<T> AsSlice<T> for CVec<T> { - /// View the stored data as a slice. - fn as_slice<'a>(&'a self) -> &'a [T] { - unsafe { - mem::transmute(raw::Slice { data: self.base as *const T, len: self.len }) - } - } -} - -#[cfg(test)] -mod tests { - use prelude::v1::*; - - use super::CVec; - use libc; - use ptr; - - fn malloc(n: uint) -> CVec<u8> { - unsafe { - let mem = ptr::Unique(libc::malloc(n as libc::size_t)); - if mem.0.is_null() { ::alloc::oom() } - - CVec::new_with_dtor(mem.0 as *mut u8, - n, - move|| { libc::free(mem.0 as *mut libc::c_void); }) - } - } - - #[test] - fn test_basic() { - let mut cv = malloc(16); - - *cv.get_mut(3).unwrap() = 8; - *cv.get_mut(4).unwrap() = 9; - assert_eq!(*cv.get(3).unwrap(), 8); - assert_eq!(*cv.get(4).unwrap(), 9); - assert_eq!(cv.len(), 16); - } - - #[test] - #[should_fail] - fn test_panic_at_null() { - unsafe { - CVec::new(ptr::null_mut::<u8>(), 9); - } - } - - #[test] - fn test_overrun_get() { - let cv = malloc(16); - - assert!(cv.get(17).is_none()); - } - - #[test] - fn test_overrun_set() { - let mut cv = malloc(16); - - assert!(cv.get_mut(17).is_none()); - } - - #[test] - fn test_unwrap() { - unsafe { - let cv = CVec::new_with_dtor(1 as *mut int, - 0, - move|:| panic!("Don't run this destructor!")); - let p = cv.into_inner(); - assert_eq!(p, 1 as *mut int); - } - } - -} diff --git a/src/libstd/dynamic_lib.rs b/src/libstd/dynamic_lib.rs index de3d75ffb32..66cb1f2c948 100644 --- a/src/libstd/dynamic_lib.rs +++ b/src/libstd/dynamic_lib.rs @@ -17,7 +17,7 @@ use prelude::v1::*; -use c_str::ToCStr; +use ffi::CString; use mem; use os; use str; @@ -51,13 +51,11 @@ impl DynamicLibrary { /// Lazily open a dynamic library. When passed None it gives a /// handle to the calling process - pub fn open<T: ToCStr>(filename: Option<T>) - -> Result<DynamicLibrary, String> { + pub fn open(filename: Option<&Path>) -> Result<DynamicLibrary, String> { unsafe { - let mut filename = filename; let maybe_library = dl::check_for_errors_in(|| { - match filename.take() { - Some(name) => dl::open_external(name), + match filename { + Some(name) => dl::open_external(name.as_vec()), None => dl::open_internal() } }); @@ -131,9 +129,8 @@ impl DynamicLibrary { // T but that feature is still unimplemented let maybe_symbol_value = dl::check_for_errors_in(|| { - symbol.with_c_str(|raw_string| { - dl::symbol(self.handle, raw_string) - }) + let raw_string = CString::from_slice(symbol.as_bytes()); + dl::symbol(self.handle, raw_string.as_ptr()) }); // The value must not be constructed if there is an error so @@ -157,7 +154,7 @@ mod test { fn test_loading_cosine() { // The math library does not need to be loaded since it is already // statically linked in - let none: Option<Path> = None; // appease the typechecker + let none: Option<&Path> = None; // appease the typechecker let libm = match DynamicLibrary::open(none) { Err(error) => panic!("Could not load self as module: {}", error), Ok(libm) => libm @@ -202,17 +199,17 @@ mod test { target_os = "freebsd", target_os = "dragonfly"))] pub mod dl { - use self::Rtld::*; - + pub use self::Rtld::*; use prelude::v1::*; - use c_str::{CString, ToCStr}; + + use ffi::{self, CString}; + use str; use libc; use ptr; - pub unsafe fn open_external<T: ToCStr>(filename: T) -> *mut u8 { - filename.with_c_str(|raw_name| { - dlopen(raw_name, Lazy as libc::c_int) as *mut u8 - }) + pub unsafe fn open_external(filename: &[u8]) -> *mut u8 { + let s = CString::from_slice(filename); + dlopen(s.as_ptr(), Lazy as libc::c_int) as *mut u8 } pub unsafe fn open_internal() -> *mut u8 { @@ -236,8 +233,8 @@ pub mod dl { let ret = if ptr::null() == last_error { Ok(result) } else { - Err(String::from_str(CString::new(last_error, false).as_str() - .unwrap())) + let s = ffi::c_str_to_bytes(&last_error); + Err(str::from_utf8(s).unwrap().to_string()) }; ret @@ -273,7 +270,6 @@ pub mod dl { #[cfg(target_os = "windows")] pub mod dl { - use c_str::ToCStr; use iter::IteratorExt; use libc; use ops::FnOnce; @@ -287,10 +283,9 @@ pub mod dl { use string::String; use vec::Vec; - pub unsafe fn open_external<T: ToCStr>(filename: T) -> *mut u8 { + pub unsafe fn open_external(filename: &[u8]) -> *mut u8 { // Windows expects Unicode data - let filename_cstr = filename.to_c_str(); - let filename_str = str::from_utf8(filename_cstr.as_bytes_no_nul()).unwrap(); + let filename_str = str::from_utf8(filename).unwrap(); let mut filename_str: Vec<u16> = filename_str.utf16_units().collect(); filename_str.push(0); LoadLibraryW(filename_str.as_ptr() as *const libc::c_void) as *mut u8 diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs new file mode 100644 index 00000000000..bef2344d9e8 --- /dev/null +++ b/src/libstd/ffi/c_str.rs @@ -0,0 +1,218 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use fmt; +use iter::IteratorExt; +use libc; +use mem; +use ops::Deref; +use slice::{self, SliceExt, AsSlice}; +use string::String; +use vec::Vec; + +/// A type representing a C-compatible string +/// +/// This type serves the primary purpose of being able to generate a +/// C-compatible string from a Rust byte slice or vector. An instance of this +/// type is a static guarantee that the underlying bytes contain no interior 0 +/// bytes and the final byte is 0. +/// +/// A `CString` is created from either a byte slice or a byte vector. After +/// being created, a `CString` predominately inherits all of its methods from +/// the `Deref` implementation to `[libc::c_char]`. Note that the underlying +/// array is represented as an array of `libc::c_char` as opposed to `u8`. A +/// `u8` slice can be obtained with the `as_bytes` method. Slices produced from +/// a `CString` do *not* contain the trailing nul terminator unless otherwise +/// specified. +/// +/// # Example +/// +/// ```no_run +/// # extern crate libc; +/// # fn main() { +/// use std::ffi::CString; +/// use libc; +/// +/// extern { +/// fn my_printer(s: *const libc::c_char); +/// } +/// +/// let to_print = "Hello, world!"; +/// let c_to_print = CString::from_slice(to_print.as_bytes()); +/// unsafe { +/// my_printer(c_to_print.as_ptr()); +/// } +/// # } +/// ``` +#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +pub struct CString { + inner: Vec<libc::c_char>, +} + +impl CString { + /// Create a new C-compatible string from a byte slice. + /// + /// This method will copy the data of the slice provided into a new + /// allocation, ensuring that there is a trailing 0 byte. + /// + /// # Panics + /// + /// This function will panic if there are any 0 bytes already in the slice + /// provided. + pub fn from_slice(v: &[u8]) -> CString { + CString::from_vec(v.to_vec()) + } + + /// Create a C-compatible string from a byte vector. + /// + /// This method will consume ownership of the provided vector, appending a 0 + /// byte to the end after verifying that there are no interior 0 bytes. + /// + /// # Panics + /// + /// This function will panic if there are any 0 bytes already in the vector + /// provided. + pub fn from_vec(v: Vec<u8>) -> CString { + assert!(!v.iter().any(|&x| x == 0)); + unsafe { CString::from_vec_unchecked(v) } + } + + /// Create a C-compatibel string from a byte vector without checking for + /// interior 0 bytes. + /// + /// This method is equivalent to `from_vec` except that no runtime assertion + /// is made that `v` contains no 0 bytes. + pub unsafe fn from_vec_unchecked(mut v: Vec<u8>) -> CString { + v.push(0); + CString { inner: mem::transmute(v) } + } + + /// Create a view into this C string which includes the trailing nul + /// terminator at the end of the string. + pub fn as_slice_with_nul(&self) -> &[libc::c_char] { self.inner.as_slice() } + + /// Similar to the `as_slice` method, but returns a `u8` slice instead of a + /// `libc::c_char` slice. + pub fn as_bytes(&self) -> &[u8] { + unsafe { mem::transmute(self.as_slice()) } + } + + /// Equivalend to `as_slice_with_nul` except that the type returned is a + /// `u8` slice instead of a `libc::c_char` slice. + pub fn as_bytes_with_nul(&self) -> &[u8] { + unsafe { mem::transmute(self.as_slice_with_nul()) } + } +} + +impl Deref for CString { + type Target = [libc::c_char]; + + fn deref(&self) -> &[libc::c_char] { + self.inner.slice_to(self.inner.len() - 1) + } +} + +impl fmt::Show for CString { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + String::from_utf8_lossy(self.as_bytes()).fmt(f) + } +} + +/// Interpret a C string as a byte slice. +/// +/// This function will calculate the length of the C string provided, and it +/// will then return a corresponding slice for the contents of the C string not +/// including the nul terminator. +/// +/// This function will tie the lifetime of the returned slice to the lifetime of +/// the pointer provided. This is done to help prevent the slice from escaping +/// the lifetime of the pointer itself. If a longer lifetime is needed, then +/// `mem::copy_lifetime` should be used. +/// +/// This function is unsafe because there is no guarantee of the validity of the +/// pointer `raw` or a guarantee that a nul terminator will be found. +/// +/// # Example +/// +/// ```no_run +/// # extern crate libc; +/// # fn main() { +/// use std::ffi; +/// use std::str; +/// use libc; +/// +/// extern { +/// fn my_string() -> *const libc::c_char; +/// } +/// +/// unsafe { +/// let to_print = my_string(); +/// let slice = ffi::c_str_to_bytes(&to_print); +/// println!("string returned: {}", str::from_utf8(slice).unwrap()); +/// } +/// # } +/// ``` +pub unsafe fn c_str_to_bytes<'a>(raw: &'a *const libc::c_char) -> &'a [u8] { + let len = libc::strlen(*raw); + slice::from_raw_buf(&*(raw as *const _ as *const *const u8), len as uint) +} + +/// Interpret a C string as a byte slice with the nul terminator. +/// +/// This function is identical to `from_raw_buf` except that the returned slice +/// will include the nul terminator of the string. +pub unsafe fn c_str_to_bytes_with_nul<'a>(raw: &'a *const libc::c_char) -> &'a [u8] { + let len = libc::strlen(*raw) + 1; + slice::from_raw_buf(&*(raw as *const _ as *const *const u8), len as uint) +} + +#[cfg(test)] +mod tests { + use prelude::v1::*; + use super::*; + use libc; + use mem; + + #[test] + fn c_to_rust() { + let data = b"123\0"; + let ptr = data.as_ptr() as *const libc::c_char; + unsafe { + assert_eq!(c_str_to_bytes(&ptr), b"123"); + assert_eq!(c_str_to_bytes_with_nul(&ptr), b"123\0"); + } + } + + #[test] + fn simple() { + let s = CString::from_slice(b"1234"); + assert_eq!(s.as_bytes(), b"1234"); + assert_eq!(s.as_bytes_with_nul(), b"1234\0"); + unsafe { + assert_eq!(s.as_slice(), + mem::transmute::<_, &[libc::c_char]>(b"1234")); + assert_eq!(s.as_slice_with_nul(), + mem::transmute::<_, &[libc::c_char]>(b"1234\0")); + } + } + + #[should_fail] #[test] + fn build_with_zero1() { CString::from_slice(b"\0"); } + #[should_fail] #[test] + fn build_with_zero2() { CString::from_vec(vec![0]); } + + #[test] + fn build_with_zero3() { + unsafe { + let s = CString::from_vec_unchecked(vec![0]); + assert_eq!(s.as_bytes(), b"\0"); + } + } +} diff --git a/src/libstd/ffi/mod.rs b/src/libstd/ffi/mod.rs new file mode 100644 index 00000000000..cc86f804e3e --- /dev/null +++ b/src/libstd/ffi/mod.rs @@ -0,0 +1,20 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Utilities related to FFI bindings. + +#![unstable = "module just underwent fairly large reorganization and the dust \ + still needs to settle"] + +pub use self::c_str::CString; +pub use self::c_str::c_str_to_bytes; +pub use self::c_str::c_str_to_bytes_with_nul; + +mod c_str; diff --git a/src/libstd/io/net/pipe.rs b/src/libstd/io/net/pipe.rs index daefdd28b30..738c70412f7 100644 --- a/src/libstd/io/net/pipe.rs +++ b/src/libstd/io/net/pipe.rs @@ -22,7 +22,8 @@ use prelude::v1::*; -use c_str::ToCStr; +use ffi::CString; +use path::BytesContainer; use io::{Listener, Acceptor, IoResult, TimedOut, standard_error}; use sys::pipe::UnixAcceptor as UnixAcceptorImp; use sys::pipe::UnixListener as UnixListenerImp; @@ -53,8 +54,9 @@ impl UnixStream { /// let mut stream = UnixStream::connect(&server); /// stream.write(&[1, 2, 3]); /// ``` - pub fn connect<P: ToCStr>(path: &P) -> IoResult<UnixStream> { - UnixStreamImp::connect(&path.to_c_str(), None) + pub fn connect<P: BytesContainer>(path: P) -> IoResult<UnixStream> { + let path = CString::from_slice(path.container_as_bytes()); + UnixStreamImp::connect(&path, None) .map(|inner| UnixStream { inner: inner }) } @@ -67,13 +69,15 @@ impl UnixStream { /// If a `timeout` with zero or negative duration is specified then /// the function returns `Err`, with the error kind set to `TimedOut`. #[experimental = "the timeout argument is likely to change types"] - pub fn connect_timeout<P: ToCStr>(path: &P, - timeout: Duration) -> IoResult<UnixStream> { + pub fn connect_timeout<P>(path: P, timeout: Duration) + -> IoResult<UnixStream> + where P: BytesContainer { if timeout <= Duration::milliseconds(0) { return Err(standard_error(TimedOut)); } - UnixStreamImp::connect(&path.to_c_str(), Some(timeout.num_milliseconds() as u64)) + let path = CString::from_slice(path.container_as_bytes()); + UnixStreamImp::connect(&path, Some(timeout.num_milliseconds() as u64)) .map(|inner| UnixStream { inner: inner }) } @@ -177,8 +181,9 @@ impl UnixListener { /// } /// # } /// ``` - pub fn bind<P: ToCStr>(path: &P) -> IoResult<UnixListener> { - UnixListenerImp::bind(&path.to_c_str()) + pub fn bind<P: BytesContainer>(path: P) -> IoResult<UnixListener> { + let path = CString::from_slice(path.container_as_bytes()); + UnixListenerImp::bind(&path) .map(|inner| UnixListener { inner: inner }) } } diff --git a/src/libstd/io/process.rs b/src/libstd/io/process.rs index 5886c9cc3e2..ea232ad0c3f 100644 --- a/src/libstd/io/process.rs +++ b/src/libstd/io/process.rs @@ -18,8 +18,8 @@ pub use self::ProcessExit::*; use prelude::v1::*; -use c_str::{CString, ToCStr}; use collections::HashMap; +use ffi::CString; use fmt; use hash::Hash; use io::pipe::{PipeStream, PipePair}; @@ -35,6 +35,7 @@ use sys; use thread::Thread; #[cfg(windows)] use std::hash::sip::SipState; +#[cfg(windows)] use str; /// Signal a process to exit, without forcibly killing it. Corresponds to /// SIGTERM on unix platforms. @@ -109,11 +110,11 @@ struct EnvKey(CString); impl Hash for EnvKey { fn hash(&self, state: &mut SipState) { let &EnvKey(ref x) = self; - match x.as_str() { - Some(s) => for ch in s.chars() { + match str::from_utf8(x.as_bytes()) { + Ok(s) => for ch in s.chars() { (ch as u8 as char).to_lowercase().hash(state); }, - None => x.hash(state) + Err(..) => x.hash(state) } } } @@ -123,8 +124,8 @@ impl PartialEq for EnvKey { fn eq(&self, other: &EnvKey) -> bool { let &EnvKey(ref x) = self; let &EnvKey(ref y) = other; - match (x.as_str(), y.as_str()) { - (Some(xs), Some(ys)) => { + match (str::from_utf8(x.as_bytes()), str::from_utf8(y.as_bytes())) { + (Ok(xs), Ok(ys)) => { if xs.len() != ys.len() { return false } else { @@ -185,10 +186,10 @@ pub struct Command { } // FIXME (#12938): Until DST lands, we cannot decompose &str into & and str, so -// we cannot usefully take ToCStr arguments by reference (without forcing an +// we cannot usefully take BytesContainer arguments by reference (without forcing an // additional & around &str). So we are instead temporarily adding an instance -// for &Path, so that we can take ToCStr as owned. When DST lands, the &Path -// instance should be removed, and arguments bound by ToCStr should be passed by +// for &Path, so that we can take BytesContainer as owned. When DST lands, the &Path +// instance should be removed, and arguments bound by BytesContainer should be passed by // reference. (Here: {new, arg, args, env}.) impl Command { @@ -203,9 +204,9 @@ impl Command { /// /// Builder methods are provided to change these defaults and /// otherwise configure the process. - pub fn new<T:ToCStr>(program: T) -> Command { + pub fn new<T: BytesContainer>(program: T) -> Command { Command { - program: program.to_c_str(), + program: CString::from_slice(program.container_as_bytes()), args: Vec::new(), env: None, cwd: None, @@ -219,27 +220,29 @@ impl Command { } /// Add an argument to pass to the program. - pub fn arg<'a, T: ToCStr>(&'a mut self, arg: T) -> &'a mut Command { - self.args.push(arg.to_c_str()); + pub fn arg<'a, T: BytesContainer>(&'a mut self, arg: T) -> &'a mut Command { + self.args.push(CString::from_slice(arg.container_as_bytes())); self } /// Add multiple arguments to pass to the program. - pub fn args<'a, T: ToCStr>(&'a mut self, args: &[T]) -> &'a mut Command { - self.args.extend(args.iter().map(|arg| arg.to_c_str()));; + pub fn args<'a, T: BytesContainer>(&'a mut self, args: &[T]) -> &'a mut Command { + self.args.extend(args.iter().map(|arg| { + CString::from_slice(arg.container_as_bytes()) + })); self } // Get a mutable borrow of the environment variable map for this `Command`. - fn get_env_map<'a>(&'a mut self) -> &'a mut EnvMap { + fn get_env_map<'a>(&'a mut self) -> &'a mut EnvMap { match self.env { Some(ref mut map) => map, None => { // if the env is currently just inheriting from the parent's, // materialize the parent's env into a hashtable. - self.env = Some(os::env_as_bytes().into_iter() - .map(|(k, v)| (EnvKey(k.to_c_str()), - v.to_c_str())) - .collect()); + self.env = Some(os::env_as_bytes().into_iter().map(|(k, v)| { + (EnvKey(CString::from_slice(k.as_slice())), + CString::from_slice(v.as_slice())) + }).collect()); self.env.as_mut().unwrap() } } @@ -249,15 +252,20 @@ impl Command { /// /// Note that environment variable names are case-insensitive (but case-preserving) on Windows, /// and case-sensitive on all other platforms. - pub fn env<'a, T: ToCStr, U: ToCStr>(&'a mut self, key: T, val: U) - -> &'a mut Command { - self.get_env_map().insert(EnvKey(key.to_c_str()), val.to_c_str()); + pub fn env<'a, T, U>(&'a mut self, key: T, val: U) + -> &'a mut Command + where T: BytesContainer, U: BytesContainer { + let key = EnvKey(CString::from_slice(key.container_as_bytes())); + let val = CString::from_slice(val.container_as_bytes()); + self.get_env_map().insert(key, val); self } /// Removes an environment variable mapping. - pub fn env_remove<'a, T: ToCStr>(&'a mut self, key: T) -> &'a mut Command { - self.get_env_map().remove(&EnvKey(key.to_c_str())); + pub fn env_remove<'a, T>(&'a mut self, key: T) -> &'a mut Command + where T: BytesContainer { + let key = EnvKey(CString::from_slice(key.container_as_bytes())); + self.get_env_map().remove(&key); self } @@ -265,16 +273,19 @@ impl Command { /// /// If the given slice contains multiple instances of an environment /// variable, the *rightmost* instance will determine the value. - pub fn env_set_all<'a, T: ToCStr, U: ToCStr>(&'a mut self, env: &[(T,U)]) - -> &'a mut Command { - self.env = Some(env.iter().map(|&(ref k, ref v)| (EnvKey(k.to_c_str()), v.to_c_str())) - .collect()); + pub fn env_set_all<'a, T, U>(&'a mut self, env: &[(T,U)]) + -> &'a mut Command + where T: BytesContainer, U: BytesContainer { + self.env = Some(env.iter().map(|&(ref k, ref v)| { + (EnvKey(CString::from_slice(k.container_as_bytes())), + CString::from_slice(v.container_as_bytes())) + }).collect()); self } /// Set the working directory for the child process. pub fn cwd<'a>(&'a mut self, dir: &Path) -> &'a mut Command { - self.cwd = Some(dir.to_c_str()); + self.cwd = Some(CString::from_slice(dir.as_vec())); self } @@ -389,9 +400,9 @@ impl fmt::Show for Command { /// non-utf8 data is lossily converted using the utf8 replacement /// character. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(write!(f, "{}", String::from_utf8_lossy(self.program.as_bytes_no_nul()))); + try!(write!(f, "{}", String::from_utf8_lossy(self.program.as_bytes()))); for arg in self.args.iter() { - try!(write!(f, " '{}'", String::from_utf8_lossy(arg.as_bytes_no_nul()))); + try!(write!(f, " '{}'", String::from_utf8_lossy(arg.as_bytes()))); } Ok(()) } @@ -1208,13 +1219,13 @@ mod tests { #[test] #[cfg(windows)] fn env_map_keys_ci() { - use c_str::ToCStr; + use ffi::CString; use super::EnvKey; let mut cmd = Command::new(""); cmd.env("path", "foo"); cmd.env("Path", "bar"); let env = &cmd.env.unwrap(); - let val = env.get(&EnvKey("PATH".to_c_str())); - assert!(val.unwrap() == &"bar".to_c_str()); + let val = env.get(&EnvKey(CString::from_slice(b"PATH"))); + assert!(val.unwrap() == &CString::from_slice(b"bar")); } } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 608ad9882b9..2d3a4639379 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -208,10 +208,10 @@ pub mod num; /* Runtime and platform support */ -pub mod thread_local; -pub mod c_str; -pub mod c_vec; +pub mod thread_local; // first for macros + pub mod dynamic_lib; +pub mod ffi; pub mod fmt; pub mod io; pub mod os; diff --git a/src/libstd/os.rs b/src/libstd/os.rs index be8f82349c2..300ceec4b45 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -57,12 +57,10 @@ use string::{String, ToString}; use sync::atomic::{AtomicInt, ATOMIC_INT_INIT, Ordering}; use vec::Vec; -#[cfg(unix)] use c_str::ToCStr; +#[cfg(unix)] use ffi::{self, CString}; -#[cfg(unix)] -pub use sys::ext as unix; -#[cfg(windows)] -pub use sys::ext as windows; +#[cfg(unix)] pub use sys::ext as unix; +#[cfg(windows)] pub use sys::ext as windows; /// Get the number of cores available pub fn num_cpus() -> uint { @@ -196,15 +194,14 @@ pub fn getenv(n: &str) -> Option<String> { /// /// Panics if `n` has any interior NULs. pub fn getenv_as_bytes(n: &str) -> Option<Vec<u8>> { - use c_str::CString; - unsafe { with_env_lock(|| { - let s = n.with_c_str(|buf| libc::getenv(buf)); + let s = CString::from_slice(n.as_bytes()); + let s = libc::getenv(s.as_ptr()) as *const _; if s.is_null() { None } else { - Some(CString::new(s as *const libc::c_char, false).as_bytes_no_nul().to_vec()) + Some(ffi::c_str_to_bytes(&s).to_vec()) } }) } @@ -253,13 +250,12 @@ pub fn setenv<T: BytesContainer>(n: &str, v: T) { fn _setenv(n: &str, v: &[u8]) { unsafe { with_env_lock(|| { - n.with_c_str(|nbuf| { - v.with_c_str(|vbuf| { - if libc::funcs::posix01::unistd::setenv(nbuf, vbuf, 1) != 0 { - panic!(IoError::last_error()); - } - }) - }) + let k = CString::from_slice(n.as_bytes()); + let v = CString::from_slice(v); + if libc::funcs::posix01::unistd::setenv(k.as_ptr(), + v.as_ptr(), 1) != 0 { + panic!(IoError::last_error()); + } }) } } @@ -289,11 +285,10 @@ pub fn unsetenv(n: &str) { fn _unsetenv(n: &str) { unsafe { with_env_lock(|| { - n.with_c_str(|nbuf| { - if libc::funcs::posix01::unistd::unsetenv(nbuf) != 0 { - panic!(IoError::last_error()); - } - }) + let nbuf = CString::from_slice(n.as_bytes()); + if libc::funcs::posix01::unistd::unsetenv(nbuf.as_ptr()) != 0 { + panic!(IoError::last_error()); + } }) } } @@ -618,11 +613,10 @@ pub fn get_exit_status() -> int { #[cfg(target_os = "macos")] unsafe fn load_argc_and_argv(argc: int, argv: *const *const c_char) -> Vec<Vec<u8>> { - use c_str::CString; use iter::range; range(0, argc as uint).map(|i| { - CString::new(*argv.offset(i as int), false).as_bytes_no_nul().to_vec() + ffi::c_str_to_bytes(&*argv.offset(i as int)).to_vec() }).collect() } @@ -652,7 +646,6 @@ fn real_args_as_bytes() -> Vec<Vec<u8>> { // res #[cfg(target_os = "ios")] fn real_args_as_bytes() -> Vec<Vec<u8>> { - use c_str::CString; use iter::range; use mem; diff --git a/src/libstd/path/mod.rs b/src/libstd/path/mod.rs index bf9ffbffe7d..2f014872402 100644 --- a/src/libstd/path/mod.rs +++ b/src/libstd/path/mod.rs @@ -62,7 +62,7 @@ #![experimental] use core::kinds::Sized; -use c_str::CString; +use ffi::CString; use clone::Clone; use fmt; use iter::IteratorExt; @@ -892,7 +892,7 @@ impl BytesContainer for Vec<u8> { impl BytesContainer for CString { #[inline] fn container_as_bytes<'a>(&'a self) -> &'a [u8] { - self.as_bytes_no_nul() + self.as_bytes() } } @@ -913,21 +913,3 @@ impl<'a, Sized? T: BytesContainer> BytesContainer for &'a T { fn contains_nul<T: BytesContainer>(v: &T) -> bool { v.container_as_bytes().iter().any(|&x| x == 0) } - -#[cfg(test)] -mod tests { - use prelude::v1::*; - use c_str::ToCStr; - use path::{WindowsPath, PosixPath}; - - #[test] - fn test_cstring() { - let input = "/foo/bar/baz"; - let path: PosixPath = PosixPath::new(input.to_c_str()); - assert_eq!(path.as_vec(), input.as_bytes()); - - let input = r"\foo\bar\baz"; - let path: WindowsPath = WindowsPath::new(input.to_c_str()); - assert_eq!(path.as_str().unwrap(), input); - } -} diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs index ae82e201cb8..013212b2705 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/path/posix.rs @@ -10,19 +10,16 @@ //! POSIX file path handling -use c_str::{CString, ToCStr}; use clone::Clone; -use cmp::{PartialEq, Eq, PartialOrd, Ord, Ordering}; +use cmp::{Ordering, Eq, Ord, PartialEq, PartialOrd}; use hash; use io::Writer; use iter::{AdditiveIterator, Extend}; use iter::{Iterator, IteratorExt, Map}; -use option::Option; -use option::Option::{None, Some}; use kinds::Sized; -use str::{FromStr, Str}; -use str; -use slice::{Split, AsSlice, SliceConcatExt, SliceExt}; +use option::Option::{self, Some, None}; +use slice::{AsSlice, Split, SliceExt, SliceConcatExt}; +use str::{self, FromStr, StrExt}; use vec::Vec; use super::{BytesContainer, GenericPath, GenericPathUnsafe}; @@ -86,26 +83,6 @@ impl FromStr for Path { } } -// FIXME (#12938): Until DST lands, we cannot decompose &str into & and str, so -// we cannot usefully take ToCStr arguments by reference (without forcing an -// additional & around &str). So we are instead temporarily adding an instance -// for &Path, so that we can take ToCStr as owned. When DST lands, the &Path -// instance should be removed, and arguments bound by ToCStr should be passed by -// reference. - -impl ToCStr for Path { - #[inline] - fn to_c_str(&self) -> CString { - // The Path impl guarantees no internal NUL - unsafe { self.to_c_str_unchecked() } - } - - #[inline] - unsafe fn to_c_str_unchecked(&self) -> CString { - self.as_vec().to_c_str_unchecked() - } -} - impl<S: hash::Writer> hash::Hash<S> for Path { #[inline] fn hash(&self, state: &mut S) { diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs index cf8bc0e6242..05129a7ab9d 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/path/windows.rs @@ -15,17 +15,15 @@ use self::PathPrefix::*; use ascii::AsciiExt; -use c_str::{CString, ToCStr}; use char::CharExt; use clone::Clone; -use cmp::{PartialEq, Eq, PartialOrd, Ord, Ordering}; +use cmp::{Ordering, Eq, Ord, PartialEq, PartialOrd}; use hash; use io::Writer; use iter::{AdditiveIterator, Extend}; use iter::{Iterator, IteratorExt, Map, repeat}; use mem; -use option::Option; -use option::Option::{Some, None}; +use option::Option::{self, Some, None}; use slice::{SliceExt, SliceConcatExt}; use str::{SplitTerminator, FromStr, StrExt}; use string::{String, ToString}; @@ -112,26 +110,6 @@ impl FromStr for Path { } } -// FIXME (#12938): Until DST lands, we cannot decompose &str into & and str, so -// we cannot usefully take ToCStr arguments by reference (without forcing an -// additional & around &str). So we are instead temporarily adding an instance -// for &Path, so that we can take ToCStr as owned. When DST lands, the &Path -// instance should be removed, and arguments bound by ToCStr should be passed by -// reference. - -impl ToCStr for Path { - #[inline] - fn to_c_str(&self) -> CString { - // The Path impl guarantees no internal NUL - unsafe { self.to_c_str_unchecked() } - } - - #[inline] - unsafe fn to_c_str_unchecked(&self) -> CString { - self.as_vec().to_c_str_unchecked() - } -} - impl<S: hash::Writer> hash::Hash<S> for Path { #[cfg(not(test))] #[inline] diff --git a/src/libstd/rt/args.rs b/src/libstd/rt/args.rs index 4734a39c835..86abacb9365 100644 --- a/src/libstd/rt/args.rs +++ b/src/libstd/rt/args.rs @@ -46,8 +46,9 @@ pub fn clone() -> Option<Vec<Vec<u8>>> { imp::clone() } mod imp { use prelude::v1::*; + use libc; use mem; - use slice; + use ffi; use sync::{StaticMutex, MUTEX_INIT}; @@ -95,13 +96,9 @@ mod imp { } unsafe fn load_argc_and_argv(argc: int, argv: *const *const u8) -> Vec<Vec<u8>> { + let argv = argv as *const *const libc::c_char; range(0, argc as uint).map(|i| { - let arg = *argv.offset(i as int); - let mut len = 0u; - while *arg.offset(len as int) != 0 { - len += 1u; - } - slice::from_raw_buf(&arg, len).to_vec() + ffi::c_str_to_bytes(&*argv.offset(i as int)).to_vec() }).collect() } diff --git a/src/libstd/rt/backtrace.rs b/src/libstd/rt/backtrace.rs index 578239c9cc4..bb0b6fe804b 100644 --- a/src/libstd/rt/backtrace.rs +++ b/src/libstd/rt/backtrace.rs @@ -15,7 +15,7 @@ use prelude::v1::*; use os; -use sync::atomic::{mod, Ordering}; +use sync::atomic::{self, Ordering}; pub use sys::backtrace::write; diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind.rs index a48a8edd82f..71169386c18 100644 --- a/src/libstd/rt/unwind.rs +++ b/src/libstd/rt/unwind.rs @@ -67,7 +67,7 @@ use fmt; use intrinsics; use libc::c_void; use mem; -use sync::atomic::{mod, Ordering}; +use sync::atomic::{self, Ordering}; use sync::{Once, ONCE_INIT}; use rt::libunwind as uw; diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs index 883a01fa318..bc01ce926f8 100644 --- a/src/libstd/rt/util.rs +++ b/src/libstd/rt/util.rs @@ -19,7 +19,7 @@ use libc::{self, uintptr_t}; use os; use slice; use str; -use sync::atomic::{mod, Ordering}; +use sync::atomic::{self, Ordering}; /// Dynamically inquire about whether we're running under V. /// You should usually not use this unless your test definitely diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs index a441e55a732..a31dcc9884f 100644 --- a/src/libstd/sys/common/mod.rs +++ b/src/libstd/sys/common/mod.rs @@ -14,7 +14,7 @@ use io::{self, IoError, IoResult}; use prelude::v1::*; use sys::{last_error, retry}; -use c_str::CString; +use ffi::CString; use num::Int; use path::BytesContainer; use collections; diff --git a/src/libstd/sys/common/net.rs b/src/libstd/sys/common/net.rs index 3f67b284f68..4cf891ac498 100644 --- a/src/libstd/sys/common/net.rs +++ b/src/libstd/sys/common/net.rs @@ -12,15 +12,16 @@ use prelude::v1::*; use self::SocketStatus::*; use self::InAddr::*; -use c_str::ToCStr; +use ffi::CString; +use ffi; use io::net::addrinfo; use io::net::ip::{SocketAddr, IpAddr, Ipv4Addr, Ipv6Addr}; use io::{IoResult, IoError}; use libc::{self, c_char, c_int}; -use c_str::CString; use mem; use num::Int; use ptr::{self, null, null_mut}; +use str; use sys::{self, retry, c, sock_t, last_error, last_net_error, last_gai_error, close_sock, wrlen, msglen_t, os, wouldblock, set_nonblocking, timer, ms_to_timeval, decode_error_detailed}; @@ -234,9 +235,9 @@ pub fn get_host_addresses(host: Option<&str>, servname: Option<&str>, assert!(host.is_some() || servname.is_some()); - let c_host = host.map(|x| x.to_c_str()); + let c_host = host.map(|x| CString::from_slice(x.as_bytes())); let c_host = c_host.as_ref().map(|x| x.as_ptr()).unwrap_or(null()); - let c_serv = servname.map(|x| x.to_c_str()); + let c_serv = servname.map(|x| CString::from_slice(x.as_bytes())); let c_serv = c_serv.as_ref().map(|x| x.as_ptr()).unwrap_or(null()); let hint = hint.map(|hint| { @@ -324,7 +325,8 @@ pub fn get_address_name(addr: IpAddr) -> Result<String, IoError> { } unsafe { - Ok(CString::new(hostbuf.as_ptr(), false).as_str().unwrap().to_string()) + Ok(str::from_utf8(ffi::c_str_to_bytes(&hostbuf.as_ptr())) + .unwrap().to_string()) } } diff --git a/src/libstd/sys/unix/backtrace.rs b/src/libstd/sys/unix/backtrace.rs index 5b261ea6b9e..ca268a8f27f 100644 --- a/src/libstd/sys/unix/backtrace.rs +++ b/src/libstd/sys/unix/backtrace.rs @@ -83,12 +83,13 @@ /// to symbols. This is a bit of a hokey implementation as-is, but it works for /// all unix platforms we support right now, so it at least gets the job done. -use c_str::CString; -use io::{IoResult, Writer}; +use prelude::v1::*; + +use ffi; +use io::IoResult; use libc; use mem; -use option::Option::{self, Some, None}; -use result::Result::{Ok, Err}; +use str; use sync::{StaticMutex, MUTEX_INIT}; use sys_common::backtrace::*; @@ -105,9 +106,7 @@ use sys_common::backtrace::*; #[cfg(all(target_os = "ios", target_arch = "arm"))] #[inline(never)] pub fn write(w: &mut Writer) -> IoResult<()> { - use iter::{IteratorExt, range}; use result; - use slice::SliceExt; extern { fn backtrace(buf: *mut *mut libc::c_void, @@ -234,19 +233,15 @@ fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> { output(w, idx,addr, None) } else { output(w, idx, addr, Some(unsafe { - CString::new(info.dli_sname, false) + ffi::c_str_to_bytes(&info.dli_sname) })) } } #[cfg(not(any(target_os = "macos", target_os = "ios")))] fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> { - use iter::{Iterator, IteratorExt}; use os; - use path::GenericPath; - use ptr::PtrExt; use ptr; - use slice::SliceExt; //////////////////////////////////////////////////////////////////////// // libbacktrace.h API @@ -368,15 +363,15 @@ fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> { if ret == 0 || data.is_null() { output(w, idx, addr, None) } else { - output(w, idx, addr, Some(unsafe { CString::new(data, false) })) + output(w, idx, addr, Some(unsafe { ffi::c_str_to_bytes(&data) })) } } // Finally, after all that work above, we can emit a symbol. fn output(w: &mut Writer, idx: int, addr: *mut libc::c_void, - s: Option<CString>) -> IoResult<()> { + s: Option<&[u8]>) -> IoResult<()> { try!(write!(w, " {:2}: {:2$} - ", idx, addr, HEX_WIDTH)); - match s.as_ref().and_then(|c| c.as_str()) { + match s.and_then(|s| str::from_utf8(s).ok()) { Some(string) => try!(demangle(w, string)), None => try!(write!(w, "<unknown>")), } diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index b49ace8e2f8..1ad775517bb 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -12,7 +12,7 @@ use prelude::v1::*; -use c_str::{CString, ToCStr}; +use ffi::{self, CString}; use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode}; use io::{IoResult, FileStat, SeekStyle}; use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append}; @@ -150,6 +150,10 @@ impl Drop for FileDesc { } } +fn cstr(path: &Path) -> CString { + CString::from_slice(path.as_vec()) +} + pub fn open(path: &Path, fm: FileMode, fa: FileAccess) -> IoResult<FileDesc> { let flags = match fm { Open => 0, @@ -165,7 +169,7 @@ pub fn open(path: &Path, fm: FileMode, fa: FileAccess) -> IoResult<FileDesc> { libc::S_IRUSR | libc::S_IWUSR), }; - let path = path.to_c_str(); + let path = cstr(path); match retry(|| unsafe { libc::open(path.as_ptr(), flags, mode) }) { -1 => Err(super::last_error()), fd => Ok(FileDesc::new(fd, true)), @@ -173,7 +177,7 @@ pub fn open(path: &Path, fm: FileMode, fa: FileAccess) -> IoResult<FileDesc> { } pub fn mkdir(p: &Path, mode: uint) -> IoResult<()> { - let p = p.to_c_str(); + let p = cstr(p); mkerr_libc(unsafe { libc::mkdir(p.as_ptr(), mode as libc::mode_t) }) } @@ -182,7 +186,6 @@ pub fn readdir(p: &Path) -> IoResult<Vec<Path>> { use libc::{opendir, readdir_r, closedir}; fn prune(root: &CString, dirs: Vec<Path>) -> Vec<Path> { - let root = unsafe { CString::new(root.as_ptr(), false) }; let root = Path::new(root); dirs.into_iter().filter(|path| { @@ -199,7 +202,7 @@ pub fn readdir(p: &Path) -> IoResult<Vec<Path>> { let mut buf = Vec::<u8>::with_capacity(size as uint); let ptr = buf.as_mut_ptr() as *mut dirent_t; - let p = p.to_c_str(); + let p = CString::from_slice(p.as_vec()); let dir_ptr = unsafe {opendir(p.as_ptr())}; if dir_ptr as uint != 0 { @@ -207,10 +210,9 @@ pub fn readdir(p: &Path) -> IoResult<Vec<Path>> { let mut entry_ptr = 0 as *mut dirent_t; while unsafe { readdir_r(dir_ptr, ptr, &mut entry_ptr) == 0 } { if entry_ptr.is_null() { break } - let cstr = unsafe { - CString::new(rust_list_dir_val(entry_ptr), false) - }; - paths.push(Path::new(cstr)); + paths.push(unsafe { + Path::new(ffi::c_str_to_bytes(&rust_list_dir_val(entry_ptr))) + }); } assert_eq!(unsafe { closedir(dir_ptr) }, 0); Ok(prune(&p, paths)) @@ -220,39 +222,39 @@ pub fn readdir(p: &Path) -> IoResult<Vec<Path>> { } pub fn unlink(p: &Path) -> IoResult<()> { - let p = p.to_c_str(); + let p = cstr(p); mkerr_libc(unsafe { libc::unlink(p.as_ptr()) }) } pub fn rename(old: &Path, new: &Path) -> IoResult<()> { - let old = old.to_c_str(); - let new = new.to_c_str(); + let old = cstr(old); + let new = cstr(new); mkerr_libc(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) }) } pub fn chmod(p: &Path, mode: uint) -> IoResult<()> { - let p = p.to_c_str(); + let p = cstr(p); mkerr_libc(retry(|| unsafe { libc::chmod(p.as_ptr(), mode as libc::mode_t) })) } pub fn rmdir(p: &Path) -> IoResult<()> { - let p = p.to_c_str(); + let p = cstr(p); mkerr_libc(unsafe { libc::rmdir(p.as_ptr()) }) } pub fn chown(p: &Path, uid: int, gid: int) -> IoResult<()> { - let p = p.to_c_str(); + let p = cstr(p); mkerr_libc(retry(|| unsafe { libc::chown(p.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })) } pub fn readlink(p: &Path) -> IoResult<Path> { - let c_path = p.to_c_str(); + let c_path = cstr(p); let p = c_path.as_ptr(); let mut len = unsafe { libc::pathconf(p as *mut _, libc::_PC_NAME_MAX) }; if len == -1 { @@ -273,14 +275,14 @@ pub fn readlink(p: &Path) -> IoResult<Path> { } pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> { - let src = src.to_c_str(); - let dst = dst.to_c_str(); + let src = cstr(src); + let dst = cstr(dst); mkerr_libc(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) }) } pub fn link(src: &Path, dst: &Path) -> IoResult<()> { - let src = src.to_c_str(); - let dst = dst.to_c_str(); + let src = cstr(src); + let dst = cstr(dst); mkerr_libc(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) }) } @@ -328,7 +330,7 @@ fn mkstat(stat: &libc::stat) -> FileStat { } pub fn stat(p: &Path) -> IoResult<FileStat> { - let p = p.to_c_str(); + let p = cstr(p); let mut stat: libc::stat = unsafe { mem::zeroed() }; match unsafe { libc::stat(p.as_ptr(), &mut stat) } { 0 => Ok(mkstat(&stat)), @@ -337,7 +339,7 @@ pub fn stat(p: &Path) -> IoResult<FileStat> { } pub fn lstat(p: &Path) -> IoResult<FileStat> { - let p = p.to_c_str(); + let p = cstr(p); let mut stat: libc::stat = unsafe { mem::zeroed() }; match unsafe { libc::lstat(p.as_ptr(), &mut stat) } { 0 => Ok(mkstat(&stat)), @@ -346,7 +348,7 @@ pub fn lstat(p: &Path) -> IoResult<FileStat> { } pub fn utime(p: &Path, atime: u64, mtime: u64) -> IoResult<()> { - let p = p.to_c_str(); + let p = cstr(p); let buf = libc::utimbuf { actime: (atime / 1000) as libc::time_t, modtime: (mtime / 1000) as libc::time_t, diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index ea0d230e8b2..6a408aa60f0 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -15,12 +15,14 @@ #![allow(unused_unsafe)] #![allow(unused_mut)] -extern crate libc; - -use num; -use num::{Int, SignedInt}; use prelude::v1::*; + +use ffi; use io::{self, IoResult, IoError}; +use libc; +use num::{Int, SignedInt}; +use num; +use str; use sys_common::mkerr_libc; macro_rules! helper_init { (static $name:ident: Helper<$m:ty>) => ( @@ -78,11 +80,10 @@ extern "system" { } pub fn last_gai_error(s: libc::c_int) -> IoError { - use c_str::CString; let mut err = decode_error(s); err.detail = Some(unsafe { - CString::new(gai_strerror(s), false).as_str().unwrap().to_string() + str::from_utf8(ffi::c_str_to_bytes(&gai_strerror(s))).unwrap().to_string() }); err } diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index 181b8fdd0f8..175c4e2e353 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -12,18 +12,18 @@ use prelude::v1::*; -use c_str::ToCStr; use error::{FromError, Error}; +use ffi::{self, CString}; use fmt; use io::{IoError, IoResult}; use libc::{self, c_int, c_char, c_void}; +use os::TMPBUF_SZ; use os; use path::{BytesContainer}; use ptr; +use str; use sys::fs::FileDesc; -use os::TMPBUF_SZ; - const BUF_BYTES : uint = 2048u; /// Returns the platform-specific value of errno @@ -108,7 +108,8 @@ pub fn error_string(errno: i32) -> String { panic!("strerror_r failure"); } - String::from_raw_buf(p as *const u8) + let p = p as *const _; + str::from_utf8(ffi::c_str_to_bytes(&p)).unwrap().to_string() } } @@ -122,21 +123,17 @@ pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> { } pub fn getcwd() -> IoResult<Path> { - use c_str::CString; - let mut buf = [0 as c_char; BUF_BYTES]; unsafe { if libc::getcwd(buf.as_mut_ptr(), buf.len() as libc::size_t).is_null() { Err(IoError::last_error()) } else { - Ok(Path::new(CString::new(buf.as_ptr(), false))) + Ok(Path::new(ffi::c_str_to_bytes(&buf.as_ptr()))) } } } pub unsafe fn get_env_pairs() -> Vec<Vec<u8>> { - use c_str::CString; - extern { fn rust_env_pairs() -> *const *const c_char; } @@ -147,8 +144,7 @@ pub unsafe fn get_env_pairs() -> Vec<Vec<u8>> { } let mut result = Vec::new(); while *environ != 0 as *const _ { - let env_pair = - CString::new(*environ, false).as_bytes_no_nul().to_vec(); + let env_pair = ffi::c_str_to_bytes(&*environ).to_vec(); result.push(env_pair); environ = environ.offset(1); } @@ -234,14 +230,13 @@ pub fn load_self() -> Option<Vec<u8>> { } pub fn chdir(p: &Path) -> IoResult<()> { - p.with_c_str(|buf| { - unsafe { - match libc::chdir(buf) == (0 as c_int) { - true => Ok(()), - false => Err(IoError::last_error()), - } + let p = CString::from_slice(p.as_vec()); + unsafe { + match libc::chdir(p.as_ptr()) == (0 as c_int) { + true => Ok(()), + false => Err(IoError::last_error()), } - }) + } } pub fn page_size() -> uint { diff --git a/src/libstd/sys/unix/pipe.rs b/src/libstd/sys/unix/pipe.rs index 9063fbc2ba9..158a1ce2204 100644 --- a/src/libstd/sys/unix/pipe.rs +++ b/src/libstd/sys/unix/pipe.rs @@ -10,8 +10,8 @@ use prelude::v1::*; +use ffi::CString; use libc; -use c_str::CString; use mem; use sync::{Arc, Mutex}; use sync::atomic::{AtomicBool, Ordering}; @@ -48,7 +48,7 @@ fn addr_to_sockaddr_un(addr: &CString, } s.sun_family = libc::AF_UNIX as libc::sa_family_t; for (slot, value) in s.sun_path.iter_mut().zip(addr.iter()) { - *slot = value; + *slot = *value; } // count the null terminator diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs index b73919fe2a2..5bc6b0c703b 100644 --- a/src/libstd/sys/unix/process.rs +++ b/src/libstd/sys/unix/process.rs @@ -11,8 +11,8 @@ use prelude::v1::*; use self::Req::*; -use c_str::{CString, ToCStr}; use collections; +use ffi::CString; use hash::Hash; use io::process::{ProcessExit, ExitStatus, ExitSignal}; use io::{self, IoResult, IoError, EndOfFile}; @@ -101,7 +101,7 @@ impl Process { // We may use this in the child, so perform allocations before the // fork - let devnull = "/dev/null".to_c_str(); + let devnull = b"/dev/null\0"; set_cloexec(output.fd()); @@ -204,7 +204,7 @@ impl Process { } else { libc::O_RDWR }; - libc::open(devnull.as_ptr(), flags, 0) + libc::open(devnull.as_ptr() as *const _, flags, 0) } Some(obj) => { let fd = obj.as_inner().fd(); diff --git a/src/libstd/sys/unix/timer.rs b/src/libstd/sys/unix/timer.rs index 11f29232a92..62f3242a206 100644 --- a/src/libstd/sys/unix/timer.rs +++ b/src/libstd/sys/unix/timer.rs @@ -54,7 +54,7 @@ use libc; use mem; use os; use ptr; -use sync::atomic::{mod, Ordering}; +use sync::atomic::{self, Ordering}; use sync::mpsc::{channel, Sender, Receiver, TryRecvError}; use sys::c; use sys::fs::FileDesc; diff --git a/src/libstd/sys/windows/backtrace.rs b/src/libstd/sys/windows/backtrace.rs index 319a458087b..4ccecfd1f5f 100644 --- a/src/libstd/sys/windows/backtrace.rs +++ b/src/libstd/sys/windows/backtrace.rs @@ -21,7 +21,8 @@ /// copy of that function in my mingw install (maybe it was broken?). Instead, /// this takes the route of using StackWalk64 in order to walk the stack. -use c_str::CString; +use dynamic_lib::DynamicLibrary; +use ffi; use intrinsics; use io::{IoResult, Writer}; use libc; @@ -30,10 +31,9 @@ use ops::Drop; use option::Option::{Some, None}; use path::Path; use result::Result::{Ok, Err}; -use sync::{StaticMutex, MUTEX_INIT}; use slice::SliceExt; -use str::StrExt; -use dynamic_lib::DynamicLibrary; +use str::{self, StrExt}; +use sync::{StaticMutex, MUTEX_INIT}; use sys_common::backtrace::*; @@ -357,11 +357,11 @@ pub fn write(w: &mut Writer) -> IoResult<()> { if ret == libc::TRUE { try!(write!(w, " - ")); - let cstr = unsafe { CString::new(info.Name.as_ptr(), false) }; - let bytes = cstr.as_bytes(); - match cstr.as_str() { - Some(s) => try!(demangle(w, s)), - None => try!(w.write(bytes[..bytes.len()-1])), + let ptr = info.Name.as_ptr() as *const libc::c_char; + let bytes = unsafe { ffi::c_str_to_bytes(&ptr) }; + match str::from_utf8(bytes) { + Ok(s) => try!(demangle(w, s)), + Err(..) => try!(w.write(bytes[..bytes.len()-1])), } } try!(w.write(&['\n' as u8])); diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index 1ee57434fb9..945c2e8e7d1 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -133,7 +133,7 @@ pub mod compat { use intrinsics::{atomic_store_relaxed, transmute}; use libc::types::os::arch::extra::{LPCWSTR, HMODULE, LPCSTR, LPVOID}; use prelude::v1::*; - use c_str::ToCStr; + use ffi::CString; extern "system" { fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE; @@ -147,14 +147,13 @@ pub mod compat { unsafe fn store_func(ptr: *mut uint, module: &str, symbol: &str, fallback: uint) { let mut module: Vec<u16> = module.utf16_units().collect(); module.push(0); - symbol.with_c_str(|symbol| { - let handle = GetModuleHandleW(module.as_ptr()); - let func: uint = transmute(GetProcAddress(handle, symbol)); - atomic_store_relaxed(ptr, if func == 0 { - fallback - } else { - func - }) + let symbol = CString::from_slice(symbol.as_bytes()); + let handle = GetModuleHandleW(module.as_ptr()); + let func: uint = transmute(GetProcAddress(handle, symbol.as_ptr())); + atomic_store_relaxed(ptr, if func == 0 { + fallback + } else { + func }) } diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index 9a942300656..f8c75335b35 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -13,7 +13,6 @@ use alloc::arc::Arc; use libc::{self, c_int}; -use c_str::CString; use mem; use sys::os::fill_utf16_buf_and_decode; use path; diff --git a/src/libstd/sys/windows/pipe.rs b/src/libstd/sys/windows/pipe.rs index 9057515cad2..9996909f2f5 100644 --- a/src/libstd/sys/windows/pipe.rs +++ b/src/libstd/sys/windows/pipe.rs @@ -87,16 +87,21 @@ use prelude::v1::*; use libc; -use c_str::CString; +use ffi::CString; +use io::{self, IoError, IoResult}; use mem; use ptr; -use sync::{Arc, Mutex}; +use str; use sync::atomic::{AtomicBool, Ordering}; -use io::{self, IoError, IoResult}; +use sync::{Arc, Mutex}; use sys_common::{self, eof}; -use super::{c, os, timer, to_utf16, decode_error_detailed}; +use super::{c, os, timer, decode_error_detailed}; + +fn to_utf16(c: &CString) -> IoResult<Vec<u16>> { + super::to_utf16(str::from_utf8(c.as_bytes()).ok()) +} struct Event(libc::HANDLE); @@ -270,7 +275,7 @@ impl UnixStream { } pub fn connect(addr: &CString, timeout: Option<u64>) -> IoResult<UnixStream> { - let addr = try!(to_utf16(addr.as_str())); + let addr = try!(to_utf16(addr)); let start = timer::now(); loop { match UnixStream::try_connect(addr.as_ptr()) { @@ -571,7 +576,7 @@ impl UnixListener { // Although we technically don't need the pipe until much later, we // create the initial handle up front to test the validity of the name // and such. - let addr_v = try!(to_utf16(addr.as_str())); + let addr_v = try!(to_utf16(addr)); let ret = unsafe { pipe(addr_v.as_ptr(), true) }; if ret == libc::INVALID_HANDLE_VALUE { Err(super::last_error()) @@ -661,7 +666,7 @@ impl UnixAcceptor { // proceed in accepting new clients in the future if self.inner.closed.load(Ordering::SeqCst) { return Err(eof()) } - let name = try!(to_utf16(self.listener.name.as_str())); + let name = try!(to_utf16(&self.listener.name)); // Once we've got a "server handle", we need to wait for a client to // connect. The ConnectNamedPipe function will block this thread until @@ -753,7 +758,7 @@ impl UnixAcceptor { impl Clone for UnixAcceptor { fn clone(&self) -> UnixAcceptor { - let name = to_utf16(self.listener.name.as_str()).ok().unwrap(); + let name = to_utf16(&self.listener.name).ok().unwrap(); UnixAcceptor { inner: self.inner.clone(), event: Event::new(true, false).ok().unwrap(), diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs index 81e8f974a12..9b3f2ca0373 100644 --- a/src/libstd/sys/windows/process.rs +++ b/src/libstd/sys/windows/process.rs @@ -10,27 +10,26 @@ use prelude::v1::*; +use collections; +use ffi::CString; +use hash::Hash; +use io::fs::PathExtensions; +use io::process::{ProcessExit, ExitStatus, ExitSignal}; +use io::{IoResult, IoError}; +use io; use libc::{pid_t, c_void, c_int}; use libc; -use c_str::{CString, ToCStr}; -use io; use mem; use os; -use ptr; -use io::process::{ProcessExit, ExitStatus, ExitSignal}; -use collections; use path::BytesContainer; -use hash::Hash; -use io::{IoResult, IoError}; - +use ptr; +use str; +use sys::fs::FileDesc; use sys::fs; use sys::{self, retry, c, wouldblock, set_nonblocking, ms_to_timeval, timer}; -use sys::fs::FileDesc; use sys_common::helper_thread::Helper; use sys_common::{AsInner, mkerr_libc, timeout}; -use io::fs::PathExtensions; - pub use sys_common::ProcessConfig; /// A value representing a child process. @@ -142,10 +141,10 @@ impl Process { // Split the value and test each path to see if the // program exists. for path in os::split_paths(v.container_as_bytes()).into_iter() { - let path = path.join(cfg.program().as_bytes_no_nul()) + let path = path.join(cfg.program().as_bytes()) .with_extension(os::consts::EXE_EXTENSION); if path.exists() { - return Some(path.to_c_str()) + return Some(CString::from_slice(path.as_vec())) } } break @@ -363,11 +362,11 @@ fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMA fn make_command_line(prog: &CString, args: &[CString]) -> String { let mut cmd = String::new(); - append_arg(&mut cmd, prog.as_str() + append_arg(&mut cmd, str::from_utf8(prog.as_bytes()).ok() .expect("expected program name to be utf-8 encoded")); for arg in args.iter() { cmd.push(' '); - append_arg(&mut cmd, arg.as_str() + append_arg(&mut cmd, str::from_utf8(arg.as_bytes()).ok() .expect("expected argument to be utf-8 encoded")); } return cmd; @@ -449,7 +448,7 @@ fn with_dirp<T, F>(d: Option<&CString>, cb: F) -> T where { match d { Some(dir) => { - let dir_str = dir.as_str() + let dir_str = str::from_utf8(dir.as_bytes()).ok() .expect("expected workingdirectory to be utf-8 encoded"); let mut dir_str: Vec<u16> = dir_str.utf16_units().collect(); dir_str.push(0); |
