rollup merge of #20507: alexcrichton/issue-20444

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
This commit is contained in:
Alex Crichton 2015-01-05 18:37:22 -08:00
commit 25d5a3a194
59 changed files with 1018 additions and 1998 deletions

View file

@ -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 })
}
}

View file

@ -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"));
}
}