mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-29 03:27:44 +00:00
std: Remove old_io/old_path/rand modules
This commit entirely removes the old I/O, path, and rand modules. All functionality has been deprecated and unstable for quite some time now!
This commit is contained in:
parent
dabf0c6371
commit
bf4e77d4b5
@ -24,15 +24,13 @@
|
|||||||
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
|
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
|
||||||
html_root_url = "http://doc.rust-lang.org/nightly/",
|
html_root_url = "http://doc.rust-lang.org/nightly/",
|
||||||
html_playground_url = "http://play.rust-lang.org/")]
|
html_playground_url = "http://play.rust-lang.org/")]
|
||||||
#![feature(no_std)]
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![unstable(feature = "rand")]
|
|
||||||
#![feature(staged_api)]
|
|
||||||
#![staged_api]
|
#![staged_api]
|
||||||
|
#![unstable(feature = "rand")]
|
||||||
#![feature(core)]
|
#![feature(core)]
|
||||||
|
#![feature(no_std)]
|
||||||
|
#![feature(staged_api)]
|
||||||
#![feature(step_by)]
|
#![feature(step_by)]
|
||||||
#![deprecated(reason = "use the crates.io `rand` library instead",
|
|
||||||
since = "1.0.0-alpha")]
|
|
||||||
|
|
||||||
#![cfg_attr(test, feature(test, rand, rustc_private))]
|
#![cfg_attr(test, feature(test, rand, rustc_private))]
|
||||||
|
|
||||||
|
@ -30,7 +30,6 @@ Core encoding and decoding interfaces.
|
|||||||
#![feature(box_syntax)]
|
#![feature(box_syntax)]
|
||||||
#![feature(collections)]
|
#![feature(collections)]
|
||||||
#![feature(core)]
|
#![feature(core)]
|
||||||
#![feature(old_path)]
|
|
||||||
#![feature(rustc_private)]
|
#![feature(rustc_private)]
|
||||||
#![feature(staged_api)]
|
#![feature(staged_api)]
|
||||||
#![feature(std_misc)]
|
#![feature(std_misc)]
|
||||||
|
@ -14,8 +14,6 @@
|
|||||||
Core encoding and decoding interfaces.
|
Core encoding and decoding interfaces.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
use std::old_path::{self, GenericPath};
|
|
||||||
use std::path;
|
use std::path;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
@ -540,36 +538,6 @@ macro_rules! tuple {
|
|||||||
|
|
||||||
tuple! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, }
|
tuple! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, }
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
impl Encodable for old_path::posix::Path {
|
|
||||||
fn encode<S: Encoder>(&self, e: &mut S) -> Result<(), S::Error> {
|
|
||||||
self.as_vec().encode(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
impl Decodable for old_path::posix::Path {
|
|
||||||
fn decode<D: Decoder>(d: &mut D) -> Result<old_path::posix::Path, D::Error> {
|
|
||||||
let bytes: Vec<u8> = try!(Decodable::decode(d));
|
|
||||||
Ok(old_path::posix::Path::new(bytes))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
impl Encodable for old_path::windows::Path {
|
|
||||||
fn encode<S: Encoder>(&self, e: &mut S) -> Result<(), S::Error> {
|
|
||||||
self.as_vec().encode(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
impl Decodable for old_path::windows::Path {
|
|
||||||
fn decode<D: Decoder>(d: &mut D) -> Result<old_path::windows::Path, D::Error> {
|
|
||||||
let bytes: Vec<u8> = try!(Decodable::decode(d));
|
|
||||||
Ok(old_path::windows::Path::new(bytes))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Encodable for path::PathBuf {
|
impl Encodable for path::PathBuf {
|
||||||
fn encode<S: Encoder>(&self, e: &mut S) -> Result<(), S::Error> {
|
fn encode<S: Encoder>(&self, e: &mut S) -> Result<(), S::Error> {
|
||||||
self.to_str().unwrap().encode(e)
|
self.to_str().unwrap().encode(e)
|
||||||
|
@ -18,8 +18,6 @@ use io;
|
|||||||
use iter::Iterator;
|
use iter::Iterator;
|
||||||
use libc;
|
use libc;
|
||||||
use mem;
|
use mem;
|
||||||
#[allow(deprecated)]
|
|
||||||
use old_io;
|
|
||||||
use ops::Deref;
|
use ops::Deref;
|
||||||
use option::Option::{self, Some, None};
|
use option::Option::{self, Some, None};
|
||||||
use result::Result::{self, Ok, Err};
|
use result::Result::{self, Ok, Err};
|
||||||
@ -245,18 +243,6 @@ impl From<NulError> for io::Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
#[allow(deprecated)]
|
|
||||||
impl From<NulError> for old_io::IoError {
|
|
||||||
fn from(_: NulError) -> old_io::IoError {
|
|
||||||
old_io::IoError {
|
|
||||||
kind: old_io::IoErrorKind::InvalidInput,
|
|
||||||
desc: "data provided contains a nul byte",
|
|
||||||
detail: None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CStr {
|
impl CStr {
|
||||||
/// Cast a raw C string to a safe C string wrapper.
|
/// Cast a raw C string to a safe C string wrapper.
|
||||||
///
|
///
|
||||||
|
@ -42,7 +42,6 @@ use string::String;
|
|||||||
use ops;
|
use ops;
|
||||||
use cmp;
|
use cmp;
|
||||||
use hash::{Hash, Hasher};
|
use hash::{Hash, Hasher};
|
||||||
use old_path::{Path, GenericPath};
|
|
||||||
use vec::Vec;
|
use vec::Vec;
|
||||||
|
|
||||||
use sys::os_str::{Buf, Slice};
|
use sys::os_str::{Buf, Slice};
|
||||||
@ -447,21 +446,6 @@ impl AsRef<OsStr> for String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
#[deprecated(since = "1.0.0", reason = "trait is deprecated")]
|
|
||||||
impl AsOsStr for Path {
|
|
||||||
#[cfg(unix)]
|
|
||||||
fn as_os_str(&self) -> &OsStr {
|
|
||||||
unsafe { mem::transmute(self.as_vec()) }
|
|
||||||
}
|
|
||||||
#[cfg(windows)]
|
|
||||||
fn as_os_str(&self) -> &OsStr {
|
|
||||||
// currently .as_str() is actually infallible on windows
|
|
||||||
OsStr::from_str(self.as_str().unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromInner<Buf> for OsString {
|
impl FromInner<Buf> for OsString {
|
||||||
fn from_inner(buf: Buf) -> OsString {
|
fn from_inner(buf: Buf) -> OsString {
|
||||||
OsString { inner: buf }
|
OsString { inner: buf }
|
||||||
|
@ -262,12 +262,9 @@ pub mod ffi;
|
|||||||
pub mod fs;
|
pub mod fs;
|
||||||
pub mod io;
|
pub mod io;
|
||||||
pub mod net;
|
pub mod net;
|
||||||
pub mod old_io;
|
|
||||||
pub mod old_path;
|
|
||||||
pub mod os;
|
pub mod os;
|
||||||
pub mod path;
|
pub mod path;
|
||||||
pub mod process;
|
pub mod process;
|
||||||
pub mod rand;
|
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
|
||||||
@ -281,6 +278,7 @@ pub mod time;
|
|||||||
|
|
||||||
pub mod rt;
|
pub mod rt;
|
||||||
mod panicking;
|
mod panicking;
|
||||||
|
mod rand;
|
||||||
|
|
||||||
// Modules that exist purely to document + host impl docs for primitive types
|
// Modules that exist purely to document + host impl docs for primitive types
|
||||||
|
|
||||||
@ -297,8 +295,6 @@ mod std {
|
|||||||
pub use sync; // used for select!()
|
pub use sync; // used for select!()
|
||||||
pub use error; // used for try!()
|
pub use error; // used for try!()
|
||||||
pub use fmt; // used for any formatting strings
|
pub use fmt; // used for any formatting strings
|
||||||
#[allow(deprecated)]
|
|
||||||
pub use old_io; // used for println!()
|
|
||||||
pub use option; // used for bitflags!{}
|
pub use option; // used for bitflags!{}
|
||||||
pub use rt; // used for panic!()
|
pub use rt; // used for panic!()
|
||||||
pub use vec; // used for vec![]
|
pub use vec; // used for vec![]
|
||||||
|
@ -1,702 +0,0 @@
|
|||||||
// Copyright 2013 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.
|
|
||||||
//
|
|
||||||
// ignore-lexer-test FIXME #15883
|
|
||||||
|
|
||||||
//! Buffering wrappers for I/O traits
|
|
||||||
|
|
||||||
use cmp;
|
|
||||||
use fmt;
|
|
||||||
use old_io::{Reader, Writer, Stream, Buffer, DEFAULT_BUF_SIZE, IoResult};
|
|
||||||
use iter::{Iterator, ExactSizeIterator, repeat};
|
|
||||||
use ops::Drop;
|
|
||||||
use option::Option;
|
|
||||||
use option::Option::{Some, None};
|
|
||||||
use result::Result::Ok;
|
|
||||||
use slice;
|
|
||||||
use vec::Vec;
|
|
||||||
|
|
||||||
/// Wraps a Reader and buffers input from it
|
|
||||||
///
|
|
||||||
/// It can be excessively inefficient to work directly with a `Reader`. For
|
|
||||||
/// example, every call to `read` on `TcpStream` results in a system call. A
|
|
||||||
/// `BufferedReader` performs large, infrequent reads on the underlying
|
|
||||||
/// `Reader` and maintains an in-memory buffer of the results.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![feature(old_io, old_path)]
|
|
||||||
/// use std::old_io::*;
|
|
||||||
/// use std::old_path::Path;
|
|
||||||
///
|
|
||||||
/// let file = File::open(&Path::new("message.txt"));
|
|
||||||
/// let mut reader = BufferedReader::new(file);
|
|
||||||
///
|
|
||||||
/// let mut buf = [0; 100];
|
|
||||||
/// match reader.read(&mut buf) {
|
|
||||||
/// Ok(nread) => println!("Read {} bytes", nread),
|
|
||||||
/// Err(e) => println!("error reading: {}", e)
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub struct BufferedReader<R> {
|
|
||||||
inner: R,
|
|
||||||
buf: Vec<u8>,
|
|
||||||
pos: usize,
|
|
||||||
cap: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl<R> fmt::Debug for BufferedReader<R> where R: fmt::Debug {
|
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(fmt, "BufferedReader {{ reader: {:?}, buffer: {}/{} }}",
|
|
||||||
self.inner, self.cap - self.pos, self.buf.len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: Reader> BufferedReader<R> {
|
|
||||||
/// Creates a new `BufferedReader` with the specified buffer capacity
|
|
||||||
pub fn with_capacity(cap: usize, inner: R) -> BufferedReader<R> {
|
|
||||||
BufferedReader {
|
|
||||||
inner: inner,
|
|
||||||
// We can't use the same trick here as we do for BufferedWriter,
|
|
||||||
// since this memory is visible to the inner Reader.
|
|
||||||
buf: repeat(0).take(cap).collect(),
|
|
||||||
pos: 0,
|
|
||||||
cap: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new `BufferedReader` with a default buffer capacity
|
|
||||||
pub fn new(inner: R) -> BufferedReader<R> {
|
|
||||||
BufferedReader::with_capacity(DEFAULT_BUF_SIZE, inner)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets a reference to the underlying reader.
|
|
||||||
pub fn get_ref<'a>(&self) -> &R { &self.inner }
|
|
||||||
|
|
||||||
/// Gets a mutable reference to the underlying reader.
|
|
||||||
///
|
|
||||||
/// # Warning
|
|
||||||
///
|
|
||||||
/// It is inadvisable to directly read from the underlying reader.
|
|
||||||
pub fn get_mut(&mut self) -> &mut R { &mut self.inner }
|
|
||||||
|
|
||||||
/// Unwraps this `BufferedReader`, returning the underlying reader.
|
|
||||||
///
|
|
||||||
/// Note that any leftover data in the internal buffer is lost.
|
|
||||||
pub fn into_inner(self) -> R { self.inner }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: Reader> Buffer for BufferedReader<R> {
|
|
||||||
fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> {
|
|
||||||
if self.pos == self.cap {
|
|
||||||
self.cap = try!(self.inner.read(&mut self.buf));
|
|
||||||
self.pos = 0;
|
|
||||||
}
|
|
||||||
Ok(&self.buf[self.pos..self.cap])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn consume(&mut self, amt: usize) {
|
|
||||||
self.pos += amt;
|
|
||||||
assert!(self.pos <= self.cap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: Reader> Reader for BufferedReader<R> {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
|
||||||
if self.pos == self.cap && buf.len() >= self.buf.len() {
|
|
||||||
return self.inner.read(buf);
|
|
||||||
}
|
|
||||||
let nread = {
|
|
||||||
let available = try!(self.fill_buf());
|
|
||||||
let nread = cmp::min(available.len(), buf.len());
|
|
||||||
slice::bytes::copy_memory(&available[..nread], buf);
|
|
||||||
nread
|
|
||||||
};
|
|
||||||
self.pos += nread;
|
|
||||||
Ok(nread)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wraps a Writer and buffers output to it
|
|
||||||
///
|
|
||||||
/// It can be excessively inefficient to work directly with a `Writer`. For
|
|
||||||
/// example, every call to `write` on `TcpStream` results in a system call. A
|
|
||||||
/// `BufferedWriter` keeps an in memory buffer of data and writes it to the
|
|
||||||
/// underlying `Writer` in large, infrequent batches.
|
|
||||||
///
|
|
||||||
/// This writer will be flushed when it is dropped.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![feature(old_io, old_path)]
|
|
||||||
/// use std::old_io::*;
|
|
||||||
/// use std::old_path::Path;
|
|
||||||
///
|
|
||||||
/// let file = File::create(&Path::new("message.txt")).unwrap();
|
|
||||||
/// let mut writer = BufferedWriter::new(file);
|
|
||||||
///
|
|
||||||
/// writer.write_str("hello, world").unwrap();
|
|
||||||
/// writer.flush().unwrap();
|
|
||||||
/// ```
|
|
||||||
pub struct BufferedWriter<W: Writer> {
|
|
||||||
inner: Option<W>,
|
|
||||||
buf: Vec<u8>,
|
|
||||||
pos: usize
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl<W: Writer> fmt::Debug for BufferedWriter<W> where W: fmt::Debug {
|
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(fmt, "BufferedWriter {{ writer: {:?}, buffer: {}/{} }}",
|
|
||||||
self.inner.as_ref().unwrap(), self.pos, self.buf.len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<W: Writer> BufferedWriter<W> {
|
|
||||||
/// Creates a new `BufferedWriter` with the specified buffer capacity
|
|
||||||
pub fn with_capacity(cap: usize, inner: W) -> BufferedWriter<W> {
|
|
||||||
// It's *much* faster to create an uninitialized buffer than it is to
|
|
||||||
// fill everything in with 0. This buffer is entirely an implementation
|
|
||||||
// detail and is never exposed, so we're safe to not initialize
|
|
||||||
// everything up-front. This allows creation of BufferedWriter instances
|
|
||||||
// to be very cheap (large mallocs are not nearly as expensive as large
|
|
||||||
// callocs).
|
|
||||||
let mut buf = Vec::with_capacity(cap);
|
|
||||||
unsafe { buf.set_len(cap); }
|
|
||||||
BufferedWriter {
|
|
||||||
inner: Some(inner),
|
|
||||||
buf: buf,
|
|
||||||
pos: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new `BufferedWriter` with a default buffer capacity
|
|
||||||
pub fn new(inner: W) -> BufferedWriter<W> {
|
|
||||||
BufferedWriter::with_capacity(DEFAULT_BUF_SIZE, inner)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush_buf(&mut self) -> IoResult<()> {
|
|
||||||
if self.pos != 0 {
|
|
||||||
let ret = self.inner.as_mut().unwrap().write_all(&self.buf[..self.pos]);
|
|
||||||
self.pos = 0;
|
|
||||||
ret
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets a reference to the underlying writer.
|
|
||||||
pub fn get_ref(&self) -> &W { self.inner.as_ref().unwrap() }
|
|
||||||
|
|
||||||
/// Gets a mutable reference to the underlying write.
|
|
||||||
///
|
|
||||||
/// # Warning
|
|
||||||
///
|
|
||||||
/// It is inadvisable to directly read from the underlying writer.
|
|
||||||
pub fn get_mut(&mut self) -> &mut W { self.inner.as_mut().unwrap() }
|
|
||||||
|
|
||||||
/// Unwraps this `BufferedWriter`, returning the underlying writer.
|
|
||||||
///
|
|
||||||
/// The buffer is flushed before returning the writer.
|
|
||||||
pub fn into_inner(mut self) -> W {
|
|
||||||
// FIXME(#12628): is panicking the right thing to do if flushing panicks?
|
|
||||||
self.flush_buf().unwrap();
|
|
||||||
self.inner.take().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<W: Writer> Writer for BufferedWriter<W> {
|
|
||||||
fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
|
|
||||||
if self.pos + buf.len() > self.buf.len() {
|
|
||||||
try!(self.flush_buf());
|
|
||||||
}
|
|
||||||
|
|
||||||
if buf.len() > self.buf.len() {
|
|
||||||
self.inner.as_mut().unwrap().write_all(buf)
|
|
||||||
} else {
|
|
||||||
let dst = &mut self.buf[self.pos..];
|
|
||||||
slice::bytes::copy_memory(buf, dst);
|
|
||||||
self.pos += buf.len();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush(&mut self) -> IoResult<()> {
|
|
||||||
self.flush_buf().and_then(|()| self.inner.as_mut().unwrap().flush())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unsafe_destructor]
|
|
||||||
impl<W: Writer> Drop for BufferedWriter<W> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if self.inner.is_some() {
|
|
||||||
// dtors should not panic, so we ignore a panicked flush
|
|
||||||
let _ = self.flush_buf();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wraps a Writer and buffers output to it, flushing whenever a newline (`0x0a`,
|
|
||||||
/// `'\n'`) is detected.
|
|
||||||
///
|
|
||||||
/// This writer will be flushed when it is dropped.
|
|
||||||
pub struct LineBufferedWriter<W: Writer> {
|
|
||||||
inner: BufferedWriter<W>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl<W: Writer> fmt::Debug for LineBufferedWriter<W> where W: fmt::Debug {
|
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(fmt, "LineBufferedWriter {{ writer: {:?}, buffer: {}/{} }}",
|
|
||||||
self.inner.inner, self.inner.pos, self.inner.buf.len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<W: Writer> LineBufferedWriter<W> {
|
|
||||||
/// Creates a new `LineBufferedWriter`
|
|
||||||
pub fn new(inner: W) -> LineBufferedWriter<W> {
|
|
||||||
// Lines typically aren't that long, don't use a giant buffer
|
|
||||||
LineBufferedWriter {
|
|
||||||
inner: BufferedWriter::with_capacity(1024, inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets a reference to the underlying writer.
|
|
||||||
///
|
|
||||||
/// This type does not expose the ability to get a mutable reference to the
|
|
||||||
/// underlying reader because that could possibly corrupt the buffer.
|
|
||||||
pub fn get_ref<'a>(&'a self) -> &'a W { self.inner.get_ref() }
|
|
||||||
|
|
||||||
/// Unwraps this `LineBufferedWriter`, returning the underlying writer.
|
|
||||||
///
|
|
||||||
/// The internal buffer is flushed before returning the writer.
|
|
||||||
pub fn into_inner(self) -> W { self.inner.into_inner() }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<W: Writer> Writer for LineBufferedWriter<W> {
|
|
||||||
fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
|
|
||||||
match buf.iter().rposition(|&b| b == b'\n') {
|
|
||||||
Some(i) => {
|
|
||||||
try!(self.inner.write_all(&buf[..i + 1]));
|
|
||||||
try!(self.inner.flush());
|
|
||||||
try!(self.inner.write_all(&buf[i + 1..]));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
None => self.inner.write_all(buf),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush(&mut self) -> IoResult<()> { self.inner.flush() }
|
|
||||||
}
|
|
||||||
|
|
||||||
struct InternalBufferedWriter<W: Writer>(BufferedWriter<W>);
|
|
||||||
|
|
||||||
impl<W: Writer> InternalBufferedWriter<W> {
|
|
||||||
fn get_mut<'a>(&'a mut self) -> &'a mut BufferedWriter<W> {
|
|
||||||
let InternalBufferedWriter(ref mut w) = *self;
|
|
||||||
return w;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<W: Reader + Writer> Reader for InternalBufferedWriter<W> {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
|
||||||
self.get_mut().inner.as_mut().unwrap().read(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wraps a Stream and buffers input and output to and from it.
|
|
||||||
///
|
|
||||||
/// It can be excessively inefficient to work directly with a `Stream`. For
|
|
||||||
/// example, every call to `read` or `write` on `TcpStream` results in a system
|
|
||||||
/// call. A `BufferedStream` keeps in memory buffers of data, making large,
|
|
||||||
/// infrequent calls to `read` and `write` on the underlying `Stream`.
|
|
||||||
///
|
|
||||||
/// The output half will be flushed when this stream is dropped.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![feature(old_io, old_path)]
|
|
||||||
/// # #![allow(unused_must_use)]
|
|
||||||
/// use std::old_io::*;
|
|
||||||
/// use std::old_path::Path;
|
|
||||||
///
|
|
||||||
/// let file = File::open(&Path::new("message.txt"));
|
|
||||||
/// let mut stream = BufferedStream::new(file);
|
|
||||||
///
|
|
||||||
/// stream.write_all("hello, world".as_bytes());
|
|
||||||
/// stream.flush();
|
|
||||||
///
|
|
||||||
/// let mut buf = [0; 100];
|
|
||||||
/// match stream.read(&mut buf) {
|
|
||||||
/// Ok(nread) => println!("Read {} bytes", nread),
|
|
||||||
/// Err(e) => println!("error reading: {}", e)
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub struct BufferedStream<S: Writer> {
|
|
||||||
inner: BufferedReader<InternalBufferedWriter<S>>
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl<S: Writer> fmt::Debug for BufferedStream<S> where S: fmt::Debug {
|
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
let reader = &self.inner;
|
|
||||||
let writer = &self.inner.inner.0;
|
|
||||||
write!(fmt, "BufferedStream {{ stream: {:?}, write_buffer: {}/{}, read_buffer: {}/{} }}",
|
|
||||||
writer.inner,
|
|
||||||
writer.pos, writer.buf.len(),
|
|
||||||
reader.cap - reader.pos, reader.buf.len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: Stream> BufferedStream<S> {
|
|
||||||
/// Creates a new buffered stream with explicitly listed capacities for the
|
|
||||||
/// reader/writer buffer.
|
|
||||||
pub fn with_capacities(reader_cap: usize, writer_cap: usize, inner: S)
|
|
||||||
-> BufferedStream<S> {
|
|
||||||
let writer = BufferedWriter::with_capacity(writer_cap, inner);
|
|
||||||
let internal_writer = InternalBufferedWriter(writer);
|
|
||||||
let reader = BufferedReader::with_capacity(reader_cap,
|
|
||||||
internal_writer);
|
|
||||||
BufferedStream { inner: reader }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new buffered stream with the default reader/writer buffer
|
|
||||||
/// capacities.
|
|
||||||
pub fn new(inner: S) -> BufferedStream<S> {
|
|
||||||
BufferedStream::with_capacities(DEFAULT_BUF_SIZE, DEFAULT_BUF_SIZE,
|
|
||||||
inner)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets a reference to the underlying stream.
|
|
||||||
pub fn get_ref(&self) -> &S {
|
|
||||||
let InternalBufferedWriter(ref w) = self.inner.inner;
|
|
||||||
w.get_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets a mutable reference to the underlying stream.
|
|
||||||
///
|
|
||||||
/// # Warning
|
|
||||||
///
|
|
||||||
/// It is inadvisable to read directly from or write directly to the
|
|
||||||
/// underlying stream.
|
|
||||||
pub fn get_mut(&mut self) -> &mut S {
|
|
||||||
let InternalBufferedWriter(ref mut w) = self.inner.inner;
|
|
||||||
w.get_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unwraps this `BufferedStream`, returning the underlying stream.
|
|
||||||
///
|
|
||||||
/// The internal buffer is flushed before returning the stream. Any leftover
|
|
||||||
/// data in the read buffer is lost.
|
|
||||||
pub fn into_inner(self) -> S {
|
|
||||||
let InternalBufferedWriter(w) = self.inner.inner;
|
|
||||||
w.into_inner()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: Stream> Buffer for BufferedStream<S> {
|
|
||||||
fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> { self.inner.fill_buf() }
|
|
||||||
fn consume(&mut self, amt: usize) { self.inner.consume(amt) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: Stream> Reader for BufferedStream<S> {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
|
||||||
self.inner.read(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: Stream> Writer for BufferedStream<S> {
|
|
||||||
fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
|
|
||||||
self.inner.inner.get_mut().write_all(buf)
|
|
||||||
}
|
|
||||||
fn flush(&mut self) -> IoResult<()> {
|
|
||||||
self.inner.inner.get_mut().flush()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
extern crate test;
|
|
||||||
use old_io::{self, Reader, Writer, Buffer, BufferPrelude};
|
|
||||||
use prelude::v1::*;
|
|
||||||
use super::*;
|
|
||||||
use super::super::{IoResult, EndOfFile};
|
|
||||||
use super::super::mem::MemReader;
|
|
||||||
use self::test::Bencher;
|
|
||||||
|
|
||||||
/// A type, free to create, primarily intended for benchmarking creation of
|
|
||||||
/// wrappers that, just for construction, don't need a Reader/Writer that
|
|
||||||
/// does anything useful. Is equivalent to `/dev/null` in semantics.
|
|
||||||
#[derive(Clone,PartialEq,PartialOrd)]
|
|
||||||
pub struct NullStream;
|
|
||||||
|
|
||||||
impl Reader for NullStream {
|
|
||||||
fn read(&mut self, _: &mut [u8]) -> old_io::IoResult<usize> {
|
|
||||||
Err(old_io::standard_error(old_io::EndOfFile))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Writer for NullStream {
|
|
||||||
fn write_all(&mut self, _: &[u8]) -> old_io::IoResult<()> { Ok(()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A dummy reader intended at testing short-reads propagation.
|
|
||||||
pub struct ShortReader {
|
|
||||||
lengths: Vec<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Reader for ShortReader {
|
|
||||||
fn read(&mut self, _: &mut [u8]) -> old_io::IoResult<usize> {
|
|
||||||
if self.lengths.is_empty() {
|
|
||||||
Err(old_io::standard_error(old_io::EndOfFile))
|
|
||||||
} else {
|
|
||||||
Ok(self.lengths.remove(0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_buffered_reader() {
|
|
||||||
let inner = MemReader::new(vec!(5, 6, 7, 0, 1, 2, 3, 4));
|
|
||||||
let mut reader = BufferedReader::with_capacity(2, inner);
|
|
||||||
|
|
||||||
let mut buf = [0, 0, 0];
|
|
||||||
let nread = reader.read(&mut buf);
|
|
||||||
assert_eq!(Ok(3), nread);
|
|
||||||
let b: &[_] = &[5, 6, 7];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
|
|
||||||
let mut buf = [0, 0];
|
|
||||||
let nread = reader.read(&mut buf);
|
|
||||||
assert_eq!(Ok(2), nread);
|
|
||||||
let b: &[_] = &[0, 1];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
|
|
||||||
let mut buf = [0];
|
|
||||||
let nread = reader.read(&mut buf);
|
|
||||||
assert_eq!(Ok(1), nread);
|
|
||||||
let b: &[_] = &[2];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
|
|
||||||
let mut buf = [0, 0, 0];
|
|
||||||
let nread = reader.read(&mut buf);
|
|
||||||
assert_eq!(Ok(1), nread);
|
|
||||||
let b: &[_] = &[3, 0, 0];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
|
|
||||||
let nread = reader.read(&mut buf);
|
|
||||||
assert_eq!(Ok(1), nread);
|
|
||||||
let b: &[_] = &[4, 0, 0];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
|
|
||||||
assert!(reader.read(&mut buf).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_buffered_writer() {
|
|
||||||
let inner = Vec::new();
|
|
||||||
let mut writer = BufferedWriter::with_capacity(2, inner);
|
|
||||||
|
|
||||||
writer.write_all(&[0, 1]).unwrap();
|
|
||||||
let b: &[_] = &[];
|
|
||||||
assert_eq!(&writer.get_ref()[..], b);
|
|
||||||
|
|
||||||
writer.write_all(&[2]).unwrap();
|
|
||||||
let b: &[_] = &[0, 1];
|
|
||||||
assert_eq!(&writer.get_ref()[..], b);
|
|
||||||
|
|
||||||
writer.write_all(&[3]).unwrap();
|
|
||||||
assert_eq!(&writer.get_ref()[..], b);
|
|
||||||
|
|
||||||
writer.flush().unwrap();
|
|
||||||
let a: &[_] = &[0, 1, 2, 3];
|
|
||||||
assert_eq!(a, &writer.get_ref()[..]);
|
|
||||||
|
|
||||||
writer.write_all(&[4]).unwrap();
|
|
||||||
writer.write_all(&[5]).unwrap();
|
|
||||||
assert_eq!(a, &writer.get_ref()[..]);
|
|
||||||
|
|
||||||
writer.write_all(&[6]).unwrap();
|
|
||||||
let a: &[_] = &[0, 1, 2, 3, 4, 5];
|
|
||||||
assert_eq!(a, &writer.get_ref()[..]);
|
|
||||||
|
|
||||||
writer.write_all(&[7, 8]).unwrap();
|
|
||||||
let a: &[_] = &[0, 1, 2, 3, 4, 5, 6];
|
|
||||||
assert_eq!(a, &writer.get_ref()[..]);
|
|
||||||
|
|
||||||
writer.write_all(&[9, 10, 11]).unwrap();
|
|
||||||
let a: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
|
||||||
assert_eq!(a, &writer.get_ref()[..]);
|
|
||||||
|
|
||||||
writer.flush().unwrap();
|
|
||||||
assert_eq!(a, &writer.get_ref()[..]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_buffered_writer_inner_flushes() {
|
|
||||||
let mut w = BufferedWriter::with_capacity(3, Vec::new());
|
|
||||||
w.write_all(&[0, 1]).unwrap();
|
|
||||||
let a: &[_] = &[];
|
|
||||||
assert_eq!(&w.get_ref()[..], a);
|
|
||||||
let w = w.into_inner();
|
|
||||||
let a: &[_] = &[0, 1];
|
|
||||||
assert_eq!(a, &w[..]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is just here to make sure that we don't infinite loop in the
|
|
||||||
// newtype struct autoderef weirdness
|
|
||||||
#[test]
|
|
||||||
fn test_buffered_stream() {
|
|
||||||
struct S;
|
|
||||||
|
|
||||||
impl old_io::Writer for S {
|
|
||||||
fn write_all(&mut self, _: &[u8]) -> old_io::IoResult<()> { Ok(()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl old_io::Reader for S {
|
|
||||||
fn read(&mut self, _: &mut [u8]) -> old_io::IoResult<usize> {
|
|
||||||
Err(old_io::standard_error(old_io::EndOfFile))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut stream = BufferedStream::new(S);
|
|
||||||
let mut buf = [];
|
|
||||||
assert!(stream.read(&mut buf).is_err());
|
|
||||||
stream.write_all(&buf).unwrap();
|
|
||||||
stream.flush().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_read_until() {
|
|
||||||
let inner = MemReader::new(vec!(0, 1, 2, 1, 0));
|
|
||||||
let mut reader = BufferedReader::with_capacity(2, inner);
|
|
||||||
assert_eq!(reader.read_until(0), Ok(vec!(0)));
|
|
||||||
assert_eq!(reader.read_until(2), Ok(vec!(1, 2)));
|
|
||||||
assert_eq!(reader.read_until(1), Ok(vec!(1)));
|
|
||||||
assert_eq!(reader.read_until(8), Ok(vec!(0)));
|
|
||||||
assert!(reader.read_until(9).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_line_buffer() {
|
|
||||||
let mut writer = LineBufferedWriter::new(Vec::new());
|
|
||||||
writer.write_all(&[0]).unwrap();
|
|
||||||
let b: &[_] = &[];
|
|
||||||
assert_eq!(&writer.get_ref()[..], b);
|
|
||||||
writer.write_all(&[1]).unwrap();
|
|
||||||
assert_eq!(&writer.get_ref()[..], b);
|
|
||||||
writer.flush().unwrap();
|
|
||||||
let b: &[_] = &[0, 1];
|
|
||||||
assert_eq!(&writer.get_ref()[..], b);
|
|
||||||
writer.write_all(&[0, b'\n', 1, b'\n', 2]).unwrap();
|
|
||||||
let b: &[_] = &[0, 1, 0, b'\n', 1, b'\n'];
|
|
||||||
assert_eq!(&writer.get_ref()[..], b);
|
|
||||||
writer.flush().unwrap();
|
|
||||||
let b: &[_] = &[0, 1, 0, b'\n', 1, b'\n', 2];
|
|
||||||
assert_eq!(&writer.get_ref()[..], b);
|
|
||||||
writer.write_all(&[3, b'\n']).unwrap();
|
|
||||||
let b: &[_] = &[0, 1, 0, b'\n', 1, b'\n', 2, 3, b'\n'];
|
|
||||||
assert_eq!(&writer.get_ref()[..], b);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_read_line() {
|
|
||||||
let in_buf = MemReader::new(b"a\nb\nc".to_vec());
|
|
||||||
let mut reader = BufferedReader::with_capacity(2, in_buf);
|
|
||||||
assert_eq!(reader.read_line(), Ok("a\n".to_string()));
|
|
||||||
assert_eq!(reader.read_line(), Ok("b\n".to_string()));
|
|
||||||
assert_eq!(reader.read_line(), Ok("c".to_string()));
|
|
||||||
assert!(reader.read_line().is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_lines() {
|
|
||||||
let in_buf = MemReader::new(b"a\nb\nc".to_vec());
|
|
||||||
let mut reader = BufferedReader::with_capacity(2, in_buf);
|
|
||||||
let mut it = reader.lines();
|
|
||||||
assert_eq!(it.next(), Some(Ok("a\n".to_string())));
|
|
||||||
assert_eq!(it.next(), Some(Ok("b\n".to_string())));
|
|
||||||
assert_eq!(it.next(), Some(Ok("c".to_string())));
|
|
||||||
assert_eq!(it.next(), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_short_reads() {
|
|
||||||
let inner = ShortReader{lengths: vec![0, 1, 2, 0, 1, 0]};
|
|
||||||
let mut reader = BufferedReader::new(inner);
|
|
||||||
let mut buf = [0, 0];
|
|
||||||
assert_eq!(reader.read(&mut buf), Ok(0));
|
|
||||||
assert_eq!(reader.read(&mut buf), Ok(1));
|
|
||||||
assert_eq!(reader.read(&mut buf), Ok(2));
|
|
||||||
assert_eq!(reader.read(&mut buf), Ok(0));
|
|
||||||
assert_eq!(reader.read(&mut buf), Ok(1));
|
|
||||||
assert_eq!(reader.read(&mut buf), Ok(0));
|
|
||||||
assert!(reader.read(&mut buf).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_char_buffered() {
|
|
||||||
let buf = [195, 159];
|
|
||||||
let mut reader = BufferedReader::with_capacity(1, &buf[..]);
|
|
||||||
assert_eq!(reader.read_char(), Ok('ß'));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_chars() {
|
|
||||||
let buf = [195, 159, b'a'];
|
|
||||||
let mut reader = BufferedReader::with_capacity(1, &buf[..]);
|
|
||||||
let mut it = reader.chars();
|
|
||||||
assert_eq!(it.next(), Some(Ok('ß')));
|
|
||||||
assert_eq!(it.next(), Some(Ok('a')));
|
|
||||||
assert_eq!(it.next(), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn dont_panic_in_drop_on_panicked_flush() {
|
|
||||||
struct FailFlushWriter;
|
|
||||||
|
|
||||||
impl Writer for FailFlushWriter {
|
|
||||||
fn write_all(&mut self, _buf: &[u8]) -> IoResult<()> { Ok(()) }
|
|
||||||
fn flush(&mut self) -> IoResult<()> { Err(old_io::standard_error(EndOfFile)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
let writer = FailFlushWriter;
|
|
||||||
let _writer = BufferedWriter::new(writer);
|
|
||||||
|
|
||||||
// If writer panics *again* due to the flush error then the process will abort.
|
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_buffered_reader(b: &mut Bencher) {
|
|
||||||
b.iter(|| {
|
|
||||||
BufferedReader::new(NullStream)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_buffered_writer(b: &mut Bencher) {
|
|
||||||
b.iter(|| {
|
|
||||||
BufferedWriter::new(NullStream)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_buffered_stream(b: &mut Bencher) {
|
|
||||||
b.iter(|| {
|
|
||||||
BufferedStream::new(NullStream);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,247 +0,0 @@
|
|||||||
// Copyright 2013 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 clone::Clone;
|
|
||||||
use cmp;
|
|
||||||
use sync::mpsc::{Sender, Receiver};
|
|
||||||
use old_io;
|
|
||||||
use option::Option::{None, Some};
|
|
||||||
use result::Result::{Ok, Err};
|
|
||||||
use slice::bytes;
|
|
||||||
use super::{Buffer, Reader, Writer, IoResult};
|
|
||||||
use vec::Vec;
|
|
||||||
|
|
||||||
/// Allows reading from a rx.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![feature(old_io)]
|
|
||||||
/// use std::sync::mpsc::channel;
|
|
||||||
/// use std::old_io::*;
|
|
||||||
///
|
|
||||||
/// let (tx, rx) = channel();
|
|
||||||
/// # drop(tx);
|
|
||||||
/// let mut reader = ChanReader::new(rx);
|
|
||||||
///
|
|
||||||
/// let mut buf = [0; 100];
|
|
||||||
/// match reader.read(&mut buf) {
|
|
||||||
/// Ok(nread) => println!("Read {} bytes", nread),
|
|
||||||
/// Err(e) => println!("read error: {}", e),
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub struct ChanReader {
|
|
||||||
buf: Vec<u8>, // A buffer of bytes received but not consumed.
|
|
||||||
pos: usize, // How many of the buffered bytes have already be consumed.
|
|
||||||
rx: Receiver<Vec<u8>>, // The Receiver to pull data from.
|
|
||||||
closed: bool, // Whether the channel this Receiver connects to has been closed.
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ChanReader {
|
|
||||||
/// Wraps a `Port` in a `ChanReader` structure
|
|
||||||
pub fn new(rx: Receiver<Vec<u8>>) -> ChanReader {
|
|
||||||
ChanReader {
|
|
||||||
buf: Vec::new(),
|
|
||||||
pos: 0,
|
|
||||||
rx: rx,
|
|
||||||
closed: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Buffer for ChanReader {
|
|
||||||
fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> {
|
|
||||||
if self.pos >= self.buf.len() {
|
|
||||||
self.pos = 0;
|
|
||||||
match self.rx.recv() {
|
|
||||||
Ok(bytes) => {
|
|
||||||
self.buf = bytes;
|
|
||||||
},
|
|
||||||
Err(..) => {
|
|
||||||
self.closed = true;
|
|
||||||
self.buf = Vec::new();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if self.closed {
|
|
||||||
Err(old_io::standard_error(old_io::EndOfFile))
|
|
||||||
} else {
|
|
||||||
Ok(&self.buf[self.pos..])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn consume(&mut self, amt: usize) {
|
|
||||||
self.pos += amt;
|
|
||||||
assert!(self.pos <= self.buf.len());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Reader for ChanReader {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
|
||||||
let mut num_read = 0;
|
|
||||||
loop {
|
|
||||||
let count = match self.fill_buf().ok() {
|
|
||||||
Some(src) => {
|
|
||||||
let dst = &mut buf[num_read..];
|
|
||||||
let count = cmp::min(src.len(), dst.len());
|
|
||||||
bytes::copy_memory(&src[..count], dst);
|
|
||||||
count
|
|
||||||
},
|
|
||||||
None => 0,
|
|
||||||
};
|
|
||||||
self.consume(count);
|
|
||||||
num_read += count;
|
|
||||||
if num_read == buf.len() || self.closed {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if self.closed && num_read == 0 {
|
|
||||||
Err(old_io::standard_error(old_io::EndOfFile))
|
|
||||||
} else {
|
|
||||||
Ok(num_read)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allows writing to a tx.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![feature(old_io, io)]
|
|
||||||
/// # #![allow(unused_must_use)]
|
|
||||||
/// use std::sync::mpsc::channel;
|
|
||||||
/// use std::old_io::*;
|
|
||||||
///
|
|
||||||
/// let (tx, rx) = channel();
|
|
||||||
/// # drop(rx);
|
|
||||||
/// let mut writer = ChanWriter::new(tx);
|
|
||||||
/// writer.write("hello, world".as_bytes());
|
|
||||||
/// ```
|
|
||||||
pub struct ChanWriter {
|
|
||||||
tx: Sender<Vec<u8>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ChanWriter {
|
|
||||||
/// Wraps a channel in a `ChanWriter` structure
|
|
||||||
pub fn new(tx: Sender<Vec<u8>>) -> ChanWriter {
|
|
||||||
ChanWriter { tx: tx }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl Clone for ChanWriter {
|
|
||||||
fn clone(&self) -> ChanWriter {
|
|
||||||
ChanWriter { tx: self.tx.clone() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Writer for ChanWriter {
|
|
||||||
fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
|
|
||||||
self.tx.send(buf.to_vec()).map_err(|_| {
|
|
||||||
old_io::IoError {
|
|
||||||
kind: old_io::BrokenPipe,
|
|
||||||
desc: "Pipe closed",
|
|
||||||
detail: None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use prelude::v1::*;
|
|
||||||
|
|
||||||
use sync::mpsc::channel;
|
|
||||||
use super::*;
|
|
||||||
use old_io::{self, Reader, Writer, Buffer};
|
|
||||||
use thread;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rx_reader() {
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
thread::spawn(move|| {
|
|
||||||
tx.send(vec![1, 2]).unwrap();
|
|
||||||
tx.send(vec![]).unwrap();
|
|
||||||
tx.send(vec![3, 4]).unwrap();
|
|
||||||
tx.send(vec![5, 6]).unwrap();
|
|
||||||
tx.send(vec![7, 8]).unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut reader = ChanReader::new(rx);
|
|
||||||
let mut buf = [0; 3];
|
|
||||||
|
|
||||||
assert_eq!(Ok(0), reader.read(&mut []));
|
|
||||||
|
|
||||||
assert_eq!(Ok(3), reader.read(&mut buf));
|
|
||||||
let a: &[u8] = &[1,2,3];
|
|
||||||
assert_eq!(a, buf);
|
|
||||||
|
|
||||||
assert_eq!(Ok(3), reader.read(&mut buf));
|
|
||||||
let a: &[u8] = &[4,5,6];
|
|
||||||
assert_eq!(a, buf);
|
|
||||||
|
|
||||||
assert_eq!(Ok(2), reader.read(&mut buf));
|
|
||||||
let a: &[u8] = &[7,8,6];
|
|
||||||
assert_eq!(a, buf);
|
|
||||||
|
|
||||||
match reader.read(&mut buf) {
|
|
||||||
Ok(..) => panic!(),
|
|
||||||
Err(e) => assert_eq!(e.kind, old_io::EndOfFile),
|
|
||||||
}
|
|
||||||
assert_eq!(a, buf);
|
|
||||||
|
|
||||||
// Ensure it continues to panic in the same way.
|
|
||||||
match reader.read(&mut buf) {
|
|
||||||
Ok(..) => panic!(),
|
|
||||||
Err(e) => assert_eq!(e.kind, old_io::EndOfFile),
|
|
||||||
}
|
|
||||||
assert_eq!(a, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rx_buffer() {
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
thread::spawn(move|| {
|
|
||||||
tx.send(b"he".to_vec()).unwrap();
|
|
||||||
tx.send(b"llo wo".to_vec()).unwrap();
|
|
||||||
tx.send(b"".to_vec()).unwrap();
|
|
||||||
tx.send(b"rld\nhow ".to_vec()).unwrap();
|
|
||||||
tx.send(b"are you?".to_vec()).unwrap();
|
|
||||||
tx.send(b"".to_vec()).unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut reader = ChanReader::new(rx);
|
|
||||||
|
|
||||||
assert_eq!(Ok("hello world\n".to_string()), reader.read_line());
|
|
||||||
assert_eq!(Ok("how are you?".to_string()), reader.read_line());
|
|
||||||
match reader.read_line() {
|
|
||||||
Ok(..) => panic!(),
|
|
||||||
Err(e) => assert_eq!(e.kind, old_io::EndOfFile),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_chan_writer() {
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
let mut writer = ChanWriter::new(tx);
|
|
||||||
writer.write_be_u32(42).unwrap();
|
|
||||||
|
|
||||||
let wanted = vec![0, 0, 0, 42];
|
|
||||||
let got = thread::scoped(move|| { rx.recv().unwrap() }).join();
|
|
||||||
assert_eq!(wanted, got);
|
|
||||||
|
|
||||||
match writer.write_u8(1) {
|
|
||||||
Ok(..) => panic!(),
|
|
||||||
Err(e) => assert_eq!(e.kind, old_io::BrokenPipe),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,564 +0,0 @@
|
|||||||
// Copyright 2013-2014 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.
|
|
||||||
|
|
||||||
//! Utility mixins that apply to all Readers and Writers
|
|
||||||
|
|
||||||
#![allow(missing_docs)]
|
|
||||||
#![unstable(feature = "old_io")]
|
|
||||||
#![deprecated(since = "1.0.0",
|
|
||||||
reason = "functionality will be removed with no immediate \
|
|
||||||
replacement")]
|
|
||||||
|
|
||||||
// FIXME: Not sure how this should be structured
|
|
||||||
// FIXME: Iteration should probably be considered separately
|
|
||||||
|
|
||||||
use old_io::{IoError, IoResult, Reader};
|
|
||||||
use old_io;
|
|
||||||
use iter::Iterator;
|
|
||||||
use num::Int;
|
|
||||||
use ops::FnOnce;
|
|
||||||
use option::Option;
|
|
||||||
use option::Option::{Some, None};
|
|
||||||
use result::Result::{Ok, Err};
|
|
||||||
|
|
||||||
/// An iterator that reads a single byte on each iteration,
|
|
||||||
/// until `.read_byte()` returns `EndOfFile`.
|
|
||||||
///
|
|
||||||
/// # Notes about the Iteration Protocol
|
|
||||||
///
|
|
||||||
/// The `Bytes` may yield `None` and thus terminate
|
|
||||||
/// an iteration, but continue to yield elements if iteration
|
|
||||||
/// is attempted again.
|
|
||||||
///
|
|
||||||
/// # Error
|
|
||||||
///
|
|
||||||
/// Any error other than `EndOfFile` that is produced by the underlying Reader
|
|
||||||
/// is returned by the iterator and should be handled by the caller.
|
|
||||||
pub struct Bytes<'r, T:'r> {
|
|
||||||
reader: &'r mut T,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'r, R: Reader> Bytes<'r, R> {
|
|
||||||
/// Constructs a new byte iterator from the given Reader instance.
|
|
||||||
pub fn new(r: &'r mut R) -> Bytes<'r, R> {
|
|
||||||
Bytes {
|
|
||||||
reader: r,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'r, R: Reader> Iterator for Bytes<'r, R> {
|
|
||||||
type Item = IoResult<u8>;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn next(&mut self) -> Option<IoResult<u8>> {
|
|
||||||
match self.reader.read_byte() {
|
|
||||||
Ok(x) => Some(Ok(x)),
|
|
||||||
Err(IoError { kind: old_io::EndOfFile, .. }) => None,
|
|
||||||
Err(e) => Some(Err(e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts an 8-bit to 64-bit unsigned value to a little-endian byte
|
|
||||||
/// representation of the given size. If the size is not big enough to
|
|
||||||
/// represent the value, then the high-order bytes are truncated.
|
|
||||||
///
|
|
||||||
/// Arguments:
|
|
||||||
///
|
|
||||||
/// * `n`: The value to convert.
|
|
||||||
/// * `size`: The size of the value, in bytes. This must be 8 or less, or task
|
|
||||||
/// panic occurs. If this is less than 8, then a value of that
|
|
||||||
/// many bytes is produced. For example, if `size` is 4, then a
|
|
||||||
/// 32-bit byte representation is produced.
|
|
||||||
/// * `f`: A callback that receives the value.
|
|
||||||
///
|
|
||||||
/// This function returns the value returned by the callback, for convenience.
|
|
||||||
pub fn u64_to_le_bytes<T, F>(n: u64, size: usize, f: F) -> T where
|
|
||||||
F: FnOnce(&[u8]) -> T,
|
|
||||||
{
|
|
||||||
use mem::transmute;
|
|
||||||
|
|
||||||
// LLVM fails to properly optimize this when using shifts instead of the to_le* intrinsics
|
|
||||||
assert!(size <= 8);
|
|
||||||
match size {
|
|
||||||
1 => f(&[n as u8]),
|
|
||||||
2 => f(unsafe { & transmute::<_, [u8; 2]>((n as u16).to_le()) }),
|
|
||||||
4 => f(unsafe { & transmute::<_, [u8; 4]>((n as u32).to_le()) }),
|
|
||||||
8 => f(unsafe { & transmute::<_, [u8; 8]>(n.to_le()) }),
|
|
||||||
_ => {
|
|
||||||
|
|
||||||
let mut bytes = vec!();
|
|
||||||
let mut i = size;
|
|
||||||
let mut n = n;
|
|
||||||
while i > 0 {
|
|
||||||
bytes.push((n & 255) as u8);
|
|
||||||
n >>= 8;
|
|
||||||
i -= 1;
|
|
||||||
}
|
|
||||||
f(&bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts an 8-bit to 64-bit unsigned value to a big-endian byte
|
|
||||||
/// representation of the given size. If the size is not big enough to
|
|
||||||
/// represent the value, then the high-order bytes are truncated.
|
|
||||||
///
|
|
||||||
/// Arguments:
|
|
||||||
///
|
|
||||||
/// * `n`: The value to convert.
|
|
||||||
/// * `size`: The size of the value, in bytes. This must be 8 or less, or task
|
|
||||||
/// panic occurs. If this is less than 8, then a value of that
|
|
||||||
/// many bytes is produced. For example, if `size` is 4, then a
|
|
||||||
/// 32-bit byte representation is produced.
|
|
||||||
/// * `f`: A callback that receives the value.
|
|
||||||
///
|
|
||||||
/// This function returns the value returned by the callback, for convenience.
|
|
||||||
pub fn u64_to_be_bytes<T, F>(n: u64, size: usize, f: F) -> T where
|
|
||||||
F: FnOnce(&[u8]) -> T,
|
|
||||||
{
|
|
||||||
use mem::transmute;
|
|
||||||
|
|
||||||
// LLVM fails to properly optimize this when using shifts instead of the to_be* intrinsics
|
|
||||||
assert!(size <= 8);
|
|
||||||
match size {
|
|
||||||
1 => f(&[n as u8]),
|
|
||||||
2 => f(unsafe { & transmute::<_, [u8; 2]>((n as u16).to_be()) }),
|
|
||||||
4 => f(unsafe { & transmute::<_, [u8; 4]>((n as u32).to_be()) }),
|
|
||||||
8 => f(unsafe { & transmute::<_, [u8; 8]>(n.to_be()) }),
|
|
||||||
_ => {
|
|
||||||
let mut bytes = vec!();
|
|
||||||
let mut i = size;
|
|
||||||
while i > 0 {
|
|
||||||
let shift = (i - 1) * 8;
|
|
||||||
bytes.push((n >> shift) as u8);
|
|
||||||
i -= 1;
|
|
||||||
}
|
|
||||||
f(&bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extracts an 8-bit to 64-bit unsigned big-endian value from the given byte
|
|
||||||
/// buffer and returns it as a 64-bit value.
|
|
||||||
///
|
|
||||||
/// Arguments:
|
|
||||||
///
|
|
||||||
/// * `data`: The buffer in which to extract the value.
|
|
||||||
/// * `start`: The offset at which to extract the value.
|
|
||||||
/// * `size`: The size of the value in bytes to extract. This must be 8 or
|
|
||||||
/// less, or task panic occurs. If this is less than 8, then only
|
|
||||||
/// that many bytes are parsed. For example, if `size` is 4, then a
|
|
||||||
/// 32-bit value is parsed.
|
|
||||||
pub fn u64_from_be_bytes(data: &[u8], start: usize, size: usize) -> u64 {
|
|
||||||
use ptr::{copy_nonoverlapping};
|
|
||||||
|
|
||||||
assert!(size <= 8);
|
|
||||||
|
|
||||||
if data.len() - start < size {
|
|
||||||
panic!("index out of bounds");
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut buf = [0; 8];
|
|
||||||
unsafe {
|
|
||||||
let ptr = data.as_ptr().offset(start as isize);
|
|
||||||
let out = buf.as_mut_ptr();
|
|
||||||
copy_nonoverlapping(ptr, out.offset((8 - size) as isize), size);
|
|
||||||
(*(out as *const u64)).to_be()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use prelude::v1::*;
|
|
||||||
use old_io::{self, Reader, Writer};
|
|
||||||
use old_io::{MemReader, BytesReader};
|
|
||||||
|
|
||||||
struct InitialZeroByteReader {
|
|
||||||
count: isize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Reader for InitialZeroByteReader {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<usize> {
|
|
||||||
if self.count == 0 {
|
|
||||||
self.count = 1;
|
|
||||||
Ok(0)
|
|
||||||
} else {
|
|
||||||
buf[0] = 10;
|
|
||||||
Ok(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct EofReader;
|
|
||||||
|
|
||||||
impl Reader for EofReader {
|
|
||||||
fn read(&mut self, _: &mut [u8]) -> old_io::IoResult<usize> {
|
|
||||||
Err(old_io::standard_error(old_io::EndOfFile))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ErroringReader;
|
|
||||||
|
|
||||||
impl Reader for ErroringReader {
|
|
||||||
fn read(&mut self, _: &mut [u8]) -> old_io::IoResult<usize> {
|
|
||||||
Err(old_io::standard_error(old_io::InvalidInput))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PartialReader {
|
|
||||||
count: isize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Reader for PartialReader {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<usize> {
|
|
||||||
if self.count == 0 {
|
|
||||||
self.count = 1;
|
|
||||||
buf[0] = 10;
|
|
||||||
buf[1] = 11;
|
|
||||||
Ok(2)
|
|
||||||
} else {
|
|
||||||
buf[0] = 12;
|
|
||||||
buf[1] = 13;
|
|
||||||
Ok(2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ErroringLaterReader {
|
|
||||||
count: isize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Reader for ErroringLaterReader {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<usize> {
|
|
||||||
if self.count == 0 {
|
|
||||||
self.count = 1;
|
|
||||||
buf[0] = 10;
|
|
||||||
Ok(1)
|
|
||||||
} else {
|
|
||||||
Err(old_io::standard_error(old_io::InvalidInput))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ThreeChunkReader {
|
|
||||||
count: isize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Reader for ThreeChunkReader {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<usize> {
|
|
||||||
if self.count == 0 {
|
|
||||||
self.count = 1;
|
|
||||||
buf[0] = 10;
|
|
||||||
buf[1] = 11;
|
|
||||||
Ok(2)
|
|
||||||
} else if self.count == 1 {
|
|
||||||
self.count = 2;
|
|
||||||
buf[0] = 12;
|
|
||||||
buf[1] = 13;
|
|
||||||
Ok(2)
|
|
||||||
} else {
|
|
||||||
Err(old_io::standard_error(old_io::EndOfFile))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_byte() {
|
|
||||||
let mut reader = MemReader::new(vec!(10));
|
|
||||||
let byte = reader.read_byte();
|
|
||||||
assert!(byte == Ok(10));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_byte_0_bytes() {
|
|
||||||
let mut reader = InitialZeroByteReader {
|
|
||||||
count: 0,
|
|
||||||
};
|
|
||||||
let byte = reader.read_byte();
|
|
||||||
assert!(byte == Ok(10));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_byte_eof() {
|
|
||||||
let mut reader = EofReader;
|
|
||||||
let byte = reader.read_byte();
|
|
||||||
assert!(byte.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_byte_error() {
|
|
||||||
let mut reader = ErroringReader;
|
|
||||||
let byte = reader.read_byte();
|
|
||||||
assert!(byte.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn bytes_0_bytes() {
|
|
||||||
let mut reader = InitialZeroByteReader {
|
|
||||||
count: 0,
|
|
||||||
};
|
|
||||||
let byte = reader.bytes().next();
|
|
||||||
assert!(byte == Some(Ok(10)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn bytes_eof() {
|
|
||||||
let mut reader = EofReader;
|
|
||||||
let byte = reader.bytes().next();
|
|
||||||
assert!(byte.is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn bytes_error() {
|
|
||||||
let mut reader = ErroringReader;
|
|
||||||
let mut it = reader.bytes();
|
|
||||||
let byte = it.next();
|
|
||||||
assert!(byte.unwrap().is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_bytes() {
|
|
||||||
let mut reader = MemReader::new(vec!(10, 11, 12, 13));
|
|
||||||
let bytes = reader.read_exact(4).unwrap();
|
|
||||||
assert_eq!(bytes, [10, 11, 12, 13]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_bytes_partial() {
|
|
||||||
let mut reader = PartialReader {
|
|
||||||
count: 0,
|
|
||||||
};
|
|
||||||
let bytes = reader.read_exact(4).unwrap();
|
|
||||||
assert_eq!(bytes, [10, 11, 12, 13]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_bytes_eof() {
|
|
||||||
let mut reader = MemReader::new(vec!(10, 11));
|
|
||||||
assert!(reader.read_exact(4).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn push_at_least() {
|
|
||||||
let mut reader = MemReader::new(vec![10, 11, 12, 13]);
|
|
||||||
let mut buf = vec![8, 9];
|
|
||||||
assert!(reader.push_at_least(4, 4, &mut buf).is_ok());
|
|
||||||
assert_eq!(buf, [8, 9, 10, 11, 12, 13]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn push_at_least_partial() {
|
|
||||||
let mut reader = PartialReader {
|
|
||||||
count: 0,
|
|
||||||
};
|
|
||||||
let mut buf = vec![8, 9];
|
|
||||||
assert!(reader.push_at_least(4, 4, &mut buf).is_ok());
|
|
||||||
assert_eq!(buf, [8, 9, 10, 11, 12, 13]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn push_at_least_eof() {
|
|
||||||
let mut reader = MemReader::new(vec![10, 11]);
|
|
||||||
let mut buf = vec![8, 9];
|
|
||||||
assert!(reader.push_at_least(4, 4, &mut buf).is_err());
|
|
||||||
assert_eq!(buf, [8, 9, 10, 11]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn push_at_least_error() {
|
|
||||||
let mut reader = ErroringLaterReader {
|
|
||||||
count: 0,
|
|
||||||
};
|
|
||||||
let mut buf = vec![8, 9];
|
|
||||||
assert!(reader.push_at_least(4, 4, &mut buf).is_err());
|
|
||||||
assert_eq!(buf, [8, 9, 10]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_to_end() {
|
|
||||||
let mut reader = ThreeChunkReader {
|
|
||||||
count: 0,
|
|
||||||
};
|
|
||||||
let buf = reader.read_to_end().unwrap();
|
|
||||||
assert_eq!(buf, [10, 11, 12, 13]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn read_to_end_error() {
|
|
||||||
let mut reader = ThreeChunkReader {
|
|
||||||
count: 0,
|
|
||||||
};
|
|
||||||
let buf = reader.read_to_end().unwrap();
|
|
||||||
assert_eq!(buf, [10, 11]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_read_write_le_mem() {
|
|
||||||
let uints = [0, 1, 2, 42, 10_123, 100_123_456, ::u64::MAX];
|
|
||||||
|
|
||||||
let mut writer = Vec::new();
|
|
||||||
for i in &uints {
|
|
||||||
writer.write_le_u64(*i).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut reader = MemReader::new(writer);
|
|
||||||
for i in &uints {
|
|
||||||
assert!(reader.read_le_u64().unwrap() == *i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_read_write_be() {
|
|
||||||
let uints = [0, 1, 2, 42, 10_123, 100_123_456, ::u64::MAX];
|
|
||||||
|
|
||||||
let mut writer = Vec::new();
|
|
||||||
for i in &uints {
|
|
||||||
writer.write_be_u64(*i).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut reader = MemReader::new(writer);
|
|
||||||
for i in &uints {
|
|
||||||
assert!(reader.read_be_u64().unwrap() == *i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_read_be_int_n() {
|
|
||||||
let ints = [::i32::MIN, -123456, -42, -5, 0, 1, ::i32::MAX];
|
|
||||||
|
|
||||||
let mut writer = Vec::new();
|
|
||||||
for i in &ints {
|
|
||||||
writer.write_be_i32(*i).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut reader = MemReader::new(writer);
|
|
||||||
for i in &ints {
|
|
||||||
// this tests that the sign extension is working
|
|
||||||
// (comparing the values as i32 would not test this)
|
|
||||||
assert!(reader.read_be_int_n(4).unwrap() == *i as i64);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_read_f32() {
|
|
||||||
//big-endian floating-point 8.1250
|
|
||||||
let buf = vec![0x41, 0x02, 0x00, 0x00];
|
|
||||||
|
|
||||||
let mut writer = Vec::new();
|
|
||||||
writer.write(&buf).unwrap();
|
|
||||||
|
|
||||||
let mut reader = MemReader::new(writer);
|
|
||||||
let f = reader.read_be_f32().unwrap();
|
|
||||||
assert!(f == 8.1250);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_read_write_f32() {
|
|
||||||
let f:f32 = 8.1250;
|
|
||||||
|
|
||||||
let mut writer = Vec::new();
|
|
||||||
writer.write_be_f32(f).unwrap();
|
|
||||||
writer.write_le_f32(f).unwrap();
|
|
||||||
|
|
||||||
let mut reader = MemReader::new(writer);
|
|
||||||
assert!(reader.read_be_f32().unwrap() == 8.1250);
|
|
||||||
assert!(reader.read_le_f32().unwrap() == 8.1250);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_u64_from_be_bytes() {
|
|
||||||
use super::u64_from_be_bytes;
|
|
||||||
|
|
||||||
let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09];
|
|
||||||
|
|
||||||
// Aligned access
|
|
||||||
assert_eq!(u64_from_be_bytes(&buf, 0, 0), 0);
|
|
||||||
assert_eq!(u64_from_be_bytes(&buf, 0, 1), 0x01);
|
|
||||||
assert_eq!(u64_from_be_bytes(&buf, 0, 2), 0x0102);
|
|
||||||
assert_eq!(u64_from_be_bytes(&buf, 0, 3), 0x010203);
|
|
||||||
assert_eq!(u64_from_be_bytes(&buf, 0, 4), 0x01020304);
|
|
||||||
assert_eq!(u64_from_be_bytes(&buf, 0, 5), 0x0102030405);
|
|
||||||
assert_eq!(u64_from_be_bytes(&buf, 0, 6), 0x010203040506);
|
|
||||||
assert_eq!(u64_from_be_bytes(&buf, 0, 7), 0x01020304050607);
|
|
||||||
assert_eq!(u64_from_be_bytes(&buf, 0, 8), 0x0102030405060708);
|
|
||||||
|
|
||||||
// Unaligned access
|
|
||||||
assert_eq!(u64_from_be_bytes(&buf, 1, 0), 0);
|
|
||||||
assert_eq!(u64_from_be_bytes(&buf, 1, 1), 0x02);
|
|
||||||
assert_eq!(u64_from_be_bytes(&buf, 1, 2), 0x0203);
|
|
||||||
assert_eq!(u64_from_be_bytes(&buf, 1, 3), 0x020304);
|
|
||||||
assert_eq!(u64_from_be_bytes(&buf, 1, 4), 0x02030405);
|
|
||||||
assert_eq!(u64_from_be_bytes(&buf, 1, 5), 0x0203040506);
|
|
||||||
assert_eq!(u64_from_be_bytes(&buf, 1, 6), 0x020304050607);
|
|
||||||
assert_eq!(u64_from_be_bytes(&buf, 1, 7), 0x02030405060708);
|
|
||||||
assert_eq!(u64_from_be_bytes(&buf, 1, 8), 0x0203040506070809);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod bench {
|
|
||||||
extern crate test;
|
|
||||||
|
|
||||||
use prelude::v1::*;
|
|
||||||
use self::test::Bencher;
|
|
||||||
|
|
||||||
// why is this a macro? wouldn't an inlined function work just as well?
|
|
||||||
macro_rules! u64_from_be_bytes_bench_impl {
|
|
||||||
($b:expr, $size:expr, $stride:expr, $start_index:expr) =>
|
|
||||||
({
|
|
||||||
use super::u64_from_be_bytes;
|
|
||||||
|
|
||||||
let len = ($stride as u8).wrapping_mul(100).wrapping_add($start_index);
|
|
||||||
let data = (0..len).collect::<Vec<_>>();
|
|
||||||
let mut sum = 0;
|
|
||||||
$b.iter(|| {
|
|
||||||
let mut i = $start_index;
|
|
||||||
while i < data.len() {
|
|
||||||
sum += u64_from_be_bytes(&data, i, $size);
|
|
||||||
i += $stride;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn u64_from_be_bytes_4_aligned(b: &mut Bencher) {
|
|
||||||
u64_from_be_bytes_bench_impl!(b, 4, 4, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn u64_from_be_bytes_4_unaligned(b: &mut Bencher) {
|
|
||||||
u64_from_be_bytes_bench_impl!(b, 4, 4, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn u64_from_be_bytes_7_aligned(b: &mut Bencher) {
|
|
||||||
u64_from_be_bytes_bench_impl!(b, 7, 8, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn u64_from_be_bytes_7_unaligned(b: &mut Bencher) {
|
|
||||||
u64_from_be_bytes_bench_impl!(b, 7, 8, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn u64_from_be_bytes_8_aligned(b: &mut Bencher) {
|
|
||||||
u64_from_be_bytes_bench_impl!(b, 8, 8, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn u64_from_be_bytes_8_unaligned(b: &mut Bencher) {
|
|
||||||
u64_from_be_bytes_bench_impl!(b, 8, 8, 1);
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,765 +0,0 @@
|
|||||||
// Copyright 2013 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.
|
|
||||||
//
|
|
||||||
// ignore-lexer-test FIXME #15679
|
|
||||||
|
|
||||||
//! Readers and Writers for in-memory buffers
|
|
||||||
|
|
||||||
use cmp::min;
|
|
||||||
use option::Option::None;
|
|
||||||
use result::Result::{Err, Ok};
|
|
||||||
use old_io;
|
|
||||||
use old_io::{Reader, Writer, Seek, Buffer, IoError, SeekStyle, IoResult};
|
|
||||||
use slice;
|
|
||||||
use vec::Vec;
|
|
||||||
|
|
||||||
const BUF_CAPACITY: usize = 128;
|
|
||||||
|
|
||||||
fn combine(seek: SeekStyle, cur: usize, end: usize, offset: i64) -> IoResult<u64> {
|
|
||||||
// compute offset as signed and clamp to prevent overflow
|
|
||||||
let pos = match seek {
|
|
||||||
old_io::SeekSet => 0,
|
|
||||||
old_io::SeekEnd => end,
|
|
||||||
old_io::SeekCur => cur,
|
|
||||||
} as i64;
|
|
||||||
|
|
||||||
if offset + pos < 0 {
|
|
||||||
Err(IoError {
|
|
||||||
kind: old_io::InvalidInput,
|
|
||||||
desc: "invalid seek to a negative offset",
|
|
||||||
detail: None
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Ok((offset + pos) as u64)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Writer for Vec<u8> {
|
|
||||||
#[inline]
|
|
||||||
fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
|
|
||||||
self.push_all(buf);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes to an owned, growable byte vector
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![feature(old_io, io)]
|
|
||||||
/// # #![allow(unused_must_use)]
|
|
||||||
/// use std::old_io::*;
|
|
||||||
///
|
|
||||||
/// let mut w = MemWriter::new();
|
|
||||||
/// w.write(&[0, 1, 2]);
|
|
||||||
///
|
|
||||||
/// assert_eq!(w.into_inner(), [0, 1, 2]);
|
|
||||||
/// ```
|
|
||||||
#[unstable(feature = "io")]
|
|
||||||
#[deprecated(since = "1.0.0",
|
|
||||||
reason = "use the Vec<u8> Writer implementation directly")]
|
|
||||||
#[derive(Clone)]
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub struct MemWriter {
|
|
||||||
buf: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
impl MemWriter {
|
|
||||||
/// Create a new `MemWriter`.
|
|
||||||
#[inline]
|
|
||||||
pub fn new() -> MemWriter {
|
|
||||||
MemWriter::with_capacity(BUF_CAPACITY)
|
|
||||||
}
|
|
||||||
/// Create a new `MemWriter`, allocating at least `n` bytes for
|
|
||||||
/// the internal buffer.
|
|
||||||
#[inline]
|
|
||||||
pub fn with_capacity(n: usize) -> MemWriter {
|
|
||||||
MemWriter::from_vec(Vec::with_capacity(n))
|
|
||||||
}
|
|
||||||
/// Create a new `MemWriter` that will append to an existing `Vec`.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_vec(buf: Vec<u8>) -> MemWriter {
|
|
||||||
MemWriter { buf: buf }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Acquires an immutable reference to the underlying buffer of this
|
|
||||||
/// `MemWriter`.
|
|
||||||
#[inline]
|
|
||||||
pub fn get_ref<'a>(&'a self) -> &'a [u8] { &self.buf }
|
|
||||||
|
|
||||||
/// Unwraps this `MemWriter`, returning the underlying buffer
|
|
||||||
#[inline]
|
|
||||||
pub fn into_inner(self) -> Vec<u8> { self.buf }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Writer for MemWriter {
|
|
||||||
#[inline]
|
|
||||||
#[allow(deprecated)]
|
|
||||||
fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
|
|
||||||
self.buf.push_all(buf);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads from an owned byte vector
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![feature(old_io)]
|
|
||||||
/// # #![allow(unused_must_use)]
|
|
||||||
/// use std::old_io::*;
|
|
||||||
///
|
|
||||||
/// let mut r = MemReader::new(vec!(0, 1, 2));
|
|
||||||
///
|
|
||||||
/// assert_eq!(r.read_to_end().unwrap(), [0, 1, 2]);
|
|
||||||
/// ```
|
|
||||||
pub struct MemReader {
|
|
||||||
buf: Vec<u8>,
|
|
||||||
pos: usize
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MemReader {
|
|
||||||
/// Creates a new `MemReader` which will read the buffer given. The buffer
|
|
||||||
/// can be re-acquired through `unwrap`
|
|
||||||
#[inline]
|
|
||||||
pub fn new(buf: Vec<u8>) -> MemReader {
|
|
||||||
MemReader {
|
|
||||||
buf: buf,
|
|
||||||
pos: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tests whether this reader has read all bytes in its buffer.
|
|
||||||
///
|
|
||||||
/// If `true`, then this will no longer return bytes from `read`.
|
|
||||||
#[inline]
|
|
||||||
pub fn eof(&self) -> bool { self.pos >= self.buf.len() }
|
|
||||||
|
|
||||||
/// Acquires an immutable reference to the underlying buffer of this
|
|
||||||
/// `MemReader`.
|
|
||||||
///
|
|
||||||
/// No method is exposed for acquiring a mutable reference to the buffer
|
|
||||||
/// because it could corrupt the state of this `MemReader`.
|
|
||||||
#[inline]
|
|
||||||
pub fn get_ref<'a>(&'a self) -> &'a [u8] { &self.buf }
|
|
||||||
|
|
||||||
/// Unwraps this `MemReader`, returning the underlying buffer
|
|
||||||
#[inline]
|
|
||||||
pub fn into_inner(self) -> Vec<u8> { self.buf }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Reader for MemReader {
|
|
||||||
#[inline]
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
|
||||||
if self.eof() { return Err(old_io::standard_error(old_io::EndOfFile)) }
|
|
||||||
|
|
||||||
let write_len = min(buf.len(), self.buf.len() - self.pos);
|
|
||||||
{
|
|
||||||
let input = &self.buf[self.pos.. self.pos + write_len];
|
|
||||||
let output = &mut buf[..write_len];
|
|
||||||
assert_eq!(input.len(), output.len());
|
|
||||||
slice::bytes::copy_memory(input, output);
|
|
||||||
}
|
|
||||||
self.pos += write_len;
|
|
||||||
assert!(self.pos <= self.buf.len());
|
|
||||||
|
|
||||||
return Ok(write_len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Seek for MemReader {
|
|
||||||
#[inline]
|
|
||||||
fn tell(&self) -> IoResult<u64> { Ok(self.pos as u64) }
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
|
|
||||||
let new = try!(combine(style, self.pos, self.buf.len(), pos));
|
|
||||||
self.pos = new as usize;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Buffer for MemReader {
|
|
||||||
#[inline]
|
|
||||||
fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> {
|
|
||||||
if self.pos < self.buf.len() {
|
|
||||||
Ok(&self.buf[self.pos..])
|
|
||||||
} else {
|
|
||||||
Err(old_io::standard_error(old_io::EndOfFile))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn consume(&mut self, amt: usize) { self.pos += amt; }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Reader for &'a [u8] {
|
|
||||||
#[inline]
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
|
||||||
if self.is_empty() { return Err(old_io::standard_error(old_io::EndOfFile)); }
|
|
||||||
|
|
||||||
let write_len = min(buf.len(), self.len());
|
|
||||||
{
|
|
||||||
let input = &self[..write_len];
|
|
||||||
let output = &mut buf[.. write_len];
|
|
||||||
slice::bytes::copy_memory(input, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
*self = &self[write_len..];
|
|
||||||
|
|
||||||
Ok(write_len)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Buffer for &'a [u8] {
|
|
||||||
#[inline]
|
|
||||||
fn fill_buf(&mut self) -> IoResult<&[u8]> {
|
|
||||||
if self.is_empty() {
|
|
||||||
Err(old_io::standard_error(old_io::EndOfFile))
|
|
||||||
} else {
|
|
||||||
Ok(*self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn consume(&mut self, amt: usize) {
|
|
||||||
*self = &self[amt..];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Writes to a fixed-size byte slice
|
|
||||||
///
|
|
||||||
/// If a write will not fit in the buffer, it returns an error and does not
|
|
||||||
/// write any data.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![feature(old_io, io)]
|
|
||||||
/// # #![allow(unused_must_use)]
|
|
||||||
/// use std::old_io::*;
|
|
||||||
///
|
|
||||||
/// let mut buf = [0; 4];
|
|
||||||
/// {
|
|
||||||
/// let mut w = BufWriter::new(&mut buf);
|
|
||||||
/// w.write(&[0, 1, 2]);
|
|
||||||
/// }
|
|
||||||
/// assert!(buf == [0, 1, 2, 0]);
|
|
||||||
/// ```
|
|
||||||
pub struct BufWriter<'a> {
|
|
||||||
buf: &'a mut [u8],
|
|
||||||
pos: usize
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> BufWriter<'a> {
|
|
||||||
/// Creates a new `BufWriter` which will wrap the specified buffer. The
|
|
||||||
/// writer initially starts at position 0.
|
|
||||||
#[inline]
|
|
||||||
pub fn new(buf: &'a mut [u8]) -> BufWriter<'a> {
|
|
||||||
BufWriter {
|
|
||||||
buf: buf,
|
|
||||||
pos: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Writer for BufWriter<'a> {
|
|
||||||
#[inline]
|
|
||||||
fn write_all(&mut self, src: &[u8]) -> IoResult<()> {
|
|
||||||
let dst = &mut self.buf[self.pos..];
|
|
||||||
let dst_len = dst.len();
|
|
||||||
|
|
||||||
if dst_len == 0 {
|
|
||||||
return Err(old_io::standard_error(old_io::EndOfFile));
|
|
||||||
}
|
|
||||||
|
|
||||||
let src_len = src.len();
|
|
||||||
|
|
||||||
if dst_len >= src_len {
|
|
||||||
slice::bytes::copy_memory(src, dst);
|
|
||||||
|
|
||||||
self.pos += src_len;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
slice::bytes::copy_memory(&src[..dst_len], dst);
|
|
||||||
|
|
||||||
self.pos += dst_len;
|
|
||||||
|
|
||||||
Err(old_io::standard_error(old_io::ShortWrite(dst_len)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Seek for BufWriter<'a> {
|
|
||||||
#[inline]
|
|
||||||
fn tell(&self) -> IoResult<u64> { Ok(self.pos as u64) }
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
|
|
||||||
let new = try!(combine(style, self.pos, self.buf.len(), pos));
|
|
||||||
self.pos = min(new as usize, self.buf.len());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads from a fixed-size byte slice
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![feature(old_io)]
|
|
||||||
/// # #![allow(unused_must_use)]
|
|
||||||
/// use std::old_io::*;
|
|
||||||
///
|
|
||||||
/// let buf = [0, 1, 2, 3];
|
|
||||||
/// let mut r = BufReader::new(&buf);
|
|
||||||
///
|
|
||||||
/// assert_eq!(r.read_to_end().unwrap(), [0, 1, 2, 3]);
|
|
||||||
/// ```
|
|
||||||
pub struct BufReader<'a> {
|
|
||||||
buf: &'a [u8],
|
|
||||||
pos: usize
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> BufReader<'a> {
|
|
||||||
/// Creates a new buffered reader which will read the specified buffer
|
|
||||||
#[inline]
|
|
||||||
pub fn new(buf: &'a [u8]) -> BufReader<'a> {
|
|
||||||
BufReader {
|
|
||||||
buf: buf,
|
|
||||||
pos: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tests whether this reader has read all bytes in its buffer.
|
|
||||||
///
|
|
||||||
/// If `true`, then this will no longer return bytes from `read`.
|
|
||||||
#[inline]
|
|
||||||
pub fn eof(&self) -> bool { self.pos >= self.buf.len() }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Reader for BufReader<'a> {
|
|
||||||
#[inline]
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
|
||||||
if self.eof() { return Err(old_io::standard_error(old_io::EndOfFile)) }
|
|
||||||
|
|
||||||
let write_len = min(buf.len(), self.buf.len() - self.pos);
|
|
||||||
{
|
|
||||||
let input = &self.buf[self.pos.. self.pos + write_len];
|
|
||||||
let output = &mut buf[..write_len];
|
|
||||||
assert_eq!(input.len(), output.len());
|
|
||||||
slice::bytes::copy_memory(input, output);
|
|
||||||
}
|
|
||||||
self.pos += write_len;
|
|
||||||
assert!(self.pos <= self.buf.len());
|
|
||||||
|
|
||||||
return Ok(write_len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Seek for BufReader<'a> {
|
|
||||||
#[inline]
|
|
||||||
fn tell(&self) -> IoResult<u64> { Ok(self.pos as u64) }
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
|
|
||||||
let new = try!(combine(style, self.pos, self.buf.len(), pos));
|
|
||||||
self.pos = new as usize;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Buffer for BufReader<'a> {
|
|
||||||
#[inline]
|
|
||||||
fn fill_buf(&mut self) -> IoResult<&[u8]> {
|
|
||||||
if self.pos < self.buf.len() {
|
|
||||||
Ok(&self.buf[self.pos..])
|
|
||||||
} else {
|
|
||||||
Err(old_io::standard_error(old_io::EndOfFile))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn consume(&mut self, amt: usize) { self.pos += amt; }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
extern crate test as test_crate;
|
|
||||||
use old_io::{SeekSet, SeekCur, SeekEnd, Reader, Writer, Seek, Buffer};
|
|
||||||
use prelude::v1::{Ok, Err, Vec};
|
|
||||||
use prelude::v1::Iterator;
|
|
||||||
use old_io;
|
|
||||||
use iter::repeat;
|
|
||||||
use self::test_crate::Bencher;
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec_writer() {
|
|
||||||
let mut writer = Vec::new();
|
|
||||||
writer.write(&[0]).unwrap();
|
|
||||||
writer.write(&[1, 2, 3]).unwrap();
|
|
||||||
writer.write(&[4, 5, 6, 7]).unwrap();
|
|
||||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7];
|
|
||||||
assert_eq!(writer, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mem_writer() {
|
|
||||||
let mut writer = MemWriter::new();
|
|
||||||
writer.write(&[0]).unwrap();
|
|
||||||
writer.write(&[1, 2, 3]).unwrap();
|
|
||||||
writer.write(&[4, 5, 6, 7]).unwrap();
|
|
||||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7];
|
|
||||||
assert_eq!(writer.get_ref(), b);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_buf_writer() {
|
|
||||||
let mut buf = [0 as u8; 9];
|
|
||||||
{
|
|
||||||
let mut writer = BufWriter::new(&mut buf);
|
|
||||||
assert_eq!(writer.tell(), Ok(0));
|
|
||||||
writer.write(&[0]).unwrap();
|
|
||||||
assert_eq!(writer.tell(), Ok(1));
|
|
||||||
writer.write(&[1, 2, 3]).unwrap();
|
|
||||||
writer.write(&[4, 5, 6, 7]).unwrap();
|
|
||||||
assert_eq!(writer.tell(), Ok(8));
|
|
||||||
writer.write(&[]).unwrap();
|
|
||||||
assert_eq!(writer.tell(), Ok(8));
|
|
||||||
|
|
||||||
assert_eq!(writer.write(&[8, 9]).err().unwrap().kind, old_io::ShortWrite(1));
|
|
||||||
assert_eq!(writer.write(&[10]).err().unwrap().kind, old_io::EndOfFile);
|
|
||||||
}
|
|
||||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_buf_writer_seek() {
|
|
||||||
let mut buf = [0 as u8; 8];
|
|
||||||
{
|
|
||||||
let mut writer = BufWriter::new(&mut buf);
|
|
||||||
assert_eq!(writer.tell(), Ok(0));
|
|
||||||
writer.write(&[1]).unwrap();
|
|
||||||
assert_eq!(writer.tell(), Ok(1));
|
|
||||||
|
|
||||||
writer.seek(2, SeekSet).unwrap();
|
|
||||||
assert_eq!(writer.tell(), Ok(2));
|
|
||||||
writer.write(&[2]).unwrap();
|
|
||||||
assert_eq!(writer.tell(), Ok(3));
|
|
||||||
|
|
||||||
writer.seek(-2, SeekCur).unwrap();
|
|
||||||
assert_eq!(writer.tell(), Ok(1));
|
|
||||||
writer.write(&[3]).unwrap();
|
|
||||||
assert_eq!(writer.tell(), Ok(2));
|
|
||||||
|
|
||||||
writer.seek(-1, SeekEnd).unwrap();
|
|
||||||
assert_eq!(writer.tell(), Ok(7));
|
|
||||||
writer.write(&[4]).unwrap();
|
|
||||||
assert_eq!(writer.tell(), Ok(8));
|
|
||||||
|
|
||||||
}
|
|
||||||
let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 4];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_buf_writer_error() {
|
|
||||||
let mut buf = [0 as u8; 2];
|
|
||||||
let mut writer = BufWriter::new(&mut buf);
|
|
||||||
writer.write(&[0]).unwrap();
|
|
||||||
|
|
||||||
match writer.write(&[0, 0]) {
|
|
||||||
Ok(..) => panic!(),
|
|
||||||
Err(e) => assert_eq!(e.kind, old_io::ShortWrite(1)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mem_reader() {
|
|
||||||
let mut reader = MemReader::new(vec!(0, 1, 2, 3, 4, 5, 6, 7));
|
|
||||||
let mut buf = [];
|
|
||||||
assert_eq!(reader.read(&mut buf), Ok(0));
|
|
||||||
assert_eq!(reader.tell(), Ok(0));
|
|
||||||
let mut buf = [0];
|
|
||||||
assert_eq!(reader.read(&mut buf), Ok(1));
|
|
||||||
assert_eq!(reader.tell(), Ok(1));
|
|
||||||
let b: &[_] = &[0];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
let mut buf = [0; 4];
|
|
||||||
assert_eq!(reader.read(&mut buf), Ok(4));
|
|
||||||
assert_eq!(reader.tell(), Ok(5));
|
|
||||||
let b: &[_] = &[1, 2, 3, 4];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
assert_eq!(reader.read(&mut buf), Ok(3));
|
|
||||||
let b: &[_] = &[5, 6, 7];
|
|
||||||
assert_eq!(&buf[..3], b);
|
|
||||||
assert!(reader.read(&mut buf).is_err());
|
|
||||||
let mut reader = MemReader::new(vec!(0, 1, 2, 3, 4, 5, 6, 7));
|
|
||||||
assert_eq!(reader.read_until(3).unwrap(), [0, 1, 2, 3]);
|
|
||||||
assert_eq!(reader.read_until(3).unwrap(), [4, 5, 6, 7]);
|
|
||||||
assert!(reader.read(&mut buf).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_slice_reader() {
|
|
||||||
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
|
|
||||||
let mut reader = &mut &*in_buf;
|
|
||||||
let mut buf = [];
|
|
||||||
assert_eq!(reader.read(&mut buf), Ok(0));
|
|
||||||
let mut buf = [0];
|
|
||||||
assert_eq!(reader.read(&mut buf), Ok(1));
|
|
||||||
assert_eq!(reader.len(), 7);
|
|
||||||
let b: &[_] = &[0];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
let mut buf = [0; 4];
|
|
||||||
assert_eq!(reader.read(&mut buf), Ok(4));
|
|
||||||
assert_eq!(reader.len(), 3);
|
|
||||||
let b: &[_] = &[1, 2, 3, 4];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
assert_eq!(reader.read(&mut buf), Ok(3));
|
|
||||||
let b: &[_] = &[5, 6, 7];
|
|
||||||
assert_eq!(&buf[..3], b);
|
|
||||||
assert!(reader.read(&mut buf).is_err());
|
|
||||||
let mut reader = &mut &*in_buf;
|
|
||||||
assert_eq!(reader.read_until(3).unwrap(), [0, 1, 2, 3]);
|
|
||||||
assert_eq!(reader.read_until(3).unwrap(), [4, 5, 6, 7]);
|
|
||||||
assert!(reader.read(&mut buf).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_buf_reader() {
|
|
||||||
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
|
|
||||||
let mut reader = BufReader::new(&in_buf);
|
|
||||||
let mut buf = [];
|
|
||||||
assert_eq!(reader.read(&mut buf), Ok(0));
|
|
||||||
assert_eq!(reader.tell(), Ok(0));
|
|
||||||
let mut buf = [0];
|
|
||||||
assert_eq!(reader.read(&mut buf), Ok(1));
|
|
||||||
assert_eq!(reader.tell(), Ok(1));
|
|
||||||
let b: &[_] = &[0];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
let mut buf = [0; 4];
|
|
||||||
assert_eq!(reader.read(&mut buf), Ok(4));
|
|
||||||
assert_eq!(reader.tell(), Ok(5));
|
|
||||||
let b: &[_] = &[1, 2, 3, 4];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
assert_eq!(reader.read(&mut buf), Ok(3));
|
|
||||||
let b: &[_] = &[5, 6, 7];
|
|
||||||
assert_eq!(&buf[..3], b);
|
|
||||||
assert!(reader.read(&mut buf).is_err());
|
|
||||||
let mut reader = BufReader::new(&in_buf);
|
|
||||||
assert_eq!(reader.read_until(3).unwrap(), [0, 1, 2, 3]);
|
|
||||||
assert_eq!(reader.read_until(3).unwrap(), [4, 5, 6, 7]);
|
|
||||||
assert!(reader.read(&mut buf).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_read_char() {
|
|
||||||
let b = b"Vi\xE1\xBB\x87t";
|
|
||||||
let mut r = BufReader::new(b);
|
|
||||||
assert_eq!(r.read_char(), Ok('V'));
|
|
||||||
assert_eq!(r.read_char(), Ok('i'));
|
|
||||||
assert_eq!(r.read_char(), Ok('ệ'));
|
|
||||||
assert_eq!(r.read_char(), Ok('t'));
|
|
||||||
assert!(r.read_char().is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_read_bad_char() {
|
|
||||||
let b = b"\x80";
|
|
||||||
let mut r = BufReader::new(b);
|
|
||||||
assert!(r.read_char().is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_write_strings() {
|
|
||||||
let mut writer = MemWriter::new();
|
|
||||||
writer.write_str("testing").unwrap();
|
|
||||||
writer.write_line("testing").unwrap();
|
|
||||||
writer.write_str("testing").unwrap();
|
|
||||||
let mut r = BufReader::new(writer.get_ref());
|
|
||||||
assert_eq!(r.read_to_string().unwrap(), "testingtesting\ntesting");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_write_char() {
|
|
||||||
let mut writer = MemWriter::new();
|
|
||||||
writer.write_char('a').unwrap();
|
|
||||||
writer.write_char('\n').unwrap();
|
|
||||||
writer.write_char('ệ').unwrap();
|
|
||||||
let mut r = BufReader::new(writer.get_ref());
|
|
||||||
assert_eq!(r.read_to_string().unwrap(), "a\nệ");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_read_whole_string_bad() {
|
|
||||||
let buf = [0xff];
|
|
||||||
let mut r = BufReader::new(&buf);
|
|
||||||
match r.read_to_string() {
|
|
||||||
Ok(..) => panic!(),
|
|
||||||
Err(..) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn seek_past_end() {
|
|
||||||
let buf = [0xff];
|
|
||||||
let mut r = BufReader::new(&buf);
|
|
||||||
r.seek(10, SeekSet).unwrap();
|
|
||||||
assert!(r.read(&mut []).is_err());
|
|
||||||
|
|
||||||
let mut r = MemReader::new(vec!(10));
|
|
||||||
r.seek(10, SeekSet).unwrap();
|
|
||||||
assert!(r.read(&mut []).is_err());
|
|
||||||
|
|
||||||
let mut buf = [0];
|
|
||||||
let mut r = BufWriter::new(&mut buf);
|
|
||||||
r.seek(10, SeekSet).unwrap();
|
|
||||||
assert!(r.write(&[3]).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn seek_before_0() {
|
|
||||||
let buf = [0xff];
|
|
||||||
let mut r = BufReader::new(&buf);
|
|
||||||
assert!(r.seek(-1, SeekSet).is_err());
|
|
||||||
|
|
||||||
let mut r = MemReader::new(vec!(10));
|
|
||||||
assert!(r.seek(-1, SeekSet).is_err());
|
|
||||||
|
|
||||||
let mut buf = [0];
|
|
||||||
let mut r = BufWriter::new(&mut buf);
|
|
||||||
assert!(r.seek(-1, SeekSet).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn io_read_at_least() {
|
|
||||||
let mut r = MemReader::new(vec![1, 2, 3, 4, 5, 6, 7, 8]);
|
|
||||||
let mut buf = [0; 3];
|
|
||||||
assert!(r.read_at_least(buf.len(), &mut buf).is_ok());
|
|
||||||
let b: &[_] = &[1, 2, 3];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
assert!(r.read_at_least(0, &mut buf[..0]).is_ok());
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
assert!(r.read_at_least(buf.len(), &mut buf).is_ok());
|
|
||||||
let b: &[_] = &[4, 5, 6];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
assert!(r.read_at_least(buf.len(), &mut buf).is_err());
|
|
||||||
let b: &[_] = &[7, 8, 6];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn do_bench_mem_writer(b: &mut Bencher, times: usize, len: usize) {
|
|
||||||
let src: Vec<u8> = repeat(5).take(len).collect();
|
|
||||||
|
|
||||||
b.bytes = (times * len) as u64;
|
|
||||||
b.iter(|| {
|
|
||||||
let mut wr = MemWriter::new();
|
|
||||||
for _ in 0..times {
|
|
||||||
wr.write(&src).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let v = wr.into_inner();
|
|
||||||
assert_eq!(v.len(), times * len);
|
|
||||||
assert!(v.iter().all(|x| *x == 5));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_mem_writer_001_0000(b: &mut Bencher) {
|
|
||||||
do_bench_mem_writer(b, 1, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_mem_writer_001_0010(b: &mut Bencher) {
|
|
||||||
do_bench_mem_writer(b, 1, 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_mem_writer_001_0100(b: &mut Bencher) {
|
|
||||||
do_bench_mem_writer(b, 1, 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_mem_writer_001_1000(b: &mut Bencher) {
|
|
||||||
do_bench_mem_writer(b, 1, 1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_mem_writer_100_0000(b: &mut Bencher) {
|
|
||||||
do_bench_mem_writer(b, 100, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_mem_writer_100_0010(b: &mut Bencher) {
|
|
||||||
do_bench_mem_writer(b, 100, 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_mem_writer_100_0100(b: &mut Bencher) {
|
|
||||||
do_bench_mem_writer(b, 100, 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_mem_writer_100_1000(b: &mut Bencher) {
|
|
||||||
do_bench_mem_writer(b, 100, 1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_mem_reader(b: &mut Bencher) {
|
|
||||||
b.iter(|| {
|
|
||||||
let buf = [5 as u8; 100].to_vec();
|
|
||||||
{
|
|
||||||
let mut rdr = MemReader::new(buf);
|
|
||||||
for _i in 0..10 {
|
|
||||||
let mut buf = [0 as u8; 10];
|
|
||||||
rdr.read(&mut buf).unwrap();
|
|
||||||
assert_eq!(buf, [5; 10]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_buf_writer(b: &mut Bencher) {
|
|
||||||
b.iter(|| {
|
|
||||||
let mut buf = [0 as u8; 100];
|
|
||||||
{
|
|
||||||
let mut wr = BufWriter::new(&mut buf);
|
|
||||||
for _i in 0..10 {
|
|
||||||
wr.write(&[5; 10]).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert_eq!(&buf[..], &[5; 100][..]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_buf_reader(b: &mut Bencher) {
|
|
||||||
b.iter(|| {
|
|
||||||
let buf = [5 as u8; 100];
|
|
||||||
{
|
|
||||||
let mut rdr = BufReader::new(&buf);
|
|
||||||
for _i in 0..10 {
|
|
||||||
let mut buf = [0 as u8; 10];
|
|
||||||
rdr.read(&mut buf).unwrap();
|
|
||||||
assert_eq!(buf, [5; 10]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,136 +0,0 @@
|
|||||||
// Copyright 2013 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.
|
|
||||||
|
|
||||||
//! Synchronous DNS Resolution
|
|
||||||
//!
|
|
||||||
//! Contains the functionality to perform DNS resolution or reverse lookup,
|
|
||||||
//! in a style related to `getaddrinfo()` and `getnameinfo()`, respectively.
|
|
||||||
|
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
|
||||||
pub use self::SocketType::*;
|
|
||||||
pub use self::Flag::*;
|
|
||||||
pub use self::Protocol::*;
|
|
||||||
|
|
||||||
use iter::Iterator;
|
|
||||||
use old_io::IoResult;
|
|
||||||
use old_io::net::ip::{SocketAddr, IpAddr};
|
|
||||||
use option::Option;
|
|
||||||
use option::Option::{Some, None};
|
|
||||||
use string::String;
|
|
||||||
use sys;
|
|
||||||
use vec::Vec;
|
|
||||||
|
|
||||||
/// Hints to the types of sockets that are desired when looking up hosts
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
pub enum SocketType {
|
|
||||||
Stream, Datagram, Raw
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Flags which can be or'd into the `flags` field of a `Hint`. These are used
|
|
||||||
/// to manipulate how a query is performed.
|
|
||||||
///
|
|
||||||
/// The meaning of each of these flags can be found with `man -s 3 getaddrinfo`
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
pub enum Flag {
|
|
||||||
AddrConfig,
|
|
||||||
All,
|
|
||||||
CanonName,
|
|
||||||
NumericHost,
|
|
||||||
NumericServ,
|
|
||||||
Passive,
|
|
||||||
V4Mapped,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A transport protocol associated with either a hint or a return value of
|
|
||||||
/// `lookup`
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
pub enum Protocol {
|
|
||||||
TCP, UDP
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This structure is used to provide hints when fetching addresses for a
|
|
||||||
/// remote host to control how the lookup is performed.
|
|
||||||
///
|
|
||||||
/// For details on these fields, see their corresponding definitions via
|
|
||||||
/// `man -s 3 getaddrinfo`
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
pub struct Hint {
|
|
||||||
pub family: usize,
|
|
||||||
pub socktype: Option<SocketType>,
|
|
||||||
pub protocol: Option<Protocol>,
|
|
||||||
pub flags: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
pub struct Info {
|
|
||||||
pub address: SocketAddr,
|
|
||||||
pub family: usize,
|
|
||||||
pub socktype: Option<SocketType>,
|
|
||||||
pub protocol: Option<Protocol>,
|
|
||||||
pub flags: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Easy name resolution. Given a hostname, returns the list of IP addresses for
|
|
||||||
/// that hostname.
|
|
||||||
pub fn get_host_addresses(host: &str) -> IoResult<Vec<IpAddr>> {
|
|
||||||
lookup(Some(host), None, None).map(|a| a.into_iter().map(|i| i.address.ip).collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reverse name resolution. Given an address, returns the corresponding
|
|
||||||
/// hostname.
|
|
||||||
pub fn get_address_name(addr: IpAddr) -> IoResult<String> {
|
|
||||||
sys::addrinfo::get_address_name(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Full-fledged resolution. This function will perform a synchronous call to
|
|
||||||
/// getaddrinfo, controlled by the parameters
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * hostname - an optional hostname to lookup against
|
|
||||||
/// * servname - an optional service name, listed in the system services
|
|
||||||
/// * hint - see the hint structure, and "man -s 3 getaddrinfo", for how this
|
|
||||||
/// controls lookup
|
|
||||||
///
|
|
||||||
/// FIXME: this is not public because the `Hint` structure is not ready for public
|
|
||||||
/// consumption just yet.
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
fn lookup(hostname: Option<&str>, servname: Option<&str>, hint: Option<Hint>)
|
|
||||||
-> IoResult<Vec<Info>> {
|
|
||||||
sys::addrinfo::get_host_addresses(hostname, servname, hint)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignored on android since we cannot give tcp/ip
|
|
||||||
// permission without help of apk
|
|
||||||
#[cfg(all(test, not(target_os = "android")))]
|
|
||||||
mod test {
|
|
||||||
use prelude::v1::*;
|
|
||||||
use super::*;
|
|
||||||
use old_io::net::ip::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn dns_smoke_test() {
|
|
||||||
let ipaddrs = get_host_addresses("localhost").unwrap();
|
|
||||||
let mut found_local = false;
|
|
||||||
let local_addr = &Ipv4Addr(127, 0, 0, 1);
|
|
||||||
for addr in &ipaddrs {
|
|
||||||
found_local = found_local || addr == local_addr;
|
|
||||||
}
|
|
||||||
assert!(found_local);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn issue_10663() {
|
|
||||||
// Something should happen here, but this certainly shouldn't cause
|
|
||||||
// everything to die. The actual outcome we don't care too much about.
|
|
||||||
let _ = get_host_addresses("example.com");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,710 +0,0 @@
|
|||||||
// Copyright 2013 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.
|
|
||||||
|
|
||||||
//! Internet Protocol (IP) addresses.
|
|
||||||
//!
|
|
||||||
//! This module contains functions useful for parsing, formatting, and
|
|
||||||
//! manipulating IP addresses.
|
|
||||||
|
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
|
||||||
pub use self::IpAddr::*;
|
|
||||||
|
|
||||||
use boxed::Box;
|
|
||||||
use fmt;
|
|
||||||
use old_io::{self, IoResult, IoError};
|
|
||||||
use old_io::net;
|
|
||||||
use iter::Iterator;
|
|
||||||
use ops::{FnOnce, FnMut};
|
|
||||||
use option::Option;
|
|
||||||
use option::Option::{None, Some};
|
|
||||||
use result::Result::{self, Ok, Err};
|
|
||||||
use str::FromStr;
|
|
||||||
use vec::Vec;
|
|
||||||
|
|
||||||
pub type Port = u16;
|
|
||||||
|
|
||||||
#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)]
|
|
||||||
pub enum IpAddr {
|
|
||||||
Ipv4Addr(u8, u8, u8, u8),
|
|
||||||
Ipv6Addr(u16, u16, u16, u16, u16, u16, u16, u16)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl fmt::Display for IpAddr {
|
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match *self {
|
|
||||||
Ipv4Addr(a, b, c, d) =>
|
|
||||||
write!(fmt, "{}.{}.{}.{}", a, b, c, d),
|
|
||||||
|
|
||||||
// Ipv4 Compatible address
|
|
||||||
Ipv6Addr(0, 0, 0, 0, 0, 0, g, h) => {
|
|
||||||
write!(fmt, "::{}.{}.{}.{}", (g >> 8) as u8, g as u8,
|
|
||||||
(h >> 8) as u8, h as u8)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ipv4-Mapped address
|
|
||||||
Ipv6Addr(0, 0, 0, 0, 0, 0xFFFF, g, h) => {
|
|
||||||
write!(fmt, "::FFFF:{}.{}.{}.{}", (g >> 8) as u8, g as u8,
|
|
||||||
(h >> 8) as u8, h as u8)
|
|
||||||
}
|
|
||||||
|
|
||||||
Ipv6Addr(a, b, c, d, e, f, g, h) =>
|
|
||||||
write!(fmt, "{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}",
|
|
||||||
a, b, c, d, e, f, g, h)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)]
|
|
||||||
pub struct SocketAddr {
|
|
||||||
pub ip: IpAddr,
|
|
||||||
pub port: Port,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl fmt::Display for SocketAddr {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self.ip {
|
|
||||||
Ipv4Addr(..) => write!(f, "{}:{}", self.ip, self.port),
|
|
||||||
Ipv6Addr(..) => write!(f, "[{}]:{}", self.ip, self.port),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Parser<'a> {
|
|
||||||
// parsing as ASCII, so can use byte array
|
|
||||||
s: &'a [u8],
|
|
||||||
pos: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
|
||||||
fn new(s: &'a str) -> Parser<'a> {
|
|
||||||
Parser {
|
|
||||||
s: s.as_bytes(),
|
|
||||||
pos: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_eof(&self) -> bool {
|
|
||||||
self.pos == self.s.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commit only if parser returns Some
|
|
||||||
fn read_atomically<T, F>(&mut self, cb: F) -> Option<T> where
|
|
||||||
F: FnOnce(&mut Parser) -> Option<T>,
|
|
||||||
{
|
|
||||||
let pos = self.pos;
|
|
||||||
let r = cb(self);
|
|
||||||
if r.is_none() {
|
|
||||||
self.pos = pos;
|
|
||||||
}
|
|
||||||
r
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commit only if parser read till EOF
|
|
||||||
fn read_till_eof<T, F>(&mut self, cb: F) -> Option<T> where
|
|
||||||
F: FnOnce(&mut Parser) -> Option<T>,
|
|
||||||
{
|
|
||||||
self.read_atomically(move |p| {
|
|
||||||
match cb(p) {
|
|
||||||
Some(x) => if p.is_eof() {Some(x)} else {None},
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return result of first successful parser
|
|
||||||
fn read_or<T>(&mut self, parsers: &mut [Box<FnMut(&mut Parser) -> Option<T>>])
|
|
||||||
-> Option<T> {
|
|
||||||
for pf in parsers {
|
|
||||||
match self.read_atomically(|p: &mut Parser| pf.call_mut((p,))) {
|
|
||||||
Some(r) => return Some(r),
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply 3 parsers sequentially
|
|
||||||
fn read_seq_3<A, B, C, PA, PB, PC>(&mut self,
|
|
||||||
pa: PA,
|
|
||||||
pb: PB,
|
|
||||||
pc: PC)
|
|
||||||
-> Option<(A, B, C)> where
|
|
||||||
PA: FnOnce(&mut Parser) -> Option<A>,
|
|
||||||
PB: FnOnce(&mut Parser) -> Option<B>,
|
|
||||||
PC: FnOnce(&mut Parser) -> Option<C>,
|
|
||||||
{
|
|
||||||
self.read_atomically(move |p| {
|
|
||||||
let a = pa(p);
|
|
||||||
let b = if a.is_some() { pb(p) } else { None };
|
|
||||||
let c = if b.is_some() { pc(p) } else { None };
|
|
||||||
match (a, b, c) {
|
|
||||||
(Some(a), Some(b), Some(c)) => Some((a, b, c)),
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read next char
|
|
||||||
fn read_char(&mut self) -> Option<char> {
|
|
||||||
if self.is_eof() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let r = self.s[self.pos] as char;
|
|
||||||
self.pos += 1;
|
|
||||||
Some(r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return char and advance iff next char is equal to requested
|
|
||||||
fn read_given_char(&mut self, c: char) -> Option<char> {
|
|
||||||
self.read_atomically(|p| {
|
|
||||||
match p.read_char() {
|
|
||||||
Some(next) if next == c => Some(next),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read digit
|
|
||||||
fn read_digit(&mut self, radix: u8) -> Option<u8> {
|
|
||||||
fn parse_digit(c: char, radix: u8) -> Option<u8> {
|
|
||||||
let c = c as u8;
|
|
||||||
// assuming radix is either 10 or 16
|
|
||||||
if c >= b'0' && c <= b'9' {
|
|
||||||
Some(c - b'0')
|
|
||||||
} else if radix > 10 && c >= b'a' && c < b'a' + (radix - 10) {
|
|
||||||
Some(c - b'a' + 10)
|
|
||||||
} else if radix > 10 && c >= b'A' && c < b'A' + (radix - 10) {
|
|
||||||
Some(c - b'A' + 10)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.read_atomically(|p| {
|
|
||||||
p.read_char().and_then(|c| parse_digit(c, radix))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_number_impl(&mut self, radix: u8, max_digits: u32, upto: u32) -> Option<u32> {
|
|
||||||
let mut r = 0;
|
|
||||||
let mut digit_count = 0;
|
|
||||||
loop {
|
|
||||||
match self.read_digit(radix) {
|
|
||||||
Some(d) => {
|
|
||||||
r = r * (radix as u32) + (d as u32);
|
|
||||||
digit_count += 1;
|
|
||||||
if digit_count > max_digits || r >= upto {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
if digit_count == 0 {
|
|
||||||
return None
|
|
||||||
} else {
|
|
||||||
return Some(r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read number, failing if max_digits of number value exceeded
|
|
||||||
fn read_number(&mut self, radix: u8, max_digits: u32, upto: u32) -> Option<u32> {
|
|
||||||
self.read_atomically(|p| p.read_number_impl(radix, max_digits, upto))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_ipv4_addr_impl(&mut self) -> Option<IpAddr> {
|
|
||||||
let mut bs = [0; 4];
|
|
||||||
let mut i = 0;
|
|
||||||
while i < 4 {
|
|
||||||
if i != 0 && self.read_given_char('.').is_none() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let octet = self.read_number(10, 3, 0x100).map(|n| n as u8);
|
|
||||||
match octet {
|
|
||||||
Some(d) => bs[i] = d,
|
|
||||||
None => return None,
|
|
||||||
};
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
Some(Ipv4Addr(bs[0], bs[1], bs[2], bs[3]))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read IPv4 address
|
|
||||||
fn read_ipv4_addr(&mut self) -> Option<IpAddr> {
|
|
||||||
self.read_atomically(|p| p.read_ipv4_addr_impl())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_ipv6_addr_impl(&mut self) -> Option<IpAddr> {
|
|
||||||
fn ipv6_addr_from_head_tail(head: &[u16], tail: &[u16]) -> IpAddr {
|
|
||||||
assert!(head.len() + tail.len() <= 8);
|
|
||||||
let mut gs = [0; 8];
|
|
||||||
gs.clone_from_slice(head);
|
|
||||||
gs[(8 - tail.len()) .. 8].clone_from_slice(tail);
|
|
||||||
Ipv6Addr(gs[0], gs[1], gs[2], gs[3], gs[4], gs[5], gs[6], gs[7])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_groups(p: &mut Parser, groups: &mut [u16; 8], limit: usize) -> (usize, bool) {
|
|
||||||
let mut i = 0;
|
|
||||||
while i < limit {
|
|
||||||
if i < limit - 1 {
|
|
||||||
let ipv4 = p.read_atomically(|p| {
|
|
||||||
if i == 0 || p.read_given_char(':').is_some() {
|
|
||||||
p.read_ipv4_addr()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
});
|
|
||||||
match ipv4 {
|
|
||||||
Some(Ipv4Addr(a, b, c, d)) => {
|
|
||||||
groups[i + 0] = ((a as u16) << 8) | (b as u16);
|
|
||||||
groups[i + 1] = ((c as u16) << 8) | (d as u16);
|
|
||||||
return (i + 2, true);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let group = p.read_atomically(|p| {
|
|
||||||
if i == 0 || p.read_given_char(':').is_some() {
|
|
||||||
p.read_number(16, 4, 0x10000).map(|n| n as u16)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
});
|
|
||||||
match group {
|
|
||||||
Some(g) => groups[i] = g,
|
|
||||||
None => return (i, false)
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
(i, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut head = [0; 8];
|
|
||||||
let (head_size, head_ipv4) = read_groups(self, &mut head, 8);
|
|
||||||
|
|
||||||
if head_size == 8 {
|
|
||||||
return Some(Ipv6Addr(
|
|
||||||
head[0], head[1], head[2], head[3],
|
|
||||||
head[4], head[5], head[6], head[7]))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IPv4 part is not allowed before `::`
|
|
||||||
if head_ipv4 {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
|
|
||||||
// read `::` if previous code parsed less than 8 groups
|
|
||||||
if !self.read_given_char(':').is_some() || !self.read_given_char(':').is_some() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut tail = [0; 8];
|
|
||||||
let (tail_size, _) = read_groups(self, &mut tail, 8 - head_size);
|
|
||||||
Some(ipv6_addr_from_head_tail(&head[..head_size], &tail[..tail_size]))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_ipv6_addr(&mut self) -> Option<IpAddr> {
|
|
||||||
self.read_atomically(|p| p.read_ipv6_addr_impl())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_ip_addr(&mut self) -> Option<IpAddr> {
|
|
||||||
let ipv4_addr: Box<_> = box |p: &mut Parser| p.read_ipv4_addr();
|
|
||||||
let ipv6_addr: Box<_> = box |p: &mut Parser| p.read_ipv6_addr();
|
|
||||||
self.read_or(&mut [ipv4_addr, ipv6_addr])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_socket_addr(&mut self) -> Option<SocketAddr> {
|
|
||||||
let ip_addr = |p: &mut Parser| {
|
|
||||||
let ipv4_p: Box<_> = box |p: &mut Parser| p.read_ip_addr();
|
|
||||||
let ipv6_p: Box<_> = box |p: &mut Parser| {
|
|
||||||
let open_br = |p: &mut Parser| p.read_given_char('[');
|
|
||||||
let ip_addr = |p: &mut Parser| p.read_ipv6_addr();
|
|
||||||
let clos_br = |p: &mut Parser| p.read_given_char(']');
|
|
||||||
p.read_seq_3::<char, IpAddr, char, _, _, _>(open_br, ip_addr, clos_br)
|
|
||||||
.map(|t| match t { (_, ip, _) => ip })
|
|
||||||
};
|
|
||||||
p.read_or(&mut [ipv4_p, ipv6_p])
|
|
||||||
};
|
|
||||||
let colon = |p: &mut Parser| p.read_given_char(':');
|
|
||||||
let port = |p: &mut Parser| p.read_number(10, 5, 0x10000).map(|n| n as u16);
|
|
||||||
|
|
||||||
// host, colon, port
|
|
||||||
self.read_seq_3::<IpAddr, char, u16, _, _, _>(ip_addr, colon, port)
|
|
||||||
.map(|t| match t { (ip, _, port) => SocketAddr { ip: ip, port: port } })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for IpAddr {
|
|
||||||
type Err = ParseError;
|
|
||||||
fn from_str(s: &str) -> Result<IpAddr, ParseError> {
|
|
||||||
match Parser::new(s).read_till_eof(|p| p.read_ip_addr()) {
|
|
||||||
Some(s) => Ok(s),
|
|
||||||
None => Err(ParseError),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for SocketAddr {
|
|
||||||
type Err = ParseError;
|
|
||||||
fn from_str(s: &str) -> Result<SocketAddr, ParseError> {
|
|
||||||
match Parser::new(s).read_till_eof(|p| p.read_socket_addr()) {
|
|
||||||
Some(s) => Ok(s),
|
|
||||||
None => Err(ParseError),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Copy)]
|
|
||||||
pub struct ParseError;
|
|
||||||
|
|
||||||
/// A trait for objects which can be converted or resolved to one or more `SocketAddr` values.
|
|
||||||
///
|
|
||||||
/// Implementing types minimally have to implement either `to_socket_addr` or `to_socket_addr_all`
|
|
||||||
/// method, and its trivial counterpart will be available automatically.
|
|
||||||
///
|
|
||||||
/// This trait is used for generic address resolution when constructing network objects.
|
|
||||||
/// By default it is implemented for the following types:
|
|
||||||
///
|
|
||||||
/// * `SocketAddr` - `to_socket_addr` is identity function.
|
|
||||||
///
|
|
||||||
/// * `(IpAddr, u16)` - `to_socket_addr` constructs `SocketAddr` trivially.
|
|
||||||
///
|
|
||||||
/// * `(&str, u16)` - the string should be either a string representation of an IP address
|
|
||||||
/// expected by `FromStr` implementation for `IpAddr` or a host name.
|
|
||||||
///
|
|
||||||
/// For the former, `to_socket_addr_all` returns a vector with a single element corresponding
|
|
||||||
/// to that IP address joined with the given port.
|
|
||||||
///
|
|
||||||
/// For the latter, it tries to resolve the host name and returns a vector of all IP addresses
|
|
||||||
/// for the host name, each joined with the given port.
|
|
||||||
///
|
|
||||||
/// * `&str` - the string should be either a string representation of a `SocketAddr` as
|
|
||||||
/// expected by its `FromStr` implementation or a string like `<host_name>:<port>` pair
|
|
||||||
/// where `<port>` is a `u16` value.
|
|
||||||
///
|
|
||||||
/// For the former, `to_socket_addr_all` returns a vector with a single element corresponding
|
|
||||||
/// to that socket address.
|
|
||||||
///
|
|
||||||
/// For the latter, it tries to resolve the host name and returns a vector of all IP addresses
|
|
||||||
/// for the host name, each joined with the port.
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// This trait allows constructing network objects like `TcpStream` or `UdpSocket` easily with
|
|
||||||
/// values of various types for the bind/connection address. It is needed because sometimes
|
|
||||||
/// one type is more appropriate than the other: for simple uses a string like `"localhost:12345"`
|
|
||||||
/// is much nicer than manual construction of the corresponding `SocketAddr`, but sometimes
|
|
||||||
/// `SocketAddr` value is *the* main source of the address, and converting it to some other type
|
|
||||||
/// (e.g. a string) just for it to be converted back to `SocketAddr` in constructor methods
|
|
||||||
/// is pointless.
|
|
||||||
///
|
|
||||||
/// Some examples:
|
|
||||||
///
|
|
||||||
/// ```rust,no_run
|
|
||||||
/// # #![feature(old_io)]
|
|
||||||
/// # #![allow(unused_must_use)]
|
|
||||||
///
|
|
||||||
/// use std::old_io::{TcpStream, TcpListener};
|
|
||||||
/// use std::old_io::net::udp::UdpSocket;
|
|
||||||
/// use std::old_io::net::ip::{Ipv4Addr, SocketAddr};
|
|
||||||
///
|
|
||||||
/// fn main() {
|
|
||||||
/// // The following lines are equivalent modulo possible "localhost" name resolution
|
|
||||||
/// // differences
|
|
||||||
/// let tcp_s = TcpStream::connect(SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 12345 });
|
|
||||||
/// let tcp_s = TcpStream::connect((Ipv4Addr(127, 0, 0, 1), 12345));
|
|
||||||
/// let tcp_s = TcpStream::connect(("127.0.0.1", 12345));
|
|
||||||
/// let tcp_s = TcpStream::connect(("localhost", 12345));
|
|
||||||
/// let tcp_s = TcpStream::connect("127.0.0.1:12345");
|
|
||||||
/// let tcp_s = TcpStream::connect("localhost:12345");
|
|
||||||
///
|
|
||||||
/// // TcpListener::bind(), UdpSocket::bind() and UdpSocket::send_to() behave similarly
|
|
||||||
/// let tcp_l = TcpListener::bind("localhost:12345");
|
|
||||||
///
|
|
||||||
/// let mut udp_s = UdpSocket::bind(("127.0.0.1", 23451)).unwrap();
|
|
||||||
/// udp_s.send_to([7, 7, 7].as_ref(), (Ipv4Addr(127, 0, 0, 1), 23451));
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub trait ToSocketAddr {
|
|
||||||
/// Converts this object to single socket address value.
|
|
||||||
///
|
|
||||||
/// If more than one value is available, this method returns the first one. If no
|
|
||||||
/// values are available, this method returns an `IoError`.
|
|
||||||
///
|
|
||||||
/// By default this method delegates to `to_socket_addr_all` method, taking the first
|
|
||||||
/// item from its result.
|
|
||||||
fn to_socket_addr(&self) -> IoResult<SocketAddr> {
|
|
||||||
self.to_socket_addr_all()
|
|
||||||
.and_then(|v| v.into_iter().next().ok_or_else(|| IoError {
|
|
||||||
kind: old_io::InvalidInput,
|
|
||||||
desc: "no address available",
|
|
||||||
detail: None
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts this object to all available socket address values.
|
|
||||||
///
|
|
||||||
/// Some values like host name string naturally correspond to multiple IP addresses.
|
|
||||||
/// This method tries to return all available addresses corresponding to this object.
|
|
||||||
///
|
|
||||||
/// By default this method delegates to `to_socket_addr` method, creating a singleton
|
|
||||||
/// vector from its result.
|
|
||||||
#[inline]
|
|
||||||
fn to_socket_addr_all(&self) -> IoResult<Vec<SocketAddr>> {
|
|
||||||
self.to_socket_addr().map(|a| vec![a])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToSocketAddr for SocketAddr {
|
|
||||||
#[inline]
|
|
||||||
fn to_socket_addr(&self) -> IoResult<SocketAddr> { Ok(*self) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToSocketAddr for (IpAddr, u16) {
|
|
||||||
#[inline]
|
|
||||||
fn to_socket_addr(&self) -> IoResult<SocketAddr> {
|
|
||||||
let (ip, port) = *self;
|
|
||||||
Ok(SocketAddr { ip: ip, port: port })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_socket_addr(s: &str, p: u16) -> IoResult<Vec<SocketAddr>> {
|
|
||||||
net::get_host_addresses(s)
|
|
||||||
.map(|v| v.into_iter().map(|a| SocketAddr { ip: a, port: p }).collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_and_resolve_socket_addr(s: &str) -> IoResult<Vec<SocketAddr>> {
|
|
||||||
macro_rules! try_opt {
|
|
||||||
($e:expr, $msg:expr) => (
|
|
||||||
match $e {
|
|
||||||
Some(r) => r,
|
|
||||||
None => return Err(IoError {
|
|
||||||
kind: old_io::InvalidInput,
|
|
||||||
desc: $msg,
|
|
||||||
detail: None
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// split the string by ':' and convert the second part to u16
|
|
||||||
let mut parts_iter = s.rsplitn(2, ':');
|
|
||||||
let port_str = try_opt!(parts_iter.next(), "invalid socket address");
|
|
||||||
let host = try_opt!(parts_iter.next(), "invalid socket address");
|
|
||||||
let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value");
|
|
||||||
resolve_socket_addr(host, port)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ToSocketAddr for (&'a str, u16) {
|
|
||||||
fn to_socket_addr_all(&self) -> IoResult<Vec<SocketAddr>> {
|
|
||||||
let (host, port) = *self;
|
|
||||||
|
|
||||||
// try to parse the host as a regular IpAddr first
|
|
||||||
match host.parse().ok() {
|
|
||||||
Some(addr) => return Ok(vec![SocketAddr {
|
|
||||||
ip: addr,
|
|
||||||
port: port
|
|
||||||
}]),
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve_socket_addr(host, port)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// accepts strings like 'localhost:12345'
|
|
||||||
impl<'a> ToSocketAddr for &'a str {
|
|
||||||
fn to_socket_addr(&self) -> IoResult<SocketAddr> {
|
|
||||||
// try to parse as a regular SocketAddr first
|
|
||||||
match self.parse().ok() {
|
|
||||||
Some(addr) => return Ok(addr),
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
parse_and_resolve_socket_addr(*self)
|
|
||||||
.and_then(|v| v.into_iter().next()
|
|
||||||
.ok_or_else(|| IoError {
|
|
||||||
kind: old_io::InvalidInput,
|
|
||||||
desc: "no address available",
|
|
||||||
detail: None
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_socket_addr_all(&self) -> IoResult<Vec<SocketAddr>> {
|
|
||||||
// try to parse as a regular SocketAddr first
|
|
||||||
match self.parse().ok() {
|
|
||||||
Some(addr) => return Ok(vec![addr]),
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
parse_and_resolve_socket_addr(*self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use prelude::v1::*;
|
|
||||||
use super::*;
|
|
||||||
use str::FromStr;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_from_str_ipv4() {
|
|
||||||
assert_eq!(Ok(Ipv4Addr(127, 0, 0, 1)), "127.0.0.1".parse());
|
|
||||||
assert_eq!(Ok(Ipv4Addr(255, 255, 255, 255)), "255.255.255.255".parse());
|
|
||||||
assert_eq!(Ok(Ipv4Addr(0, 0, 0, 0)), "0.0.0.0".parse());
|
|
||||||
|
|
||||||
// out of range
|
|
||||||
let none: Option<IpAddr> = "256.0.0.1".parse().ok();
|
|
||||||
assert_eq!(None, none);
|
|
||||||
// too short
|
|
||||||
let none: Option<IpAddr> = "255.0.0".parse().ok();
|
|
||||||
assert_eq!(None, none);
|
|
||||||
// too long
|
|
||||||
let none: Option<IpAddr> = "255.0.0.1.2".parse().ok();
|
|
||||||
assert_eq!(None, none);
|
|
||||||
// no number between dots
|
|
||||||
let none: Option<IpAddr> = "255.0..1".parse().ok();
|
|
||||||
assert_eq!(None, none);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_from_str_ipv6() {
|
|
||||||
assert_eq!(Ok(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 0)), "0:0:0:0:0:0:0:0".parse());
|
|
||||||
assert_eq!(Ok(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1)), "0:0:0:0:0:0:0:1".parse());
|
|
||||||
|
|
||||||
assert_eq!(Ok(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1)), "::1".parse());
|
|
||||||
assert_eq!(Ok(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 0)), "::".parse());
|
|
||||||
|
|
||||||
assert_eq!(Ok(Ipv6Addr(0x2a02, 0x6b8, 0, 0, 0, 0, 0x11, 0x11)),
|
|
||||||
"2a02:6b8::11:11".parse());
|
|
||||||
|
|
||||||
// too long group
|
|
||||||
let none: Option<IpAddr> = "::00000".parse().ok();
|
|
||||||
assert_eq!(None, none);
|
|
||||||
// too short
|
|
||||||
let none: Option<IpAddr> = "1:2:3:4:5:6:7".parse().ok();
|
|
||||||
assert_eq!(None, none);
|
|
||||||
// too long
|
|
||||||
let none: Option<IpAddr> = "1:2:3:4:5:6:7:8:9".parse().ok();
|
|
||||||
assert_eq!(None, none);
|
|
||||||
// triple colon
|
|
||||||
let none: Option<IpAddr> = "1:2:::6:7:8".parse().ok();
|
|
||||||
assert_eq!(None, none);
|
|
||||||
// two double colons
|
|
||||||
let none: Option<IpAddr> = "1:2::6::8".parse().ok();
|
|
||||||
assert_eq!(None, none);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_from_str_ipv4_in_ipv6() {
|
|
||||||
assert_eq!(Ok(Ipv6Addr(0, 0, 0, 0, 0, 0, 49152, 545)),
|
|
||||||
"::192.0.2.33".parse());
|
|
||||||
assert_eq!(Ok(Ipv6Addr(0, 0, 0, 0, 0, 0xFFFF, 49152, 545)),
|
|
||||||
"::FFFF:192.0.2.33".parse());
|
|
||||||
assert_eq!(Ok(Ipv6Addr(0x64, 0xff9b, 0, 0, 0, 0, 49152, 545)),
|
|
||||||
"64:ff9b::192.0.2.33".parse());
|
|
||||||
assert_eq!(Ok(Ipv6Addr(0x2001, 0xdb8, 0x122, 0xc000, 0x2, 0x2100, 49152, 545)),
|
|
||||||
"2001:db8:122:c000:2:2100:192.0.2.33".parse());
|
|
||||||
|
|
||||||
// colon after v4
|
|
||||||
let none: Option<IpAddr> = "::127.0.0.1:".parse().ok();
|
|
||||||
assert_eq!(None, none);
|
|
||||||
// not enough groups
|
|
||||||
let none: Option<IpAddr> = "1.2.3.4.5:127.0.0.1".parse().ok();
|
|
||||||
assert_eq!(None, none);
|
|
||||||
// too many groups
|
|
||||||
let none: Option<IpAddr> = "1.2.3.4.5:6:7:127.0.0.1".parse().ok();
|
|
||||||
assert_eq!(None, none);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_from_str_socket_addr() {
|
|
||||||
assert_eq!(Ok(SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 80 }),
|
|
||||||
"77.88.21.11:80".parse());
|
|
||||||
assert_eq!(Ok(SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 }),
|
|
||||||
"[2a02:6b8:0:1::1]:53".parse());
|
|
||||||
assert_eq!(Ok(SocketAddr { ip: Ipv6Addr(0, 0, 0, 0, 0, 0, 0x7F00, 1), port: 22 }),
|
|
||||||
"[::127.0.0.1]:22".parse());
|
|
||||||
|
|
||||||
// without port
|
|
||||||
let none: Option<SocketAddr> = "127.0.0.1".parse().ok();
|
|
||||||
assert_eq!(None, none);
|
|
||||||
// without port
|
|
||||||
let none: Option<SocketAddr> = "127.0.0.1:".parse().ok();
|
|
||||||
assert_eq!(None, none);
|
|
||||||
// wrong brackets around v4
|
|
||||||
let none: Option<SocketAddr> = "[127.0.0.1]:22".parse().ok();
|
|
||||||
assert_eq!(None, none);
|
|
||||||
// port out of range
|
|
||||||
let none: Option<SocketAddr> = "127.0.0.1:123456".parse().ok();
|
|
||||||
assert_eq!(None, none);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ipv6_addr_to_string() {
|
|
||||||
let a1 = Ipv6Addr(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280);
|
|
||||||
assert!(a1.to_string() == "::ffff:192.0.2.128" ||
|
|
||||||
a1.to_string() == "::FFFF:192.0.2.128");
|
|
||||||
assert_eq!(Ipv6Addr(8, 9, 10, 11, 12, 13, 14, 15).to_string(),
|
|
||||||
"8:9:a:b:c:d:e:f");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn to_socket_addr_socketaddr() {
|
|
||||||
let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 12345 };
|
|
||||||
assert_eq!(Ok(a), a.to_socket_addr());
|
|
||||||
assert_eq!(Ok(vec![a]), a.to_socket_addr_all());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn to_socket_addr_ipaddr_u16() {
|
|
||||||
let a = Ipv4Addr(77, 88, 21, 11);
|
|
||||||
let p = 12345;
|
|
||||||
let e = SocketAddr { ip: a, port: p };
|
|
||||||
assert_eq!(Ok(e), (a, p).to_socket_addr());
|
|
||||||
assert_eq!(Ok(vec![e]), (a, p).to_socket_addr_all());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn to_socket_addr_str_u16() {
|
|
||||||
let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 24352 };
|
|
||||||
assert_eq!(Ok(a), ("77.88.21.11", 24352).to_socket_addr());
|
|
||||||
assert_eq!(Ok(vec![a]), ("77.88.21.11", 24352).to_socket_addr_all());
|
|
||||||
|
|
||||||
let a = SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 };
|
|
||||||
assert_eq!(Ok(a), ("2a02:6b8:0:1::1", 53).to_socket_addr());
|
|
||||||
assert_eq!(Ok(vec![a]), ("2a02:6b8:0:1::1", 53).to_socket_addr_all());
|
|
||||||
|
|
||||||
let a = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 23924 };
|
|
||||||
assert!(("localhost", 23924).to_socket_addr_all().unwrap().contains(&a));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn to_socket_addr_str() {
|
|
||||||
let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 24352 };
|
|
||||||
assert_eq!(Ok(a), "77.88.21.11:24352".to_socket_addr());
|
|
||||||
assert_eq!(Ok(vec![a]), "77.88.21.11:24352".to_socket_addr_all());
|
|
||||||
|
|
||||||
let a = SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 };
|
|
||||||
assert_eq!(Ok(a), "[2a02:6b8:0:1::1]:53".to_socket_addr());
|
|
||||||
assert_eq!(Ok(vec![a]), "[2a02:6b8:0:1::1]:53".to_socket_addr_all());
|
|
||||||
|
|
||||||
let a = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 23924 };
|
|
||||||
assert!("localhost:23924".to_socket_addr_all().unwrap().contains(&a));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
// Copyright 2013 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.
|
|
||||||
|
|
||||||
//! Networking I/O
|
|
||||||
|
|
||||||
#![deprecated(since = "1.0.0",
|
|
||||||
reason = "replaced with new I/O primitives in `std::net`")]
|
|
||||||
#![unstable(feature = "old_io")]
|
|
||||||
|
|
||||||
use old_io::{IoError, IoResult, InvalidInput};
|
|
||||||
use ops::FnMut;
|
|
||||||
use option::Option::None;
|
|
||||||
use result::Result::{Ok, Err};
|
|
||||||
use self::ip::{SocketAddr, ToSocketAddr};
|
|
||||||
|
|
||||||
pub use self::addrinfo::get_host_addresses;
|
|
||||||
|
|
||||||
pub mod addrinfo;
|
|
||||||
pub mod tcp;
|
|
||||||
pub mod udp;
|
|
||||||
pub mod ip;
|
|
||||||
pub mod pipe;
|
|
||||||
|
|
||||||
fn with_addresses<A, T, F>(addr: A, mut action: F) -> IoResult<T> where
|
|
||||||
A: ToSocketAddr,
|
|
||||||
F: FnMut(SocketAddr) -> IoResult<T>,
|
|
||||||
{
|
|
||||||
const DEFAULT_ERROR: IoError = IoError {
|
|
||||||
kind: InvalidInput,
|
|
||||||
desc: "no addresses found for hostname",
|
|
||||||
detail: None
|
|
||||||
};
|
|
||||||
|
|
||||||
let addresses = try!(addr.to_socket_addr_all());
|
|
||||||
let mut err = DEFAULT_ERROR;
|
|
||||||
for addr in addresses {
|
|
||||||
match action(addr) {
|
|
||||||
Ok(r) => return Ok(r),
|
|
||||||
Err(e) => err = e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err)
|
|
||||||
}
|
|
@ -1,883 +0,0 @@
|
|||||||
// Copyright 2013 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.
|
|
||||||
|
|
||||||
//! Named pipes
|
|
||||||
//!
|
|
||||||
//! This module contains the ability to communicate over named pipes with
|
|
||||||
//! synchronous I/O. On windows, this corresponds to talking over a Named Pipe,
|
|
||||||
//! while on Unix it corresponds to UNIX domain sockets.
|
|
||||||
//!
|
|
||||||
//! These pipes are similar to TCP in the sense that you can have both a stream to a
|
|
||||||
//! server and a server itself. The server provided accepts other `UnixStream`
|
|
||||||
//! instances as clients.
|
|
||||||
|
|
||||||
#![allow(missing_docs)]
|
|
||||||
#![deprecated(since = "1.0.0",
|
|
||||||
reason = "will be removed to be reintroduced at a later date; \
|
|
||||||
in the meantime consider using the `unix_socket` crate \
|
|
||||||
for unix sockets; there is currently no replacement \
|
|
||||||
for named pipes")]
|
|
||||||
#![unstable(feature = "old_io")]
|
|
||||||
|
|
||||||
use prelude::v1::*;
|
|
||||||
|
|
||||||
use ffi::CString;
|
|
||||||
use old_path::BytesContainer;
|
|
||||||
use old_io::{Listener, Acceptor, IoResult, TimedOut, standard_error};
|
|
||||||
use old_io::{Reader, Writer};
|
|
||||||
use sys::pipe::UnixAcceptor as UnixAcceptorImp;
|
|
||||||
use sys::pipe::UnixListener as UnixListenerImp;
|
|
||||||
use sys::pipe::UnixStream as UnixStreamImp;
|
|
||||||
use time::Duration;
|
|
||||||
|
|
||||||
use sys_common;
|
|
||||||
|
|
||||||
/// A stream which communicates over a named pipe.
|
|
||||||
pub struct UnixStream {
|
|
||||||
inner: UnixStreamImp,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UnixStream {
|
|
||||||
|
|
||||||
/// Connect to a pipe named by `path`. This will attempt to open a
|
|
||||||
/// connection to the underlying socket.
|
|
||||||
///
|
|
||||||
/// The returned stream will be closed when the object falls out of scope.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![feature(old_io, old_path, io)]
|
|
||||||
/// # #![allow(unused_must_use)]
|
|
||||||
/// use std::old_io::net::pipe::UnixStream;
|
|
||||||
/// use std::old_io::*;
|
|
||||||
/// use std::old_path::Path;
|
|
||||||
///
|
|
||||||
/// let server = Path::new("path/to/my/socket");
|
|
||||||
/// let mut stream = UnixStream::connect(&server);
|
|
||||||
/// stream.write(&[1, 2, 3]);
|
|
||||||
/// ```
|
|
||||||
pub fn connect<P: BytesContainer>(path: P) -> IoResult<UnixStream> {
|
|
||||||
let path = try!(CString::new(path.container_as_bytes()));
|
|
||||||
UnixStreamImp::connect(&path, None)
|
|
||||||
.map(|inner| UnixStream { inner: inner })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Connect to a pipe named by `path`, timing out if the specified number of
|
|
||||||
/// milliseconds.
|
|
||||||
///
|
|
||||||
/// This function is similar to `connect`, except that if `timeout`
|
|
||||||
/// elapses the function will return an error of kind `TimedOut`.
|
|
||||||
///
|
|
||||||
/// If a `timeout` with zero or negative duration is specified then
|
|
||||||
/// the function returns `Err`, with the error kind set to `TimedOut`.
|
|
||||||
#[unstable(feature = "io",
|
|
||||||
reason = "the timeout argument is likely to change types")]
|
|
||||||
pub fn connect_timeout<P>(path: P, timeout: Duration)
|
|
||||||
-> IoResult<UnixStream>
|
|
||||||
where P: BytesContainer {
|
|
||||||
if timeout <= Duration::milliseconds(0) {
|
|
||||||
return Err(standard_error(TimedOut));
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = try!(CString::new(path.container_as_bytes()));
|
|
||||||
UnixStreamImp::connect(&path, Some(timeout.num_milliseconds() as u64))
|
|
||||||
.map(|inner| UnixStream { inner: inner })
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Closes the reading half of this connection.
|
|
||||||
///
|
|
||||||
/// This method will close the reading portion of this connection, causing
|
|
||||||
/// all pending and future reads to immediately return with an error.
|
|
||||||
///
|
|
||||||
/// Note that this method affects all cloned handles associated with this
|
|
||||||
/// stream, not just this one handle.
|
|
||||||
pub fn close_read(&mut self) -> IoResult<()> {
|
|
||||||
self.inner.close_read()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Closes the writing half of this connection.
|
|
||||||
///
|
|
||||||
/// This method will close the writing portion of this connection, causing
|
|
||||||
/// all pending and future writes to immediately return with an error.
|
|
||||||
///
|
|
||||||
/// Note that this method affects all cloned handles associated with this
|
|
||||||
/// stream, not just this one handle.
|
|
||||||
pub fn close_write(&mut self) -> IoResult<()> {
|
|
||||||
self.inner.close_write()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the read/write timeout for this socket.
|
|
||||||
///
|
|
||||||
/// For more information, see `TcpStream::set_timeout`
|
|
||||||
#[unstable(feature = "io",
|
|
||||||
reason = "the timeout argument may change in type and value")]
|
|
||||||
pub fn set_timeout(&mut self, timeout_ms: Option<u64>) {
|
|
||||||
self.inner.set_timeout(timeout_ms)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the read timeout for this socket.
|
|
||||||
///
|
|
||||||
/// For more information, see `TcpStream::set_timeout`
|
|
||||||
#[unstable(feature = "io",
|
|
||||||
reason = "the timeout argument may change in type and value")]
|
|
||||||
pub fn set_read_timeout(&mut self, timeout_ms: Option<u64>) {
|
|
||||||
self.inner.set_read_timeout(timeout_ms)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the write timeout for this socket.
|
|
||||||
///
|
|
||||||
/// For more information, see `TcpStream::set_timeout`
|
|
||||||
#[unstable(feature = "io",
|
|
||||||
reason = "the timeout argument may change in type and value")]
|
|
||||||
pub fn set_write_timeout(&mut self, timeout_ms: Option<u64>) {
|
|
||||||
self.inner.set_write_timeout(timeout_ms)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for UnixStream {
|
|
||||||
fn clone(&self) -> UnixStream {
|
|
||||||
UnixStream { inner: self.inner.clone() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Reader for UnixStream {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
|
||||||
self.inner.read(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Writer for UnixStream {
|
|
||||||
fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
|
|
||||||
self.inner.write(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl sys_common::AsInner<UnixStreamImp> for UnixStream {
|
|
||||||
fn as_inner(&self) -> &UnixStreamImp {
|
|
||||||
&self.inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A value that can listen for incoming named pipe connection requests.
|
|
||||||
pub struct UnixListener {
|
|
||||||
/// The internal, opaque runtime Unix listener.
|
|
||||||
inner: UnixListenerImp,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UnixListener {
|
|
||||||
/// Creates a new listener, ready to receive incoming connections on the
|
|
||||||
/// specified socket. The server will be named by `path`.
|
|
||||||
///
|
|
||||||
/// This listener will be closed when it falls out of scope.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![feature(old_io, io, old_path)]
|
|
||||||
/// # fn foo() {
|
|
||||||
/// use std::old_io::net::pipe::UnixListener;
|
|
||||||
/// use std::old_io::*;
|
|
||||||
/// use std::old_path::Path;
|
|
||||||
///
|
|
||||||
/// let server = Path::new("/path/to/my/socket");
|
|
||||||
/// let stream = UnixListener::bind(&server);
|
|
||||||
/// for mut client in stream.listen().incoming() {
|
|
||||||
/// let _ = client.write(&[1, 2, 3, 4]);
|
|
||||||
/// }
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
pub fn bind<P: BytesContainer>(path: P) -> IoResult<UnixListener> {
|
|
||||||
let path = try!(CString::new(path.container_as_bytes()));
|
|
||||||
UnixListenerImp::bind(&path)
|
|
||||||
.map(|inner| UnixListener { inner: inner })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Listener<UnixAcceptor> for UnixListener {
|
|
||||||
fn listen(self) -> IoResult<UnixAcceptor> {
|
|
||||||
self.inner.listen()
|
|
||||||
.map(|inner| UnixAcceptor { inner: inner })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl sys_common::AsInner<UnixListenerImp> for UnixListener {
|
|
||||||
fn as_inner(&self) -> &UnixListenerImp {
|
|
||||||
&self.inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A value that can accept named pipe connections, returned from `listen()`.
|
|
||||||
pub struct UnixAcceptor {
|
|
||||||
/// The internal, opaque runtime Unix acceptor.
|
|
||||||
inner: UnixAcceptorImp
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UnixAcceptor {
|
|
||||||
/// Sets a timeout for this acceptor, after which accept() will no longer
|
|
||||||
/// block indefinitely.
|
|
||||||
///
|
|
||||||
/// The argument specified is the amount of time, in milliseconds, into the
|
|
||||||
/// future after which all invocations of accept() will not block (and any
|
|
||||||
/// pending invocation will return). A value of `None` will clear any
|
|
||||||
/// existing timeout.
|
|
||||||
///
|
|
||||||
/// When using this method, it is likely necessary to reset the timeout as
|
|
||||||
/// appropriate, the timeout specified is specific to this object, not
|
|
||||||
/// specific to the next request.
|
|
||||||
#[unstable(feature = "io",
|
|
||||||
reason = "the name and arguments to this function are likely \
|
|
||||||
to change")]
|
|
||||||
pub fn set_timeout(&mut self, timeout_ms: Option<u64>) {
|
|
||||||
self.inner.set_timeout(timeout_ms)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Closes the accepting capabilities of this acceptor.
|
|
||||||
///
|
|
||||||
/// This function has the same semantics as `TcpAcceptor::close_accept`, and
|
|
||||||
/// more information can be found in that documentation.
|
|
||||||
#[unstable(feature = "io")]
|
|
||||||
pub fn close_accept(&mut self) -> IoResult<()> {
|
|
||||||
self.inner.close_accept()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Acceptor for UnixAcceptor {
|
|
||||||
type Connection = UnixStream;
|
|
||||||
fn accept(&mut self) -> IoResult<UnixStream> {
|
|
||||||
self.inner.accept().map(|s| {
|
|
||||||
UnixStream { inner: s }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for UnixAcceptor {
|
|
||||||
/// Creates a new handle to this unix acceptor, allowing for simultaneous
|
|
||||||
/// accepts.
|
|
||||||
///
|
|
||||||
/// The underlying unix acceptor will not be closed until all handles to the
|
|
||||||
/// acceptor have been deallocated. Incoming connections will be received on
|
|
||||||
/// at most once acceptor, the same connection will not be accepted twice.
|
|
||||||
///
|
|
||||||
/// The `close_accept` method will shut down *all* acceptors cloned from the
|
|
||||||
/// same original acceptor, whereas the `set_timeout` method only affects
|
|
||||||
/// the selector that it is called on.
|
|
||||||
///
|
|
||||||
/// This function is useful for creating a handle to invoke `close_accept`
|
|
||||||
/// on to wake up any other task blocked in `accept`.
|
|
||||||
fn clone(&self) -> UnixAcceptor {
|
|
||||||
UnixAcceptor { inner: self.inner.clone() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl sys_common::AsInner<UnixAcceptorImp> for UnixAcceptor {
|
|
||||||
fn as_inner(&self) -> &UnixAcceptorImp {
|
|
||||||
&self.inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use prelude::v1::*;
|
|
||||||
|
|
||||||
use old_io::fs::PathExtensions;
|
|
||||||
use old_io::{EndOfFile, TimedOut, ShortWrite, IoError, ConnectionReset};
|
|
||||||
use old_io::{NotConnected, BrokenPipe, FileNotFound, InvalidInput, OtherIoError};
|
|
||||||
use old_io::{PermissionDenied, Acceptor, Listener};
|
|
||||||
use old_io::{Reader, Writer};
|
|
||||||
use old_io::test::*;
|
|
||||||
use super::*;
|
|
||||||
use sync::mpsc::channel;
|
|
||||||
use thread;
|
|
||||||
use time::Duration;
|
|
||||||
|
|
||||||
pub fn smalltest<F,G>(server: F, client: G)
|
|
||||||
where F : FnOnce(UnixStream), F : Send,
|
|
||||||
G : FnOnce(UnixStream), G : Send + 'static
|
|
||||||
{
|
|
||||||
let path1 = next_test_unix();
|
|
||||||
let path2 = path1.clone();
|
|
||||||
|
|
||||||
let mut acceptor = UnixListener::bind(&path1).listen();
|
|
||||||
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
match UnixStream::connect(&path2) {
|
|
||||||
Ok(c) => client(c),
|
|
||||||
Err(e) => panic!("failed connect: {}", e),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
match acceptor.accept() {
|
|
||||||
Ok(c) => server(c),
|
|
||||||
Err(e) => panic!("failed accept: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn bind_error() {
|
|
||||||
let path = "path/to/nowhere";
|
|
||||||
match UnixListener::bind(&path) {
|
|
||||||
Ok(..) => panic!(),
|
|
||||||
Err(e) => {
|
|
||||||
assert!(e.kind == PermissionDenied || e.kind == FileNotFound ||
|
|
||||||
e.kind == InvalidInput);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn connect_error() {
|
|
||||||
let path = if cfg!(windows) {
|
|
||||||
r"\\.\pipe\this_should_not_exist_ever"
|
|
||||||
} else {
|
|
||||||
"path/to/nowhere"
|
|
||||||
};
|
|
||||||
match UnixStream::connect(&path) {
|
|
||||||
Ok(..) => panic!(),
|
|
||||||
Err(e) => {
|
|
||||||
assert!(e.kind == FileNotFound || e.kind == OtherIoError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn smoke() {
|
|
||||||
smalltest(move |mut server| {
|
|
||||||
let mut buf = [0];
|
|
||||||
server.read(&mut buf).unwrap();
|
|
||||||
assert!(buf[0] == 99);
|
|
||||||
}, move|mut client| {
|
|
||||||
client.write(&[99]).unwrap();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(windows, ignore)] // FIXME(#12516)
|
|
||||||
#[test]
|
|
||||||
fn read_eof() {
|
|
||||||
smalltest(move|mut server| {
|
|
||||||
let mut buf = [0];
|
|
||||||
assert!(server.read(&mut buf).is_err());
|
|
||||||
assert!(server.read(&mut buf).is_err());
|
|
||||||
}, move|_client| {
|
|
||||||
// drop the client
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn write_begone() {
|
|
||||||
smalltest(move|mut server| {
|
|
||||||
let buf = [0];
|
|
||||||
loop {
|
|
||||||
match server.write(&buf) {
|
|
||||||
Ok(..) => {}
|
|
||||||
Err(e) => {
|
|
||||||
assert!(e.kind == BrokenPipe ||
|
|
||||||
e.kind == NotConnected ||
|
|
||||||
e.kind == ConnectionReset,
|
|
||||||
"unknown error {}", e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, move|_client| {
|
|
||||||
// drop the client
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn accept_lots() {
|
|
||||||
let times = 10;
|
|
||||||
let path1 = next_test_unix();
|
|
||||||
let path2 = path1.clone();
|
|
||||||
|
|
||||||
let mut acceptor = match UnixListener::bind(&path1).listen() {
|
|
||||||
Ok(a) => a,
|
|
||||||
Err(e) => panic!("failed listen: {}", e),
|
|
||||||
};
|
|
||||||
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
for _ in 0..times {
|
|
||||||
let mut stream = UnixStream::connect(&path2);
|
|
||||||
match stream.write(&[100]) {
|
|
||||||
Ok(..) => {}
|
|
||||||
Err(e) => panic!("failed write: {}", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
for _ in 0..times {
|
|
||||||
let mut client = acceptor.accept();
|
|
||||||
let mut buf = [0];
|
|
||||||
match client.read(&mut buf) {
|
|
||||||
Ok(..) => {}
|
|
||||||
Err(e) => panic!("failed read/accept: {}", e),
|
|
||||||
}
|
|
||||||
assert_eq!(buf[0], 100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
#[test]
|
|
||||||
fn path_exists() {
|
|
||||||
let path = next_test_unix();
|
|
||||||
let _acceptor = UnixListener::bind(&path).listen();
|
|
||||||
assert!(path.exists());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn unix_clone_smoke() {
|
|
||||||
let addr = next_test_unix();
|
|
||||||
let mut acceptor = UnixListener::bind(&addr).listen();
|
|
||||||
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
let mut s = UnixStream::connect(&addr);
|
|
||||||
let mut buf = [0, 0];
|
|
||||||
debug!("client reading");
|
|
||||||
assert_eq!(s.read(&mut buf), Ok(1));
|
|
||||||
assert_eq!(buf[0], 1);
|
|
||||||
debug!("client writing");
|
|
||||||
s.write(&[2]).unwrap();
|
|
||||||
debug!("client dropping");
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut s1 = acceptor.accept().unwrap();
|
|
||||||
let s2 = s1.clone();
|
|
||||||
|
|
||||||
let (tx1, rx1) = channel();
|
|
||||||
let (tx2, rx2) = channel();
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
let mut s2 = s2;
|
|
||||||
rx1.recv().unwrap();
|
|
||||||
debug!("writer writing");
|
|
||||||
s2.write(&[1]).unwrap();
|
|
||||||
debug!("writer done");
|
|
||||||
tx2.send(()).unwrap();
|
|
||||||
});
|
|
||||||
tx1.send(()).unwrap();
|
|
||||||
let mut buf = [0, 0];
|
|
||||||
debug!("reader reading");
|
|
||||||
assert_eq!(s1.read(&mut buf), Ok(1));
|
|
||||||
debug!("reader done");
|
|
||||||
rx2.recv().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn unix_clone_two_read() {
|
|
||||||
let addr = next_test_unix();
|
|
||||||
let mut acceptor = UnixListener::bind(&addr).listen();
|
|
||||||
let (tx1, rx) = channel();
|
|
||||||
let tx2 = tx1.clone();
|
|
||||||
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
let mut s = UnixStream::connect(&addr);
|
|
||||||
s.write(&[1]).unwrap();
|
|
||||||
rx.recv().unwrap();
|
|
||||||
s.write(&[2]).unwrap();
|
|
||||||
rx.recv().unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut s1 = acceptor.accept().unwrap();
|
|
||||||
let s2 = s1.clone();
|
|
||||||
|
|
||||||
let (done, rx) = channel();
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
let mut s2 = s2;
|
|
||||||
let mut buf = [0, 0];
|
|
||||||
s2.read(&mut buf).unwrap();
|
|
||||||
tx2.send(()).unwrap();
|
|
||||||
done.send(()).unwrap();
|
|
||||||
});
|
|
||||||
let mut buf = [0, 0];
|
|
||||||
s1.read(&mut buf).unwrap();
|
|
||||||
tx1.send(()).unwrap();
|
|
||||||
|
|
||||||
rx.recv().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn unix_clone_two_write() {
|
|
||||||
let addr = next_test_unix();
|
|
||||||
let mut acceptor = UnixListener::bind(&addr).listen();
|
|
||||||
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
let mut s = UnixStream::connect(&addr);
|
|
||||||
let buf = &mut [0, 1];
|
|
||||||
s.read(buf).unwrap();
|
|
||||||
s.read(buf).unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut s1 = acceptor.accept().unwrap();
|
|
||||||
let s2 = s1.clone();
|
|
||||||
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
let mut s2 = s2;
|
|
||||||
s2.write(&[1]).unwrap();
|
|
||||||
tx.send(()).unwrap();
|
|
||||||
});
|
|
||||||
s1.write(&[2]).unwrap();
|
|
||||||
|
|
||||||
rx.recv().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
#[test]
|
|
||||||
fn drop_removes_listener_path() {
|
|
||||||
let path = next_test_unix();
|
|
||||||
let l = UnixListener::bind(&path).unwrap();
|
|
||||||
assert!(path.exists());
|
|
||||||
drop(l);
|
|
||||||
assert!(!path.exists());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
#[test]
|
|
||||||
fn drop_removes_acceptor_path() {
|
|
||||||
let path = next_test_unix();
|
|
||||||
let l = UnixListener::bind(&path).unwrap();
|
|
||||||
assert!(path.exists());
|
|
||||||
drop(l.listen().unwrap());
|
|
||||||
assert!(!path.exists());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn accept_timeout() {
|
|
||||||
let addr = next_test_unix();
|
|
||||||
let mut a = UnixListener::bind(&addr).unwrap().listen().unwrap();
|
|
||||||
|
|
||||||
a.set_timeout(Some(10));
|
|
||||||
|
|
||||||
// Make sure we time out once and future invocations also time out
|
|
||||||
let err = a.accept().err().unwrap();
|
|
||||||
assert_eq!(err.kind, TimedOut);
|
|
||||||
let err = a.accept().err().unwrap();
|
|
||||||
assert_eq!(err.kind, TimedOut);
|
|
||||||
|
|
||||||
// Also make sure that even though the timeout is expired that we will
|
|
||||||
// continue to receive any pending connections.
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
let addr2 = addr.clone();
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
tx.send(UnixStream::connect(&addr2).unwrap()).unwrap();
|
|
||||||
});
|
|
||||||
let l = rx.recv().unwrap();
|
|
||||||
for i in 0..1001 {
|
|
||||||
match a.accept() {
|
|
||||||
Ok(..) => break,
|
|
||||||
Err(ref e) if e.kind == TimedOut => {}
|
|
||||||
Err(e) => panic!("error: {}", e),
|
|
||||||
}
|
|
||||||
::thread::yield_now();
|
|
||||||
if i == 1000 { panic!("should have a pending connection") }
|
|
||||||
}
|
|
||||||
drop(l);
|
|
||||||
|
|
||||||
// Unset the timeout and make sure that this always blocks.
|
|
||||||
a.set_timeout(None);
|
|
||||||
let addr2 = addr.clone();
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
drop(UnixStream::connect(&addr2).unwrap());
|
|
||||||
});
|
|
||||||
a.accept().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn connect_timeout_error() {
|
|
||||||
let addr = next_test_unix();
|
|
||||||
assert!(UnixStream::connect_timeout(&addr, Duration::milliseconds(100)).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn connect_timeout_success() {
|
|
||||||
let addr = next_test_unix();
|
|
||||||
let _a = UnixListener::bind(&addr).unwrap().listen().unwrap();
|
|
||||||
assert!(UnixStream::connect_timeout(&addr, Duration::milliseconds(100)).is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn connect_timeout_zero() {
|
|
||||||
let addr = next_test_unix();
|
|
||||||
let _a = UnixListener::bind(&addr).unwrap().listen().unwrap();
|
|
||||||
assert!(UnixStream::connect_timeout(&addr, Duration::milliseconds(0)).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn connect_timeout_negative() {
|
|
||||||
let addr = next_test_unix();
|
|
||||||
let _a = UnixListener::bind(&addr).unwrap().listen().unwrap();
|
|
||||||
assert!(UnixStream::connect_timeout(&addr, Duration::milliseconds(-1)).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn close_readwrite_smoke() {
|
|
||||||
let addr = next_test_unix();
|
|
||||||
let a = UnixListener::bind(&addr).listen().unwrap();
|
|
||||||
let (_tx, rx) = channel::<()>();
|
|
||||||
thread::spawn(move|| {
|
|
||||||
let mut a = a;
|
|
||||||
let _s = a.accept().unwrap();
|
|
||||||
let _ = rx.recv();
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut b = [0];
|
|
||||||
let mut s = UnixStream::connect(&addr).unwrap();
|
|
||||||
let mut s2 = s.clone();
|
|
||||||
|
|
||||||
// closing should prevent reads/writes
|
|
||||||
s.close_write().unwrap();
|
|
||||||
assert!(s.write(&[0]).is_err());
|
|
||||||
s.close_read().unwrap();
|
|
||||||
assert!(s.read(&mut b).is_err());
|
|
||||||
|
|
||||||
// closing should affect previous handles
|
|
||||||
assert!(s2.write(&[0]).is_err());
|
|
||||||
assert!(s2.read(&mut b).is_err());
|
|
||||||
|
|
||||||
// closing should affect new handles
|
|
||||||
let mut s3 = s.clone();
|
|
||||||
assert!(s3.write(&[0]).is_err());
|
|
||||||
assert!(s3.read(&mut b).is_err());
|
|
||||||
|
|
||||||
// make sure these don't die
|
|
||||||
let _ = s2.close_read();
|
|
||||||
let _ = s2.close_write();
|
|
||||||
let _ = s3.close_read();
|
|
||||||
let _ = s3.close_write();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn close_read_wakes_up() {
|
|
||||||
let addr = next_test_unix();
|
|
||||||
let a = UnixListener::bind(&addr).listen().unwrap();
|
|
||||||
let (_tx, rx) = channel::<()>();
|
|
||||||
thread::spawn(move|| {
|
|
||||||
let mut a = a;
|
|
||||||
let _s = a.accept().unwrap();
|
|
||||||
let _ = rx.recv();
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut s = UnixStream::connect(&addr).unwrap();
|
|
||||||
let s2 = s.clone();
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
let mut s2 = s2;
|
|
||||||
assert!(s2.read(&mut [0]).is_err());
|
|
||||||
tx.send(()).unwrap();
|
|
||||||
});
|
|
||||||
// this should wake up the child task
|
|
||||||
s.close_read().unwrap();
|
|
||||||
|
|
||||||
// this test will never finish if the child doesn't wake up
|
|
||||||
rx.recv().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn readwrite_timeouts() {
|
|
||||||
let addr = next_test_unix();
|
|
||||||
let mut a = UnixListener::bind(&addr).listen().unwrap();
|
|
||||||
let (tx, rx) = channel::<()>();
|
|
||||||
thread::spawn(move|| {
|
|
||||||
let mut s = UnixStream::connect(&addr).unwrap();
|
|
||||||
rx.recv().unwrap();
|
|
||||||
assert!(s.write(&[0]).is_ok());
|
|
||||||
let _ = rx.recv();
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut s = a.accept().unwrap();
|
|
||||||
s.set_timeout(Some(20));
|
|
||||||
assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut);
|
|
||||||
assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut);
|
|
||||||
|
|
||||||
s.set_timeout(Some(20));
|
|
||||||
for i in 0..1001 {
|
|
||||||
match s.write(&[0; 128 * 1024]) {
|
|
||||||
Ok(()) | Err(IoError { kind: ShortWrite(..), .. }) => {},
|
|
||||||
Err(IoError { kind: TimedOut, .. }) => break,
|
|
||||||
Err(e) => panic!("{}", e),
|
|
||||||
}
|
|
||||||
if i == 1000 { panic!("should have filled up?!"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
// I'm not sure as to why, but apparently the write on windows always
|
|
||||||
// succeeds after the previous timeout. Who knows?
|
|
||||||
if !cfg!(windows) {
|
|
||||||
assert_eq!(s.write(&[0]).err().unwrap().kind, TimedOut);
|
|
||||||
}
|
|
||||||
|
|
||||||
tx.send(()).unwrap();
|
|
||||||
s.set_timeout(None);
|
|
||||||
assert_eq!(s.read(&mut [0, 0]), Ok(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_timeouts() {
|
|
||||||
let addr = next_test_unix();
|
|
||||||
let mut a = UnixListener::bind(&addr).listen().unwrap();
|
|
||||||
let (tx, rx) = channel::<()>();
|
|
||||||
thread::spawn(move|| {
|
|
||||||
let mut s = UnixStream::connect(&addr).unwrap();
|
|
||||||
rx.recv().unwrap();
|
|
||||||
let mut amt = 0;
|
|
||||||
while amt < 100 * 128 * 1024 {
|
|
||||||
match s.read(&mut [0;128 * 1024]) {
|
|
||||||
Ok(n) => { amt += n; }
|
|
||||||
Err(e) => panic!("{}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let _ = rx.recv();
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut s = a.accept().unwrap();
|
|
||||||
s.set_read_timeout(Some(20));
|
|
||||||
assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut);
|
|
||||||
assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut);
|
|
||||||
|
|
||||||
tx.send(()).unwrap();
|
|
||||||
for _ in 0..100 {
|
|
||||||
assert!(s.write(&[0;128 * 1024]).is_ok());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn write_timeouts() {
|
|
||||||
let addr = next_test_unix();
|
|
||||||
let mut a = UnixListener::bind(&addr).listen().unwrap();
|
|
||||||
let (tx, rx) = channel::<()>();
|
|
||||||
thread::spawn(move|| {
|
|
||||||
let mut s = UnixStream::connect(&addr).unwrap();
|
|
||||||
rx.recv().unwrap();
|
|
||||||
assert!(s.write(&[0]).is_ok());
|
|
||||||
let _ = rx.recv();
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut s = a.accept().unwrap();
|
|
||||||
s.set_write_timeout(Some(20));
|
|
||||||
for i in 0..1001 {
|
|
||||||
match s.write(&[0; 128 * 1024]) {
|
|
||||||
Ok(()) | Err(IoError { kind: ShortWrite(..), .. }) => {},
|
|
||||||
Err(IoError { kind: TimedOut, .. }) => break,
|
|
||||||
Err(e) => panic!("{}", e),
|
|
||||||
}
|
|
||||||
if i == 1000 { panic!("should have filled up?!"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
tx.send(()).unwrap();
|
|
||||||
assert!(s.read(&mut [0]).is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn timeout_concurrent_read() {
|
|
||||||
let addr = next_test_unix();
|
|
||||||
let mut a = UnixListener::bind(&addr).listen().unwrap();
|
|
||||||
let (tx, rx) = channel::<()>();
|
|
||||||
thread::spawn(move|| {
|
|
||||||
let mut s = UnixStream::connect(&addr).unwrap();
|
|
||||||
rx.recv().unwrap();
|
|
||||||
assert!(s.write(&[0]).is_ok());
|
|
||||||
let _ = rx.recv();
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut s = a.accept().unwrap();
|
|
||||||
let s2 = s.clone();
|
|
||||||
let (tx2, rx2) = channel();
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
let mut s2 = s2;
|
|
||||||
assert!(s2.read(&mut [0]).is_ok());
|
|
||||||
tx2.send(()).unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
s.set_read_timeout(Some(20));
|
|
||||||
assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut);
|
|
||||||
tx.send(()).unwrap();
|
|
||||||
|
|
||||||
rx2.recv().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
#[test]
|
|
||||||
fn clone_accept_smoke() {
|
|
||||||
let addr = next_test_unix();
|
|
||||||
let l = UnixListener::bind(&addr);
|
|
||||||
let mut a = l.listen().unwrap();
|
|
||||||
let mut a2 = a.clone();
|
|
||||||
|
|
||||||
let addr2 = addr.clone();
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
let _ = UnixStream::connect(&addr2);
|
|
||||||
});
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
let _ = UnixStream::connect(&addr);
|
|
||||||
});
|
|
||||||
|
|
||||||
assert!(a.accept().is_ok());
|
|
||||||
drop(a);
|
|
||||||
assert!(a2.accept().is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(windows))] // FIXME #17553
|
|
||||||
#[test]
|
|
||||||
fn clone_accept_concurrent() {
|
|
||||||
let addr = next_test_unix();
|
|
||||||
let l = UnixListener::bind(&addr);
|
|
||||||
let a = l.listen().unwrap();
|
|
||||||
let a2 = a.clone();
|
|
||||||
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
let tx2 = tx.clone();
|
|
||||||
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
let mut a = a;
|
|
||||||
tx.send(a.accept()).unwrap()
|
|
||||||
});
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
let mut a = a2;
|
|
||||||
tx2.send(a.accept()).unwrap()
|
|
||||||
});
|
|
||||||
|
|
||||||
let addr2 = addr.clone();
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
let _ = UnixStream::connect(&addr2);
|
|
||||||
});
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
let _ = UnixStream::connect(&addr);
|
|
||||||
});
|
|
||||||
|
|
||||||
assert!(rx.recv().unwrap().is_ok());
|
|
||||||
assert!(rx.recv().unwrap().is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn close_accept_smoke() {
|
|
||||||
let addr = next_test_unix();
|
|
||||||
let l = UnixListener::bind(&addr);
|
|
||||||
let mut a = l.listen().unwrap();
|
|
||||||
|
|
||||||
a.close_accept().unwrap();
|
|
||||||
assert_eq!(a.accept().err().unwrap().kind, EndOfFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn close_accept_concurrent() {
|
|
||||||
let addr = next_test_unix();
|
|
||||||
let l = UnixListener::bind(&addr);
|
|
||||||
let a = l.listen().unwrap();
|
|
||||||
let mut a2 = a.clone();
|
|
||||||
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
let mut a = a;
|
|
||||||
tx.send(a.accept()).unwrap();
|
|
||||||
});
|
|
||||||
a2.close_accept().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(rx.recv().unwrap().err().unwrap().kind, EndOfFile);
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,459 +0,0 @@
|
|||||||
// Copyright 2013 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.
|
|
||||||
|
|
||||||
//! UDP (User Datagram Protocol) network connections.
|
|
||||||
//!
|
|
||||||
//! This module contains the ability to open a UDP stream to a socket address.
|
|
||||||
//! The destination and binding addresses can either be an IPv4 or IPv6
|
|
||||||
//! address. There is no corresponding notion of a server because UDP is a
|
|
||||||
//! datagram protocol.
|
|
||||||
|
|
||||||
use clone::Clone;
|
|
||||||
use old_io::net::ip::{SocketAddr, IpAddr, ToSocketAddr};
|
|
||||||
use old_io::IoResult;
|
|
||||||
use option::Option;
|
|
||||||
use sys::udp::UdpSocket as UdpSocketImp;
|
|
||||||
use sys_common;
|
|
||||||
|
|
||||||
/// A User Datagram Protocol socket.
|
|
||||||
///
|
|
||||||
/// This is an implementation of a bound UDP socket. This supports both IPv4 and
|
|
||||||
/// IPv6 addresses, and there is no corresponding notion of a server because UDP
|
|
||||||
/// is a datagram protocol.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust,no_run
|
|
||||||
/// # #![feature(old_io)]
|
|
||||||
/// # #![allow(unused_must_use)]
|
|
||||||
///
|
|
||||||
/// use std::old_io::net::udp::UdpSocket;
|
|
||||||
/// use std::old_io::net::ip::{Ipv4Addr, SocketAddr};
|
|
||||||
/// fn main() {
|
|
||||||
/// let addr = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 34254 };
|
|
||||||
/// let mut socket = match UdpSocket::bind(addr) {
|
|
||||||
/// Ok(s) => s,
|
|
||||||
/// Err(e) => panic!("couldn't bind socket: {}", e),
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// let mut buf = [0; 10];
|
|
||||||
/// match socket.recv_from(&mut buf) {
|
|
||||||
/// Ok((amt, src)) => {
|
|
||||||
/// // Send a reply to the socket we received data from
|
|
||||||
/// let buf = &mut buf[..amt];
|
|
||||||
/// buf.reverse();
|
|
||||||
/// socket.send_to(buf, src);
|
|
||||||
/// }
|
|
||||||
/// Err(e) => println!("couldn't receive a datagram: {}", e)
|
|
||||||
/// }
|
|
||||||
/// drop(socket); // close the socket
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub struct UdpSocket {
|
|
||||||
inner: UdpSocketImp,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UdpSocket {
|
|
||||||
/// Creates a UDP socket from the given address.
|
|
||||||
///
|
|
||||||
/// Address type can be any implementor of `ToSocketAddr` trait. See its
|
|
||||||
/// documentation for concrete examples.
|
|
||||||
pub fn bind<A: ToSocketAddr>(addr: A) -> IoResult<UdpSocket> {
|
|
||||||
super::with_addresses(addr, |addr| {
|
|
||||||
UdpSocketImp::bind(addr).map(|s| UdpSocket { inner: s })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Receives data from the socket. On success, returns the number of bytes
|
|
||||||
/// read and the address from whence the data came.
|
|
||||||
pub fn recv_from(&mut self, buf: &mut [u8]) -> IoResult<(usize, SocketAddr)> {
|
|
||||||
self.inner.recv_from(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends data on the socket to the given address. Returns nothing on
|
|
||||||
/// success.
|
|
||||||
///
|
|
||||||
/// Address type can be any implementer of `ToSocketAddr` trait. See its
|
|
||||||
/// documentation for concrete examples.
|
|
||||||
pub fn send_to<A: ToSocketAddr>(&mut self, buf: &[u8], addr: A) -> IoResult<()> {
|
|
||||||
super::with_addresses(addr, |addr| self.inner.send_to(buf, addr))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the socket address that this socket was created from.
|
|
||||||
pub fn socket_name(&mut self) -> IoResult<SocketAddr> {
|
|
||||||
self.inner.socket_name()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Joins a multicast IP address (becomes a member of it)
|
|
||||||
#[unstable(feature = "io")]
|
|
||||||
pub fn join_multicast(&mut self, multi: IpAddr) -> IoResult<()> {
|
|
||||||
self.inner.join_multicast(multi)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Leaves a multicast IP address (drops membership from it)
|
|
||||||
#[unstable(feature = "io")]
|
|
||||||
pub fn leave_multicast(&mut self, multi: IpAddr) -> IoResult<()> {
|
|
||||||
self.inner.leave_multicast(multi)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the multicast loop flag to the specified value
|
|
||||||
///
|
|
||||||
/// This lets multicast packets loop back to local sockets (if enabled)
|
|
||||||
#[unstable(feature = "io")]
|
|
||||||
pub fn set_multicast_loop(&mut self, on: bool) -> IoResult<()> {
|
|
||||||
self.inner.set_multicast_loop(on)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the multicast TTL
|
|
||||||
#[unstable(feature = "io")]
|
|
||||||
pub fn set_multicast_ttl(&mut self, ttl: isize) -> IoResult<()> {
|
|
||||||
self.inner.multicast_time_to_live(ttl)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets this socket's TTL
|
|
||||||
#[unstable(feature = "io")]
|
|
||||||
pub fn set_ttl(&mut self, ttl: isize) -> IoResult<()> {
|
|
||||||
self.inner.time_to_live(ttl)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the broadcast flag on or off
|
|
||||||
#[unstable(feature = "io")]
|
|
||||||
pub fn set_broadcast(&mut self, broadcast: bool) -> IoResult<()> {
|
|
||||||
self.inner.set_broadcast(broadcast)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the read/write timeout for this socket.
|
|
||||||
///
|
|
||||||
/// For more information, see `TcpStream::set_timeout`
|
|
||||||
#[unstable(feature = "io",
|
|
||||||
reason = "the timeout argument may change in type and value")]
|
|
||||||
pub fn set_timeout(&mut self, timeout_ms: Option<u64>) {
|
|
||||||
self.inner.set_timeout(timeout_ms)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the read timeout for this socket.
|
|
||||||
///
|
|
||||||
/// For more information, see `TcpStream::set_timeout`
|
|
||||||
#[unstable(feature = "io",
|
|
||||||
reason = "the timeout argument may change in type and value")]
|
|
||||||
pub fn set_read_timeout(&mut self, timeout_ms: Option<u64>) {
|
|
||||||
self.inner.set_read_timeout(timeout_ms)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the write timeout for this socket.
|
|
||||||
///
|
|
||||||
/// For more information, see `TcpStream::set_timeout`
|
|
||||||
#[unstable(feature = "io",
|
|
||||||
reason = "the timeout argument may change in type and value")]
|
|
||||||
pub fn set_write_timeout(&mut self, timeout_ms: Option<u64>) {
|
|
||||||
self.inner.set_write_timeout(timeout_ms)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for UdpSocket {
|
|
||||||
/// Creates a new handle to this UDP socket, allowing for simultaneous
|
|
||||||
/// reads and writes of the socket.
|
|
||||||
///
|
|
||||||
/// The underlying UDP socket will not be closed until all handles to the
|
|
||||||
/// socket have been deallocated. Two concurrent reads will not receive
|
|
||||||
/// the same data. Instead, the first read will receive the first packet
|
|
||||||
/// received, and the second read will receive the second packet.
|
|
||||||
fn clone(&self) -> UdpSocket {
|
|
||||||
UdpSocket {
|
|
||||||
inner: self.inner.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl sys_common::AsInner<UdpSocketImp> for UdpSocket {
|
|
||||||
fn as_inner(&self) -> &UdpSocketImp {
|
|
||||||
&self.inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use prelude::v1::*;
|
|
||||||
|
|
||||||
use sync::mpsc::channel;
|
|
||||||
use old_io::net::ip::*;
|
|
||||||
use old_io::test::*;
|
|
||||||
use old_io::{IoError, TimedOut, PermissionDenied, ShortWrite};
|
|
||||||
use super::*;
|
|
||||||
use thread;
|
|
||||||
|
|
||||||
// FIXME #11530 this fails on android because tests are run as root
|
|
||||||
#[cfg_attr(any(windows, target_os = "android"), ignore)]
|
|
||||||
#[test]
|
|
||||||
fn bind_error() {
|
|
||||||
let addr = SocketAddr { ip: Ipv4Addr(0, 0, 0, 0), port: 1 };
|
|
||||||
match UdpSocket::bind(addr) {
|
|
||||||
Ok(..) => panic!(),
|
|
||||||
Err(e) => assert_eq!(e.kind, PermissionDenied),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn socket_smoke_test_ip4() {
|
|
||||||
let server_ip = next_test_ip4();
|
|
||||||
let client_ip = next_test_ip4();
|
|
||||||
let (tx1, rx1) = channel();
|
|
||||||
let (tx2, rx2) = channel();
|
|
||||||
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
match UdpSocket::bind(client_ip) {
|
|
||||||
Ok(ref mut client) => {
|
|
||||||
rx1.recv().unwrap();
|
|
||||||
client.send_to(&[99], server_ip).unwrap()
|
|
||||||
}
|
|
||||||
Err(..) => panic!()
|
|
||||||
}
|
|
||||||
tx2.send(()).unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
match UdpSocket::bind(server_ip) {
|
|
||||||
Ok(ref mut server) => {
|
|
||||||
tx1.send(()).unwrap();
|
|
||||||
let mut buf = [0];
|
|
||||||
match server.recv_from(&mut buf) {
|
|
||||||
Ok((nread, src)) => {
|
|
||||||
assert_eq!(nread, 1);
|
|
||||||
assert_eq!(buf[0], 99);
|
|
||||||
assert_eq!(src, client_ip);
|
|
||||||
}
|
|
||||||
Err(..) => panic!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(..) => panic!()
|
|
||||||
}
|
|
||||||
rx2.recv().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn socket_smoke_test_ip6() {
|
|
||||||
let server_ip = next_test_ip6();
|
|
||||||
let client_ip = next_test_ip6();
|
|
||||||
let (tx, rx) = channel::<()>();
|
|
||||||
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
match UdpSocket::bind(client_ip) {
|
|
||||||
Ok(ref mut client) => {
|
|
||||||
rx.recv().unwrap();
|
|
||||||
client.send_to(&[99], server_ip).unwrap()
|
|
||||||
}
|
|
||||||
Err(..) => panic!()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
match UdpSocket::bind(server_ip) {
|
|
||||||
Ok(ref mut server) => {
|
|
||||||
tx.send(()).unwrap();
|
|
||||||
let mut buf = [0];
|
|
||||||
match server.recv_from(&mut buf) {
|
|
||||||
Ok((nread, src)) => {
|
|
||||||
assert_eq!(nread, 1);
|
|
||||||
assert_eq!(buf[0], 99);
|
|
||||||
assert_eq!(src, client_ip);
|
|
||||||
}
|
|
||||||
Err(..) => panic!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(..) => panic!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn socket_name(addr: SocketAddr) {
|
|
||||||
let server = UdpSocket::bind(addr);
|
|
||||||
|
|
||||||
assert!(server.is_ok());
|
|
||||||
let mut server = server.unwrap();
|
|
||||||
|
|
||||||
// Make sure socket_name gives
|
|
||||||
// us the socket we binded to.
|
|
||||||
let so_name = server.socket_name();
|
|
||||||
assert!(so_name.is_ok());
|
|
||||||
assert_eq!(addr, so_name.unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn socket_name_ip4() {
|
|
||||||
socket_name(next_test_ip4());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn socket_name_ip6() {
|
|
||||||
socket_name(next_test_ip6());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn udp_clone_smoke() {
|
|
||||||
let addr1 = next_test_ip4();
|
|
||||||
let addr2 = next_test_ip4();
|
|
||||||
let mut sock1 = UdpSocket::bind(addr1).unwrap();
|
|
||||||
let sock2 = UdpSocket::bind(addr2).unwrap();
|
|
||||||
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
let mut sock2 = sock2;
|
|
||||||
let mut buf = [0, 0];
|
|
||||||
assert_eq!(sock2.recv_from(&mut buf), Ok((1, addr1)));
|
|
||||||
assert_eq!(buf[0], 1);
|
|
||||||
sock2.send_to(&[2], addr1).unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
let sock3 = sock1.clone();
|
|
||||||
|
|
||||||
let (tx1, rx1) = channel();
|
|
||||||
let (tx2, rx2) = channel();
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
let mut sock3 = sock3;
|
|
||||||
rx1.recv().unwrap();
|
|
||||||
sock3.send_to(&[1], addr2).unwrap();
|
|
||||||
tx2.send(()).unwrap();
|
|
||||||
});
|
|
||||||
tx1.send(()).unwrap();
|
|
||||||
let mut buf = [0, 0];
|
|
||||||
assert_eq!(sock1.recv_from(&mut buf), Ok((1, addr2)));
|
|
||||||
rx2.recv().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn udp_clone_two_read() {
|
|
||||||
let addr1 = next_test_ip4();
|
|
||||||
let addr2 = next_test_ip4();
|
|
||||||
let mut sock1 = UdpSocket::bind(addr1).unwrap();
|
|
||||||
let sock2 = UdpSocket::bind(addr2).unwrap();
|
|
||||||
let (tx1, rx) = channel();
|
|
||||||
let tx2 = tx1.clone();
|
|
||||||
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
let mut sock2 = sock2;
|
|
||||||
sock2.send_to(&[1], addr1).unwrap();
|
|
||||||
rx.recv().unwrap();
|
|
||||||
sock2.send_to(&[2], addr1).unwrap();
|
|
||||||
rx.recv().unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
let sock3 = sock1.clone();
|
|
||||||
|
|
||||||
let (done, rx) = channel();
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
let mut sock3 = sock3;
|
|
||||||
let mut buf = [0, 0];
|
|
||||||
sock3.recv_from(&mut buf).unwrap();
|
|
||||||
tx2.send(()).unwrap();
|
|
||||||
done.send(()).unwrap();
|
|
||||||
});
|
|
||||||
let mut buf = [0, 0];
|
|
||||||
sock1.recv_from(&mut buf).unwrap();
|
|
||||||
tx1.send(()).unwrap();
|
|
||||||
|
|
||||||
rx.recv().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn udp_clone_two_write() {
|
|
||||||
let addr1 = next_test_ip4();
|
|
||||||
let addr2 = next_test_ip4();
|
|
||||||
let mut sock1 = UdpSocket::bind(addr1).unwrap();
|
|
||||||
let sock2 = UdpSocket::bind(addr2).unwrap();
|
|
||||||
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
let (serv_tx, serv_rx) = channel();
|
|
||||||
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
let mut sock2 = sock2;
|
|
||||||
let mut buf = [0, 1];
|
|
||||||
|
|
||||||
rx.recv().unwrap();
|
|
||||||
match sock2.recv_from(&mut buf) {
|
|
||||||
Ok(..) => {}
|
|
||||||
Err(e) => panic!("failed receive: {}", e),
|
|
||||||
}
|
|
||||||
serv_tx.send(()).unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
let sock3 = sock1.clone();
|
|
||||||
|
|
||||||
let (done, rx) = channel();
|
|
||||||
let tx2 = tx.clone();
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
let mut sock3 = sock3;
|
|
||||||
match sock3.send_to(&[1], addr2) {
|
|
||||||
Ok(..) => { let _ = tx2.send(()); }
|
|
||||||
Err(..) => {}
|
|
||||||
}
|
|
||||||
done.send(()).unwrap();
|
|
||||||
});
|
|
||||||
match sock1.send_to(&[2], addr2) {
|
|
||||||
Ok(..) => { let _ = tx.send(()); }
|
|
||||||
Err(..) => {}
|
|
||||||
}
|
|
||||||
drop(tx);
|
|
||||||
|
|
||||||
rx.recv().unwrap();
|
|
||||||
serv_rx.recv().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(windows))] // FIXME #17553
|
|
||||||
#[test]
|
|
||||||
fn recv_from_timeout() {
|
|
||||||
let addr1 = next_test_ip4();
|
|
||||||
let addr2 = next_test_ip4();
|
|
||||||
let mut a = UdpSocket::bind(addr1).unwrap();
|
|
||||||
let a2 = UdpSocket::bind(addr2).unwrap();
|
|
||||||
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
let (tx2, rx2) = channel();
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
let mut a = a2;
|
|
||||||
assert_eq!(a.recv_from(&mut [0]), Ok((1, addr1)));
|
|
||||||
assert_eq!(a.send_to(&[0], addr1), Ok(()));
|
|
||||||
rx.recv().unwrap();
|
|
||||||
assert_eq!(a.send_to(&[0], addr1), Ok(()));
|
|
||||||
|
|
||||||
tx2.send(()).unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Make sure that reads time out, but writes can continue
|
|
||||||
a.set_read_timeout(Some(20));
|
|
||||||
assert_eq!(a.recv_from(&mut [0]).err().unwrap().kind, TimedOut);
|
|
||||||
assert_eq!(a.recv_from(&mut [0]).err().unwrap().kind, TimedOut);
|
|
||||||
assert_eq!(a.send_to(&[0], addr2), Ok(()));
|
|
||||||
|
|
||||||
// Cloned handles should be able to block
|
|
||||||
let mut a2 = a.clone();
|
|
||||||
assert_eq!(a2.recv_from(&mut [0]), Ok((1, addr2)));
|
|
||||||
|
|
||||||
// Clearing the timeout should allow for receiving
|
|
||||||
a.set_timeout(None);
|
|
||||||
tx.send(()).unwrap();
|
|
||||||
assert_eq!(a2.recv_from(&mut [0]), Ok((1, addr2)));
|
|
||||||
|
|
||||||
// Make sure the child didn't die
|
|
||||||
rx2.recv().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn send_to_timeout() {
|
|
||||||
let addr1 = next_test_ip4();
|
|
||||||
let addr2 = next_test_ip4();
|
|
||||||
let mut a = UdpSocket::bind(addr1).unwrap();
|
|
||||||
let _b = UdpSocket::bind(addr2).unwrap();
|
|
||||||
|
|
||||||
a.set_write_timeout(Some(1000));
|
|
||||||
for _ in 0..100 {
|
|
||||||
match a.send_to(&[0;4*1024], addr2) {
|
|
||||||
Ok(()) | Err(IoError { kind: ShortWrite(..), .. }) => {},
|
|
||||||
Err(IoError { kind: TimedOut, .. }) => break,
|
|
||||||
Err(e) => panic!("other error: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,141 +0,0 @@
|
|||||||
// Copyright 2013 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.
|
|
||||||
|
|
||||||
//! Synchronous, in-memory pipes.
|
|
||||||
//!
|
|
||||||
//! Currently these aren't particularly useful, there only exists bindings
|
|
||||||
//! enough so that pipes can be created to child processes.
|
|
||||||
|
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
|
||||||
use prelude::v1::*;
|
|
||||||
|
|
||||||
use old_io::{IoResult, Reader, Writer};
|
|
||||||
use libc;
|
|
||||||
use sync::Arc;
|
|
||||||
|
|
||||||
use sys_common;
|
|
||||||
use sys;
|
|
||||||
use sys::fs::FileDesc as FileDesc;
|
|
||||||
|
|
||||||
/// A synchronous, in-memory pipe.
|
|
||||||
pub struct PipeStream {
|
|
||||||
inner: Arc<FileDesc>
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PipePair {
|
|
||||||
pub reader: PipeStream,
|
|
||||||
pub writer: PipeStream,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PipeStream {
|
|
||||||
/// Consumes a file descriptor to return a pipe stream that will have
|
|
||||||
/// synchronous, but non-blocking reads/writes. This is useful if the file
|
|
||||||
/// descriptor is acquired via means other than the standard methods.
|
|
||||||
///
|
|
||||||
/// This operation consumes ownership of the file descriptor and it will be
|
|
||||||
/// closed once the object is deallocated.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```{rust,no_run}
|
|
||||||
/// # #![feature(old_io, libc, io)]
|
|
||||||
/// # #![allow(unused_must_use)]
|
|
||||||
/// extern crate libc;
|
|
||||||
///
|
|
||||||
/// use std::old_io::*;
|
|
||||||
///
|
|
||||||
/// fn main() {
|
|
||||||
/// let mut pipe = PipeStream::open(libc::STDERR_FILENO);
|
|
||||||
/// pipe.write(b"Hello, stderr!");
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub fn open(fd: libc::c_int) -> IoResult<PipeStream> {
|
|
||||||
Ok(PipeStream::from_filedesc(FileDesc::new(fd, true)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: expose this some other way
|
|
||||||
/// Wrap a FileDesc directly, taking ownership.
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn from_filedesc(fd: FileDesc) -> PipeStream {
|
|
||||||
PipeStream { inner: Arc::new(fd) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a pair of in-memory OS pipes for a unidirectional communication
|
|
||||||
/// stream.
|
|
||||||
///
|
|
||||||
/// The structure returned contains a reader and writer I/O object. Data
|
|
||||||
/// written to the writer can be read from the reader.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// This function can fail to succeed if the underlying OS has run out of
|
|
||||||
/// available resources to allocate a new pipe.
|
|
||||||
pub fn pair() -> IoResult<PipePair> {
|
|
||||||
let (reader, writer) = try!(unsafe { sys::os::pipe() });
|
|
||||||
Ok(PipePair {
|
|
||||||
reader: PipeStream::from_filedesc(reader),
|
|
||||||
writer: PipeStream::from_filedesc(writer),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl sys_common::AsInner<sys::fs::FileDesc> for PipeStream {
|
|
||||||
fn as_inner(&self) -> &sys::fs::FileDesc {
|
|
||||||
&*self.inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for PipeStream {
|
|
||||||
fn clone(&self) -> PipeStream {
|
|
||||||
PipeStream { inner: self.inner.clone() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Reader for PipeStream {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
|
||||||
self.inner.read(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Writer for PipeStream {
|
|
||||||
fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
|
|
||||||
self.inner.write(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use prelude::v1::*;
|
|
||||||
|
|
||||||
use old_io::{Writer, Reader};
|
|
||||||
use sync::mpsc::channel;
|
|
||||||
use thread;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn partial_read() {
|
|
||||||
use os;
|
|
||||||
use old_io::pipe::PipeStream;
|
|
||||||
|
|
||||||
let (reader, writer) = unsafe { ::sys::os::pipe().unwrap() };
|
|
||||||
let out = PipeStream::open(writer.unwrap());
|
|
||||||
let mut input = PipeStream::open(reader.unwrap());
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
let _t = thread::spawn(move|| {
|
|
||||||
let mut out = out;
|
|
||||||
out.write(&[10]).unwrap();
|
|
||||||
rx.recv().unwrap(); // don't close the pipe until the other read has finished
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut buf = [0; 10];
|
|
||||||
input.read(&mut buf).unwrap();
|
|
||||||
tx.send(()).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,130 +0,0 @@
|
|||||||
// Copyright 2013 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.
|
|
||||||
|
|
||||||
//! Implementations of I/O traits for the IoResult type
|
|
||||||
//!
|
|
||||||
//! I/O constructors return option types to allow errors to be handled.
|
|
||||||
//! These implementations allow e.g. `IoResult<File>` to be used
|
|
||||||
//! as a `Reader` without unwrapping the result first.
|
|
||||||
|
|
||||||
use clone::Clone;
|
|
||||||
use result::Result::{Ok, Err};
|
|
||||||
use super::{Reader, Writer, Listener, Acceptor, Seek, SeekStyle, IoResult};
|
|
||||||
|
|
||||||
impl<W: Writer> Writer for IoResult<W> {
|
|
||||||
fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
|
|
||||||
match *self {
|
|
||||||
Ok(ref mut writer) => writer.write_all(buf),
|
|
||||||
Err(ref e) => Err((*e).clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush(&mut self) -> IoResult<()> {
|
|
||||||
match *self {
|
|
||||||
Ok(ref mut writer) => writer.flush(),
|
|
||||||
Err(ref e) => Err(e.clone()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: Reader> Reader for IoResult<R> {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
|
||||||
match *self {
|
|
||||||
Ok(ref mut reader) => reader.read(buf),
|
|
||||||
Err(ref e) => Err(e.clone()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: Seek> Seek for IoResult<S> {
|
|
||||||
fn tell(&self) -> IoResult<u64> {
|
|
||||||
match *self {
|
|
||||||
Ok(ref seeker) => seeker.tell(),
|
|
||||||
Err(ref e) => Err(e.clone()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
|
|
||||||
match *self {
|
|
||||||
Ok(ref mut seeker) => seeker.seek(pos, style),
|
|
||||||
Err(ref e) => Err(e.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A: Acceptor, L: Listener<A>> Listener<A> for IoResult<L> {
|
|
||||||
fn listen(self) -> IoResult<A> {
|
|
||||||
match self {
|
|
||||||
Ok(listener) => listener.listen(),
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A: Acceptor> Acceptor for IoResult<A> {
|
|
||||||
type Connection = A::Connection;
|
|
||||||
fn accept(&mut self) -> IoResult<A::Connection> {
|
|
||||||
match *self {
|
|
||||||
Ok(ref mut acceptor) => acceptor.accept(),
|
|
||||||
Err(ref e) => Err(e.clone()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use prelude::v1::*;
|
|
||||||
use super::super::mem::*;
|
|
||||||
use old_io::{self, Reader, Writer};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_option_writer() {
|
|
||||||
let mut writer: old_io::IoResult<Vec<u8>> = Ok(Vec::new());
|
|
||||||
writer.write_all(&[0, 1, 2]).unwrap();
|
|
||||||
writer.flush().unwrap();
|
|
||||||
assert_eq!(writer.unwrap(), [0, 1, 2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_option_writer_error() {
|
|
||||||
let mut writer: old_io::IoResult<Vec<u8>> =
|
|
||||||
Err(old_io::standard_error(old_io::EndOfFile));
|
|
||||||
|
|
||||||
match writer.write_all(&[0, 0, 0]) {
|
|
||||||
Ok(..) => panic!(),
|
|
||||||
Err(e) => assert_eq!(e.kind, old_io::EndOfFile),
|
|
||||||
}
|
|
||||||
match writer.flush() {
|
|
||||||
Ok(..) => panic!(),
|
|
||||||
Err(e) => assert_eq!(e.kind, old_io::EndOfFile),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_option_reader() {
|
|
||||||
let mut reader: old_io::IoResult<MemReader> =
|
|
||||||
Ok(MemReader::new(vec!(0, 1, 2, 3)));
|
|
||||||
let mut buf = [0, 0];
|
|
||||||
reader.read(&mut buf).unwrap();
|
|
||||||
let b: &[_] = &[0, 1];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_option_reader_error() {
|
|
||||||
let mut reader: old_io::IoResult<MemReader> =
|
|
||||||
Err(old_io::standard_error(old_io::EndOfFile));
|
|
||||||
let mut buf = [];
|
|
||||||
|
|
||||||
match reader.read(&mut buf) {
|
|
||||||
Ok(..) => panic!(),
|
|
||||||
Err(e) => assert_eq!(e.kind, old_io::EndOfFile),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,540 +0,0 @@
|
|||||||
// Copyright 2013 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.
|
|
||||||
|
|
||||||
//! Non-blocking access to stdin, stdout, and stderr.
|
|
||||||
//!
|
|
||||||
//! This module provides bindings to the local event loop's TTY interface, using it
|
|
||||||
//! to offer synchronous but non-blocking versions of stdio. These handles can be
|
|
||||||
//! inspected for information about terminal dimensions or for related information
|
|
||||||
//! about the stream or terminal to which it is attached.
|
|
||||||
//!
|
|
||||||
//! # Examples
|
|
||||||
//!
|
|
||||||
//! ```rust
|
|
||||||
//! # #![feature(old_io)]
|
|
||||||
//! # #![allow(unused_must_use)]
|
|
||||||
//! use std::old_io;
|
|
||||||
//! use std::old_io::*;
|
|
||||||
//!
|
|
||||||
//! let mut out = old_io::stdout();
|
|
||||||
//! out.write_all(b"Hello, world!");
|
|
||||||
//! ```
|
|
||||||
|
|
||||||
use self::StdSource::*;
|
|
||||||
|
|
||||||
use boxed;
|
|
||||||
use boxed::Box;
|
|
||||||
use cell::RefCell;
|
|
||||||
use clone::Clone;
|
|
||||||
use fmt;
|
|
||||||
use old_io::{Reader, Writer, IoResult, IoError, OtherIoError, Buffer,
|
|
||||||
standard_error, EndOfFile, LineBufferedWriter, BufferedReader};
|
|
||||||
use marker::{Sync, Send};
|
|
||||||
use libc;
|
|
||||||
use mem;
|
|
||||||
use option::Option;
|
|
||||||
use option::Option::{Some, None};
|
|
||||||
use ops::{Deref, DerefMut, FnOnce};
|
|
||||||
use ptr;
|
|
||||||
use result::Result::{Ok, Err};
|
|
||||||
use rt;
|
|
||||||
use string::String;
|
|
||||||
use sys::{fs, tty};
|
|
||||||
use sync::{Arc, Mutex, MutexGuard, Once, ONCE_INIT};
|
|
||||||
use usize;
|
|
||||||
use vec::Vec;
|
|
||||||
|
|
||||||
// And so begins the tale of acquiring a uv handle to a stdio stream on all
|
|
||||||
// platforms in all situations. Our story begins by splitting the world into two
|
|
||||||
// categories, windows and unix. Then one day the creators of unix said let
|
|
||||||
// there be redirection! And henceforth there was redirection away from the
|
|
||||||
// console for standard I/O streams.
|
|
||||||
//
|
|
||||||
// After this day, the world split into four factions:
|
|
||||||
//
|
|
||||||
// 1. Unix with stdout on a terminal.
|
|
||||||
// 2. Unix with stdout redirected.
|
|
||||||
// 3. Windows with stdout on a terminal.
|
|
||||||
// 4. Windows with stdout redirected.
|
|
||||||
//
|
|
||||||
// Many years passed, and then one day the nation of libuv decided to unify this
|
|
||||||
// world. After months of toiling, uv created three ideas: TTY, Pipe, File.
|
|
||||||
// These three ideas propagated throughout the lands and the four great factions
|
|
||||||
// decided to settle among them.
|
|
||||||
//
|
|
||||||
// The groups of 1, 2, and 3 all worked very hard towards the idea of TTY. Upon
|
|
||||||
// doing so, they even enhanced themselves further then their Pipe/File
|
|
||||||
// brethren, becoming the dominant powers.
|
|
||||||
//
|
|
||||||
// The group of 4, however, decided to work independently. They abandoned the
|
|
||||||
// common TTY belief throughout, and even abandoned the fledgling Pipe belief.
|
|
||||||
// The members of the 4th faction decided to only align themselves with File.
|
|
||||||
//
|
|
||||||
// tl;dr; TTY works on everything but when windows stdout is redirected, in that
|
|
||||||
// case pipe also doesn't work, but magically file does!
|
|
||||||
enum StdSource {
|
|
||||||
TTY(tty::TTY),
|
|
||||||
File(fs::FileDesc),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn src<T, F>(fd: libc::c_int, _readable: bool, f: F) -> T where
|
|
||||||
F: FnOnce(StdSource) -> T,
|
|
||||||
{
|
|
||||||
match tty::TTY::new(fd) {
|
|
||||||
Ok(tty) => f(TTY(tty)),
|
|
||||||
Err(_) => f(File(fs::FileDesc::new(fd, false))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
thread_local! {
|
|
||||||
static LOCAL_STDOUT: RefCell<Option<Box<Writer + Send>>> = {
|
|
||||||
RefCell::new(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RaceBox(BufferedReader<StdReader>);
|
|
||||||
|
|
||||||
unsafe impl Send for RaceBox {}
|
|
||||||
unsafe impl Sync for RaceBox {}
|
|
||||||
|
|
||||||
/// A synchronized wrapper around a buffered reader from stdin
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct StdinReader {
|
|
||||||
inner: Arc<Mutex<RaceBox>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for StdinReader {}
|
|
||||||
unsafe impl Sync for StdinReader {}
|
|
||||||
|
|
||||||
/// A guard for exclusive access to `StdinReader`'s internal `BufferedReader`.
|
|
||||||
pub struct StdinReaderGuard<'a> {
|
|
||||||
inner: MutexGuard<'a, RaceBox>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Deref for StdinReaderGuard<'a> {
|
|
||||||
type Target = BufferedReader<StdReader>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &BufferedReader<StdReader> {
|
|
||||||
&self.inner.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> DerefMut for StdinReaderGuard<'a> {
|
|
||||||
fn deref_mut(&mut self) -> &mut BufferedReader<StdReader> {
|
|
||||||
&mut self.inner.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StdinReader {
|
|
||||||
/// Locks the `StdinReader`, granting the calling thread exclusive access
|
|
||||||
/// to the underlying `BufferedReader`.
|
|
||||||
///
|
|
||||||
/// This provides access to methods like `chars` and `lines`.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![feature(old_io)]
|
|
||||||
/// use std::old_io;
|
|
||||||
/// use std::old_io::*;
|
|
||||||
///
|
|
||||||
/// let mut stdin = old_io::stdin();
|
|
||||||
/// for line in stdin.lock().lines() {
|
|
||||||
/// println!("{}", line.unwrap());
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub fn lock<'a>(&'a mut self) -> StdinReaderGuard<'a> {
|
|
||||||
StdinReaderGuard {
|
|
||||||
inner: self.inner.lock().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Like `Buffer::read_line`.
|
|
||||||
///
|
|
||||||
/// The read is performed atomically - concurrent read calls in other
|
|
||||||
/// threads will not interleave with this one.
|
|
||||||
pub fn read_line(&mut self) -> IoResult<String> {
|
|
||||||
self.inner.lock().unwrap().0.read_line()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Like `Buffer::read_until`.
|
|
||||||
///
|
|
||||||
/// The read is performed atomically - concurrent read calls in other
|
|
||||||
/// threads will not interleave with this one.
|
|
||||||
pub fn read_until(&mut self, byte: u8) -> IoResult<Vec<u8>> {
|
|
||||||
self.inner.lock().unwrap().0.read_until(byte)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Like `Buffer::read_char`.
|
|
||||||
///
|
|
||||||
/// The read is performed atomically - concurrent read calls in other
|
|
||||||
/// threads will not interleave with this one.
|
|
||||||
pub fn read_char(&mut self) -> IoResult<char> {
|
|
||||||
self.inner.lock().unwrap().0.read_char()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Reader for StdinReader {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
|
||||||
self.inner.lock().unwrap().0.read(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// We have to manually delegate all of these because the default impls call
|
|
||||||
// read more than once and we don't want those calls to interleave (or
|
|
||||||
// incur the costs of repeated locking).
|
|
||||||
|
|
||||||
fn read_at_least(&mut self, min: usize, buf: &mut [u8]) -> IoResult<usize> {
|
|
||||||
self.inner.lock().unwrap().0.read_at_least(min, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push_at_least(&mut self, min: usize, len: usize, buf: &mut Vec<u8>) -> IoResult<usize> {
|
|
||||||
self.inner.lock().unwrap().0.push_at_least(min, len, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_to_end(&mut self) -> IoResult<Vec<u8>> {
|
|
||||||
self.inner.lock().unwrap().0.read_to_end()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_le_uint_n(&mut self, nbytes: usize) -> IoResult<u64> {
|
|
||||||
self.inner.lock().unwrap().0.read_le_uint_n(nbytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_be_uint_n(&mut self, nbytes: usize) -> IoResult<u64> {
|
|
||||||
self.inner.lock().unwrap().0.read_be_uint_n(nbytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new handle to the stdin of the current process.
|
|
||||||
///
|
|
||||||
/// The returned handle is a wrapper around a global `BufferedReader` shared
|
|
||||||
/// by all threads. If buffered access is not desired, the `stdin_raw` function
|
|
||||||
/// is provided to provided unbuffered access to stdin.
|
|
||||||
///
|
|
||||||
/// See `stdout()` for more notes about this function.
|
|
||||||
pub fn stdin() -> StdinReader {
|
|
||||||
// We're following the same strategy as kimundi's lazy_static library
|
|
||||||
static mut STDIN: *mut StdinReader = 0 as *mut StdinReader;
|
|
||||||
static ONCE: Once = ONCE_INIT;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
ONCE.call_once(|| {
|
|
||||||
// The default buffer capacity is 64k, but apparently windows
|
|
||||||
// doesn't like 64k reads on stdin. See #13304 for details, but the
|
|
||||||
// idea is that on windows we use a slightly smaller buffer that's
|
|
||||||
// been seen to be acceptable.
|
|
||||||
let stdin = if cfg!(windows) {
|
|
||||||
BufferedReader::with_capacity(8 * 1024, stdin_raw())
|
|
||||||
} else {
|
|
||||||
BufferedReader::new(stdin_raw())
|
|
||||||
};
|
|
||||||
let stdin = StdinReader {
|
|
||||||
inner: Arc::new(Mutex::new(RaceBox(stdin)))
|
|
||||||
};
|
|
||||||
STDIN = boxed::into_raw(box stdin);
|
|
||||||
|
|
||||||
// Make sure to free it at exit
|
|
||||||
let _ = rt::at_exit(|| {
|
|
||||||
Box::from_raw(STDIN);
|
|
||||||
STDIN = ptr::null_mut();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
(*STDIN).clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new non-blocking handle to the stdin of the current process.
|
|
||||||
///
|
|
||||||
/// Unlike `stdin()`, the returned reader is *not* a buffered reader.
|
|
||||||
///
|
|
||||||
/// See `stdout()` for more notes about this function.
|
|
||||||
pub fn stdin_raw() -> StdReader {
|
|
||||||
src(libc::STDIN_FILENO, true, |src| StdReader { inner: src })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a line-buffered handle to the stdout of the current process.
|
|
||||||
///
|
|
||||||
/// Note that this is a fairly expensive operation in that at least one memory
|
|
||||||
/// allocation is performed. Additionally, this must be called from a runtime
|
|
||||||
/// task context because the stream returned will be a non-blocking object using
|
|
||||||
/// the local scheduler to perform the I/O.
|
|
||||||
///
|
|
||||||
/// Care should be taken when creating multiple handles to an output stream for
|
|
||||||
/// a single process. While usage is still safe, the output may be surprising if
|
|
||||||
/// no synchronization is performed to ensure a sane output.
|
|
||||||
pub fn stdout() -> LineBufferedWriter<StdWriter> {
|
|
||||||
LineBufferedWriter::new(stdout_raw())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an unbuffered handle to the stdout of the current process
|
|
||||||
///
|
|
||||||
/// See notes in `stdout()` for more information.
|
|
||||||
pub fn stdout_raw() -> StdWriter {
|
|
||||||
src(libc::STDOUT_FILENO, false, |src| StdWriter { inner: src })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a line-buffered handle to the stderr of the current process.
|
|
||||||
///
|
|
||||||
/// See `stdout()` for notes about this function.
|
|
||||||
pub fn stderr() -> LineBufferedWriter<StdWriter> {
|
|
||||||
LineBufferedWriter::new(stderr_raw())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an unbuffered handle to the stderr of the current process
|
|
||||||
///
|
|
||||||
/// See notes in `stdout()` for more information.
|
|
||||||
pub fn stderr_raw() -> StdWriter {
|
|
||||||
src(libc::STDERR_FILENO, false, |src| StdWriter { inner: src })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resets the task-local stdout handle to the specified writer
|
|
||||||
///
|
|
||||||
/// This will replace the current task's stdout handle, returning the old
|
|
||||||
/// handle. All future calls to `print` and friends will emit their output to
|
|
||||||
/// this specified handle.
|
|
||||||
///
|
|
||||||
/// Note that this does not need to be called for all new tasks; the default
|
|
||||||
/// output handle is to the process's stdout stream.
|
|
||||||
pub fn set_stdout(stdout: Box<Writer + Send>) -> Option<Box<Writer + Send>> {
|
|
||||||
let mut new = Some(stdout);
|
|
||||||
LOCAL_STDOUT.with(|slot| {
|
|
||||||
mem::replace(&mut *slot.borrow_mut(), new.take())
|
|
||||||
}).and_then(|mut s| {
|
|
||||||
let _ = s.flush();
|
|
||||||
Some(s)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resets the task-local stderr handle to the specified writer
|
|
||||||
///
|
|
||||||
/// This will replace the current task's stderr handle, returning the old
|
|
||||||
/// handle. Currently, the stderr handle is used for printing panic messages
|
|
||||||
/// during task panic.
|
|
||||||
///
|
|
||||||
/// Note that this does not need to be called for all new tasks; the default
|
|
||||||
/// output handle is to the process's stderr stream.
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
#[deprecated(since = "1.0.0", reason = "replaced with std::io::set_panic")]
|
|
||||||
pub fn set_stderr(_stderr: Box<Writer + Send>) -> Option<Box<Writer + Send>> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper to access the local task's stdout handle
|
|
||||||
//
|
|
||||||
// Note that this is not a safe function to expose because you can create an
|
|
||||||
// aliased pointer very easily:
|
|
||||||
//
|
|
||||||
// with_task_stdout(|io1| {
|
|
||||||
// with_task_stdout(|io2| {
|
|
||||||
// // io1 aliases io2
|
|
||||||
// })
|
|
||||||
// })
|
|
||||||
fn with_task_stdout<F>(f: F) where F: FnOnce(&mut Writer) -> IoResult<()> {
|
|
||||||
let mut my_stdout: Box<Writer + Send> = LOCAL_STDOUT.with(|slot| {
|
|
||||||
slot.borrow_mut().take()
|
|
||||||
}).unwrap_or_else(|| {
|
|
||||||
box stdout()
|
|
||||||
});
|
|
||||||
let result = f(&mut *my_stdout);
|
|
||||||
let mut var = Some(my_stdout);
|
|
||||||
LOCAL_STDOUT.with(|slot| {
|
|
||||||
*slot.borrow_mut() = var.take();
|
|
||||||
});
|
|
||||||
match result {
|
|
||||||
Ok(()) => {}
|
|
||||||
Err(e) => panic!("failed printing to stdout: {:?}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Flushes the local task's stdout handle.
|
|
||||||
///
|
|
||||||
/// By default, this stream is a line-buffering stream, so flushing may be
|
|
||||||
/// necessary to ensure that all output is printed to the screen (if there are
|
|
||||||
/// no newlines printed).
|
|
||||||
///
|
|
||||||
/// Note that logging macros do not use this stream. Using the logging macros
|
|
||||||
/// will emit output to stderr, and while they are line buffered the log
|
|
||||||
/// messages are always terminated in a newline (no need to flush).
|
|
||||||
pub fn flush() {
|
|
||||||
with_task_stdout(|io| io.flush())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prints a string to the stdout of the current process. No newline is emitted
|
|
||||||
/// after the string is printed.
|
|
||||||
pub fn print(s: &str) {
|
|
||||||
with_task_stdout(|io| io.write_all(s.as_bytes()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prints a string to the stdout of the current process. A literal
|
|
||||||
/// `\n` character is printed to the console after the string.
|
|
||||||
pub fn println(s: &str) {
|
|
||||||
with_task_stdout(|io| {
|
|
||||||
io.write_all(s.as_bytes()).and_then(|()| io.write_all(&[b'\n']))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible
|
|
||||||
/// with the `format_args!` macro.
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
pub fn print_args(fmt: fmt::Arguments) {
|
|
||||||
with_task_stdout(|io| write!(io, "{}", fmt))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Similar to `println`, but takes a `fmt::Arguments` structure to be
|
|
||||||
/// compatible with the `format_args!` macro.
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
pub fn println_args(fmt: fmt::Arguments) {
|
|
||||||
with_task_stdout(|io| writeln!(io, "{}", fmt))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Representation of a reader of a standard input stream
|
|
||||||
pub struct StdReader {
|
|
||||||
inner: StdSource
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StdReader {
|
|
||||||
/// Returns whether this stream is attached to a TTY instance or not.
|
|
||||||
pub fn isatty(&self) -> bool {
|
|
||||||
match self.inner {
|
|
||||||
TTY(..) => true,
|
|
||||||
File(..) => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Reader for StdReader {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
|
||||||
let ret = match self.inner {
|
|
||||||
TTY(ref mut tty) => {
|
|
||||||
// Flush the task-local stdout so that weird issues like a
|
|
||||||
// print!'d prompt not being shown until after the user hits
|
|
||||||
// enter.
|
|
||||||
flush();
|
|
||||||
tty.read(buf).map(|i| i as usize)
|
|
||||||
},
|
|
||||||
File(ref mut file) => file.read(buf).map(|i| i as usize),
|
|
||||||
};
|
|
||||||
match ret {
|
|
||||||
// When reading a piped stdin, libuv will return 0-length reads when
|
|
||||||
// stdin reaches EOF. For pretty much all other streams it will
|
|
||||||
// return an actual EOF error, but apparently for stdin it's a
|
|
||||||
// little different. Hence, here we convert a 0 length read to an
|
|
||||||
// end-of-file indicator so the caller knows to stop reading.
|
|
||||||
Ok(0) => { Err(standard_error(EndOfFile)) }
|
|
||||||
ret @ Ok(..) | ret @ Err(..) => ret,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Representation of a writer to a standard output stream
|
|
||||||
pub struct StdWriter {
|
|
||||||
inner: StdSource
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for StdWriter {}
|
|
||||||
unsafe impl Sync for StdWriter {}
|
|
||||||
|
|
||||||
impl StdWriter {
|
|
||||||
/// Gets the size of this output window, if possible. This is typically used
|
|
||||||
/// when the writer is attached to something like a terminal, this is used
|
|
||||||
/// to fetch the dimensions of the terminal.
|
|
||||||
///
|
|
||||||
/// If successful, returns `Ok((width, height))`.
|
|
||||||
///
|
|
||||||
/// # Error
|
|
||||||
///
|
|
||||||
/// This function will return an error if the output stream is not actually
|
|
||||||
/// connected to a TTY instance, or if querying the TTY instance fails.
|
|
||||||
pub fn winsize(&mut self) -> IoResult<(isize, isize)> {
|
|
||||||
match self.inner {
|
|
||||||
TTY(ref mut tty) => {
|
|
||||||
tty.get_winsize()
|
|
||||||
}
|
|
||||||
File(..) => {
|
|
||||||
Err(IoError {
|
|
||||||
kind: OtherIoError,
|
|
||||||
desc: "stream is not a tty",
|
|
||||||
detail: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Controls whether this output stream is a "raw stream" or simply a normal
|
|
||||||
/// stream.
|
|
||||||
///
|
|
||||||
/// # Error
|
|
||||||
///
|
|
||||||
/// This function will return an error if the output stream is not actually
|
|
||||||
/// connected to a TTY instance, or if querying the TTY instance fails.
|
|
||||||
pub fn set_raw(&mut self, raw: bool) -> IoResult<()> {
|
|
||||||
match self.inner {
|
|
||||||
TTY(ref mut tty) => {
|
|
||||||
tty.set_raw(raw)
|
|
||||||
}
|
|
||||||
File(..) => {
|
|
||||||
Err(IoError {
|
|
||||||
kind: OtherIoError,
|
|
||||||
desc: "stream is not a tty",
|
|
||||||
detail: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether this stream is attached to a TTY instance or not.
|
|
||||||
pub fn isatty(&self) -> bool {
|
|
||||||
match self.inner {
|
|
||||||
TTY(..) => true,
|
|
||||||
File(..) => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Writer for StdWriter {
|
|
||||||
fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
|
|
||||||
// As with stdin on windows, stdout often can't handle writes of large
|
|
||||||
// sizes. For an example, see #14940. For this reason, chunk the output
|
|
||||||
// buffer on windows, but on unix we can just write the whole buffer all
|
|
||||||
// at once.
|
|
||||||
//
|
|
||||||
// For some other references, it appears that this problem has been
|
|
||||||
// encountered by others [1] [2]. We choose the number 8KB just because
|
|
||||||
// libuv does the same.
|
|
||||||
//
|
|
||||||
// [1]: https://tahoe-lafs.org/trac/tahoe-lafs/ticket/1232
|
|
||||||
// [2]: http://www.mail-archive.com/log4net-dev@logging.apache.org/msg00661.html
|
|
||||||
let max_size = if cfg!(windows) {8192} else {usize::MAX};
|
|
||||||
for chunk in buf.chunks(max_size) {
|
|
||||||
try!(match self.inner {
|
|
||||||
TTY(ref mut tty) => tty.write(chunk),
|
|
||||||
File(ref mut file) => file.write(chunk),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use prelude::v1::*;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use sync::mpsc::channel;
|
|
||||||
use thread;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn smoke() {
|
|
||||||
// Just make sure we can acquire handles
|
|
||||||
stdin();
|
|
||||||
stdout();
|
|
||||||
stderr();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,188 +0,0 @@
|
|||||||
// Copyright 2013 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.
|
|
||||||
|
|
||||||
//! Temporary files and directories
|
|
||||||
#![allow(deprecated)] // rand
|
|
||||||
|
|
||||||
use env;
|
|
||||||
use iter::Iterator;
|
|
||||||
use old_io::{fs, IoError, IoErrorKind, IoResult};
|
|
||||||
use old_io;
|
|
||||||
use ops::Drop;
|
|
||||||
use option::Option::{None, Some};
|
|
||||||
use option::Option;
|
|
||||||
use old_path::{Path, GenericPath};
|
|
||||||
use rand::{Rng, thread_rng};
|
|
||||||
use result::Result::{Ok, Err};
|
|
||||||
use string::String;
|
|
||||||
|
|
||||||
/// A wrapper for a path to temporary directory implementing automatic
|
|
||||||
/// scope-based deletion.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// # #![feature(old_io, old_path)]
|
|
||||||
/// use std::old_io::*;
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
///
|
|
||||||
/// {
|
|
||||||
/// // create a temporary directory
|
|
||||||
/// let tmpdir = match TempDir::new("myprefix") {
|
|
||||||
/// Ok(dir) => dir,
|
|
||||||
/// Err(e) => panic!("couldn't create temporary directory: {}", e)
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// // get the path of the temporary directory without affecting the wrapper
|
|
||||||
/// let tmppath = tmpdir.path();
|
|
||||||
///
|
|
||||||
/// println!("The path of temporary directory is {}", tmppath.display());
|
|
||||||
///
|
|
||||||
/// // the temporary directory is automatically removed when tmpdir goes
|
|
||||||
/// // out of scope at the end of the block
|
|
||||||
/// }
|
|
||||||
/// {
|
|
||||||
/// // create a temporary directory, this time using a custom path
|
|
||||||
/// let tmpdir = match TempDir::new_in(&Path::new("/tmp/best/custom/path"), "myprefix") {
|
|
||||||
/// Ok(dir) => dir,
|
|
||||||
/// Err(e) => panic!("couldn't create temporary directory: {}", e)
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// // get the path of the temporary directory and disable automatic deletion in the wrapper
|
|
||||||
/// let tmppath = tmpdir.into_inner();
|
|
||||||
///
|
|
||||||
/// println!("The path of the not-so-temporary directory is {}", tmppath.display());
|
|
||||||
///
|
|
||||||
/// // the temporary directory is not removed here
|
|
||||||
/// // because the directory is detached from the wrapper
|
|
||||||
/// }
|
|
||||||
/// {
|
|
||||||
/// // create a temporary directory
|
|
||||||
/// let tmpdir = match TempDir::new("myprefix") {
|
|
||||||
/// Ok(dir) => dir,
|
|
||||||
/// Err(e) => panic!("couldn't create temporary directory: {}", e)
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// // close the temporary directory manually and check the result
|
|
||||||
/// match tmpdir.close() {
|
|
||||||
/// Ok(_) => println!("success!"),
|
|
||||||
/// Err(e) => panic!("couldn't remove temporary directory: {}", e)
|
|
||||||
/// };
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub struct TempDir {
|
|
||||||
path: Option<Path>,
|
|
||||||
disarmed: bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// How many times should we (re)try finding an unused random name? It should be
|
|
||||||
// enough that an attacker will run out of luck before we run out of patience.
|
|
||||||
const NUM_RETRIES: u32 = 1 << 31;
|
|
||||||
// How many characters should we include in a random file name? It needs to
|
|
||||||
// be enough to dissuade an attacker from trying to preemptively create names
|
|
||||||
// of that length, but not so huge that we unnecessarily drain the random number
|
|
||||||
// generator of entropy.
|
|
||||||
const NUM_RAND_CHARS: usize = 12;
|
|
||||||
|
|
||||||
impl TempDir {
|
|
||||||
/// Attempts to make a temporary directory inside of `tmpdir` whose name
|
|
||||||
/// will have the prefix `prefix`. The directory will be automatically
|
|
||||||
/// deleted once the returned wrapper is destroyed.
|
|
||||||
///
|
|
||||||
/// If no directory can be created, `Err` is returned.
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn new_in(tmpdir: &Path, prefix: &str) -> IoResult<TempDir> {
|
|
||||||
if !tmpdir.is_absolute() {
|
|
||||||
let cur_dir = ::env::current_dir().unwrap();
|
|
||||||
let cur_dir = Path::new(cur_dir.to_str().unwrap());
|
|
||||||
return TempDir::new_in(&cur_dir.join(tmpdir), prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut rng = thread_rng();
|
|
||||||
for _ in 0..NUM_RETRIES {
|
|
||||||
let suffix: String = rng.gen_ascii_chars().take(NUM_RAND_CHARS).collect();
|
|
||||||
let leaf = if prefix.len() > 0 {
|
|
||||||
format!("{}.{}", prefix, suffix)
|
|
||||||
} else {
|
|
||||||
// If we're given an empty string for a prefix, then creating a
|
|
||||||
// directory starting with "." would lead to it being
|
|
||||||
// semi-invisible on some systems.
|
|
||||||
suffix
|
|
||||||
};
|
|
||||||
let path = tmpdir.join(leaf);
|
|
||||||
match fs::mkdir(&path, old_io::USER_RWX) {
|
|
||||||
Ok(_) => return Ok(TempDir { path: Some(path), disarmed: false }),
|
|
||||||
Err(IoError{kind:IoErrorKind::PathAlreadyExists,..}) => (),
|
|
||||||
Err(e) => return Err(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Err(IoError{
|
|
||||||
kind: IoErrorKind::PathAlreadyExists,
|
|
||||||
desc:"Exhausted",
|
|
||||||
detail: None});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempts to make a temporary directory inside of `os::tmpdir()` whose
|
|
||||||
/// name will have the prefix `prefix`. The directory will be automatically
|
|
||||||
/// deleted once the returned wrapper is destroyed.
|
|
||||||
///
|
|
||||||
/// If no directory can be created, `Err` is returned.
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn new(prefix: &str) -> IoResult<TempDir> {
|
|
||||||
let tmp = Path::new(::env::temp_dir().to_str().unwrap());
|
|
||||||
TempDir::new_in(&tmp, prefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unwrap the wrapped `std::path::Path` from the `TempDir` wrapper.
|
|
||||||
/// This discards the wrapper so that the automatic deletion of the
|
|
||||||
/// temporary directory is prevented.
|
|
||||||
pub fn into_inner(self) -> Path {
|
|
||||||
let mut tmpdir = self;
|
|
||||||
tmpdir.path.take().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Access the wrapped `std::path::Path` to the temporary directory.
|
|
||||||
pub fn path<'a>(&'a self) -> &'a Path {
|
|
||||||
self.path.as_ref().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Close and remove the temporary directory
|
|
||||||
///
|
|
||||||
/// Although `TempDir` removes the directory on drop, in the destructor
|
|
||||||
/// any errors are ignored. To detect errors cleaning up the temporary
|
|
||||||
/// directory, call `close` instead.
|
|
||||||
pub fn close(mut self) -> IoResult<()> {
|
|
||||||
self.cleanup_dir()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cleanup_dir(&mut self) -> IoResult<()> {
|
|
||||||
assert!(!self.disarmed);
|
|
||||||
self.disarmed = true;
|
|
||||||
match self.path {
|
|
||||||
Some(ref p) => {
|
|
||||||
fs::rmdir_recursive(p)
|
|
||||||
}
|
|
||||||
None => Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for TempDir {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if !self.disarmed {
|
|
||||||
let _ = self.cleanup_dir();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// the tests for this module need to change the path using change_dir,
|
|
||||||
// and this doesn't play nicely with other tests so these unit tests are located
|
|
||||||
// in src/test/run-pass/tempfile.rs
|
|
@ -1,177 +0,0 @@
|
|||||||
// Copyright 2013 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.
|
|
||||||
|
|
||||||
//! Various utility functions useful for writing I/O tests
|
|
||||||
|
|
||||||
use prelude::v1::*;
|
|
||||||
|
|
||||||
use env;
|
|
||||||
use libc;
|
|
||||||
use old_io::net::ip::*;
|
|
||||||
use old_path::{Path, GenericPath};
|
|
||||||
use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
|
|
||||||
|
|
||||||
/// Get a port number, starting at 9600, for use in tests
|
|
||||||
pub fn next_test_port() -> u16 {
|
|
||||||
static NEXT_OFFSET: AtomicUsize = ATOMIC_USIZE_INIT;
|
|
||||||
base_port() + NEXT_OFFSET.fetch_add(1, Ordering::Relaxed) as u16
|
|
||||||
}
|
|
||||||
|
|
||||||
// iOS has a pretty long tmpdir path which causes pipe creation
|
|
||||||
// to like: invalid argument: path must be smaller than SUN_LEN
|
|
||||||
fn next_test_unix_socket() -> String {
|
|
||||||
static COUNT: AtomicUsize = ATOMIC_USIZE_INIT;
|
|
||||||
// base port and pid are an attempt to be unique between multiple
|
|
||||||
// test-runners of different configurations running on one
|
|
||||||
// buildbot, the count is to be unique within this executable.
|
|
||||||
format!("rust-test-unix-path-{}-{}-{}",
|
|
||||||
base_port(),
|
|
||||||
unsafe {libc::getpid()},
|
|
||||||
COUNT.fetch_add(1, Ordering::Relaxed))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a temporary path which could be the location of a unix socket
|
|
||||||
#[cfg(not(target_os = "ios"))]
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn next_test_unix() -> Path {
|
|
||||||
let string = next_test_unix_socket();
|
|
||||||
if cfg!(unix) {
|
|
||||||
Path::new(::env::temp_dir().to_str().unwrap()).join(string)
|
|
||||||
} else {
|
|
||||||
Path::new(format!("{}{}", r"\\.\pipe\", string))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a temporary path which could be the location of a unix socket
|
|
||||||
#[cfg(target_os = "ios")]
|
|
||||||
pub fn next_test_unix() -> Path {
|
|
||||||
Path::new(format!("/var/tmp/{}", next_test_unix_socket()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a unique IPv4 localhost:port pair starting at 9600
|
|
||||||
pub fn next_test_ip4() -> SocketAddr {
|
|
||||||
SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: next_test_port() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a unique IPv6 localhost:port pair starting at 9600
|
|
||||||
pub fn next_test_ip6() -> SocketAddr {
|
|
||||||
SocketAddr { ip: Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1), port: next_test_port() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
XXX: Welcome to MegaHack City.
|
|
||||||
|
|
||||||
The bots run multiple builds at the same time, and these builds
|
|
||||||
all want to use ports. This function figures out which workspace
|
|
||||||
it is running in and assigns a port range based on it.
|
|
||||||
*/
|
|
||||||
fn base_port() -> u16 {
|
|
||||||
|
|
||||||
let base = 9600;
|
|
||||||
let range = 1000;
|
|
||||||
|
|
||||||
let bases = [
|
|
||||||
("32-opt", base + range * 1),
|
|
||||||
("32-nopt", base + range * 2),
|
|
||||||
("64-opt", base + range * 3),
|
|
||||||
("64-nopt", base + range * 4),
|
|
||||||
("64-opt-vg", base + range * 5),
|
|
||||||
("all-opt", base + range * 6),
|
|
||||||
("snap3", base + range * 7),
|
|
||||||
("dist", base + range * 8)
|
|
||||||
];
|
|
||||||
|
|
||||||
// FIXME (#9639): This needs to handle non-utf8 paths
|
|
||||||
let path = env::current_dir().unwrap();
|
|
||||||
let path_s = path.to_str().unwrap();
|
|
||||||
|
|
||||||
let mut final_base = base;
|
|
||||||
|
|
||||||
for &(dir, base) in &bases {
|
|
||||||
if path_s.contains(dir) {
|
|
||||||
final_base = base;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return final_base;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Raises the file descriptor limit when running tests if necessary
|
|
||||||
pub fn raise_fd_limit() {
|
|
||||||
unsafe { darwin_fd_limit::raise_fd_limit() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// darwin_fd_limit exists to work around an issue where launchctl on Mac OS X defaults the rlimit
|
|
||||||
/// maxfiles to 256/unlimited. The default soft limit of 256 ends up being far too low for our
|
|
||||||
/// multithreaded scheduler testing, depending on the number of cores available.
|
|
||||||
///
|
|
||||||
/// This fixes issue #7772.
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
mod darwin_fd_limit {
|
|
||||||
use libc;
|
|
||||||
type rlim_t = libc::uint64_t;
|
|
||||||
#[repr(C)]
|
|
||||||
struct rlimit {
|
|
||||||
rlim_cur: rlim_t,
|
|
||||||
rlim_max: rlim_t
|
|
||||||
}
|
|
||||||
extern {
|
|
||||||
// name probably doesn't need to be mut, but the C function doesn't specify const
|
|
||||||
fn sysctl(name: *mut libc::c_int, namelen: libc::c_uint,
|
|
||||||
oldp: *mut libc::c_void, oldlenp: *mut libc::size_t,
|
|
||||||
newp: *mut libc::c_void, newlen: libc::size_t) -> libc::c_int;
|
|
||||||
fn getrlimit(resource: libc::c_int, rlp: *mut rlimit) -> libc::c_int;
|
|
||||||
fn setrlimit(resource: libc::c_int, rlp: *const rlimit) -> libc::c_int;
|
|
||||||
}
|
|
||||||
static CTL_KERN: libc::c_int = 1;
|
|
||||||
static KERN_MAXFILESPERPROC: libc::c_int = 29;
|
|
||||||
static RLIMIT_NOFILE: libc::c_int = 8;
|
|
||||||
|
|
||||||
pub unsafe fn raise_fd_limit() {
|
|
||||||
// The strategy here is to fetch the current resource limits, read the kern.maxfilesperproc
|
|
||||||
// sysctl value, and bump the soft resource limit for maxfiles up to the sysctl value.
|
|
||||||
use ptr::null_mut;
|
|
||||||
use mem::size_of_val;
|
|
||||||
use io;
|
|
||||||
|
|
||||||
// Fetch the kern.maxfilesperproc value
|
|
||||||
let mut mib: [libc::c_int; 2] = [CTL_KERN, KERN_MAXFILESPERPROC];
|
|
||||||
let mut maxfiles: libc::c_int = 0;
|
|
||||||
let mut size: libc::size_t = size_of_val(&maxfiles) as libc::size_t;
|
|
||||||
if sysctl(&mut mib[0], 2, &mut maxfiles as *mut libc::c_int as *mut libc::c_void, &mut size,
|
|
||||||
null_mut(), 0) != 0 {
|
|
||||||
let err = io::Error::last_os_error();
|
|
||||||
panic!("raise_fd_limit: error calling sysctl: {}", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch the current resource limits
|
|
||||||
let mut rlim = rlimit{rlim_cur: 0, rlim_max: 0};
|
|
||||||
if getrlimit(RLIMIT_NOFILE, &mut rlim) != 0 {
|
|
||||||
let err = io::Error::last_os_error();
|
|
||||||
panic!("raise_fd_limit: error calling getrlimit: {}", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bump the soft limit to the smaller of kern.maxfilesperproc and the hard limit
|
|
||||||
rlim.rlim_cur = ::cmp::min(maxfiles as rlim_t, rlim.rlim_max);
|
|
||||||
|
|
||||||
// Set our newly-increased resource limit
|
|
||||||
if setrlimit(RLIMIT_NOFILE, &rlim) != 0 {
|
|
||||||
let err = io::Error::last_os_error();
|
|
||||||
panic!("raise_fd_limit: error calling setrlimit: {}", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
|
|
||||||
mod darwin_fd_limit {
|
|
||||||
pub unsafe fn raise_fd_limit() {}
|
|
||||||
}
|
|
@ -1,488 +0,0 @@
|
|||||||
// Copyright 2013 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.
|
|
||||||
|
|
||||||
//! Synchronous Timers
|
|
||||||
//!
|
|
||||||
//! This module exposes the functionality to create timers, block the current task,
|
|
||||||
//! and create receivers which will receive notifications after a period of time.
|
|
||||||
|
|
||||||
// FIXME: These functions take Durations but only pass ms to the backend impls.
|
|
||||||
|
|
||||||
use boxed::Box;
|
|
||||||
use sync::mpsc::{Receiver, Sender, channel};
|
|
||||||
use time::Duration;
|
|
||||||
use old_io::IoResult;
|
|
||||||
use sys::timer::Callback;
|
|
||||||
use sys::timer::Timer as TimerImp;
|
|
||||||
|
|
||||||
/// A synchronous timer object
|
|
||||||
///
|
|
||||||
/// Values of this type can be used to put the current task to sleep for a
|
|
||||||
/// period of time. Handles to this timer can also be created in the form of
|
|
||||||
/// receivers which will receive notifications over time.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![feature(old_io, std_misc)]
|
|
||||||
/// # fn foo() {
|
|
||||||
/// use std::old_io::Timer;
|
|
||||||
/// use std::time::Duration;
|
|
||||||
///
|
|
||||||
/// let mut timer = Timer::new().unwrap();
|
|
||||||
/// timer.sleep(Duration::milliseconds(10)); // block the task for awhile
|
|
||||||
///
|
|
||||||
/// let timeout = timer.oneshot(Duration::milliseconds(10));
|
|
||||||
/// // do some work
|
|
||||||
/// timeout.recv().unwrap(); // wait for the timeout to expire
|
|
||||||
///
|
|
||||||
/// let periodic = timer.periodic(Duration::milliseconds(10));
|
|
||||||
/// loop {
|
|
||||||
/// periodic.recv().unwrap();
|
|
||||||
/// // this loop is only executed once every 10ms
|
|
||||||
/// }
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// If only sleeping is necessary, then a convenience API is provided through
|
|
||||||
/// the `old_io::timer` module.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![feature(old_io, std_misc)]
|
|
||||||
/// # fn foo() {
|
|
||||||
/// use std::old_io::timer;
|
|
||||||
/// use std::time::Duration;
|
|
||||||
///
|
|
||||||
/// // Put this task to sleep for 5 seconds
|
|
||||||
/// timer::sleep(Duration::seconds(5));
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
pub struct Timer {
|
|
||||||
inner: TimerImp,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TimerCallback { tx: Sender<()> }
|
|
||||||
|
|
||||||
/// Sleep the current task for the specified duration.
|
|
||||||
///
|
|
||||||
/// When provided a zero or negative `duration`, the function will
|
|
||||||
/// return immediately.
|
|
||||||
pub fn sleep(duration: Duration) {
|
|
||||||
let timer = Timer::new();
|
|
||||||
let mut timer = timer.ok().expect("timer::sleep: could not create a Timer");
|
|
||||||
|
|
||||||
timer.sleep(duration)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Timer {
|
|
||||||
/// Creates a new timer which can be used to put the current task to sleep
|
|
||||||
/// for a number of milliseconds, or to possibly create channels which will
|
|
||||||
/// get notified after an amount of time has passed.
|
|
||||||
pub fn new() -> IoResult<Timer> {
|
|
||||||
TimerImp::new().map(|t| Timer { inner: t })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Blocks the current task for the specified duration.
|
|
||||||
///
|
|
||||||
/// Note that this function will cause any other receivers for this timer to
|
|
||||||
/// be invalidated (the other end will be closed).
|
|
||||||
///
|
|
||||||
/// When provided a zero or negative `duration`, the function will
|
|
||||||
/// return immediately.
|
|
||||||
pub fn sleep(&mut self, duration: Duration) {
|
|
||||||
// Short-circuit the timer backend for 0 duration
|
|
||||||
let ms = in_ms_u64(duration);
|
|
||||||
if ms == 0 { return }
|
|
||||||
self.inner.sleep(ms);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a oneshot receiver which will have a notification sent when
|
|
||||||
/// the specified duration has elapsed.
|
|
||||||
///
|
|
||||||
/// This does *not* block the current task, but instead returns immediately.
|
|
||||||
///
|
|
||||||
/// Note that this invalidates any previous receiver which has been created
|
|
||||||
/// by this timer, and that the returned receiver will be invalidated once
|
|
||||||
/// the timer is destroyed (when it falls out of scope). In particular, if
|
|
||||||
/// this is called in method-chaining style, the receiver will be
|
|
||||||
/// invalidated at the end of that statement, and all `recv` calls will
|
|
||||||
/// fail.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![feature(old_io, std_misc)]
|
|
||||||
/// use std::old_io::Timer;
|
|
||||||
/// use std::time::Duration;
|
|
||||||
///
|
|
||||||
/// let mut timer = Timer::new().unwrap();
|
|
||||||
/// let ten_milliseconds = timer.oneshot(Duration::milliseconds(10));
|
|
||||||
///
|
|
||||||
/// for _ in 0..100 { /* do work */ }
|
|
||||||
///
|
|
||||||
/// // blocks until 10 ms after the `oneshot` call
|
|
||||||
/// ten_milliseconds.recv().unwrap();
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![feature(old_io, std_misc)]
|
|
||||||
/// use std::old_io::Timer;
|
|
||||||
/// use std::time::Duration;
|
|
||||||
///
|
|
||||||
/// // Incorrect, method chaining-style:
|
|
||||||
/// let mut five_ms = Timer::new().unwrap().oneshot(Duration::milliseconds(5));
|
|
||||||
/// // The timer object was destroyed, so this will always fail:
|
|
||||||
/// // five_ms.recv().unwrap()
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// When provided a zero or negative `duration`, the message will
|
|
||||||
/// be sent immediately.
|
|
||||||
pub fn oneshot(&mut self, duration: Duration) -> Receiver<()> {
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
// Short-circuit the timer backend for 0 duration
|
|
||||||
if in_ms_u64(duration) != 0 {
|
|
||||||
self.inner.oneshot(in_ms_u64(duration), Box::new(TimerCallback { tx: tx }));
|
|
||||||
} else {
|
|
||||||
tx.send(()).unwrap();
|
|
||||||
}
|
|
||||||
return rx
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a receiver which will have a continuous stream of notifications
|
|
||||||
/// being sent each time the specified duration has elapsed.
|
|
||||||
///
|
|
||||||
/// This does *not* block the current task, but instead returns
|
|
||||||
/// immediately. The first notification will not be received immediately,
|
|
||||||
/// but rather after the first duration.
|
|
||||||
///
|
|
||||||
/// Note that this invalidates any previous receiver which has been created
|
|
||||||
/// by this timer, and that the returned receiver will be invalidated once
|
|
||||||
/// the timer is destroyed (when it falls out of scope). In particular, if
|
|
||||||
/// this is called in method-chaining style, the receiver will be
|
|
||||||
/// invalidated at the end of that statement, and all `recv` calls will
|
|
||||||
/// fail.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![feature(old_io, std_misc)]
|
|
||||||
/// use std::old_io::Timer;
|
|
||||||
/// use std::time::Duration;
|
|
||||||
///
|
|
||||||
/// let mut timer = Timer::new().unwrap();
|
|
||||||
/// let ten_milliseconds = timer.periodic(Duration::milliseconds(10));
|
|
||||||
///
|
|
||||||
/// for _ in 0..100 { /* do work */ }
|
|
||||||
///
|
|
||||||
/// // blocks until 10 ms after the `periodic` call
|
|
||||||
/// ten_milliseconds.recv().unwrap();
|
|
||||||
///
|
|
||||||
/// for _ in 0..100 { /* do work */ }
|
|
||||||
///
|
|
||||||
/// // blocks until 20 ms after the `periodic` call (*not* 10ms after the
|
|
||||||
/// // previous `recv`)
|
|
||||||
/// ten_milliseconds.recv().unwrap();
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![feature(old_io, std_misc)]
|
|
||||||
/// use std::old_io::Timer;
|
|
||||||
/// use std::time::Duration;
|
|
||||||
///
|
|
||||||
/// // Incorrect, method chaining-style.
|
|
||||||
/// let mut five_ms = Timer::new().unwrap().periodic(Duration::milliseconds(5));
|
|
||||||
/// // The timer object was destroyed, so this will always fail:
|
|
||||||
/// // five_ms.recv().unwrap()
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// When provided a zero or negative `duration`, the messages will
|
|
||||||
/// be sent without delay.
|
|
||||||
pub fn periodic(&mut self, duration: Duration) -> Receiver<()> {
|
|
||||||
let ms = in_ms_u64(duration);
|
|
||||||
// FIXME: The backend implementations don't ever send a message
|
|
||||||
// if given a 0 ms duration. Temporarily using 1ms. It's
|
|
||||||
// not clear what use a 0ms period is anyway...
|
|
||||||
let ms = if ms == 0 { 1 } else { ms };
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
self.inner.period(ms, Box::new(TimerCallback { tx: tx }));
|
|
||||||
return rx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Callback for TimerCallback {
|
|
||||||
fn call(&mut self) {
|
|
||||||
let _ = self.tx.send(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn in_ms_u64(d: Duration) -> u64 {
|
|
||||||
let ms = d.num_milliseconds();
|
|
||||||
if ms < 0 { return 0 };
|
|
||||||
return ms as u64;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::Timer;
|
|
||||||
use thread;
|
|
||||||
use time::Duration;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_timer_send() {
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
thread::spawn(move || timer.sleep(Duration::milliseconds(1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_io_timer_sleep_simple() {
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
timer.sleep(Duration::milliseconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_io_timer_sleep_oneshot() {
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
timer.oneshot(Duration::milliseconds(1)).recv().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_io_timer_sleep_oneshot_forget() {
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
timer.oneshot(Duration::milliseconds(100000000));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn oneshot_twice() {
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
let rx1 = timer.oneshot(Duration::milliseconds(10000));
|
|
||||||
let rx = timer.oneshot(Duration::milliseconds(1));
|
|
||||||
rx.recv().unwrap();
|
|
||||||
assert!(rx1.recv().is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_io_timer_oneshot_then_sleep() {
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
let rx = timer.oneshot(Duration::milliseconds(100000000));
|
|
||||||
timer.sleep(Duration::milliseconds(1)); // this should invalidate rx
|
|
||||||
|
|
||||||
assert!(rx.recv().is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_io_timer_sleep_periodic() {
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
let rx = timer.periodic(Duration::milliseconds(1));
|
|
||||||
rx.recv().unwrap();
|
|
||||||
rx.recv().unwrap();
|
|
||||||
rx.recv().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_io_timer_sleep_periodic_forget() {
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
timer.periodic(Duration::milliseconds(100000000));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_io_timer_sleep_standalone() {
|
|
||||||
super::sleep(Duration::milliseconds(1))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn oneshot() {
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
|
|
||||||
let rx = timer.oneshot(Duration::milliseconds(1));
|
|
||||||
rx.recv().unwrap();
|
|
||||||
assert!(rx.recv().is_err());
|
|
||||||
|
|
||||||
let rx = timer.oneshot(Duration::milliseconds(1));
|
|
||||||
rx.recv().unwrap();
|
|
||||||
assert!(rx.recv().is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_override() {
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
let orx = timer.oneshot(Duration::milliseconds(100));
|
|
||||||
let prx = timer.periodic(Duration::milliseconds(100));
|
|
||||||
timer.sleep(Duration::milliseconds(1));
|
|
||||||
assert!(orx.recv().is_err());
|
|
||||||
assert!(prx.recv().is_err());
|
|
||||||
timer.oneshot(Duration::milliseconds(1)).recv().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn period() {
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
let rx = timer.periodic(Duration::milliseconds(1));
|
|
||||||
rx.recv().unwrap();
|
|
||||||
rx.recv().unwrap();
|
|
||||||
let rx2 = timer.periodic(Duration::milliseconds(1));
|
|
||||||
rx2.recv().unwrap();
|
|
||||||
rx2.recv().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sleep() {
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
timer.sleep(Duration::milliseconds(1));
|
|
||||||
timer.sleep(Duration::milliseconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn oneshot_fail() {
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
let _rx = timer.oneshot(Duration::milliseconds(1));
|
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn period_fail() {
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
let _rx = timer.periodic(Duration::milliseconds(1));
|
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn normal_fail() {
|
|
||||||
let _timer = Timer::new().unwrap();
|
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn closing_channel_during_drop_doesnt_kill_everything() {
|
|
||||||
// see issue #10375
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
let timer_rx = timer.periodic(Duration::milliseconds(1000));
|
|
||||||
|
|
||||||
thread::spawn(move|| {
|
|
||||||
let _ = timer_rx.recv();
|
|
||||||
});
|
|
||||||
|
|
||||||
// when we drop the TimerWatcher we're going to destroy the channel,
|
|
||||||
// which must wake up the task on the other end
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn reset_doesnt_switch_tasks() {
|
|
||||||
// similar test to the one above.
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
let timer_rx = timer.periodic(Duration::milliseconds(1000));
|
|
||||||
|
|
||||||
thread::spawn(move|| {
|
|
||||||
let _ = timer_rx.recv();
|
|
||||||
});
|
|
||||||
|
|
||||||
timer.oneshot(Duration::milliseconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn reset_doesnt_switch_tasks2() {
|
|
||||||
// similar test to the one above.
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
let timer_rx = timer.periodic(Duration::milliseconds(1000));
|
|
||||||
|
|
||||||
thread::spawn(move|| {
|
|
||||||
let _ = timer_rx.recv();
|
|
||||||
});
|
|
||||||
|
|
||||||
timer.sleep(Duration::milliseconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sender_goes_away_oneshot() {
|
|
||||||
let rx = {
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
timer.oneshot(Duration::milliseconds(1000))
|
|
||||||
};
|
|
||||||
assert!(rx.recv().is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sender_goes_away_period() {
|
|
||||||
let rx = {
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
timer.periodic(Duration::milliseconds(1000))
|
|
||||||
};
|
|
||||||
assert!(rx.recv().is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn receiver_goes_away_oneshot() {
|
|
||||||
let mut timer1 = Timer::new().unwrap();
|
|
||||||
timer1.oneshot(Duration::milliseconds(1));
|
|
||||||
let mut timer2 = Timer::new().unwrap();
|
|
||||||
// while sleeping, the previous timer should fire and not have its
|
|
||||||
// callback do something terrible.
|
|
||||||
timer2.sleep(Duration::milliseconds(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn receiver_goes_away_period() {
|
|
||||||
let mut timer1 = Timer::new().unwrap();
|
|
||||||
timer1.periodic(Duration::milliseconds(1));
|
|
||||||
let mut timer2 = Timer::new().unwrap();
|
|
||||||
// while sleeping, the previous timer should fire and not have its
|
|
||||||
// callback do something terrible.
|
|
||||||
timer2.sleep(Duration::milliseconds(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sleep_zero() {
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
timer.sleep(Duration::milliseconds(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sleep_negative() {
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
timer.sleep(Duration::milliseconds(-1000000));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn oneshot_zero() {
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
let rx = timer.oneshot(Duration::milliseconds(0));
|
|
||||||
rx.recv().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn oneshot_negative() {
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
let rx = timer.oneshot(Duration::milliseconds(-1000000));
|
|
||||||
rx.recv().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn periodic_zero() {
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
let rx = timer.periodic(Duration::milliseconds(0));
|
|
||||||
rx.recv().unwrap();
|
|
||||||
rx.recv().unwrap();
|
|
||||||
rx.recv().unwrap();
|
|
||||||
rx.recv().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn periodic_negative() {
|
|
||||||
let mut timer = Timer::new().unwrap();
|
|
||||||
let rx = timer.periodic(Duration::milliseconds(-1000000));
|
|
||||||
rx.recv().unwrap();
|
|
||||||
rx.recv().unwrap();
|
|
||||||
rx.recv().unwrap();
|
|
||||||
rx.recv().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,495 +0,0 @@
|
|||||||
// Copyright 2013 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.
|
|
||||||
|
|
||||||
//! Utility implementations of Reader and Writer
|
|
||||||
|
|
||||||
#![allow(deprecated)]
|
|
||||||
|
|
||||||
use prelude::v1::*;
|
|
||||||
use cmp;
|
|
||||||
use old_io::{self, Reader, Writer, Buffer};
|
|
||||||
use slice::bytes::MutableByteVector;
|
|
||||||
|
|
||||||
/// Wraps a `Reader`, limiting the number of bytes that can be read from it.
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io::Take")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
pub struct LimitReader<R> {
|
|
||||||
limit: usize,
|
|
||||||
inner: R
|
|
||||||
}
|
|
||||||
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io::Take")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
impl<R: Reader> LimitReader<R> {
|
|
||||||
/// Creates a new `LimitReader`
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io's take method instead")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
pub fn new(r: R, limit: usize) -> LimitReader<R> {
|
|
||||||
LimitReader { limit: limit, inner: r }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consumes the `LimitReader`, returning the underlying `Reader`.
|
|
||||||
pub fn into_inner(self) -> R { self.inner }
|
|
||||||
|
|
||||||
/// Returns the number of bytes that can be read before the `LimitReader`
|
|
||||||
/// will return EOF.
|
|
||||||
///
|
|
||||||
/// # Note
|
|
||||||
///
|
|
||||||
/// The reader may reach EOF after reading fewer bytes than indicated by
|
|
||||||
/// this method if the underlying reader reaches EOF.
|
|
||||||
pub fn limit(&self) -> usize { self.limit }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io's take method instead")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
impl<R: Reader> Reader for LimitReader<R> {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<usize> {
|
|
||||||
if self.limit == 0 {
|
|
||||||
return Err(old_io::standard_error(old_io::EndOfFile));
|
|
||||||
}
|
|
||||||
|
|
||||||
let len = cmp::min(self.limit, buf.len());
|
|
||||||
let res = self.inner.read(&mut buf[..len]);
|
|
||||||
match res {
|
|
||||||
Ok(len) => self.limit -= len,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io's take method instead")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
impl<R: Buffer> Buffer for LimitReader<R> {
|
|
||||||
fn fill_buf<'a>(&'a mut self) -> old_io::IoResult<&'a [u8]> {
|
|
||||||
let amt = try!(self.inner.fill_buf());
|
|
||||||
let buf = &amt[..cmp::min(amt.len(), self.limit)];
|
|
||||||
if buf.len() == 0 {
|
|
||||||
Err(old_io::standard_error(old_io::EndOfFile))
|
|
||||||
} else {
|
|
||||||
Ok(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn consume(&mut self, amt: usize) {
|
|
||||||
// Don't let callers reset the limit by passing an overlarge value
|
|
||||||
let amt = cmp::min(amt, self.limit);
|
|
||||||
self.limit -= amt;
|
|
||||||
self.inner.consume(amt);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `Writer` which ignores bytes written to it, like /dev/null.
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io::sink() instead")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
pub struct NullWriter;
|
|
||||||
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io::sink() instead")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
impl Writer for NullWriter {
|
|
||||||
#[inline]
|
|
||||||
fn write_all(&mut self, _buf: &[u8]) -> old_io::IoResult<()> { Ok(()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `Reader` which returns an infinite stream of 0 bytes, like /dev/zero.
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io::repeat(0) instead")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
pub struct ZeroReader;
|
|
||||||
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io::repeat(0) instead")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
impl Reader for ZeroReader {
|
|
||||||
#[inline]
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<usize> {
|
|
||||||
buf.set_memory(0);
|
|
||||||
Ok(buf.len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io::repeat(0) instead")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
impl Buffer for ZeroReader {
|
|
||||||
fn fill_buf<'a>(&'a mut self) -> old_io::IoResult<&'a [u8]> {
|
|
||||||
static DATA: [u8; 64] = [0; 64];
|
|
||||||
Ok(&DATA)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn consume(&mut self, _amt: usize) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `Reader` which is always at EOF, like /dev/null.
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io::empty() instead")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
pub struct NullReader;
|
|
||||||
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io::empty() instead")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
impl Reader for NullReader {
|
|
||||||
#[inline]
|
|
||||||
fn read(&mut self, _buf: &mut [u8]) -> old_io::IoResult<usize> {
|
|
||||||
Err(old_io::standard_error(old_io::EndOfFile))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io::empty() instead")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
impl Buffer for NullReader {
|
|
||||||
fn fill_buf<'a>(&'a mut self) -> old_io::IoResult<&'a [u8]> {
|
|
||||||
Err(old_io::standard_error(old_io::EndOfFile))
|
|
||||||
}
|
|
||||||
fn consume(&mut self, _amt: usize) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `Writer` which multiplexes writes to a set of `Writer`s.
|
|
||||||
///
|
|
||||||
/// The `Writer`s are delegated to in order. If any `Writer` returns an error,
|
|
||||||
/// that error is returned immediately and remaining `Writer`s are not called.
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io::Broadcast instead")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
pub struct MultiWriter<W> {
|
|
||||||
writers: Vec<W>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<W> MultiWriter<W> where W: Writer {
|
|
||||||
/// Creates a new `MultiWriter`
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io's broadcast method instead")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
pub fn new(writers: Vec<W>) -> MultiWriter<W> {
|
|
||||||
MultiWriter { writers: writers }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io::Broadcast instead")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
impl<W> Writer for MultiWriter<W> where W: Writer {
|
|
||||||
#[inline]
|
|
||||||
fn write_all(&mut self, buf: &[u8]) -> old_io::IoResult<()> {
|
|
||||||
for writer in &mut self.writers {
|
|
||||||
try!(writer.write_all(buf));
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn flush(&mut self) -> old_io::IoResult<()> {
|
|
||||||
for writer in &mut self.writers {
|
|
||||||
try!(writer.flush());
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `Reader` which chains input from multiple `Reader`s, reading each to
|
|
||||||
/// completion before moving onto the next.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io::Chain instead")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
pub struct ChainedReader<I, R> {
|
|
||||||
readers: I,
|
|
||||||
cur_reader: Option<R>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: Reader, I: Iterator<Item=R>> ChainedReader<I, R> {
|
|
||||||
/// Creates a new `ChainedReader`
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io's chain method instead")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
pub fn new(mut readers: I) -> ChainedReader<I, R> {
|
|
||||||
let r = readers.next();
|
|
||||||
ChainedReader { readers: readers, cur_reader: r }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io::Chain instead")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
impl<R: Reader, I: Iterator<Item=R>> Reader for ChainedReader<I, R> {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<usize> {
|
|
||||||
loop {
|
|
||||||
let err = match self.cur_reader {
|
|
||||||
Some(ref mut r) => {
|
|
||||||
match r.read(buf) {
|
|
||||||
Ok(len) => return Ok(len),
|
|
||||||
Err(ref e) if e.kind == old_io::EndOfFile => None,
|
|
||||||
Err(e) => Some(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => break
|
|
||||||
};
|
|
||||||
self.cur_reader = self.readers.next();
|
|
||||||
match err {
|
|
||||||
Some(e) => return Err(e),
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(old_io::standard_error(old_io::EndOfFile))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `Reader` which forwards input from another `Reader`, passing it along to
|
|
||||||
/// a `Writer` as well. Similar to the `tee(1)` command.
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io::Tee instead")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
pub struct TeeReader<R, W> {
|
|
||||||
reader: R,
|
|
||||||
writer: W,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io::Tee instead")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
impl<R: Reader, W: Writer> TeeReader<R, W> {
|
|
||||||
/// Creates a new `TeeReader`
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io's tee method instead")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
pub fn new(r: R, w: W) -> TeeReader<R, W> {
|
|
||||||
TeeReader { reader: r, writer: w }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consumes the `TeeReader`, returning the underlying `Reader` and
|
|
||||||
/// `Writer`.
|
|
||||||
pub fn into_inner(self) -> (R, W) {
|
|
||||||
let TeeReader { reader, writer } = self;
|
|
||||||
(reader, writer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io::Tee instead")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
impl<R: Reader, W: Writer> Reader for TeeReader<R, W> {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<usize> {
|
|
||||||
self.reader.read(buf).and_then(|len| {
|
|
||||||
self.writer.write_all(&mut buf[..len]).map(|()| len)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Copies all data from a `Reader` to a `Writer`.
|
|
||||||
#[deprecated(since = "1.0.0", reason = "use std::io's copy function instead")]
|
|
||||||
#[unstable(feature = "old_io")]
|
|
||||||
pub fn copy<R: Reader, W: Writer>(r: &mut R, w: &mut W) -> old_io::IoResult<()> {
|
|
||||||
let mut buf = [0; super::DEFAULT_BUF_SIZE];
|
|
||||||
loop {
|
|
||||||
let len = match r.read(&mut buf) {
|
|
||||||
Ok(len) => len,
|
|
||||||
Err(ref e) if e.kind == old_io::EndOfFile => return Ok(()),
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
};
|
|
||||||
try!(w.write_all(&buf[..len]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An adaptor converting an `Iterator<u8>` to a `Reader`.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct IterReader<T> {
|
|
||||||
iter: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Iterator<Item=u8>> IterReader<T> {
|
|
||||||
/// Creates a new `IterReader` which will read from the specified
|
|
||||||
/// `Iterator`.
|
|
||||||
pub fn new(iter: T) -> IterReader<T> {
|
|
||||||
IterReader { iter: iter }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Iterator<Item=u8>> Reader for IterReader<T> {
|
|
||||||
#[inline]
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<usize> {
|
|
||||||
let mut len = 0;
|
|
||||||
for (slot, elt) in buf.iter_mut().zip(self.iter.by_ref()) {
|
|
||||||
*slot = elt;
|
|
||||||
len += 1;
|
|
||||||
}
|
|
||||||
if len == 0 && buf.len() != 0 {
|
|
||||||
Err(old_io::standard_error(old_io::EndOfFile))
|
|
||||||
} else {
|
|
||||||
Ok(len)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use prelude::v1::*;
|
|
||||||
|
|
||||||
use old_io::{MemReader, ByRefReader, Reader, Writer, Buffer};
|
|
||||||
use old_io;
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_limit_reader_unlimited() {
|
|
||||||
let mut r = MemReader::new(vec!(0, 1, 2));
|
|
||||||
{
|
|
||||||
let mut r = LimitReader::new(r.by_ref(), 4);
|
|
||||||
assert_eq!(r.read_to_end().unwrap(), [0, 1, 2]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_limit_reader_limited() {
|
|
||||||
let mut r = MemReader::new(vec!(0, 1, 2));
|
|
||||||
{
|
|
||||||
let mut r = LimitReader::new(r.by_ref(), 2);
|
|
||||||
assert_eq!(r.read_to_end().unwrap(), [0, 1]);
|
|
||||||
}
|
|
||||||
assert_eq!(r.read_to_end().unwrap(), [2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_limit_reader_limit() {
|
|
||||||
let r = MemReader::new(vec!(0, 1, 2));
|
|
||||||
let mut r = LimitReader::new(r, 3);
|
|
||||||
assert_eq!(3, r.limit());
|
|
||||||
assert_eq!(0, r.read_byte().unwrap());
|
|
||||||
assert_eq!(2, r.limit());
|
|
||||||
assert_eq!(r.read_to_end().unwrap(), [1, 2]);
|
|
||||||
assert_eq!(0, r.limit());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_limit_reader_overlong_consume() {
|
|
||||||
let mut r = MemReader::new(vec![0, 1, 2, 3, 4, 5]);
|
|
||||||
let mut r = LimitReader::new(r.by_ref(), 1);
|
|
||||||
r.consume(2);
|
|
||||||
assert_eq!(r.read_to_end().unwrap(), []);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_null_writer() {
|
|
||||||
let mut s = NullWriter;
|
|
||||||
let buf = vec![0, 0, 0];
|
|
||||||
s.write_all(&buf).unwrap();
|
|
||||||
s.flush().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_zero_reader() {
|
|
||||||
let mut s = ZeroReader;
|
|
||||||
let mut buf = vec![1, 2, 3];
|
|
||||||
assert_eq!(s.read(&mut buf), Ok(3));
|
|
||||||
assert_eq!(buf, [0, 0, 0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_null_reader() {
|
|
||||||
let mut r = NullReader;
|
|
||||||
let mut buf = vec![0];
|
|
||||||
assert!(r.read(&mut buf).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_multi_writer() {
|
|
||||||
static mut writes: usize = 0;
|
|
||||||
static mut flushes: usize = 0;
|
|
||||||
|
|
||||||
struct TestWriter;
|
|
||||||
impl Writer for TestWriter {
|
|
||||||
fn write_all(&mut self, _buf: &[u8]) -> old_io::IoResult<()> {
|
|
||||||
unsafe { writes += 1 }
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush(&mut self) -> old_io::IoResult<()> {
|
|
||||||
unsafe { flushes += 1 }
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut multi = MultiWriter::new(vec!(box TestWriter as Box<Writer>,
|
|
||||||
box TestWriter as Box<Writer>));
|
|
||||||
multi.write_all(&[1, 2, 3]).unwrap();
|
|
||||||
assert_eq!(2, unsafe { writes });
|
|
||||||
assert_eq!(0, unsafe { flushes });
|
|
||||||
multi.flush().unwrap();
|
|
||||||
assert_eq!(2, unsafe { writes });
|
|
||||||
assert_eq!(2, unsafe { flushes });
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_chained_reader() {
|
|
||||||
let rs = vec!(MemReader::new(vec!(0, 1)), MemReader::new(vec!()),
|
|
||||||
MemReader::new(vec!(2, 3)));
|
|
||||||
let mut r = ChainedReader::new(rs.into_iter());
|
|
||||||
assert_eq!(r.read_to_end().unwrap(), [0, 1, 2, 3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_tee_reader() {
|
|
||||||
let mut r = TeeReader::new(MemReader::new(vec!(0, 1, 2)),
|
|
||||||
Vec::new());
|
|
||||||
assert_eq!(r.read_to_end().unwrap(), [0, 1, 2]);
|
|
||||||
let (_, w) = r.into_inner();
|
|
||||||
assert_eq!(w, [0, 1, 2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_copy() {
|
|
||||||
let mut r = MemReader::new(vec!(0, 1, 2, 3, 4));
|
|
||||||
let mut w = Vec::new();
|
|
||||||
copy(&mut r, &mut w).unwrap();
|
|
||||||
assert_eq!(w, [0, 1, 2, 3, 4]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn limit_reader_buffer() {
|
|
||||||
let mut r: &[u8] = b"0123456789\n0123456789\n";
|
|
||||||
let r = &mut r;
|
|
||||||
{
|
|
||||||
let mut r = LimitReader::new(r.by_ref(), 3);
|
|
||||||
assert_eq!(r.read_line(), Ok("012".to_string()));
|
|
||||||
assert_eq!(r.limit(), 0);
|
|
||||||
assert_eq!(r.read_line().err().unwrap().kind, old_io::EndOfFile);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let mut r = LimitReader::new(r.by_ref(), 9);
|
|
||||||
assert_eq!(r.read_line(), Ok("3456789\n".to_string()));
|
|
||||||
assert_eq!(r.limit(), 1);
|
|
||||||
assert_eq!(r.read_line(), Ok("0".to_string()));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let mut r = LimitReader::new(r.by_ref(), 100);
|
|
||||||
assert_eq!(r.read_char(), Ok('1'));
|
|
||||||
assert_eq!(r.limit(), 99);
|
|
||||||
assert_eq!(r.read_line(), Ok("23456789\n".to_string()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_iter_reader() {
|
|
||||||
let mut r = IterReader::new(0..8);
|
|
||||||
let mut buf = [0, 0, 0];
|
|
||||||
let len = r.read(&mut buf).unwrap();
|
|
||||||
assert_eq!(len, 3);
|
|
||||||
assert!(buf == [0, 1, 2]);
|
|
||||||
|
|
||||||
let len = r.read(&mut buf).unwrap();
|
|
||||||
assert_eq!(len, 3);
|
|
||||||
assert!(buf == [3, 4, 5]);
|
|
||||||
|
|
||||||
let len = r.read(&mut buf).unwrap();
|
|
||||||
assert_eq!(len, 2);
|
|
||||||
assert!(buf == [6, 7, 5]);
|
|
||||||
|
|
||||||
assert_eq!(r.read(&mut buf).unwrap_err().kind, old_io::EndOfFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn iter_reader_zero_length() {
|
|
||||||
let mut r = IterReader::new(0..8);
|
|
||||||
let mut buf = [];
|
|
||||||
assert_eq!(Ok(0), r.read(&mut buf));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,985 +0,0 @@
|
|||||||
// Copyright 2013 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.
|
|
||||||
|
|
||||||
//! Cross-platform path support
|
|
||||||
//!
|
|
||||||
//! This module implements support for two flavors of paths. `PosixPath` represents a path on any
|
|
||||||
//! unix-like system, whereas `WindowsPath` represents a path on Windows. This module also exposes
|
|
||||||
//! a typedef `Path` which is equal to the appropriate platform-specific path variant.
|
|
||||||
//!
|
|
||||||
//! Both `PosixPath` and `WindowsPath` implement a trait `GenericPath`, which contains the set of
|
|
||||||
//! methods that behave the same for both paths. They each also implement some methods that could
|
|
||||||
//! not be expressed in `GenericPath`, yet behave identically for both path flavors, such as
|
|
||||||
//! `.components()`.
|
|
||||||
//!
|
|
||||||
//! The three main design goals of this module are 1) to avoid unnecessary allocation, 2) to behave
|
|
||||||
//! the same regardless of which flavor of path is being used, and 3) to support paths that cannot
|
|
||||||
//! be represented in UTF-8 (as Linux has no restriction on paths beyond disallowing NUL).
|
|
||||||
//!
|
|
||||||
//! ## Usage
|
|
||||||
//!
|
|
||||||
//! Usage of this module is fairly straightforward. Unless writing platform-specific code, `Path`
|
|
||||||
//! should be used to refer to the platform-native path.
|
|
||||||
//!
|
|
||||||
//! Creation of a path is typically done with either `Path::new(some_str)` or
|
|
||||||
//! `Path::new(some_vec)`. This path can be modified with `.push()` and `.pop()` (and other
|
|
||||||
//! setters). The resulting Path can either be passed to another API that expects a path, or can be
|
|
||||||
//! turned into a `&[u8]` with `.as_vec()` or a `Option<&str>` with `.as_str()`. Similarly,
|
|
||||||
//! attributes of the path can be queried with methods such as `.filename()`. There are also
|
|
||||||
//! methods that return a new path instead of modifying the receiver, such as `.join()` or
|
|
||||||
//! `.dir_path()`.
|
|
||||||
//!
|
|
||||||
//! Paths are always kept in normalized form. This means that creating the path
|
|
||||||
//! `Path::new("a/b/../c")` will return the path `a/c`. Similarly any attempt to mutate the path
|
|
||||||
//! will always leave it in normalized form.
|
|
||||||
//!
|
|
||||||
//! When rendering a path to some form of output, there is a method `.display()` which is
|
|
||||||
//! compatible with the `format!()` parameter `{}`. This will render the path as a string,
|
|
||||||
//! replacing all non-utf8 sequences with the Replacement Character (U+FFFD). As such it is not
|
|
||||||
//! suitable for passing to any API that actually operates on the path; it is only intended for
|
|
||||||
//! display.
|
|
||||||
//!
|
|
||||||
//! ## Examples
|
|
||||||
//!
|
|
||||||
//! ```rust,ignore
|
|
||||||
//! # #![feature(old_path, old_io)]
|
|
||||||
//! use std::old_io::fs::PathExtensions;
|
|
||||||
//! use std::old_path::{Path, GenericPath};
|
|
||||||
//!
|
|
||||||
//! let mut path = Path::new("/tmp/path");
|
|
||||||
//! println!("path: {}", path.display());
|
|
||||||
//! path.set_filename("foo");
|
|
||||||
//! path.push("bar");
|
|
||||||
//! println!("new path: {}", path.display());
|
|
||||||
//! println!("path exists: {}", path.exists());
|
|
||||||
//! ```
|
|
||||||
|
|
||||||
#![unstable(feature = "old_path")]
|
|
||||||
#![deprecated(since = "1.0.0", reason = "use std::path instead")]
|
|
||||||
#![allow(deprecated)] // seriously this is all deprecated
|
|
||||||
#![allow(unused_imports)]
|
|
||||||
|
|
||||||
use core::marker::Sized;
|
|
||||||
use ffi::CString;
|
|
||||||
use clone::Clone;
|
|
||||||
use borrow::Cow;
|
|
||||||
use fmt;
|
|
||||||
use iter::Iterator;
|
|
||||||
use option::Option;
|
|
||||||
use option::Option::{None, Some};
|
|
||||||
use str;
|
|
||||||
use string::String;
|
|
||||||
use vec::Vec;
|
|
||||||
|
|
||||||
/// Typedef for POSIX file paths.
|
|
||||||
/// See `posix::Path` for more info.
|
|
||||||
pub use self::posix::Path as PosixPath;
|
|
||||||
|
|
||||||
/// Typedef for Windows file paths.
|
|
||||||
/// See `windows::Path` for more info.
|
|
||||||
pub use self::windows::Path as WindowsPath;
|
|
||||||
|
|
||||||
/// Typedef for the platform-native path type
|
|
||||||
#[cfg(unix)]
|
|
||||||
pub use self::posix::Path as Path;
|
|
||||||
/// Typedef for the platform-native path type
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub use self::windows::Path as Path;
|
|
||||||
|
|
||||||
/// Typedef for the platform-native component iterator
|
|
||||||
#[cfg(unix)]
|
|
||||||
pub use self::posix::Components as Components;
|
|
||||||
/// Typedef for the platform-native component iterator
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub use self::windows::Components as Components;
|
|
||||||
|
|
||||||
/// Typedef for the platform-native str component iterator
|
|
||||||
#[cfg(unix)]
|
|
||||||
pub use self::posix::StrComponents as StrComponents;
|
|
||||||
/// Typedef for the platform-native str component iterator
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub use self::windows::StrComponents as StrComponents;
|
|
||||||
|
|
||||||
/// Alias for the platform-native separator character.
|
|
||||||
#[cfg(unix)]
|
|
||||||
pub use self::posix::SEP as SEP;
|
|
||||||
/// Alias for the platform-native separator character.
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub use self::windows::SEP as SEP;
|
|
||||||
|
|
||||||
/// Alias for the platform-native separator byte.
|
|
||||||
#[cfg(unix)]
|
|
||||||
pub use self::posix::SEP_BYTE as SEP_BYTE;
|
|
||||||
/// Alias for the platform-native separator byte.
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub use self::windows::SEP_BYTE as SEP_BYTE;
|
|
||||||
|
|
||||||
/// Typedef for the platform-native separator char func
|
|
||||||
#[cfg(unix)]
|
|
||||||
pub use self::posix::is_sep as is_sep;
|
|
||||||
/// Typedef for the platform-native separator char func
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub use self::windows::is_sep as is_sep;
|
|
||||||
/// Typedef for the platform-native separator byte func
|
|
||||||
#[cfg(unix)]
|
|
||||||
pub use self::posix::is_sep_byte as is_sep_byte;
|
|
||||||
/// Typedef for the platform-native separator byte func
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub use self::windows::is_sep_byte as is_sep_byte;
|
|
||||||
|
|
||||||
pub mod posix;
|
|
||||||
pub mod windows;
|
|
||||||
|
|
||||||
/// A trait that represents the generic operations available on paths
|
|
||||||
pub trait GenericPath: Clone + GenericPathUnsafe {
|
|
||||||
/// Creates a new Path from a byte vector or string.
|
|
||||||
/// The resulting Path will always be normalized.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// # fn main() {
|
|
||||||
/// use std::old_path::Path;
|
|
||||||
/// let path = Path::new("foo/bar");
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics the task if the path contains a NUL.
|
|
||||||
///
|
|
||||||
/// See individual Path impls for additional restrictions.
|
|
||||||
#[inline]
|
|
||||||
fn new<T: BytesContainer>(path: T) -> Self {
|
|
||||||
assert!(!contains_nul(&path));
|
|
||||||
unsafe { GenericPathUnsafe::new_unchecked(path) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new Path from a byte vector or string, if possible.
|
|
||||||
/// The resulting Path will always be normalized.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// # fn main() {
|
|
||||||
/// use std::old_path::Path;
|
|
||||||
/// let x: &[u8] = b"foo\0";
|
|
||||||
/// assert!(Path::new_opt(x).is_none());
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
fn new_opt<T: BytesContainer>(path: T) -> Option<Self> {
|
|
||||||
if contains_nul(&path) {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(unsafe { GenericPathUnsafe::new_unchecked(path) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the path as a string, if possible.
|
|
||||||
/// If the path is not representable in utf-8, this returns None.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let p = Path::new("/abc/def");
|
|
||||||
/// assert_eq!(p.as_str(), Some("/abc/def"));
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
fn as_str<'a>(&'a self) -> Option<&'a str> {
|
|
||||||
str::from_utf8(self.as_vec()).ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the path as a byte vector
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let p = Path::new("abc/def");
|
|
||||||
/// assert_eq!(p.as_vec(), b"abc/def");
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
fn as_vec<'a>(&'a self) -> &'a [u8];
|
|
||||||
|
|
||||||
/// Converts the Path into an owned byte vector
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let p = Path::new("abc/def");
|
|
||||||
/// assert_eq!(p.into_vec(), b"abc/def".to_vec());
|
|
||||||
/// // attempting to use p now results in "error: use of moved value"
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
fn into_vec(self) -> Vec<u8>;
|
|
||||||
|
|
||||||
/// Returns an object that implements `Display` for printing paths
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let p = Path::new("abc/def");
|
|
||||||
/// println!("{}", p.display()); // prints "abc/def"
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
fn display<'a>(&'a self) -> Display<'a, Self> {
|
|
||||||
Display{ path: self, filename: false }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an object that implements `Display` for printing filenames
|
|
||||||
///
|
|
||||||
/// If there is no filename, nothing will be printed.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let p = Path::new("abc/def");
|
|
||||||
/// println!("{}", p.filename_display()); // prints "def"
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
fn filename_display<'a>(&'a self) -> Display<'a, Self> {
|
|
||||||
Display{ path: self, filename: true }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the directory component of `self`, as a byte vector (with no trailing separator).
|
|
||||||
/// If `self` has no directory component, returns ['.'].
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let p = Path::new("abc/def/ghi");
|
|
||||||
/// assert_eq!(p.dirname(), b"abc/def");
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
fn dirname<'a>(&'a self) -> &'a [u8];
|
|
||||||
|
|
||||||
/// Returns the directory component of `self`, as a string, if possible.
|
|
||||||
/// See `dirname` for details.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let p = Path::new("abc/def/ghi");
|
|
||||||
/// assert_eq!(p.dirname_str(), Some("abc/def"));
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
fn dirname_str<'a>(&'a self) -> Option<&'a str> {
|
|
||||||
str::from_utf8(self.dirname()).ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the file component of `self`, as a byte vector.
|
|
||||||
/// If `self` represents the root of the file hierarchy, returns None.
|
|
||||||
/// If `self` is "." or "..", returns None.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let p = Path::new("abc/def/ghi");
|
|
||||||
/// assert_eq!(p.filename(), Some(&b"ghi"[..]));
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
fn filename<'a>(&'a self) -> Option<&'a [u8]>;
|
|
||||||
|
|
||||||
/// Returns the file component of `self`, as a string, if possible.
|
|
||||||
/// See `filename` for details.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let p = Path::new("abc/def/ghi");
|
|
||||||
/// assert_eq!(p.filename_str(), Some("ghi"));
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
fn filename_str<'a>(&'a self) -> Option<&'a str> {
|
|
||||||
self.filename().and_then(|s| str::from_utf8(s).ok())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the stem of the filename of `self`, as a byte vector.
|
|
||||||
/// The stem is the portion of the filename just before the last '.'.
|
|
||||||
/// If there is no '.', the entire filename is returned.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let p = Path::new("/abc/def.txt");
|
|
||||||
/// assert_eq!(p.filestem(), Some(&b"def"[..]));
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
fn filestem<'a>(&'a self) -> Option<&'a [u8]> {
|
|
||||||
match self.filename() {
|
|
||||||
None => None,
|
|
||||||
Some(name) => Some({
|
|
||||||
let dot = b'.';
|
|
||||||
match name.rposition_elem(&dot) {
|
|
||||||
None | Some(0) => name,
|
|
||||||
Some(1) if name == b".." => name,
|
|
||||||
Some(pos) => &name[..pos]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the stem of the filename of `self`, as a string, if possible.
|
|
||||||
/// See `filestem` for details.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let p = Path::new("/abc/def.txt");
|
|
||||||
/// assert_eq!(p.filestem_str(), Some("def"));
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
fn filestem_str<'a>(&'a self) -> Option<&'a str> {
|
|
||||||
self.filestem().and_then(|s| str::from_utf8(s).ok())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the extension of the filename of `self`, as an optional byte vector.
|
|
||||||
/// The extension is the portion of the filename just after the last '.'.
|
|
||||||
/// If there is no extension, None is returned.
|
|
||||||
/// If the filename ends in '.', the empty vector is returned.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let p = Path::new("abc/def.txt");
|
|
||||||
/// assert_eq!(p.extension(), Some(&b"txt"[..]));
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
fn extension<'a>(&'a self) -> Option<&'a [u8]> {
|
|
||||||
match self.filename() {
|
|
||||||
None => None,
|
|
||||||
Some(name) => {
|
|
||||||
let dot = b'.';
|
|
||||||
match name.rposition_elem(&dot) {
|
|
||||||
None | Some(0) => None,
|
|
||||||
Some(1) if name == b".." => None,
|
|
||||||
Some(pos) => Some(&name[pos+1..])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the extension of the filename of `self`, as a string, if possible.
|
|
||||||
/// See `extension` for details.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let p = Path::new("abc/def.txt");
|
|
||||||
/// assert_eq!(p.extension_str(), Some("txt"));
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
fn extension_str<'a>(&'a self) -> Option<&'a str> {
|
|
||||||
self.extension().and_then(|s| str::from_utf8(s).ok())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Replaces the filename portion of the path with the given byte vector or string.
|
|
||||||
/// If the replacement name is [], this is equivalent to popping the path.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let mut p = Path::new("abc/def.txt");
|
|
||||||
/// p.set_filename("foo.dat");
|
|
||||||
/// assert!(p == Path::new("abc/foo.dat"));
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics the task if the filename contains a NUL.
|
|
||||||
#[inline]
|
|
||||||
fn set_filename<T: BytesContainer>(&mut self, filename: T) {
|
|
||||||
assert!(!contains_nul(&filename));
|
|
||||||
unsafe { self.set_filename_unchecked(filename) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Replaces the extension with the given byte vector or string.
|
|
||||||
/// If there is no extension in `self`, this adds one.
|
|
||||||
/// If the argument is [] or "", this removes the extension.
|
|
||||||
/// If `self` has no filename, this is a no-op.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let mut p = Path::new("abc/def.txt");
|
|
||||||
/// p.set_extension("csv");
|
|
||||||
/// assert_eq!(p, Path::new("abc/def.csv"));
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics the task if the extension contains a NUL.
|
|
||||||
fn set_extension<T: BytesContainer>(&mut self, extension: T) {
|
|
||||||
assert!(!contains_nul(&extension));
|
|
||||||
|
|
||||||
let val = self.filename().and_then(|name| {
|
|
||||||
let dot = b'.';
|
|
||||||
let extlen = extension.container_as_bytes().len();
|
|
||||||
match (name.rposition_elem(&dot), extlen) {
|
|
||||||
(None, 0) | (Some(0), 0) => None,
|
|
||||||
(Some(idx), 0) => Some(name[..idx].to_vec()),
|
|
||||||
(idx, extlen) => {
|
|
||||||
let idx = match idx {
|
|
||||||
None | Some(0) => name.len(),
|
|
||||||
Some(val) => val
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut v;
|
|
||||||
v = Vec::with_capacity(idx + extlen + 1);
|
|
||||||
v.push_all(&name[..idx]);
|
|
||||||
v.push(dot);
|
|
||||||
v.push_all(extension.container_as_bytes());
|
|
||||||
Some(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
match val {
|
|
||||||
None => (),
|
|
||||||
Some(v) => unsafe { self.set_filename_unchecked(v) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new Path constructed by replacing the filename with the given
|
|
||||||
/// byte vector or string.
|
|
||||||
/// See `set_filename` for details.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let mut p = Path::new("abc/def.txt");
|
|
||||||
/// assert_eq!(p.with_filename("foo.dat"), Path::new("abc/foo.dat"));
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics the task if the filename contains a NUL.
|
|
||||||
#[inline]
|
|
||||||
fn with_filename<T: BytesContainer>(&self, filename: T) -> Self {
|
|
||||||
let mut p = self.clone();
|
|
||||||
p.set_filename(filename);
|
|
||||||
p
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new Path constructed by setting the extension to the given
|
|
||||||
/// byte vector or string.
|
|
||||||
/// See `set_extension` for details.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let mut p = Path::new("abc/def.txt");
|
|
||||||
/// assert_eq!(p.with_extension("csv"), Path::new("abc/def.csv"));
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics the task if the extension contains a NUL.
|
|
||||||
#[inline]
|
|
||||||
fn with_extension<T: BytesContainer>(&self, extension: T) -> Self {
|
|
||||||
let mut p = self.clone();
|
|
||||||
p.set_extension(extension);
|
|
||||||
p
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the directory component of `self`, as a Path.
|
|
||||||
/// If `self` represents the root of the filesystem hierarchy, returns `self`.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let p = Path::new("abc/def/ghi");
|
|
||||||
/// assert_eq!(p.dir_path(), Path::new("abc/def"));
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
fn dir_path(&self) -> Self {
|
|
||||||
// self.dirname() returns a NUL-free vector
|
|
||||||
unsafe { GenericPathUnsafe::new_unchecked(self.dirname()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a Path that represents the filesystem root that `self` is rooted in.
|
|
||||||
///
|
|
||||||
/// If `self` is not absolute, or vol/cwd-relative in the case of Windows, this returns None.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// assert_eq!(Path::new("abc/def").root_path(), None);
|
|
||||||
/// assert_eq!(Path::new("/abc/def").root_path(), Some(Path::new("/")));
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
fn root_path(&self) -> Option<Self>;
|
|
||||||
|
|
||||||
/// Pushes a path (as a byte vector or string) onto `self`.
|
|
||||||
/// If the argument represents an absolute path, it replaces `self`.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let mut p = Path::new("foo/bar");
|
|
||||||
/// p.push("baz.txt");
|
|
||||||
/// assert_eq!(p, Path::new("foo/bar/baz.txt"));
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics the task if the path contains a NUL.
|
|
||||||
#[inline]
|
|
||||||
fn push<T: BytesContainer>(&mut self, path: T) {
|
|
||||||
assert!(!contains_nul(&path));
|
|
||||||
unsafe { self.push_unchecked(path) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pushes multiple paths (as byte vectors or strings) onto `self`.
|
|
||||||
/// See `push` for details.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let mut p = Path::new("foo");
|
|
||||||
/// p.push_many(&["bar", "baz.txt"]);
|
|
||||||
/// assert_eq!(p, Path::new("foo/bar/baz.txt"));
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
fn push_many<T: BytesContainer>(&mut self, paths: &[T]) {
|
|
||||||
let t: Option<&T> = None;
|
|
||||||
if BytesContainer::is_str(t) {
|
|
||||||
for p in paths {
|
|
||||||
self.push(p.container_as_str().unwrap())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for p in paths {
|
|
||||||
self.push(p.container_as_bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes the last path component from the receiver.
|
|
||||||
/// Returns `true` if the receiver was modified, or `false` if it already
|
|
||||||
/// represented the root of the file hierarchy.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let mut p = Path::new("foo/bar/baz.txt");
|
|
||||||
/// p.pop();
|
|
||||||
/// assert_eq!(p, Path::new("foo/bar"));
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
fn pop(&mut self) -> bool;
|
|
||||||
|
|
||||||
/// Returns a new Path constructed by joining `self` with the given path
|
|
||||||
/// (as a byte vector or string).
|
|
||||||
/// If the given path is absolute, the new Path will represent just that.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let p = Path::new("/foo");
|
|
||||||
/// assert_eq!(p.join("bar.txt"), Path::new("/foo/bar.txt"));
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics the task if the path contains a NUL.
|
|
||||||
#[inline]
|
|
||||||
fn join<T: BytesContainer>(&self, path: T) -> Self {
|
|
||||||
let mut p = self.clone();
|
|
||||||
p.push(path);
|
|
||||||
p
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new Path constructed by joining `self` with the given paths
|
|
||||||
/// (as byte vectors or strings).
|
|
||||||
/// See `join` for details.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let p = Path::new("foo");
|
|
||||||
/// let fbbq = Path::new("foo/bar/baz/quux.txt");
|
|
||||||
/// assert_eq!(p.join_many(&["bar", "baz", "quux.txt"]), fbbq);
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
fn join_many<T: BytesContainer>(&self, paths: &[T]) -> Self {
|
|
||||||
let mut p = self.clone();
|
|
||||||
p.push_many(paths);
|
|
||||||
p
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether `self` represents an absolute path.
|
|
||||||
/// An absolute path is defined as one that, when joined to another path, will
|
|
||||||
/// yield back the same absolute path.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let p = Path::new("/abc/def");
|
|
||||||
/// assert!(p.is_absolute());
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
fn is_absolute(&self) -> bool;
|
|
||||||
|
|
||||||
/// Returns whether `self` represents a relative path.
|
|
||||||
/// Typically this is the inverse of `is_absolute`.
|
|
||||||
/// But for Windows paths, it also means the path is not volume-relative or
|
|
||||||
/// relative to the current working directory.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let p = Path::new("abc/def");
|
|
||||||
/// assert!(p.is_relative());
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
fn is_relative(&self) -> bool {
|
|
||||||
!self.is_absolute()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether `self` is equal to, or is an ancestor of, the given path.
|
|
||||||
/// If both paths are relative, they are compared as though they are relative
|
|
||||||
/// to the same parent path.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let p = Path::new("foo/bar/baz/quux.txt");
|
|
||||||
/// let fb = Path::new("foo/bar");
|
|
||||||
/// let bq = Path::new("baz/quux.txt");
|
|
||||||
/// assert!(fb.is_ancestor_of(&p));
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
fn is_ancestor_of(&self, other: &Self) -> bool;
|
|
||||||
|
|
||||||
/// Returns the Path that, were it joined to `base`, would yield `self`.
|
|
||||||
/// If no such path exists, None is returned.
|
|
||||||
/// If `self` is absolute and `base` is relative, or on Windows if both
|
|
||||||
/// paths refer to separate drives, an absolute path is returned.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let p = Path::new("foo/bar/baz/quux.txt");
|
|
||||||
/// let fb = Path::new("foo/bar");
|
|
||||||
/// let bq = Path::new("baz/quux.txt");
|
|
||||||
/// assert_eq!(p.path_relative_from(&fb), Some(bq));
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
fn path_relative_from(&self, base: &Self) -> Option<Self>;
|
|
||||||
|
|
||||||
/// Returns whether the relative path `child` is a suffix of `self`.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// # #![feature(old_path)]
|
|
||||||
/// use std::old_path::{Path, GenericPath};
|
|
||||||
/// # foo();
|
|
||||||
/// # #[cfg(windows)] fn foo() {}
|
|
||||||
/// # #[cfg(unix)] fn foo() {
|
|
||||||
/// let p = Path::new("foo/bar/baz/quux.txt");
|
|
||||||
/// let bq = Path::new("baz/quux.txt");
|
|
||||||
/// assert!(p.ends_with_path(&bq));
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
fn ends_with_path(&self, child: &Self) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A trait that represents something bytes-like (e.g. a &[u8] or a &str)
|
|
||||||
pub trait BytesContainer {
|
|
||||||
/// Returns a &[u8] representing the receiver
|
|
||||||
fn container_as_bytes<'a>(&'a self) -> &'a [u8];
|
|
||||||
/// Returns the receiver interpreted as a utf-8 string, if possible
|
|
||||||
#[inline]
|
|
||||||
fn container_as_str<'a>(&'a self) -> Option<&'a str> {
|
|
||||||
str::from_utf8(self.container_as_bytes()).ok()
|
|
||||||
}
|
|
||||||
/// Returns whether .container_as_str() is guaranteed to not fail
|
|
||||||
// FIXME (#8888): Remove unused arg once ::<for T> works
|
|
||||||
#[inline]
|
|
||||||
fn is_str(_: Option<&Self>) -> bool { false }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A trait that represents the unsafe operations on GenericPaths
|
|
||||||
pub trait GenericPathUnsafe {
|
|
||||||
/// Creates a new Path without checking for null bytes.
|
|
||||||
/// The resulting Path will always be normalized.
|
|
||||||
unsafe fn new_unchecked<T: BytesContainer>(path: T) -> Self;
|
|
||||||
|
|
||||||
/// Replaces the filename portion of the path without checking for null
|
|
||||||
/// bytes.
|
|
||||||
/// See `set_filename` for details.
|
|
||||||
unsafe fn set_filename_unchecked<T: BytesContainer>(&mut self, filename: T);
|
|
||||||
|
|
||||||
/// Pushes a path onto `self` without checking for null bytes.
|
|
||||||
/// See `push` for details.
|
|
||||||
unsafe fn push_unchecked<T: BytesContainer>(&mut self, path: T);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Helper struct for printing paths with format!()
|
|
||||||
pub struct Display<'a, P:'a> {
|
|
||||||
path: &'a P,
|
|
||||||
filename: bool
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl<'a, P: GenericPath> fmt::Debug for Display<'a, P> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
fmt::Debug::fmt(&self.as_cow(), f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl<'a, P: GenericPath> fmt::Display for Display<'a, P> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
self.as_cow().fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, P: GenericPath> Display<'a, P> {
|
|
||||||
/// Returns the path as a possibly-owned string.
|
|
||||||
///
|
|
||||||
/// If the path is not UTF-8, invalid sequences will be replaced with the
|
|
||||||
/// Unicode replacement char. This involves allocation.
|
|
||||||
#[inline]
|
|
||||||
pub fn as_cow(&self) -> Cow<'a, str> {
|
|
||||||
String::from_utf8_lossy(if self.filename {
|
|
||||||
match self.path.filename() {
|
|
||||||
None => {
|
|
||||||
let result: &[u8] = &[];
|
|
||||||
result
|
|
||||||
}
|
|
||||||
Some(v) => v
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.path.as_vec()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BytesContainer for str {
|
|
||||||
#[inline]
|
|
||||||
fn container_as_bytes(&self) -> &[u8] {
|
|
||||||
self.as_bytes()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn container_as_str(&self) -> Option<&str> {
|
|
||||||
Some(self)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn is_str(_: Option<&str>) -> bool { true }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BytesContainer for String {
|
|
||||||
#[inline]
|
|
||||||
fn container_as_bytes(&self) -> &[u8] {
|
|
||||||
self.as_bytes()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn container_as_str(&self) -> Option<&str> {
|
|
||||||
Some(&self[..])
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn is_str(_: Option<&String>) -> bool { true }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BytesContainer for [u8] {
|
|
||||||
#[inline]
|
|
||||||
fn container_as_bytes(&self) -> &[u8] {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BytesContainer for Vec<u8> {
|
|
||||||
#[inline]
|
|
||||||
fn container_as_bytes(&self) -> &[u8] {
|
|
||||||
&self[..]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BytesContainer for CString {
|
|
||||||
#[inline]
|
|
||||||
fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
|
|
||||||
self.as_bytes()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: ?Sized + BytesContainer> BytesContainer for &'a T {
|
|
||||||
#[inline]
|
|
||||||
fn container_as_bytes(&self) -> &[u8] {
|
|
||||||
(**self).container_as_bytes()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn container_as_str(&self) -> Option<&str> {
|
|
||||||
(**self).container_as_str()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn is_str(_: Option<& &'a T>) -> bool { BytesContainer::is_str(None::<&T>) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn contains_nul<T: BytesContainer>(v: &T) -> bool {
|
|
||||||
v.container_as_bytes().iter().any(|&x| x == 0)
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -224,27 +224,22 @@
|
|||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
#![unstable(feature = "rand")]
|
#![unstable(feature = "rand")]
|
||||||
#![deprecated(reason = "use the crates.io `rand` library instead",
|
|
||||||
since = "1.0.0-alpha")]
|
use prelude::v1::*;
|
||||||
#![allow(deprecated)]
|
|
||||||
|
|
||||||
use cell::RefCell;
|
use cell::RefCell;
|
||||||
use clone::Clone;
|
use io;
|
||||||
use old_io::IoResult;
|
|
||||||
use iter::Iterator;
|
|
||||||
use mem;
|
use mem;
|
||||||
use rc::Rc;
|
use rc::Rc;
|
||||||
use result::Result::{Ok, Err};
|
|
||||||
use vec::Vec;
|
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
use core_rand::IsaacRng as IsaacWordRng;
|
use core_rand::IsaacRng as IsaacWordRng;
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[cfg(target_pointer_width = "64")]
|
||||||
use core_rand::Isaac64Rng as IsaacWordRng;
|
use core_rand::Isaac64Rng as IsaacWordRng;
|
||||||
|
|
||||||
pub use core_rand::{Rand, Rng, SeedableRng, Open01, Closed01};
|
pub use core_rand::{Rand, Rng, SeedableRng};
|
||||||
pub use core_rand::{XorShiftRng, IsaacRng, Isaac64Rng, ChaChaRng};
|
pub use core_rand::{XorShiftRng, IsaacRng, Isaac64Rng};
|
||||||
pub use core_rand::{distributions, reseeding};
|
pub use core_rand::reseeding;
|
||||||
pub use rand::os::OsRng;
|
pub use rand::os::OsRng;
|
||||||
|
|
||||||
pub mod os;
|
pub mod os;
|
||||||
@ -269,7 +264,7 @@ impl StdRng {
|
|||||||
///
|
///
|
||||||
/// Reading the randomness from the OS may fail, and any error is
|
/// Reading the randomness from the OS may fail, and any error is
|
||||||
/// propagated via the `IoResult` return value.
|
/// propagated via the `IoResult` return value.
|
||||||
pub fn new() -> IoResult<StdRng> {
|
pub fn new() -> io::Result<StdRng> {
|
||||||
OsRng::new().map(|mut r| StdRng { rng: r.gen() })
|
OsRng::new().map(|mut r| StdRng { rng: r.gen() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -298,22 +293,6 @@ impl<'a> SeedableRng<&'a [usize]> for StdRng {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a weak random number generator with a default algorithm and seed.
|
|
||||||
///
|
|
||||||
/// It returns the fastest `Rng` algorithm currently available in Rust without
|
|
||||||
/// consideration for cryptography or security. If you require a specifically
|
|
||||||
/// seeded `Rng` for consistency over time you should pick one algorithm and
|
|
||||||
/// create the `Rng` yourself.
|
|
||||||
///
|
|
||||||
/// This will read randomness from the operating system to seed the
|
|
||||||
/// generator.
|
|
||||||
pub fn weak_rng() -> XorShiftRng {
|
|
||||||
match OsRng::new() {
|
|
||||||
Ok(mut r) => r.gen(),
|
|
||||||
Err(e) => panic!("weak_rng: failed to create seeded RNG: {:?}", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Controls how the thread-local RNG is reseeded.
|
/// Controls how the thread-local RNG is reseeded.
|
||||||
struct ThreadRngReseeder;
|
struct ThreadRngReseeder;
|
||||||
|
|
||||||
@ -376,83 +355,6 @@ impl Rng for ThreadRng {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a random value using the thread-local random number generator.
|
|
||||||
///
|
|
||||||
/// `random()` can generate various types of random things, and so may require
|
|
||||||
/// type hinting to generate the specific type you want.
|
|
||||||
///
|
|
||||||
/// This function uses the thread local random number generator. This means
|
|
||||||
/// that if you're calling `random()` in a loop, caching the generator can
|
|
||||||
/// increase performance. An example is shown below.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![feature(rand)]
|
|
||||||
/// use std::rand;
|
|
||||||
///
|
|
||||||
/// let x: u8 = rand::random();
|
|
||||||
/// println!("{}", 2 * x as u16);
|
|
||||||
///
|
|
||||||
/// let y = rand::random::<f64>();
|
|
||||||
/// println!("{}", y);
|
|
||||||
///
|
|
||||||
/// if rand::random() { // generates a boolean
|
|
||||||
/// println!("Better lucky than good!");
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Caching the thread local random number generator:
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![feature(rand)]
|
|
||||||
/// use std::rand;
|
|
||||||
/// use std::rand::Rng;
|
|
||||||
///
|
|
||||||
/// let mut v = vec![1, 2, 3];
|
|
||||||
///
|
|
||||||
/// for x in v.iter_mut() {
|
|
||||||
/// *x = rand::random()
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// // would be faster as
|
|
||||||
///
|
|
||||||
/// let mut rng = rand::thread_rng();
|
|
||||||
///
|
|
||||||
/// for x in v.iter_mut() {
|
|
||||||
/// *x = rng.gen();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
pub fn random<T: Rand>() -> T {
|
|
||||||
thread_rng().gen()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Randomly sample up to `amount` elements from an iterator.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![feature(rand)]
|
|
||||||
/// use std::rand::{thread_rng, sample};
|
|
||||||
///
|
|
||||||
/// let mut rng = thread_rng();
|
|
||||||
/// let sample = sample(&mut rng, 1..100, 5);
|
|
||||||
/// println!("{:?}", sample);
|
|
||||||
/// ```
|
|
||||||
pub fn sample<T, I: Iterator<Item=T>, R: Rng>(rng: &mut R,
|
|
||||||
mut iter: I,
|
|
||||||
amount: usize) -> Vec<T> {
|
|
||||||
let mut reservoir: Vec<T> = iter.by_ref().take(amount).collect();
|
|
||||||
for (i, elem) in iter.enumerate() {
|
|
||||||
let k = rng.gen_range(0, i + 1 + amount);
|
|
||||||
if k < amount {
|
|
||||||
reservoir[k] = elem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return reservoir;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use prelude::v1::*;
|
use prelude::v1::*;
|
||||||
|
@ -18,10 +18,10 @@ mod imp {
|
|||||||
use prelude::v1::*;
|
use prelude::v1::*;
|
||||||
use self::OsRngInner::*;
|
use self::OsRngInner::*;
|
||||||
|
|
||||||
|
use fs::File;
|
||||||
|
use io;
|
||||||
use libc;
|
use libc;
|
||||||
use mem;
|
use mem;
|
||||||
use old_io::{IoResult, File};
|
|
||||||
use old_path::Path;
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use rand::reader::ReaderRng;
|
use rand::reader::ReaderRng;
|
||||||
use sys::os::errno;
|
use sys::os::errno;
|
||||||
@ -147,12 +147,12 @@ mod imp {
|
|||||||
|
|
||||||
impl OsRng {
|
impl OsRng {
|
||||||
/// Create a new `OsRng`.
|
/// Create a new `OsRng`.
|
||||||
pub fn new() -> IoResult<OsRng> {
|
pub fn new() -> io::Result<OsRng> {
|
||||||
if is_getrandom_available() {
|
if is_getrandom_available() {
|
||||||
return Ok(OsRng { inner: OsGetrandomRng });
|
return Ok(OsRng { inner: OsGetrandomRng });
|
||||||
}
|
}
|
||||||
|
|
||||||
let reader = try!(File::open(&Path::new("/dev/urandom")));
|
let reader = try!(File::open("/dev/urandom"));
|
||||||
let reader_rng = ReaderRng::new(reader);
|
let reader_rng = ReaderRng::new(reader);
|
||||||
|
|
||||||
Ok(OsRng { inner: OsReaderRng(reader_rng) })
|
Ok(OsRng { inner: OsReaderRng(reader_rng) })
|
||||||
@ -186,7 +186,6 @@ mod imp {
|
|||||||
use prelude::v1::*;
|
use prelude::v1::*;
|
||||||
|
|
||||||
use io;
|
use io;
|
||||||
use old_io::IoResult;
|
|
||||||
use mem;
|
use mem;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use libc::{c_int, size_t};
|
use libc::{c_int, size_t};
|
||||||
@ -202,7 +201,8 @@ mod imp {
|
|||||||
///
|
///
|
||||||
/// This does not block.
|
/// This does not block.
|
||||||
pub struct OsRng {
|
pub struct OsRng {
|
||||||
// dummy field to ensure that this struct cannot be constructed outside of this module
|
// dummy field to ensure that this struct cannot be constructed outside
|
||||||
|
// of this module
|
||||||
_dummy: (),
|
_dummy: (),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,7 +220,7 @@ mod imp {
|
|||||||
|
|
||||||
impl OsRng {
|
impl OsRng {
|
||||||
/// Create a new `OsRng`.
|
/// Create a new `OsRng`.
|
||||||
pub fn new() -> IoResult<OsRng> {
|
pub fn new() -> io::Result<OsRng> {
|
||||||
Ok(OsRng { _dummy: () })
|
Ok(OsRng { _dummy: () })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -238,10 +238,12 @@ mod imp {
|
|||||||
}
|
}
|
||||||
fn fill_bytes(&mut self, v: &mut [u8]) {
|
fn fill_bytes(&mut self, v: &mut [u8]) {
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, v.as_mut_ptr())
|
SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t,
|
||||||
|
v.as_mut_ptr())
|
||||||
};
|
};
|
||||||
if ret == -1 {
|
if ret == -1 {
|
||||||
panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
|
panic!("couldn't generate random bytes: {}",
|
||||||
|
io::Error::last_os_error());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -253,7 +255,6 @@ mod imp {
|
|||||||
|
|
||||||
use io;
|
use io;
|
||||||
use mem;
|
use mem;
|
||||||
use old_io::{IoResult, IoError};
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use libc::types::os::arch::extra::{LONG_PTR};
|
use libc::types::os::arch::extra::{LONG_PTR};
|
||||||
use libc::{DWORD, BYTE, LPCSTR, BOOL};
|
use libc::{DWORD, BYTE, LPCSTR, BOOL};
|
||||||
@ -293,7 +294,7 @@ mod imp {
|
|||||||
|
|
||||||
impl OsRng {
|
impl OsRng {
|
||||||
/// Create a new `OsRng`.
|
/// Create a new `OsRng`.
|
||||||
pub fn new() -> IoResult<OsRng> {
|
pub fn new() -> io::Result<OsRng> {
|
||||||
let mut hcp = 0;
|
let mut hcp = 0;
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
|
CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
|
||||||
@ -302,7 +303,7 @@ mod imp {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if ret == 0 {
|
if ret == 0 {
|
||||||
Err(IoError::last_error())
|
Err(io::Error::last_os_error())
|
||||||
} else {
|
} else {
|
||||||
Ok(OsRng { hcryptprov: hcp })
|
Ok(OsRng { hcryptprov: hcp })
|
||||||
}
|
}
|
||||||
|
@ -8,35 +8,26 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
//! A wrapper around any Reader to treat it as an RNG.
|
//! A wrapper around any Read to treat it as an RNG.
|
||||||
|
|
||||||
use old_io::Reader;
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use prelude::v1::*;
|
||||||
|
use io::prelude::*;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use result::Result::{Ok, Err};
|
|
||||||
|
|
||||||
/// An RNG that reads random bytes straight from a `Reader`. This will
|
/// An RNG that reads random bytes straight from a `Read`. This will
|
||||||
/// work best with an infinite reader, but this is not required.
|
/// work best with an infinite reader, but this is not required.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// It will panic if it there is insufficient data to fulfill a request.
|
/// It will panic if it there is insufficient data to fulfill a request.
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![feature(rand, old_io)]
|
|
||||||
/// use std::rand::{reader, Rng};
|
|
||||||
/// use std::old_io::MemReader;
|
|
||||||
///
|
|
||||||
/// let mut rng = reader::ReaderRng::new(MemReader::new(vec!(1,2,3,4,5,6,7,8)));
|
|
||||||
/// println!("{:x}", rng.gen::<usize>());
|
|
||||||
/// ```
|
|
||||||
pub struct ReaderRng<R> {
|
pub struct ReaderRng<R> {
|
||||||
reader: R
|
reader: R
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Reader> ReaderRng<R> {
|
impl<R: Read> ReaderRng<R> {
|
||||||
/// Create a new `ReaderRng` from a `Reader`.
|
/// Create a new `ReaderRng` from a `Read`.
|
||||||
pub fn new(r: R) -> ReaderRng<R> {
|
pub fn new(r: R) -> ReaderRng<R> {
|
||||||
ReaderRng {
|
ReaderRng {
|
||||||
reader: r
|
reader: r
|
||||||
@ -44,30 +35,29 @@ impl<R: Reader> ReaderRng<R> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Reader> Rng for ReaderRng<R> {
|
impl<R: Read> Rng for ReaderRng<R> {
|
||||||
fn next_u32(&mut self) -> u32 {
|
fn next_u32(&mut self) -> u32 {
|
||||||
// This is designed for speed: reading a LE integer on a LE
|
// This is designed for speed: reading a LE integer on a LE
|
||||||
// platform just involves blitting the bytes into the memory
|
// platform just involves blitting the bytes into the memory
|
||||||
// of the u32, similarly for BE on BE; avoiding byteswapping.
|
// of the u32, similarly for BE on BE; avoiding byteswapping.
|
||||||
if cfg!(target_endian="little") {
|
let mut bytes = [0; 4];
|
||||||
self.reader.read_le_u32().unwrap()
|
self.fill_bytes(&mut bytes);
|
||||||
} else {
|
unsafe { *(bytes.as_ptr() as *const u32) }
|
||||||
self.reader.read_be_u32().unwrap()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fn next_u64(&mut self) -> u64 {
|
fn next_u64(&mut self) -> u64 {
|
||||||
// see above for explanation.
|
// see above for explanation.
|
||||||
if cfg!(target_endian="little") {
|
let mut bytes = [0; 8];
|
||||||
self.reader.read_le_u64().unwrap()
|
self.fill_bytes(&mut bytes);
|
||||||
} else {
|
unsafe { *(bytes.as_ptr() as *const u64) }
|
||||||
self.reader.read_be_u64().unwrap()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fn fill_bytes(&mut self, v: &mut [u8]) {
|
fn fill_bytes(&mut self, mut v: &mut [u8]) {
|
||||||
if v.len() == 0 { return }
|
while v.len() > 0 {
|
||||||
match self.reader.read_at_least(v.len(), v) {
|
let t = v;
|
||||||
Ok(_) => {}
|
match self.reader.read(t) {
|
||||||
Err(e) => panic!("ReaderRng.fill_bytes error: {:?}", e)
|
Ok(0) => panic!("ReaderRng.fill_bytes: EOF reached"),
|
||||||
|
Ok(n) => v = t.split_at_mut(n).1,
|
||||||
|
Err(e) => panic!("ReaderRng.fill_bytes: {}", e),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,24 +10,11 @@
|
|||||||
|
|
||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
use old_io::{self, IoError, IoResult};
|
|
||||||
use prelude::v1::*;
|
use prelude::v1::*;
|
||||||
use sys::{last_error, retry};
|
|
||||||
use ffi::CString;
|
|
||||||
#[allow(deprecated)] // Int
|
|
||||||
use num::Int;
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
use old_path::BytesContainer;
|
|
||||||
|
|
||||||
use collections;
|
|
||||||
|
|
||||||
#[macro_use] pub mod helper_thread;
|
|
||||||
|
|
||||||
pub mod backtrace;
|
pub mod backtrace;
|
||||||
pub mod condvar;
|
pub mod condvar;
|
||||||
pub mod mutex;
|
pub mod mutex;
|
||||||
pub mod net;
|
|
||||||
pub mod net2;
|
pub mod net2;
|
||||||
pub mod poison;
|
pub mod poison;
|
||||||
pub mod remutex;
|
pub mod remutex;
|
||||||
@ -40,72 +27,6 @@ pub mod wtf8;
|
|||||||
|
|
||||||
// common error constructors
|
// common error constructors
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn eof() -> IoError {
|
|
||||||
IoError {
|
|
||||||
kind: old_io::EndOfFile,
|
|
||||||
desc: "end of file",
|
|
||||||
detail: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn timeout(desc: &'static str) -> IoError {
|
|
||||||
IoError {
|
|
||||||
kind: old_io::TimedOut,
|
|
||||||
desc: desc,
|
|
||||||
detail: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn short_write(n: usize, desc: &'static str) -> IoError {
|
|
||||||
IoError {
|
|
||||||
kind: if n == 0 { old_io::TimedOut } else { old_io::ShortWrite(n) },
|
|
||||||
desc: desc,
|
|
||||||
detail: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn unimpl() -> IoError {
|
|
||||||
IoError {
|
|
||||||
kind: old_io::IoUnavailable,
|
|
||||||
desc: "operations not yet supported",
|
|
||||||
detail: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// unix has nonzero values as errors
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn mkerr_libc<T: Int>(ret: T) -> IoResult<()> {
|
|
||||||
if ret != Int::zero() {
|
|
||||||
Err(last_error())
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn keep_going<F>(data: &[u8], mut f: F) -> i64 where
|
|
||||||
F: FnMut(*const u8, usize) -> i64,
|
|
||||||
{
|
|
||||||
let origamt = data.len();
|
|
||||||
let mut data = data.as_ptr();
|
|
||||||
let mut amt = origamt;
|
|
||||||
while amt > 0 {
|
|
||||||
let ret = retry(|| f(data, amt));
|
|
||||||
if ret == 0 {
|
|
||||||
break
|
|
||||||
} else if ret != -1 {
|
|
||||||
amt -= ret as usize;
|
|
||||||
data = unsafe { data.offset(ret as isize) };
|
|
||||||
} else {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (origamt - amt) as i64;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A trait for viewing representations from std types
|
/// A trait for viewing representations from std types
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub trait AsInner<Inner: ?Sized> {
|
pub trait AsInner<Inner: ?Sized> {
|
||||||
@ -129,15 +50,3 @@ pub trait IntoInner<Inner> {
|
|||||||
pub trait FromInner<Inner> {
|
pub trait FromInner<Inner> {
|
||||||
fn from_inner(inner: Inner) -> Self;
|
fn from_inner(inner: Inner) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub trait ProcessConfig<K: BytesContainer, V: BytesContainer> {
|
|
||||||
fn program(&self) -> &CString;
|
|
||||||
fn args(&self) -> &[CString];
|
|
||||||
fn env(&self) -> Option<&collections::HashMap<K, V>>;
|
|
||||||
fn cwd(&self) -> Option<&CString>;
|
|
||||||
fn uid(&self) -> Option<usize>;
|
|
||||||
fn gid(&self) -> Option<usize>;
|
|
||||||
fn detach(&self) -> bool;
|
|
||||||
}
|
|
||||||
|
@ -15,14 +15,12 @@
|
|||||||
//!
|
//!
|
||||||
//! # Example
|
//! # Example
|
||||||
//!
|
//!
|
||||||
//! ```rust,ignore
|
//! ```no_run
|
||||||
//! #![feature(globs)]
|
//! use std::fs::File;
|
||||||
//!
|
|
||||||
//! use std::old_io::fs::File;
|
|
||||||
//! use std::os::unix::prelude::*;
|
//! use std::os::unix::prelude::*;
|
||||||
//!
|
//!
|
||||||
//! fn main() {
|
//! fn main() {
|
||||||
//! let f = File::create(&Path::new("foo.txt")).unwrap();
|
//! let f = File::create("foo.txt").unwrap();
|
||||||
//! let fd = f.as_raw_fd();
|
//! let fd = f.as_raw_fd();
|
||||||
//!
|
//!
|
||||||
//! // use fd with native unix bindings
|
//! // use fd with native unix bindings
|
||||||
@ -34,7 +32,6 @@
|
|||||||
/// Unix-specific extensions to general I/O primitives
|
/// Unix-specific extensions to general I/O primitives
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
pub mod io {
|
pub mod io {
|
||||||
#[allow(deprecated)] use old_io;
|
|
||||||
use fs;
|
use fs;
|
||||||
use libc;
|
use libc;
|
||||||
use net;
|
use net;
|
||||||
@ -82,14 +79,6 @@ pub mod io {
|
|||||||
unsafe fn from_raw_fd(fd: RawFd) -> Self;
|
unsafe fn from_raw_fd(fd: RawFd) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl AsRawFd for old_io::fs::File {
|
|
||||||
fn as_raw_fd(&self) -> RawFd {
|
|
||||||
self.as_inner().fd()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
impl AsRawFd for fs::File {
|
impl AsRawFd for fs::File {
|
||||||
fn as_raw_fd(&self) -> RawFd {
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
@ -103,70 +92,6 @@ pub mod io {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl AsRawFd for old_io::pipe::PipeStream {
|
|
||||||
fn as_raw_fd(&self) -> RawFd {
|
|
||||||
self.as_inner().fd()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl AsRawFd for old_io::net::pipe::UnixStream {
|
|
||||||
fn as_raw_fd(&self) -> RawFd {
|
|
||||||
self.as_inner().fd()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl AsRawFd for old_io::net::pipe::UnixListener {
|
|
||||||
fn as_raw_fd(&self) -> RawFd {
|
|
||||||
self.as_inner().fd()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl AsRawFd for old_io::net::pipe::UnixAcceptor {
|
|
||||||
fn as_raw_fd(&self) -> RawFd {
|
|
||||||
self.as_inner().fd()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
#[allow(deprecated)]
|
|
||||||
impl AsRawFd for old_io::net::tcp::TcpStream {
|
|
||||||
fn as_raw_fd(&self) -> RawFd {
|
|
||||||
self.as_inner().fd()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
#[allow(deprecated)]
|
|
||||||
impl AsRawFd for old_io::net::tcp::TcpListener {
|
|
||||||
fn as_raw_fd(&self) -> RawFd {
|
|
||||||
self.as_inner().fd()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
#[allow(deprecated)]
|
|
||||||
impl AsRawFd for old_io::net::tcp::TcpAcceptor {
|
|
||||||
fn as_raw_fd(&self) -> RawFd {
|
|
||||||
self.as_inner().fd()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl AsRawFd for old_io::net::udp::UdpSocket {
|
|
||||||
fn as_raw_fd(&self) -> RawFd {
|
|
||||||
self.as_inner().fd()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
impl AsRawFd for net::TcpStream {
|
impl AsRawFd for net::TcpStream {
|
||||||
fn as_raw_fd(&self) -> RawFd { *self.as_inner().socket().as_inner() }
|
fn as_raw_fd(&self) -> RawFd { *self.as_inner().socket().as_inner() }
|
||||||
|
@ -1,409 +0,0 @@
|
|||||||
// Copyright 2013-2014 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.
|
|
||||||
|
|
||||||
//! Blocking posix-based file I/O
|
|
||||||
#![allow(deprecated)]
|
|
||||||
|
|
||||||
#![allow(deprecated)] // this module itself is essentially deprecated
|
|
||||||
|
|
||||||
use prelude::v1::*;
|
|
||||||
|
|
||||||
use ffi::{CString, CStr};
|
|
||||||
use old_io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode};
|
|
||||||
use old_io::{IoResult, FileStat, SeekStyle};
|
|
||||||
use old_io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append};
|
|
||||||
use old_io;
|
|
||||||
use old_path::{Path, GenericPath};
|
|
||||||
use libc::{self, c_int, c_void};
|
|
||||||
use mem;
|
|
||||||
use ptr;
|
|
||||||
use sys::retry;
|
|
||||||
use sys_common::{keep_going, eof, mkerr_libc};
|
|
||||||
|
|
||||||
pub type fd_t = libc::c_int;
|
|
||||||
|
|
||||||
pub struct FileDesc {
|
|
||||||
/// The underlying C file descriptor.
|
|
||||||
fd: fd_t,
|
|
||||||
|
|
||||||
/// Whether to close the file descriptor on drop.
|
|
||||||
close_on_drop: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FileDesc {
|
|
||||||
pub fn new(fd: fd_t, close_on_drop: bool) -> FileDesc {
|
|
||||||
FileDesc { fd: fd, close_on_drop: close_on_drop }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read(&self, buf: &mut [u8]) -> IoResult<usize> {
|
|
||||||
let ret = retry(|| unsafe {
|
|
||||||
libc::read(self.fd(),
|
|
||||||
buf.as_mut_ptr() as *mut libc::c_void,
|
|
||||||
buf.len() as libc::size_t)
|
|
||||||
});
|
|
||||||
if ret == 0 {
|
|
||||||
Err(eof())
|
|
||||||
} else if ret < 0 {
|
|
||||||
Err(super::last_error())
|
|
||||||
} else {
|
|
||||||
Ok(ret as usize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn write(&self, buf: &[u8]) -> IoResult<()> {
|
|
||||||
let ret = keep_going(buf, |buf, len| {
|
|
||||||
unsafe {
|
|
||||||
libc::write(self.fd(), buf as *const libc::c_void,
|
|
||||||
len as libc::size_t) as i64
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if ret < 0 {
|
|
||||||
Err(super::last_error())
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fd(&self) -> fd_t { self.fd }
|
|
||||||
|
|
||||||
pub fn seek(&self, pos: i64, whence: SeekStyle) -> IoResult<u64> {
|
|
||||||
let whence = match whence {
|
|
||||||
SeekSet => libc::SEEK_SET,
|
|
||||||
SeekEnd => libc::SEEK_END,
|
|
||||||
SeekCur => libc::SEEK_CUR,
|
|
||||||
};
|
|
||||||
let n = unsafe { libc::lseek(self.fd(), pos as libc::off_t, whence) };
|
|
||||||
if n < 0 {
|
|
||||||
Err(super::last_error())
|
|
||||||
} else {
|
|
||||||
Ok(n as u64)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tell(&self) -> IoResult<u64> {
|
|
||||||
let n = unsafe { libc::lseek(self.fd(), 0, libc::SEEK_CUR) };
|
|
||||||
if n < 0 {
|
|
||||||
Err(super::last_error())
|
|
||||||
} else {
|
|
||||||
Ok(n as u64)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fsync(&self) -> IoResult<()> {
|
|
||||||
mkerr_libc(retry(|| unsafe { libc::fsync(self.fd()) }))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn datasync(&self) -> IoResult<()> {
|
|
||||||
return mkerr_libc(os_datasync(self.fd()));
|
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
|
||||||
fn os_datasync(fd: c_int) -> c_int {
|
|
||||||
unsafe { libc::fcntl(fd, libc::F_FULLFSYNC) }
|
|
||||||
}
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
fn os_datasync(fd: c_int) -> c_int {
|
|
||||||
retry(|| unsafe { libc::fdatasync(fd) })
|
|
||||||
}
|
|
||||||
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "linux")))]
|
|
||||||
fn os_datasync(fd: c_int) -> c_int {
|
|
||||||
retry(|| unsafe { libc::fsync(fd) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn truncate(&self, offset: i64) -> IoResult<()> {
|
|
||||||
mkerr_libc(retry(|| unsafe {
|
|
||||||
libc::ftruncate(self.fd(), offset as libc::off_t)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fstat(&self) -> IoResult<FileStat> {
|
|
||||||
let mut stat: libc::stat = unsafe { mem::zeroed() };
|
|
||||||
match unsafe { libc::fstat(self.fd(), &mut stat) } {
|
|
||||||
0 => Ok(mkstat(&stat)),
|
|
||||||
_ => Err(super::last_error()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extract the actual filedescriptor without closing it.
|
|
||||||
pub fn unwrap(self) -> fd_t {
|
|
||||||
let fd = self.fd;
|
|
||||||
unsafe { mem::forget(self) };
|
|
||||||
fd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for FileDesc {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
// closing stdio file handles makes no sense, so never do it. Also, note
|
|
||||||
// that errors are ignored when closing a file descriptor. The reason
|
|
||||||
// for this is that if an error occurs we don't actually know if the
|
|
||||||
// file descriptor was closed or not, and if we retried (for something
|
|
||||||
// like EINTR), we might close another valid file descriptor (opened
|
|
||||||
// after we closed ours.
|
|
||||||
if self.close_on_drop && self.fd > libc::STDERR_FILENO {
|
|
||||||
let n = unsafe { libc::close(self.fd) };
|
|
||||||
if n != 0 {
|
|
||||||
println!("error {} when closing file descriptor {}", n, self.fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cstr(path: &Path) -> IoResult<CString> {
|
|
||||||
Ok(try!(CString::new(path.as_vec())))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn open(path: &Path, fm: FileMode, fa: FileAccess) -> IoResult<FileDesc> {
|
|
||||||
let flags = match fm {
|
|
||||||
Open => 0,
|
|
||||||
Append => libc::O_APPEND,
|
|
||||||
Truncate => libc::O_TRUNC,
|
|
||||||
};
|
|
||||||
// Opening with a write permission must silently create the file.
|
|
||||||
let (flags, mode) = match fa {
|
|
||||||
Read => (flags | libc::O_RDONLY, 0),
|
|
||||||
Write => (flags | libc::O_WRONLY | libc::O_CREAT,
|
|
||||||
libc::S_IRUSR | libc::S_IWUSR),
|
|
||||||
ReadWrite => (flags | libc::O_RDWR | libc::O_CREAT,
|
|
||||||
libc::S_IRUSR | libc::S_IWUSR),
|
|
||||||
};
|
|
||||||
|
|
||||||
let path = try!(cstr(path));
|
|
||||||
match retry(|| unsafe { libc::open(path.as_ptr(), flags, mode) }) {
|
|
||||||
-1 => Err(super::last_error()),
|
|
||||||
fd => Ok(FileDesc::new(fd, true)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mkdir(p: &Path, mode: usize) -> IoResult<()> {
|
|
||||||
let p = try!(cstr(p));
|
|
||||||
mkerr_libc(unsafe { libc::mkdir(p.as_ptr(), mode as libc::mode_t) })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn readdir(p: &Path) -> IoResult<Vec<Path>> {
|
|
||||||
use libc::{dirent_t};
|
|
||||||
use libc::{opendir, readdir_r, closedir};
|
|
||||||
|
|
||||||
fn prune(root: &CString, dirs: Vec<Path>) -> Vec<Path> {
|
|
||||||
let root = Path::new(root);
|
|
||||||
|
|
||||||
dirs.into_iter().filter(|path| {
|
|
||||||
path.as_vec() != b"." && path.as_vec() != b".."
|
|
||||||
}).map(|path| root.join(path)).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
extern {
|
|
||||||
fn rust_dirent_t_size() -> libc::c_int;
|
|
||||||
fn rust_list_dir_val(ptr: *mut dirent_t) -> *const libc::c_char;
|
|
||||||
}
|
|
||||||
|
|
||||||
let size = unsafe { rust_dirent_t_size() };
|
|
||||||
let mut buf = Vec::<u8>::with_capacity(size as usize);
|
|
||||||
let ptr = buf.as_mut_ptr() as *mut dirent_t;
|
|
||||||
|
|
||||||
let p = try!(CString::new(p.as_vec()));
|
|
||||||
let dir_ptr = unsafe {opendir(p.as_ptr())};
|
|
||||||
|
|
||||||
if dir_ptr as usize != 0 {
|
|
||||||
let mut paths = vec!();
|
|
||||||
let mut entry_ptr = ptr::null_mut();
|
|
||||||
while unsafe { readdir_r(dir_ptr, ptr, &mut entry_ptr) == 0 } {
|
|
||||||
if entry_ptr.is_null() { break }
|
|
||||||
paths.push(unsafe {
|
|
||||||
Path::new(CStr::from_ptr(rust_list_dir_val(entry_ptr)).to_bytes())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
assert_eq!(unsafe { closedir(dir_ptr) }, 0);
|
|
||||||
Ok(prune(&p, paths))
|
|
||||||
} else {
|
|
||||||
Err(super::last_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unlink(p: &Path) -> IoResult<()> {
|
|
||||||
let p = try!(cstr(p));
|
|
||||||
mkerr_libc(unsafe { libc::unlink(p.as_ptr()) })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rename(old: &Path, new: &Path) -> IoResult<()> {
|
|
||||||
let old = try!(cstr(old));
|
|
||||||
let new = try!(cstr(new));
|
|
||||||
mkerr_libc(unsafe {
|
|
||||||
libc::rename(old.as_ptr(), new.as_ptr())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn chmod(p: &Path, mode: usize) -> IoResult<()> {
|
|
||||||
let p = try!(cstr(p));
|
|
||||||
mkerr_libc(retry(|| unsafe {
|
|
||||||
libc::chmod(p.as_ptr(), mode as libc::mode_t)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rmdir(p: &Path) -> IoResult<()> {
|
|
||||||
let p = try!(cstr(p));
|
|
||||||
mkerr_libc(unsafe { libc::rmdir(p.as_ptr()) })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn chown(p: &Path, uid: isize, gid: isize) -> IoResult<()> {
|
|
||||||
let p = try!(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 = try!(cstr(p));
|
|
||||||
let p = c_path.as_ptr();
|
|
||||||
let mut len = unsafe { libc::pathconf(p as *mut _, libc::_PC_NAME_MAX) };
|
|
||||||
if len == -1 {
|
|
||||||
len = 1024; // FIXME: read PATH_MAX from C ffi?
|
|
||||||
}
|
|
||||||
let mut buf: Vec<u8> = Vec::with_capacity(len as usize);
|
|
||||||
match unsafe {
|
|
||||||
libc::readlink(p, buf.as_ptr() as *mut libc::c_char,
|
|
||||||
len as libc::size_t) as libc::c_int
|
|
||||||
} {
|
|
||||||
-1 => Err(super::last_error()),
|
|
||||||
n => {
|
|
||||||
assert!(n > 0);
|
|
||||||
unsafe { buf.set_len(n as usize); }
|
|
||||||
Ok(Path::new(buf))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
|
|
||||||
let src = try!(cstr(src));
|
|
||||||
let dst = try!(cstr(dst));
|
|
||||||
mkerr_libc(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn link(src: &Path, dst: &Path) -> IoResult<()> {
|
|
||||||
let src = try!(cstr(src));
|
|
||||||
let dst = try!(cstr(dst));
|
|
||||||
mkerr_libc(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mkstat(stat: &libc::stat) -> FileStat {
|
|
||||||
// FileStat times are in milliseconds
|
|
||||||
fn mktime(secs: u64, nsecs: u64) -> u64 { secs * 1000 + nsecs / 1000000 }
|
|
||||||
|
|
||||||
fn ctime(stat: &libc::stat) -> u64 {
|
|
||||||
mktime(stat.st_ctime as u64, stat.st_ctime_nsec as u64)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn atime(stat: &libc::stat) -> u64 {
|
|
||||||
mktime(stat.st_atime as u64, stat.st_atime_nsec as u64)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mtime(stat: &libc::stat) -> u64 {
|
|
||||||
mktime(stat.st_mtime as u64, stat.st_mtime_nsec as u64)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
|
||||||
fn flags(stat: &libc::stat) -> u64 { stat.st_flags as u64 }
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
||||||
fn flags(_stat: &libc::stat) -> u64 { 0 }
|
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
|
||||||
fn gen(stat: &libc::stat) -> u64 { stat.st_gen as u64 }
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
||||||
fn gen(_stat: &libc::stat) -> u64 { 0 }
|
|
||||||
|
|
||||||
FileStat {
|
|
||||||
size: stat.st_size as u64,
|
|
||||||
kind: match (stat.st_mode as libc::mode_t) & libc::S_IFMT {
|
|
||||||
libc::S_IFREG => old_io::FileType::RegularFile,
|
|
||||||
libc::S_IFDIR => old_io::FileType::Directory,
|
|
||||||
libc::S_IFIFO => old_io::FileType::NamedPipe,
|
|
||||||
libc::S_IFBLK => old_io::FileType::BlockSpecial,
|
|
||||||
libc::S_IFLNK => old_io::FileType::Symlink,
|
|
||||||
_ => old_io::FileType::Unknown,
|
|
||||||
},
|
|
||||||
perm: FilePermission::from_bits_truncate(stat.st_mode as u32),
|
|
||||||
created: ctime(stat),
|
|
||||||
modified: mtime(stat),
|
|
||||||
accessed: atime(stat),
|
|
||||||
unstable: UnstableFileStat {
|
|
||||||
device: stat.st_dev as u64,
|
|
||||||
inode: stat.st_ino as u64,
|
|
||||||
rdev: stat.st_rdev as u64,
|
|
||||||
nlink: stat.st_nlink as u64,
|
|
||||||
uid: stat.st_uid as u64,
|
|
||||||
gid: stat.st_gid as u64,
|
|
||||||
blksize: stat.st_blksize as u64,
|
|
||||||
blocks: stat.st_blocks as u64,
|
|
||||||
flags: flags(stat),
|
|
||||||
gen: gen(stat),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn stat(p: &Path) -> IoResult<FileStat> {
|
|
||||||
let p = try!(cstr(p));
|
|
||||||
let mut stat: libc::stat = unsafe { mem::zeroed() };
|
|
||||||
match unsafe { libc::stat(p.as_ptr(), &mut stat) } {
|
|
||||||
0 => Ok(mkstat(&stat)),
|
|
||||||
_ => Err(super::last_error()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lstat(p: &Path) -> IoResult<FileStat> {
|
|
||||||
let p = try!(cstr(p));
|
|
||||||
let mut stat: libc::stat = unsafe { mem::zeroed() };
|
|
||||||
match unsafe { libc::lstat(p.as_ptr(), &mut stat) } {
|
|
||||||
0 => Ok(mkstat(&stat)),
|
|
||||||
_ => Err(super::last_error()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn utime(p: &Path, atime: u64, mtime: u64) -> IoResult<()> {
|
|
||||||
let p = try!(cstr(p));
|
|
||||||
let buf = libc::utimbuf {
|
|
||||||
actime: (atime / 1000) as libc::time_t,
|
|
||||||
modtime: (mtime / 1000) as libc::time_t,
|
|
||||||
};
|
|
||||||
mkerr_libc(unsafe { libc::utime(p.as_ptr(), &buf) })
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::FileDesc;
|
|
||||||
use libc;
|
|
||||||
use os;
|
|
||||||
use prelude::v1::*;
|
|
||||||
|
|
||||||
#[cfg_attr(any(target_os = "freebsd",
|
|
||||||
target_os = "openbsd",
|
|
||||||
target_os = "bitrig"),
|
|
||||||
ignore)]
|
|
||||||
// under some system, pipe(2) will return a bidrectionnal pipe
|
|
||||||
#[test]
|
|
||||||
fn test_file_desc() {
|
|
||||||
// Run this test with some pipes so we don't have to mess around with
|
|
||||||
// opening or closing files.
|
|
||||||
let (mut reader, mut writer) = unsafe { ::sys::os::pipe().unwrap() };
|
|
||||||
|
|
||||||
writer.write(b"test").unwrap();
|
|
||||||
let mut buf = [0; 4];
|
|
||||||
match reader.read(&mut buf) {
|
|
||||||
Ok(4) => {
|
|
||||||
assert_eq!(buf[0], 't' as u8);
|
|
||||||
assert_eq!(buf[1], 'e' as u8);
|
|
||||||
assert_eq!(buf[2], 's' as u8);
|
|
||||||
assert_eq!(buf[3], 't' as u8);
|
|
||||||
}
|
|
||||||
r => panic!("invalid read: {:?}", r),
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(writer.read(&mut buf).is_err());
|
|
||||||
assert!(reader.write(&buf).is_err());
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,124 +13,30 @@
|
|||||||
|
|
||||||
use prelude::v1::*;
|
use prelude::v1::*;
|
||||||
|
|
||||||
use ffi::CStr;
|
|
||||||
use io::{self, ErrorKind};
|
use io::{self, ErrorKind};
|
||||||
use libc;
|
use libc;
|
||||||
use num::{Int, SignedInt};
|
use num::{Int, SignedInt};
|
||||||
use old_io::{self, IoError};
|
|
||||||
use str;
|
|
||||||
use sys_common::mkerr_libc;
|
|
||||||
|
|
||||||
pub mod backtrace;
|
pub mod backtrace;
|
||||||
pub mod c;
|
pub mod c;
|
||||||
pub mod condvar;
|
pub mod condvar;
|
||||||
pub mod ext;
|
pub mod ext;
|
||||||
pub mod fd;
|
pub mod fd;
|
||||||
pub mod fs; // support for std::old_io
|
pub mod fs2;
|
||||||
pub mod fs2; // support for std::fs
|
|
||||||
pub mod helper_signal;
|
|
||||||
pub mod mutex;
|
pub mod mutex;
|
||||||
pub mod net;
|
pub mod net;
|
||||||
pub mod os;
|
pub mod os;
|
||||||
pub mod os_str;
|
pub mod os_str;
|
||||||
pub mod pipe;
|
|
||||||
pub mod pipe2;
|
pub mod pipe2;
|
||||||
pub mod process;
|
|
||||||
pub mod process2;
|
pub mod process2;
|
||||||
pub mod rwlock;
|
pub mod rwlock;
|
||||||
pub mod stack_overflow;
|
pub mod stack_overflow;
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
pub mod tcp;
|
|
||||||
pub mod thread;
|
pub mod thread;
|
||||||
pub mod thread_local;
|
pub mod thread_local;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
pub mod timer;
|
|
||||||
pub mod tty;
|
|
||||||
pub mod udp;
|
|
||||||
pub mod stdio;
|
pub mod stdio;
|
||||||
|
|
||||||
pub mod addrinfo {
|
|
||||||
pub use sys_common::net::get_host_addresses;
|
|
||||||
pub use sys_common::net::get_address_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: move these to c module
|
|
||||||
pub type sock_t = self::fs::fd_t;
|
|
||||||
pub type wrlen = libc::size_t;
|
|
||||||
pub type msglen_t = libc::size_t;
|
|
||||||
pub unsafe fn close_sock(sock: sock_t) { let _ = libc::close(sock); }
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn last_error() -> IoError {
|
|
||||||
decode_error_detailed(os::errno() as i32)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn last_net_error() -> IoError {
|
|
||||||
last_error()
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "system" {
|
|
||||||
fn gai_strerror(errcode: libc::c_int) -> *const libc::c_char;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn last_gai_error(s: libc::c_int) -> IoError {
|
|
||||||
|
|
||||||
let mut err = decode_error(s);
|
|
||||||
err.detail = Some(unsafe {
|
|
||||||
let data = CStr::from_ptr(gai_strerror(s));
|
|
||||||
str::from_utf8(data.to_bytes()).unwrap().to_string()
|
|
||||||
});
|
|
||||||
err
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert an `errno` value into a high-level error variant and description.
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn decode_error(errno: i32) -> IoError {
|
|
||||||
// FIXME: this should probably be a bit more descriptive...
|
|
||||||
let (kind, desc) = match errno {
|
|
||||||
libc::EOF => (old_io::EndOfFile, "end of file"),
|
|
||||||
libc::ECONNREFUSED => (old_io::ConnectionRefused, "connection refused"),
|
|
||||||
libc::ECONNRESET => (old_io::ConnectionReset, "connection reset"),
|
|
||||||
libc::EPERM | libc::EACCES =>
|
|
||||||
(old_io::PermissionDenied, "permission denied"),
|
|
||||||
libc::EPIPE => (old_io::BrokenPipe, "broken pipe"),
|
|
||||||
libc::ENOTCONN => (old_io::NotConnected, "not connected"),
|
|
||||||
libc::ECONNABORTED => (old_io::ConnectionAborted, "connection aborted"),
|
|
||||||
libc::EADDRNOTAVAIL => (old_io::ConnectionRefused, "address not available"),
|
|
||||||
libc::EADDRINUSE => (old_io::ConnectionRefused, "address in use"),
|
|
||||||
libc::ENOENT => (old_io::FileNotFound, "no such file or directory"),
|
|
||||||
libc::EISDIR => (old_io::InvalidInput, "illegal operation on a directory"),
|
|
||||||
libc::ENOSYS => (old_io::IoUnavailable, "function not implemented"),
|
|
||||||
libc::EINVAL => (old_io::InvalidInput, "invalid argument"),
|
|
||||||
libc::ENOTTY =>
|
|
||||||
(old_io::MismatchedFileTypeForOperation,
|
|
||||||
"file descriptor is not a TTY"),
|
|
||||||
libc::ETIMEDOUT => (old_io::TimedOut, "operation timed out"),
|
|
||||||
libc::ECANCELED => (old_io::TimedOut, "operation aborted"),
|
|
||||||
libc::consts::os::posix88::EEXIST =>
|
|
||||||
(old_io::PathAlreadyExists, "path already exists"),
|
|
||||||
|
|
||||||
// These two constants can have the same value on some systems,
|
|
||||||
// but different values on others, so we can't use a match
|
|
||||||
// clause
|
|
||||||
x if x == libc::EAGAIN || x == libc::EWOULDBLOCK =>
|
|
||||||
(old_io::ResourceUnavailable, "resource temporarily unavailable"),
|
|
||||||
|
|
||||||
_ => (old_io::OtherIoError, "unknown error")
|
|
||||||
};
|
|
||||||
IoError { kind: kind, desc: desc, detail: None }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn decode_error_detailed(errno: i32) -> IoError {
|
|
||||||
let mut err = decode_error(errno);
|
|
||||||
err.detail = Some(os::error_string(errno));
|
|
||||||
err
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn decode_error_kind(errno: i32) -> ErrorKind {
|
pub fn decode_error_kind(errno: i32) -> ErrorKind {
|
||||||
match errno as libc::c_int {
|
match errno as libc::c_int {
|
||||||
libc::ECONNREFUSED => ErrorKind::ConnectionRefused,
|
libc::ECONNREFUSED => ErrorKind::ConnectionRefused,
|
||||||
@ -199,18 +105,3 @@ pub fn ms_to_timeval(ms: u64) -> libc::timeval {
|
|||||||
tv_usec: ((ms % 1000) * 1000) as libc::suseconds_t,
|
tv_usec: ((ms % 1000) * 1000) as libc::suseconds_t,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn wouldblock() -> bool {
|
|
||||||
let err = os::errno();
|
|
||||||
err == libc::EWOULDBLOCK as i32 || err == libc::EAGAIN as i32
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn set_nonblocking(fd: sock_t, nb: bool) {
|
|
||||||
let set = nb as libc::c_int;
|
|
||||||
mkerr_libc(retry(|| unsafe { c::ioctl(fd, c::FIONBIO, &set) })).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// nothing needed on unix platforms
|
|
||||||
pub fn init_net() {}
|
|
||||||
|
@ -22,14 +22,12 @@ use io;
|
|||||||
use iter;
|
use iter;
|
||||||
use libc::{self, c_int, c_char, c_void};
|
use libc::{self, c_int, c_char, c_void};
|
||||||
use mem;
|
use mem;
|
||||||
#[allow(deprecated)] use old_io::{IoError, IoResult};
|
|
||||||
use ptr;
|
use ptr;
|
||||||
use path::{self, PathBuf};
|
use path::{self, PathBuf};
|
||||||
use slice;
|
use slice;
|
||||||
use str;
|
use str;
|
||||||
use sys::c;
|
use sys::c;
|
||||||
use sys::fd;
|
use sys::fd;
|
||||||
use sys::fs::FileDesc;
|
|
||||||
use vec;
|
use vec;
|
||||||
|
|
||||||
const BUF_BYTES: usize = 2048;
|
const BUF_BYTES: usize = 2048;
|
||||||
@ -448,16 +446,6 @@ pub fn unsetenv(n: &OsStr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> {
|
|
||||||
let mut fds = [0; 2];
|
|
||||||
if libc::pipe(fds.as_mut_ptr()) == 0 {
|
|
||||||
Ok((FileDesc::new(fds[0], true), FileDesc::new(fds[1], true)))
|
|
||||||
} else {
|
|
||||||
Err(IoError::last_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn page_size() -> usize {
|
pub fn page_size() -> usize {
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::sysconf(libc::_SC_PAGESIZE) as usize
|
libc::sysconf(libc::_SC_PAGESIZE) as usize
|
||||||
|
@ -1,328 +0,0 @@
|
|||||||
// Copyright 2014 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.
|
|
||||||
|
|
||||||
#![allow(deprecated)]
|
|
||||||
|
|
||||||
use prelude::v1::*;
|
|
||||||
|
|
||||||
use ffi::CString;
|
|
||||||
use libc;
|
|
||||||
use mem;
|
|
||||||
use sync::{Arc, Mutex};
|
|
||||||
use sync::atomic::{AtomicBool, Ordering};
|
|
||||||
use old_io::{self, IoResult, IoError};
|
|
||||||
|
|
||||||
use sys::{self, timer, retry, c, set_nonblocking, wouldblock};
|
|
||||||
use sys::fs::{fd_t, FileDesc};
|
|
||||||
use sys_common::net::*;
|
|
||||||
use sys_common::net::SocketStatus::*;
|
|
||||||
use sys_common::{eof, mkerr_libc};
|
|
||||||
|
|
||||||
fn unix_socket(ty: libc::c_int) -> IoResult<fd_t> {
|
|
||||||
match unsafe { libc::socket(libc::AF_UNIX, ty, 0) } {
|
|
||||||
-1 => Err(super::last_error()),
|
|
||||||
fd => Ok(fd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn addr_to_sockaddr_un(addr: &CString,
|
|
||||||
storage: &mut libc::sockaddr_storage)
|
|
||||||
-> IoResult<libc::socklen_t> {
|
|
||||||
// the sun_path length is limited to SUN_LEN (with null)
|
|
||||||
assert!(mem::size_of::<libc::sockaddr_storage>() >=
|
|
||||||
mem::size_of::<libc::sockaddr_un>());
|
|
||||||
let s = unsafe { &mut *(storage as *mut _ as *mut libc::sockaddr_un) };
|
|
||||||
|
|
||||||
let len = addr.as_bytes().len();
|
|
||||||
if len > s.sun_path.len() - 1 {
|
|
||||||
return Err(IoError {
|
|
||||||
kind: old_io::InvalidInput,
|
|
||||||
desc: "invalid argument: path must be smaller than SUN_LEN",
|
|
||||||
detail: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
s.sun_family = libc::AF_UNIX as libc::sa_family_t;
|
|
||||||
for (slot, value) in s.sun_path.iter_mut().zip(addr.as_bytes().iter()) {
|
|
||||||
*slot = *value as libc::c_char;
|
|
||||||
}
|
|
||||||
|
|
||||||
// count the null terminator
|
|
||||||
let len = mem::size_of::<libc::sa_family_t>() + len + 1;
|
|
||||||
return Ok(len as libc::socklen_t);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Inner {
|
|
||||||
fd: fd_t,
|
|
||||||
|
|
||||||
// Unused on Linux, where this lock is not necessary.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
lock: Mutex<()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Inner {
|
|
||||||
fn new(fd: fd_t) -> Inner {
|
|
||||||
Inner { fd: fd, lock: Mutex::new(()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Inner {
|
|
||||||
fn drop(&mut self) { unsafe { let _ = libc::close(self.fd); } }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn connect(addr: &CString, ty: libc::c_int,
|
|
||||||
timeout: Option<u64>) -> IoResult<Inner> {
|
|
||||||
let mut storage = unsafe { mem::zeroed() };
|
|
||||||
let len = try!(addr_to_sockaddr_un(addr, &mut storage));
|
|
||||||
let inner = Inner::new(try!(unix_socket(ty)));
|
|
||||||
let addrp = &storage as *const _ as *const libc::sockaddr;
|
|
||||||
|
|
||||||
match timeout {
|
|
||||||
None => {
|
|
||||||
match retry(|| unsafe { libc::connect(inner.fd, addrp, len) }) {
|
|
||||||
-1 => Err(super::last_error()),
|
|
||||||
_ => Ok(inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(timeout_ms) => {
|
|
||||||
try!(connect_timeout(inner.fd, addrp, len, timeout_ms));
|
|
||||||
Ok(inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bind(addr: &CString, ty: libc::c_int) -> IoResult<Inner> {
|
|
||||||
let mut storage = unsafe { mem::zeroed() };
|
|
||||||
let len = try!(addr_to_sockaddr_un(addr, &mut storage));
|
|
||||||
let inner = Inner::new(try!(unix_socket(ty)));
|
|
||||||
let addrp = &storage as *const _ as *const libc::sockaddr;
|
|
||||||
match unsafe {
|
|
||||||
libc::bind(inner.fd, addrp, len)
|
|
||||||
} {
|
|
||||||
-1 => Err(super::last_error()),
|
|
||||||
_ => Ok(inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Unix Streams
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
pub struct UnixStream {
|
|
||||||
inner: Arc<Inner>,
|
|
||||||
read_deadline: u64,
|
|
||||||
write_deadline: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UnixStream {
|
|
||||||
pub fn connect(addr: &CString,
|
|
||||||
timeout: Option<u64>) -> IoResult<UnixStream> {
|
|
||||||
connect(addr, libc::SOCK_STREAM, timeout).map(|inner| {
|
|
||||||
UnixStream::new(Arc::new(inner))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new(inner: Arc<Inner>) -> UnixStream {
|
|
||||||
UnixStream {
|
|
||||||
inner: inner,
|
|
||||||
read_deadline: 0,
|
|
||||||
write_deadline: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fd(&self) -> fd_t { self.inner.fd }
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
fn lock_nonblocking(&self) {}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "linux"))]
|
|
||||||
fn lock_nonblocking<'a>(&'a self) -> Guard<'a> {
|
|
||||||
let ret = Guard {
|
|
||||||
fd: self.fd(),
|
|
||||||
guard: self.inner.lock.lock().unwrap(),
|
|
||||||
};
|
|
||||||
set_nonblocking(self.fd(), true);
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
|
||||||
let fd = self.fd();
|
|
||||||
let dolock = || self.lock_nonblocking();
|
|
||||||
let doread = |nb| unsafe {
|
|
||||||
let flags = if nb {c::MSG_DONTWAIT} else {0};
|
|
||||||
libc::recv(fd,
|
|
||||||
buf.as_mut_ptr() as *mut libc::c_void,
|
|
||||||
buf.len() as libc::size_t,
|
|
||||||
flags) as libc::c_int
|
|
||||||
};
|
|
||||||
read(fd, self.read_deadline, dolock, doread)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
|
||||||
let fd = self.fd();
|
|
||||||
let dolock = || self.lock_nonblocking();
|
|
||||||
let dowrite = |nb: bool, buf: *const u8, len: usize| unsafe {
|
|
||||||
let flags = if nb {c::MSG_DONTWAIT} else {0};
|
|
||||||
libc::send(fd,
|
|
||||||
buf as *const _,
|
|
||||||
len as libc::size_t,
|
|
||||||
flags) as i64
|
|
||||||
};
|
|
||||||
match write(fd, self.write_deadline, buf, true, dolock, dowrite) {
|
|
||||||
Ok(_) => Ok(()),
|
|
||||||
Err(e) => Err(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn close_write(&mut self) -> IoResult<()> {
|
|
||||||
mkerr_libc(unsafe { libc::shutdown(self.fd(), libc::SHUT_WR) })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn close_read(&mut self) -> IoResult<()> {
|
|
||||||
mkerr_libc(unsafe { libc::shutdown(self.fd(), libc::SHUT_RD) })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_timeout(&mut self, timeout: Option<u64>) {
|
|
||||||
let deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
|
|
||||||
self.read_deadline = deadline;
|
|
||||||
self.write_deadline = deadline;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_read_timeout(&mut self, timeout: Option<u64>) {
|
|
||||||
self.read_deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_write_timeout(&mut self, timeout: Option<u64>) {
|
|
||||||
self.write_deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for UnixStream {
|
|
||||||
fn clone(&self) -> UnixStream {
|
|
||||||
UnixStream::new(self.inner.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Unix Listener
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
pub struct UnixListener {
|
|
||||||
inner: Inner,
|
|
||||||
path: CString,
|
|
||||||
}
|
|
||||||
|
|
||||||
// we currently own the CString, so these impls should be safe
|
|
||||||
unsafe impl Send for UnixListener {}
|
|
||||||
unsafe impl Sync for UnixListener {}
|
|
||||||
|
|
||||||
impl UnixListener {
|
|
||||||
pub fn bind(addr: &CString) -> IoResult<UnixListener> {
|
|
||||||
bind(addr, libc::SOCK_STREAM).map(|fd| {
|
|
||||||
UnixListener { inner: fd, path: addr.clone() }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fd(&self) -> fd_t { self.inner.fd }
|
|
||||||
|
|
||||||
pub fn listen(self) -> IoResult<UnixAcceptor> {
|
|
||||||
match unsafe { libc::listen(self.fd(), 128) } {
|
|
||||||
-1 => Err(super::last_error()),
|
|
||||||
|
|
||||||
_ => {
|
|
||||||
let (reader, writer) = try!(unsafe { sys::os::pipe() });
|
|
||||||
set_nonblocking(reader.fd(), true);
|
|
||||||
set_nonblocking(writer.fd(), true);
|
|
||||||
set_nonblocking(self.fd(), true);
|
|
||||||
Ok(UnixAcceptor {
|
|
||||||
inner: Arc::new(AcceptorInner {
|
|
||||||
listener: self,
|
|
||||||
reader: reader,
|
|
||||||
writer: writer,
|
|
||||||
closed: AtomicBool::new(false),
|
|
||||||
}),
|
|
||||||
deadline: 0,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UnixAcceptor {
|
|
||||||
inner: Arc<AcceptorInner>,
|
|
||||||
deadline: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AcceptorInner {
|
|
||||||
listener: UnixListener,
|
|
||||||
reader: FileDesc,
|
|
||||||
writer: FileDesc,
|
|
||||||
closed: AtomicBool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UnixAcceptor {
|
|
||||||
pub fn fd(&self) -> fd_t { self.inner.listener.fd() }
|
|
||||||
|
|
||||||
pub fn accept(&mut self) -> IoResult<UnixStream> {
|
|
||||||
let deadline = if self.deadline == 0 {None} else {Some(self.deadline)};
|
|
||||||
|
|
||||||
while !self.inner.closed.load(Ordering::SeqCst) {
|
|
||||||
unsafe {
|
|
||||||
let mut storage: libc::sockaddr_storage = mem::zeroed();
|
|
||||||
let storagep = &mut storage as *mut libc::sockaddr_storage;
|
|
||||||
let size = mem::size_of::<libc::sockaddr_storage>();
|
|
||||||
let mut size = size as libc::socklen_t;
|
|
||||||
match retry(|| {
|
|
||||||
libc::accept(self.fd(),
|
|
||||||
storagep as *mut libc::sockaddr,
|
|
||||||
&mut size as *mut libc::socklen_t) as libc::c_int
|
|
||||||
}) {
|
|
||||||
-1 if wouldblock() => {}
|
|
||||||
-1 => return Err(super::last_error()),
|
|
||||||
fd => return Ok(UnixStream::new(Arc::new(Inner::new(fd)))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try!(await(&[self.fd(), self.inner.reader.fd()],
|
|
||||||
deadline, Readable));
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(eof())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_timeout(&mut self, timeout: Option<u64>) {
|
|
||||||
self.deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn close_accept(&mut self) -> IoResult<()> {
|
|
||||||
self.inner.closed.store(true, Ordering::SeqCst);
|
|
||||||
let fd = FileDesc::new(self.inner.writer.fd(), false);
|
|
||||||
match fd.write(&[0]) {
|
|
||||||
Ok(..) => Ok(()),
|
|
||||||
Err(..) if wouldblock() => Ok(()),
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for UnixAcceptor {
|
|
||||||
fn clone(&self) -> UnixAcceptor {
|
|
||||||
UnixAcceptor { inner: self.inner.clone(), deadline: 0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for UnixListener {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
// Unlink the path to the socket to ensure that it doesn't linger. We're
|
|
||||||
// careful to unlink the path before we close the file descriptor to
|
|
||||||
// prevent races where we unlink someone else's path.
|
|
||||||
unsafe {
|
|
||||||
let _ = libc::unlink(self.path.as_ptr());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,627 +0,0 @@
|
|||||||
// Copyright 2014-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.
|
|
||||||
|
|
||||||
#![allow(deprecated)] // this module itself is essentially deprecated
|
|
||||||
|
|
||||||
use prelude::v1::*;
|
|
||||||
use self::Req::*;
|
|
||||||
|
|
||||||
use collections::HashMap;
|
|
||||||
use ffi::CString;
|
|
||||||
use hash::Hash;
|
|
||||||
use old_io::process::{ProcessExit, ExitStatus, ExitSignal};
|
|
||||||
use old_io::{IoResult, EndOfFile};
|
|
||||||
use libc::{self, pid_t, c_void, c_int};
|
|
||||||
use io;
|
|
||||||
use mem;
|
|
||||||
use sys::os;
|
|
||||||
use old_path::BytesContainer;
|
|
||||||
use ptr;
|
|
||||||
use sync::mpsc::{channel, Sender, Receiver};
|
|
||||||
use sys::fs::FileDesc;
|
|
||||||
use sys::{self, retry, c, wouldblock, set_nonblocking, ms_to_timeval};
|
|
||||||
use sys_common::helper_thread::Helper;
|
|
||||||
use sys_common::{AsInner, mkerr_libc, timeout};
|
|
||||||
|
|
||||||
pub use sys_common::ProcessConfig;
|
|
||||||
|
|
||||||
helper_init! { static HELPER: Helper<Req> }
|
|
||||||
|
|
||||||
/// The unique id of the process (this should never be negative).
|
|
||||||
pub struct Process {
|
|
||||||
pub pid: pid_t
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Req {
|
|
||||||
NewChild(libc::pid_t, Sender<ProcessExit>, u64),
|
|
||||||
}
|
|
||||||
|
|
||||||
const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";
|
|
||||||
|
|
||||||
impl Process {
|
|
||||||
pub fn id(&self) -> pid_t {
|
|
||||||
self.pid
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn kill(&self, signal: isize) -> IoResult<()> {
|
|
||||||
Process::killpid(self.pid, signal)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn killpid(pid: pid_t, signal: isize) -> IoResult<()> {
|
|
||||||
let r = libc::funcs::posix88::signal::kill(pid, signal as c_int);
|
|
||||||
mkerr_libc(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn spawn<K, V, C, P>(cfg: &C, in_fd: Option<P>,
|
|
||||||
out_fd: Option<P>, err_fd: Option<P>)
|
|
||||||
-> IoResult<Process>
|
|
||||||
where C: ProcessConfig<K, V>, P: AsInner<FileDesc>,
|
|
||||||
K: BytesContainer + Eq + Hash, V: BytesContainer
|
|
||||||
{
|
|
||||||
use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp};
|
|
||||||
|
|
||||||
mod rustrt {
|
|
||||||
extern {
|
|
||||||
pub fn rust_unset_sigprocmask();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn set_cloexec(fd: c_int) {
|
|
||||||
let ret = c::ioctl(fd, c::FIOCLEX);
|
|
||||||
assert_eq!(ret, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "android", target_arch = "aarch64"))]
|
|
||||||
unsafe fn getdtablesize() -> c_int {
|
|
||||||
libc::sysconf(libc::consts::os::sysconf::_SC_OPEN_MAX) as c_int
|
|
||||||
}
|
|
||||||
#[cfg(not(all(target_os = "android", target_arch = "aarch64")))]
|
|
||||||
unsafe fn getdtablesize() -> c_int {
|
|
||||||
libc::funcs::bsd44::getdtablesize()
|
|
||||||
}
|
|
||||||
|
|
||||||
let dirp = cfg.cwd().map(|c| c.as_ptr()).unwrap_or(ptr::null());
|
|
||||||
|
|
||||||
// temporary until unboxed closures land
|
|
||||||
let cfg = unsafe {
|
|
||||||
mem::transmute::<&ProcessConfig<K,V>,&'static ProcessConfig<K,V>>(cfg)
|
|
||||||
};
|
|
||||||
|
|
||||||
with_envp(cfg.env(), move|envp: *const c_void| {
|
|
||||||
with_argv(cfg.program(), cfg.args(), move|argv: *const *const libc::c_char| unsafe {
|
|
||||||
let (input, mut output) = try!(sys::os::pipe());
|
|
||||||
|
|
||||||
// We may use this in the child, so perform allocations before the
|
|
||||||
// fork
|
|
||||||
let devnull = b"/dev/null\0";
|
|
||||||
|
|
||||||
set_cloexec(output.fd());
|
|
||||||
|
|
||||||
let pid = fork();
|
|
||||||
if pid < 0 {
|
|
||||||
return Err(super::last_error())
|
|
||||||
} else if pid > 0 {
|
|
||||||
#[inline]
|
|
||||||
fn combine(arr: &[u8]) -> i32 {
|
|
||||||
let a = arr[0] as u32;
|
|
||||||
let b = arr[1] as u32;
|
|
||||||
let c = arr[2] as u32;
|
|
||||||
let d = arr[3] as u32;
|
|
||||||
|
|
||||||
((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32
|
|
||||||
}
|
|
||||||
|
|
||||||
let p = Process{ pid: pid };
|
|
||||||
drop(output);
|
|
||||||
let mut bytes = [0; 8];
|
|
||||||
return match input.read(&mut bytes) {
|
|
||||||
Ok(8) => {
|
|
||||||
assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]),
|
|
||||||
"Validation on the CLOEXEC pipe failed: {:?}", bytes);
|
|
||||||
let errno = combine(&bytes[0.. 4]);
|
|
||||||
assert!(p.wait(0).is_ok(), "wait(0) should either return Ok or panic");
|
|
||||||
Err(super::decode_error(errno))
|
|
||||||
}
|
|
||||||
Err(ref e) if e.kind == EndOfFile => Ok(p),
|
|
||||||
Err(e) => {
|
|
||||||
assert!(p.wait(0).is_ok(), "wait(0) should either return Ok or panic");
|
|
||||||
panic!("the CLOEXEC pipe failed: {:?}", e)
|
|
||||||
},
|
|
||||||
Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic
|
|
||||||
assert!(p.wait(0).is_ok(), "wait(0) should either return Ok or panic");
|
|
||||||
panic!("short read on the CLOEXEC pipe")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// And at this point we've reached a special time in the life of the
|
|
||||||
// child. The child must now be considered hamstrung and unable to
|
|
||||||
// do anything other than syscalls really. Consider the following
|
|
||||||
// scenario:
|
|
||||||
//
|
|
||||||
// 1. Thread A of process 1 grabs the malloc() mutex
|
|
||||||
// 2. Thread B of process 1 forks(), creating thread C
|
|
||||||
// 3. Thread C of process 2 then attempts to malloc()
|
|
||||||
// 4. The memory of process 2 is the same as the memory of
|
|
||||||
// process 1, so the mutex is locked.
|
|
||||||
//
|
|
||||||
// This situation looks a lot like deadlock, right? It turns out
|
|
||||||
// that this is what pthread_atfork() takes care of, which is
|
|
||||||
// presumably implemented across platforms. The first thing that
|
|
||||||
// threads to *before* forking is to do things like grab the malloc
|
|
||||||
// mutex, and then after the fork they unlock it.
|
|
||||||
//
|
|
||||||
// Despite this information, libnative's spawn has been witnessed to
|
|
||||||
// deadlock on both OSX and FreeBSD. I'm not entirely sure why, but
|
|
||||||
// all collected backtraces point at malloc/free traffic in the
|
|
||||||
// child spawned process.
|
|
||||||
//
|
|
||||||
// For this reason, the block of code below should contain 0
|
|
||||||
// invocations of either malloc of free (or their related friends).
|
|
||||||
//
|
|
||||||
// As an example of not having malloc/free traffic, we don't close
|
|
||||||
// this file descriptor by dropping the FileDesc (which contains an
|
|
||||||
// allocation). Instead we just close it manually. This will never
|
|
||||||
// have the drop glue anyway because this code never returns (the
|
|
||||||
// child will either exec() or invoke libc::exit)
|
|
||||||
let _ = libc::close(input.fd());
|
|
||||||
|
|
||||||
fn fail(output: &mut FileDesc) -> ! {
|
|
||||||
let errno = sys::os::errno() as u32;
|
|
||||||
let bytes = [
|
|
||||||
(errno >> 24) as u8,
|
|
||||||
(errno >> 16) as u8,
|
|
||||||
(errno >> 8) as u8,
|
|
||||||
(errno >> 0) as u8,
|
|
||||||
CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1],
|
|
||||||
CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3]
|
|
||||||
];
|
|
||||||
// pipe I/O up to PIPE_BUF bytes should be atomic
|
|
||||||
assert!(output.write(&bytes).is_ok());
|
|
||||||
unsafe { libc::_exit(1) }
|
|
||||||
}
|
|
||||||
|
|
||||||
rustrt::rust_unset_sigprocmask();
|
|
||||||
|
|
||||||
// If a stdio file descriptor is set to be ignored (via a -1 file
|
|
||||||
// descriptor), then we don't actually close it, but rather open
|
|
||||||
// up /dev/null into that file descriptor. Otherwise, the first file
|
|
||||||
// descriptor opened up in the child would be numbered as one of the
|
|
||||||
// stdio file descriptors, which is likely to wreak havoc.
|
|
||||||
let setup = |src: Option<P>, dst: c_int| {
|
|
||||||
let src = match src {
|
|
||||||
None => {
|
|
||||||
let flags = if dst == libc::STDIN_FILENO {
|
|
||||||
libc::O_RDONLY
|
|
||||||
} else {
|
|
||||||
libc::O_RDWR
|
|
||||||
};
|
|
||||||
libc::open(devnull.as_ptr() as *const _, flags, 0)
|
|
||||||
}
|
|
||||||
Some(obj) => {
|
|
||||||
let fd = obj.as_inner().fd();
|
|
||||||
// Leak the memory and the file descriptor. We're in the
|
|
||||||
// child now an all our resources are going to be
|
|
||||||
// cleaned up very soon
|
|
||||||
mem::forget(obj);
|
|
||||||
fd
|
|
||||||
}
|
|
||||||
};
|
|
||||||
src != -1 && retry(|| dup2(src, dst)) != -1
|
|
||||||
};
|
|
||||||
|
|
||||||
if !setup(in_fd, libc::STDIN_FILENO) { fail(&mut output) }
|
|
||||||
if !setup(out_fd, libc::STDOUT_FILENO) { fail(&mut output) }
|
|
||||||
if !setup(err_fd, libc::STDERR_FILENO) { fail(&mut output) }
|
|
||||||
|
|
||||||
// close all other fds
|
|
||||||
for fd in (3..getdtablesize()).rev() {
|
|
||||||
if fd != output.fd() {
|
|
||||||
let _ = close(fd as c_int);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match cfg.gid() {
|
|
||||||
Some(u) => {
|
|
||||||
if libc::setgid(u as libc::gid_t) != 0 {
|
|
||||||
fail(&mut output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
match cfg.uid() {
|
|
||||||
Some(u) => {
|
|
||||||
// When dropping privileges from root, the `setgroups` call
|
|
||||||
// will remove any extraneous groups. If we don't call this,
|
|
||||||
// then even though our uid has dropped, we may still have
|
|
||||||
// groups that enable us to do super-user things. This will
|
|
||||||
// fail if we aren't root, so don't bother checking the
|
|
||||||
// return value, this is just done as an optimistic
|
|
||||||
// privilege dropping function.
|
|
||||||
extern {
|
|
||||||
fn setgroups(ngroups: libc::c_int,
|
|
||||||
ptr: *const libc::c_void) -> libc::c_int;
|
|
||||||
}
|
|
||||||
let _ = setgroups(0, ptr::null());
|
|
||||||
|
|
||||||
if libc::setuid(u as libc::uid_t) != 0 {
|
|
||||||
fail(&mut output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
if cfg.detach() {
|
|
||||||
// Don't check the error of setsid because it fails if we're the
|
|
||||||
// process leader already. We just forked so it shouldn't return
|
|
||||||
// error, but ignore it anyway.
|
|
||||||
let _ = libc::setsid();
|
|
||||||
}
|
|
||||||
if !dirp.is_null() && chdir(dirp) == -1 {
|
|
||||||
fail(&mut output);
|
|
||||||
}
|
|
||||||
if !envp.is_null() {
|
|
||||||
*sys::os::environ() = envp as *const _;
|
|
||||||
}
|
|
||||||
let _ = execvp(*argv, argv as *mut _);
|
|
||||||
fail(&mut output);
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn wait(&self, deadline: u64) -> IoResult<ProcessExit> {
|
|
||||||
use cmp;
|
|
||||||
use sync::mpsc::TryRecvError;
|
|
||||||
|
|
||||||
static mut WRITE_FD: libc::c_int = 0;
|
|
||||||
|
|
||||||
let mut status = 0 as c_int;
|
|
||||||
if deadline == 0 {
|
|
||||||
return match retry(|| unsafe { c::waitpid(self.pid, &mut status, 0) }) {
|
|
||||||
-1 => panic!("unknown waitpid error: {:?}", super::last_error()),
|
|
||||||
_ => Ok(translate_status(status)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// On unix, wait() and its friends have no timeout parameters, so there is
|
|
||||||
// no way to time out a thread in wait(). From some googling and some
|
|
||||||
// thinking, it appears that there are a few ways to handle timeouts in
|
|
||||||
// wait(), but the only real reasonable one for a multi-threaded program is
|
|
||||||
// to listen for SIGCHLD.
|
|
||||||
//
|
|
||||||
// With this in mind, the waiting mechanism with a timeout barely uses
|
|
||||||
// waitpid() at all. There are a few times that waitpid() is invoked with
|
|
||||||
// WNOHANG, but otherwise all the necessary blocking is done by waiting for
|
|
||||||
// a SIGCHLD to arrive (and that blocking has a timeout). Note, however,
|
|
||||||
// that waitpid() is still used to actually reap the child.
|
|
||||||
//
|
|
||||||
// Signal handling is super tricky in general, and this is no exception. Due
|
|
||||||
// to the async nature of SIGCHLD, we use the self-pipe trick to transmit
|
|
||||||
// data out of the signal handler to the rest of the application. The first
|
|
||||||
// idea would be to have each thread waiting with a timeout to read this
|
|
||||||
// output file descriptor, but a write() is akin to a signal(), not a
|
|
||||||
// broadcast(), so it would only wake up one thread, and possibly the wrong
|
|
||||||
// thread. Hence a helper thread is used.
|
|
||||||
//
|
|
||||||
// The helper thread here is responsible for farming requests for a
|
|
||||||
// waitpid() with a timeout, and then processing all of the wait requests.
|
|
||||||
// By guaranteeing that only this helper thread is reading half of the
|
|
||||||
// self-pipe, we're sure that we'll never lose a SIGCHLD. This helper thread
|
|
||||||
// is also responsible for select() to wait for incoming messages or
|
|
||||||
// incoming SIGCHLD messages, along with passing an appropriate timeout to
|
|
||||||
// select() to wake things up as necessary.
|
|
||||||
//
|
|
||||||
// The ordering of the following statements is also very purposeful. First,
|
|
||||||
// we must be guaranteed that the helper thread is booted and available to
|
|
||||||
// receive SIGCHLD signals, and then we must also ensure that we do a
|
|
||||||
// nonblocking waitpid() at least once before we go ask the sigchld helper.
|
|
||||||
// This prevents the race where the child exits, we boot the helper, and
|
|
||||||
// then we ask for the child's exit status (never seeing a sigchld).
|
|
||||||
//
|
|
||||||
// The actual communication between the helper thread and this thread is
|
|
||||||
// quite simple, just a channel moving data around.
|
|
||||||
|
|
||||||
HELPER.boot(register_sigchld, waitpid_helper);
|
|
||||||
|
|
||||||
match self.try_wait() {
|
|
||||||
Some(ret) => return Ok(ret),
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
HELPER.send(NewChild(self.pid, tx, deadline));
|
|
||||||
return match rx.recv() {
|
|
||||||
Ok(e) => Ok(e),
|
|
||||||
Err(..) => Err(timeout("wait timed out")),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Register a new SIGCHLD handler, returning the reading half of the
|
|
||||||
// self-pipe plus the old handler registered (return value of sigaction).
|
|
||||||
//
|
|
||||||
// Be sure to set up the self-pipe first because as soon as we register a
|
|
||||||
// handler we're going to start receiving signals.
|
|
||||||
fn register_sigchld() -> (libc::c_int, c::sigaction) {
|
|
||||||
unsafe {
|
|
||||||
let mut pipes = [0; 2];
|
|
||||||
assert_eq!(libc::pipe(pipes.as_mut_ptr()), 0);
|
|
||||||
set_nonblocking(pipes[0], true);
|
|
||||||
set_nonblocking(pipes[1], true);
|
|
||||||
WRITE_FD = pipes[1];
|
|
||||||
|
|
||||||
let mut old: c::sigaction = mem::zeroed();
|
|
||||||
let mut new: c::sigaction = mem::zeroed();
|
|
||||||
new.sa_handler = sigchld_handler;
|
|
||||||
new.sa_flags = c::SA_NOCLDSTOP;
|
|
||||||
assert_eq!(c::sigaction(c::SIGCHLD, &new, &mut old), 0);
|
|
||||||
(pipes[0], old)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper thread for processing SIGCHLD messages
|
|
||||||
fn waitpid_helper(input: libc::c_int,
|
|
||||||
messages: Receiver<Req>,
|
|
||||||
(read_fd, old): (libc::c_int, c::sigaction)) {
|
|
||||||
set_nonblocking(input, true);
|
|
||||||
let mut set: c::fd_set = unsafe { mem::zeroed() };
|
|
||||||
let mut tv: libc::timeval;
|
|
||||||
let mut active = Vec::<(libc::pid_t, Sender<ProcessExit>, u64)>::new();
|
|
||||||
let max = cmp::max(input, read_fd) + 1;
|
|
||||||
|
|
||||||
'outer: loop {
|
|
||||||
// Figure out the timeout of our syscall-to-happen. If we're waiting
|
|
||||||
// for some processes, then they'll have a timeout, otherwise we
|
|
||||||
// wait indefinitely for a message to arrive.
|
|
||||||
//
|
|
||||||
// FIXME: sure would be nice to not have to scan the entire array
|
|
||||||
let min = active.iter().map(|a| a.2).enumerate().min_by(|p| {
|
|
||||||
p.1
|
|
||||||
});
|
|
||||||
let (p, idx) = match min {
|
|
||||||
Some((idx, deadline)) => {
|
|
||||||
let now = sys::timer::now();
|
|
||||||
let ms = if now < deadline {deadline - now} else {0};
|
|
||||||
tv = ms_to_timeval(ms);
|
|
||||||
(&mut tv as *mut _, idx)
|
|
||||||
}
|
|
||||||
None => (ptr::null_mut(), -1),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Wait for something to happen
|
|
||||||
c::fd_set(&mut set, input);
|
|
||||||
c::fd_set(&mut set, read_fd);
|
|
||||||
match unsafe { c::select(max, &mut set, ptr::null_mut(),
|
|
||||||
ptr::null_mut(), p) } {
|
|
||||||
// interrupted, retry
|
|
||||||
-1 if os::errno() == libc::EINTR as i32 => continue,
|
|
||||||
|
|
||||||
// We read something, break out and process
|
|
||||||
1 | 2 => {}
|
|
||||||
|
|
||||||
// Timeout, the pending request is removed
|
|
||||||
0 => {
|
|
||||||
drop(active.remove(idx));
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
n => panic!("error in select {:?} ({:?})", os::errno(), n),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process any pending messages
|
|
||||||
if drain(input) {
|
|
||||||
loop {
|
|
||||||
match messages.try_recv() {
|
|
||||||
Ok(NewChild(pid, tx, deadline)) => {
|
|
||||||
active.push((pid, tx, deadline));
|
|
||||||
}
|
|
||||||
// Once we've been disconnected it means the main
|
|
||||||
// thread is exiting (at_exit has run). We could
|
|
||||||
// still have active waiter for other threads, so
|
|
||||||
// we're just going to drop them all on the floor.
|
|
||||||
// This means that they won't receive a "you're
|
|
||||||
// done" message in which case they'll be considered
|
|
||||||
// as timed out, but more generally errors will
|
|
||||||
// start propagating.
|
|
||||||
Err(TryRecvError::Disconnected) => {
|
|
||||||
break 'outer;
|
|
||||||
}
|
|
||||||
Err(TryRecvError::Empty) => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a child exited (somehow received SIGCHLD), then poll all
|
|
||||||
// children to see if any of them exited.
|
|
||||||
//
|
|
||||||
// We also attempt to be responsible netizens when dealing with
|
|
||||||
// SIGCHLD by invoking any previous SIGCHLD handler instead of just
|
|
||||||
// ignoring any previous SIGCHLD handler. Note that we don't provide
|
|
||||||
// a 1:1 mapping of our handler invocations to the previous handler
|
|
||||||
// invocations because we drain the `read_fd` entirely. This is
|
|
||||||
// probably OK because the kernel is already allowed to coalesce
|
|
||||||
// simultaneous signals, we're just doing some extra coalescing.
|
|
||||||
//
|
|
||||||
// Another point of note is that this likely runs the signal handler
|
|
||||||
// on a different thread than the one that received the signal. I
|
|
||||||
// *think* this is ok at this time.
|
|
||||||
//
|
|
||||||
// The main reason for doing this is to allow stdtest to run native
|
|
||||||
// tests as well. Both libgreen and libnative are running around
|
|
||||||
// with process timeouts, but libgreen should get there first
|
|
||||||
// (currently libuv doesn't handle old signal handlers).
|
|
||||||
if drain(read_fd) {
|
|
||||||
let i: usize = unsafe { mem::transmute(old.sa_handler) };
|
|
||||||
if i != 0 {
|
|
||||||
assert!(old.sa_flags & c::SA_SIGINFO == 0);
|
|
||||||
(old.sa_handler)(c::SIGCHLD);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: sure would be nice to not have to scan the entire
|
|
||||||
// array...
|
|
||||||
active.retain(|&(pid, ref tx, _)| {
|
|
||||||
let pr = Process { pid: pid };
|
|
||||||
match pr.try_wait() {
|
|
||||||
Some(msg) => { tx.send(msg).unwrap(); false }
|
|
||||||
None => true,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Once this helper thread is done, we re-register the old sigchld
|
|
||||||
// handler and close our intermediate file descriptors.
|
|
||||||
unsafe {
|
|
||||||
assert_eq!(c::sigaction(c::SIGCHLD, &old, ptr::null_mut()), 0);
|
|
||||||
let _ = libc::close(read_fd);
|
|
||||||
let _ = libc::close(WRITE_FD);
|
|
||||||
WRITE_FD = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drain all pending data from the file descriptor, returning if any data
|
|
||||||
// could be drained. This requires that the file descriptor is in
|
|
||||||
// nonblocking mode.
|
|
||||||
fn drain(fd: libc::c_int) -> bool {
|
|
||||||
let mut ret = false;
|
|
||||||
loop {
|
|
||||||
let mut buf = [0u8; 1];
|
|
||||||
match unsafe {
|
|
||||||
libc::read(fd, buf.as_mut_ptr() as *mut libc::c_void,
|
|
||||||
buf.len() as libc::size_t)
|
|
||||||
} {
|
|
||||||
n if n > 0 => { ret = true; }
|
|
||||||
0 => return true,
|
|
||||||
-1 if wouldblock() => return ret,
|
|
||||||
n => panic!("bad read {} ({})",
|
|
||||||
io::Error::last_os_error(), n),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Signal handler for SIGCHLD signals, must be async-signal-safe!
|
|
||||||
//
|
|
||||||
// This function will write to the writing half of the "self pipe" to wake
|
|
||||||
// up the helper thread if it's waiting. Note that this write must be
|
|
||||||
// nonblocking because if it blocks and the reader is the thread we
|
|
||||||
// interrupted, then we'll deadlock.
|
|
||||||
//
|
|
||||||
// When writing, if the write returns EWOULDBLOCK then we choose to ignore
|
|
||||||
// it. At that point we're guaranteed that there's something in the pipe
|
|
||||||
// which will wake up the other end at some point, so we just allow this
|
|
||||||
// signal to be coalesced with the pending signals on the pipe.
|
|
||||||
extern fn sigchld_handler(_signum: libc::c_int) {
|
|
||||||
let msg = 1;
|
|
||||||
match unsafe {
|
|
||||||
libc::write(WRITE_FD, &msg as *const _ as *const libc::c_void, 1)
|
|
||||||
} {
|
|
||||||
1 => {}
|
|
||||||
-1 if wouldblock() => {} // see above comments
|
|
||||||
n => panic!("bad error on write fd: {:?} {:?}", n, os::errno()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_wait(&self) -> Option<ProcessExit> {
|
|
||||||
let mut status = 0 as c_int;
|
|
||||||
match retry(|| unsafe {
|
|
||||||
c::waitpid(self.pid, &mut status, c::WNOHANG)
|
|
||||||
}) {
|
|
||||||
n if n == self.pid => Some(translate_status(status)),
|
|
||||||
0 => None,
|
|
||||||
n => panic!("unknown waitpid error `{:?}`: {:?}", n,
|
|
||||||
super::last_error()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_argv<T,F>(prog: &CString, args: &[CString],
|
|
||||||
cb: F)
|
|
||||||
-> T
|
|
||||||
where F : FnOnce(*const *const libc::c_char) -> T
|
|
||||||
{
|
|
||||||
let mut ptrs: Vec<*const libc::c_char> = Vec::with_capacity(args.len()+1);
|
|
||||||
|
|
||||||
// Convert the CStrings into an array of pointers. Note: the
|
|
||||||
// lifetime of the various CStrings involved is guaranteed to be
|
|
||||||
// larger than the lifetime of our invocation of cb, but this is
|
|
||||||
// technically unsafe as the callback could leak these pointers
|
|
||||||
// out of our scope.
|
|
||||||
ptrs.push(prog.as_ptr());
|
|
||||||
ptrs.extend(args.iter().map(|tmp| tmp.as_ptr()));
|
|
||||||
|
|
||||||
// Add a terminating null pointer (required by libc).
|
|
||||||
ptrs.push(ptr::null());
|
|
||||||
|
|
||||||
cb(ptrs.as_ptr())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_envp<K,V,T,F>(env: Option<&HashMap<K, V>>,
|
|
||||||
cb: F)
|
|
||||||
-> T
|
|
||||||
where F : FnOnce(*const c_void) -> T,
|
|
||||||
K : BytesContainer + Eq + Hash,
|
|
||||||
V : BytesContainer
|
|
||||||
{
|
|
||||||
// On posixy systems we can pass a char** for envp, which is a
|
|
||||||
// null-terminated array of "k=v\0" strings. Since we must create
|
|
||||||
// these strings locally, yet expose a raw pointer to them, we
|
|
||||||
// create a temporary vector to own the CStrings that outlives the
|
|
||||||
// call to cb.
|
|
||||||
match env {
|
|
||||||
Some(env) => {
|
|
||||||
let mut tmps = Vec::with_capacity(env.len());
|
|
||||||
|
|
||||||
for pair in env {
|
|
||||||
let mut kv = Vec::new();
|
|
||||||
kv.push_all(pair.0.container_as_bytes());
|
|
||||||
kv.push('=' as u8);
|
|
||||||
kv.push_all(pair.1.container_as_bytes());
|
|
||||||
kv.push(0); // terminating null
|
|
||||||
tmps.push(kv);
|
|
||||||
}
|
|
||||||
|
|
||||||
// As with `with_argv`, this is unsafe, since cb could leak the pointers.
|
|
||||||
let mut ptrs: Vec<*const libc::c_char> =
|
|
||||||
tmps.iter()
|
|
||||||
.map(|tmp| tmp.as_ptr() as *const libc::c_char)
|
|
||||||
.collect();
|
|
||||||
ptrs.push(ptr::null());
|
|
||||||
|
|
||||||
cb(ptrs.as_ptr() as *const c_void)
|
|
||||||
}
|
|
||||||
_ => cb(ptr::null())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_status(status: c_int) -> ProcessExit {
|
|
||||||
#![allow(non_snake_case)]
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
||||||
mod imp {
|
|
||||||
pub fn WIFEXITED(status: i32) -> bool { (status & 0xff) == 0 }
|
|
||||||
pub fn WEXITSTATUS(status: i32) -> i32 { (status >> 8) & 0xff }
|
|
||||||
pub fn WTERMSIG(status: i32) -> i32 { status & 0x7f }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos",
|
|
||||||
target_os = "ios",
|
|
||||||
target_os = "freebsd",
|
|
||||||
target_os = "dragonfly",
|
|
||||||
target_os = "bitrig",
|
|
||||||
target_os = "openbsd"))]
|
|
||||||
mod imp {
|
|
||||||
pub fn WIFEXITED(status: i32) -> bool { (status & 0x7f) == 0 }
|
|
||||||
pub fn WEXITSTATUS(status: i32) -> i32 { status >> 8 }
|
|
||||||
pub fn WTERMSIG(status: i32) -> i32 { status & 0o177 }
|
|
||||||
}
|
|
||||||
|
|
||||||
if imp::WIFEXITED(status) {
|
|
||||||
ExitStatus(imp::WEXITSTATUS(status) as isize)
|
|
||||||
} else {
|
|
||||||
ExitSignal(imp::WTERMSIG(status) as isize)
|
|
||||||
}
|
|
||||||
}
|
|
@ -328,8 +328,8 @@ impl Process {
|
|||||||
}) {
|
}) {
|
||||||
n if n == self.pid => Some(translate_status(status)),
|
n if n == self.pid => Some(translate_status(status)),
|
||||||
0 => None,
|
0 => None,
|
||||||
n => panic!("unknown waitpid error `{:?}`: {:?}", n,
|
n => panic!("unknown waitpid error `{}`: {}", n,
|
||||||
super::last_error()),
|
io::Error::last_os_error()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,164 +0,0 @@
|
|||||||
// Copyright 2014 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.
|
|
||||||
|
|
||||||
#![allow(deprecated)]
|
|
||||||
|
|
||||||
use prelude::v1::*;
|
|
||||||
|
|
||||||
use old_io::net::ip;
|
|
||||||
use old_io::IoResult;
|
|
||||||
use libc;
|
|
||||||
use mem;
|
|
||||||
use ptr;
|
|
||||||
use super::{last_error, last_net_error, retry, sock_t};
|
|
||||||
use sync::Arc;
|
|
||||||
use sync::atomic::{AtomicBool, Ordering};
|
|
||||||
use sys::fs::FileDesc;
|
|
||||||
use sys::{set_nonblocking, wouldblock};
|
|
||||||
use sys;
|
|
||||||
use sys_common;
|
|
||||||
use sys_common::net;
|
|
||||||
use sys_common::net::SocketStatus::Readable;
|
|
||||||
|
|
||||||
pub use sys_common::net::TcpStream;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// TCP listeners
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
pub struct TcpListener {
|
|
||||||
pub inner: FileDesc,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Sync for TcpListener {}
|
|
||||||
|
|
||||||
impl TcpListener {
|
|
||||||
pub fn bind(addr: ip::SocketAddr) -> IoResult<TcpListener> {
|
|
||||||
let fd = try!(net::socket(addr, libc::SOCK_STREAM));
|
|
||||||
let ret = TcpListener { inner: FileDesc::new(fd, true) };
|
|
||||||
|
|
||||||
let mut storage = unsafe { mem::zeroed() };
|
|
||||||
let len = net::addr_to_sockaddr(addr, &mut storage);
|
|
||||||
let addrp = &storage as *const _ as *const libc::sockaddr;
|
|
||||||
|
|
||||||
// On platforms with Berkeley-derived sockets, this allows
|
|
||||||
// to quickly rebind a socket, without needing to wait for
|
|
||||||
// the OS to clean up the previous one.
|
|
||||||
try!(net::setsockopt(fd, libc::SOL_SOCKET,
|
|
||||||
libc::SO_REUSEADDR,
|
|
||||||
1 as libc::c_int));
|
|
||||||
|
|
||||||
|
|
||||||
match unsafe { libc::bind(fd, addrp, len) } {
|
|
||||||
-1 => Err(last_error()),
|
|
||||||
_ => Ok(ret),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fd(&self) -> sock_t { self.inner.fd() }
|
|
||||||
|
|
||||||
pub fn listen(self, backlog: isize) -> IoResult<TcpAcceptor> {
|
|
||||||
match unsafe { libc::listen(self.fd(), backlog as libc::c_int) } {
|
|
||||||
-1 => Err(last_net_error()),
|
|
||||||
_ => {
|
|
||||||
let (reader, writer) = try!(unsafe { sys::os::pipe() });
|
|
||||||
set_nonblocking(reader.fd(), true);
|
|
||||||
set_nonblocking(writer.fd(), true);
|
|
||||||
set_nonblocking(self.fd(), true);
|
|
||||||
Ok(TcpAcceptor {
|
|
||||||
inner: Arc::new(AcceptorInner {
|
|
||||||
listener: self,
|
|
||||||
reader: reader,
|
|
||||||
writer: writer,
|
|
||||||
closed: AtomicBool::new(false),
|
|
||||||
}),
|
|
||||||
deadline: 0,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn socket_name(&mut self) -> IoResult<ip::SocketAddr> {
|
|
||||||
net::sockname(self.fd(), libc::getsockname)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TcpAcceptor {
|
|
||||||
inner: Arc<AcceptorInner>,
|
|
||||||
deadline: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AcceptorInner {
|
|
||||||
listener: TcpListener,
|
|
||||||
reader: FileDesc,
|
|
||||||
writer: FileDesc,
|
|
||||||
closed: AtomicBool,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Sync for AcceptorInner {}
|
|
||||||
|
|
||||||
impl TcpAcceptor {
|
|
||||||
pub fn fd(&self) -> sock_t { self.inner.listener.fd() }
|
|
||||||
|
|
||||||
pub fn accept(&mut self) -> IoResult<TcpStream> {
|
|
||||||
// In implementing accept, the two main concerns are dealing with
|
|
||||||
// close_accept() and timeouts. The unix implementation is based on a
|
|
||||||
// nonblocking accept plus a call to select(). Windows ends up having
|
|
||||||
// an entirely separate implementation than unix, which is explained
|
|
||||||
// below.
|
|
||||||
//
|
|
||||||
// To implement timeouts, all blocking is done via select() instead of
|
|
||||||
// accept() by putting the socket in non-blocking mode. Because
|
|
||||||
// select() takes a timeout argument, we just pass through the timeout
|
|
||||||
// to select().
|
|
||||||
//
|
|
||||||
// To implement close_accept(), we have a self-pipe to ourselves which
|
|
||||||
// is passed to select() along with the socket being accepted on. The
|
|
||||||
// self-pipe is never written to unless close_accept() is called.
|
|
||||||
let deadline = if self.deadline == 0 {None} else {Some(self.deadline)};
|
|
||||||
|
|
||||||
while !self.inner.closed.load(Ordering::SeqCst) {
|
|
||||||
match retry(|| unsafe {
|
|
||||||
libc::accept(self.fd(), ptr::null_mut(), ptr::null_mut())
|
|
||||||
}) {
|
|
||||||
-1 if wouldblock() => {}
|
|
||||||
-1 => return Err(last_net_error()),
|
|
||||||
fd => return Ok(TcpStream::new(fd as sock_t)),
|
|
||||||
}
|
|
||||||
try!(net::await(&[self.fd(), self.inner.reader.fd()],
|
|
||||||
deadline, Readable));
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(sys_common::eof())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_timeout(&mut self, timeout: Option<u64>) {
|
|
||||||
self.deadline = timeout.map(|a| sys::timer::now() + a).unwrap_or(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn close_accept(&mut self) -> IoResult<()> {
|
|
||||||
self.inner.closed.store(true, Ordering::SeqCst);
|
|
||||||
let fd = FileDesc::new(self.inner.writer.fd(), false);
|
|
||||||
match fd.write(&[0]) {
|
|
||||||
Ok(..) => Ok(()),
|
|
||||||
Err(..) if wouldblock() => Ok(()),
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for TcpAcceptor {
|
|
||||||
fn clone(&self) -> TcpAcceptor {
|
|
||||||
TcpAcceptor {
|
|
||||||
inner: self.inner.clone(),
|
|
||||||
deadline: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,294 +0,0 @@
|
|||||||
// Copyright 2013 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.
|
|
||||||
|
|
||||||
//! Timers for non-Linux/non-Windows OSes
|
|
||||||
//!
|
|
||||||
//! This module implements timers with a worker thread, select(), and a lot of
|
|
||||||
//! witchcraft that turns out to be horribly inaccurate timers. The unfortunate
|
|
||||||
//! part is that I'm at a loss of what else to do one these OSes. This is also
|
|
||||||
//! why Linux has a specialized timerfd implementation and windows has its own
|
|
||||||
//! implementation (they're more accurate than this one).
|
|
||||||
//!
|
|
||||||
//! The basic idea is that there is a worker thread that's communicated to via a
|
|
||||||
//! channel and a pipe, the pipe is used by the worker thread in a select()
|
|
||||||
//! syscall with a timeout. The timeout is the "next timer timeout" while the
|
|
||||||
//! channel is used to send data over to the worker thread.
|
|
||||||
//!
|
|
||||||
//! Whenever the call to select() times out, then a channel receives a message.
|
|
||||||
//! Whenever the call returns that the file descriptor has information, then the
|
|
||||||
//! channel from timers is drained, enqueuing all incoming requests.
|
|
||||||
//!
|
|
||||||
//! The actual implementation of the helper thread is a sorted array of
|
|
||||||
//! timers in terms of target firing date. The target is the absolute time at
|
|
||||||
//! which the timer should fire. Timers are then re-enqueued after a firing if
|
|
||||||
//! the repeat boolean is set.
|
|
||||||
//!
|
|
||||||
//! Naturally, all this logic of adding times and keeping track of
|
|
||||||
//! relative/absolute time is a little lossy and not quite exact. I've done the
|
|
||||||
//! best I could to reduce the amount of calls to 'now()', but there's likely
|
|
||||||
//! still inaccuracies trickling in here and there.
|
|
||||||
//!
|
|
||||||
//! One of the tricky parts of this implementation is that whenever a timer is
|
|
||||||
//! acted upon, it must cancel whatever the previous action was (if one is
|
|
||||||
//! active) in order to act like the other implementations of this timer. In
|
|
||||||
//! order to do this, the timer's inner pointer is transferred to the worker
|
|
||||||
//! thread. Whenever the timer is modified, it first takes ownership back from
|
|
||||||
//! the worker thread in order to modify the same data structure. This has the
|
|
||||||
//! side effect of "cancelling" the previous requests while allowing a
|
|
||||||
//! re-enqueuing later on.
|
|
||||||
//!
|
|
||||||
//! Note that all time units in this file are in *milliseconds*.
|
|
||||||
|
|
||||||
#![allow(deprecated)]
|
|
||||||
|
|
||||||
use prelude::v1::*;
|
|
||||||
use self::Req::*;
|
|
||||||
|
|
||||||
use old_io::IoResult;
|
|
||||||
use libc;
|
|
||||||
use mem;
|
|
||||||
use sys::os;
|
|
||||||
use io;
|
|
||||||
use ptr;
|
|
||||||
use sync::atomic::{self, Ordering};
|
|
||||||
use sync::mpsc::{channel, Sender, Receiver, TryRecvError};
|
|
||||||
use sys::c;
|
|
||||||
use sys::fs::FileDesc;
|
|
||||||
use sys_common::helper_thread::Helper;
|
|
||||||
|
|
||||||
helper_init! { static HELPER: Helper<Req> }
|
|
||||||
|
|
||||||
pub trait Callback {
|
|
||||||
fn call(&mut self);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Timer {
|
|
||||||
id: usize,
|
|
||||||
inner: Option<Box<Inner>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Inner {
|
|
||||||
cb: Option<Box<Callback + Send>>,
|
|
||||||
interval: u64,
|
|
||||||
repeat: bool,
|
|
||||||
target: u64,
|
|
||||||
id: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Req {
|
|
||||||
// Add a new timer to the helper thread.
|
|
||||||
NewTimer(Box<Inner>),
|
|
||||||
|
|
||||||
// Remove a timer based on its id and then send it back on the channel
|
|
||||||
// provided
|
|
||||||
RemoveTimer(usize, Sender<Box<Inner>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns the current time (in milliseconds)
|
|
||||||
pub fn now() -> u64 {
|
|
||||||
unsafe {
|
|
||||||
let mut now: libc::timeval = mem::zeroed();
|
|
||||||
assert_eq!(c::gettimeofday(&mut now, ptr::null_mut()), 0);
|
|
||||||
return (now.tv_sec as u64) * 1000 + (now.tv_usec as u64) / 1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn helper(input: libc::c_int, messages: Receiver<Req>, _: ()) {
|
|
||||||
let mut set: c::fd_set = unsafe { mem::zeroed() };
|
|
||||||
|
|
||||||
let fd = FileDesc::new(input, true);
|
|
||||||
let mut timeout: libc::timeval = unsafe { mem::zeroed() };
|
|
||||||
|
|
||||||
// active timers are those which are able to be selected upon (and it's a
|
|
||||||
// sorted list, and dead timers are those which have expired, but ownership
|
|
||||||
// hasn't yet been transferred back to the timer itself.
|
|
||||||
let mut active: Vec<Box<Inner>> = vec![];
|
|
||||||
let mut dead = vec![];
|
|
||||||
|
|
||||||
// inserts a timer into an array of timers (sorted by firing time)
|
|
||||||
fn insert(t: Box<Inner>, active: &mut Vec<Box<Inner>>) {
|
|
||||||
match active.iter().position(|tm| tm.target > t.target) {
|
|
||||||
Some(pos) => { active.insert(pos, t); }
|
|
||||||
None => { active.push(t); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// signals the first requests in the queue, possible re-enqueueing it.
|
|
||||||
fn signal(active: &mut Vec<Box<Inner>>,
|
|
||||||
dead: &mut Vec<(usize, Box<Inner>)>) {
|
|
||||||
if active.is_empty() { return }
|
|
||||||
|
|
||||||
let mut timer = active.remove(0);
|
|
||||||
let mut cb = timer.cb.take().unwrap();
|
|
||||||
cb.call();
|
|
||||||
if timer.repeat {
|
|
||||||
timer.cb = Some(cb);
|
|
||||||
timer.target += timer.interval;
|
|
||||||
insert(timer, active);
|
|
||||||
} else {
|
|
||||||
dead.push((timer.id, timer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
'outer: loop {
|
|
||||||
let timeout = if active.len() == 0 {
|
|
||||||
// Empty array? no timeout (wait forever for the next request)
|
|
||||||
ptr::null_mut()
|
|
||||||
} else {
|
|
||||||
let now = now();
|
|
||||||
// If this request has already expired, then signal it and go
|
|
||||||
// through another iteration
|
|
||||||
if active[0].target <= now {
|
|
||||||
signal(&mut active, &mut dead);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The actual timeout listed in the requests array is an
|
|
||||||
// absolute date, so here we translate the absolute time to a
|
|
||||||
// relative time.
|
|
||||||
let tm = active[0].target - now;
|
|
||||||
timeout.tv_sec = (tm / 1000) as libc::time_t;
|
|
||||||
timeout.tv_usec = ((tm % 1000) * 1000) as libc::suseconds_t;
|
|
||||||
&mut timeout as *mut libc::timeval
|
|
||||||
};
|
|
||||||
|
|
||||||
c::fd_set(&mut set, input);
|
|
||||||
match unsafe {
|
|
||||||
c::select(input + 1, &mut set, ptr::null_mut(),
|
|
||||||
ptr::null_mut(), timeout)
|
|
||||||
} {
|
|
||||||
// timed out
|
|
||||||
0 => signal(&mut active, &mut dead),
|
|
||||||
|
|
||||||
// file descriptor write woke us up, we've got some new requests
|
|
||||||
1 => {
|
|
||||||
loop {
|
|
||||||
match messages.try_recv() {
|
|
||||||
// Once we've been disconnected it means the main thread
|
|
||||||
// is exiting (at_exit has run). We could still have
|
|
||||||
// active timers for other threads, so we're just going
|
|
||||||
// to drop them all on the floor. This is all we can
|
|
||||||
// really do, however, to prevent resource leakage. The
|
|
||||||
// remaining timers will likely start panicking quickly
|
|
||||||
// as they attempt to re-use this thread but are
|
|
||||||
// disallowed to do so.
|
|
||||||
Err(TryRecvError::Disconnected) => {
|
|
||||||
break 'outer;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(NewTimer(timer)) => insert(timer, &mut active),
|
|
||||||
|
|
||||||
Ok(RemoveTimer(id, ack)) => {
|
|
||||||
match dead.iter().position(|&(i, _)| id == i) {
|
|
||||||
Some(i) => {
|
|
||||||
let (_, i) = dead.remove(i);
|
|
||||||
ack.send(i).unwrap();
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
let i = active.iter().position(|i| i.id == id);
|
|
||||||
let i = i.expect("no timer found");
|
|
||||||
let t = active.remove(i);
|
|
||||||
ack.send(t).unwrap();
|
|
||||||
}
|
|
||||||
Err(..) => break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// drain the file descriptor
|
|
||||||
let mut buf = [0];
|
|
||||||
assert_eq!(fd.read(&mut buf).unwrap(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
-1 if os::errno() == libc::EINTR as i32 => {}
|
|
||||||
n => panic!("helper thread failed in select() with error: {} ({})",
|
|
||||||
n, io::Error::last_os_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Timer {
|
|
||||||
pub fn new() -> IoResult<Timer> {
|
|
||||||
// See notes above regarding using isize return value
|
|
||||||
// instead of ()
|
|
||||||
HELPER.boot(|| {}, helper);
|
|
||||||
|
|
||||||
static ID: atomic::AtomicUsize = atomic::ATOMIC_USIZE_INIT;
|
|
||||||
let id = ID.fetch_add(1, Ordering::Relaxed);
|
|
||||||
Ok(Timer {
|
|
||||||
id: id,
|
|
||||||
inner: Some(box Inner {
|
|
||||||
cb: None,
|
|
||||||
interval: 0,
|
|
||||||
target: 0,
|
|
||||||
repeat: false,
|
|
||||||
id: id,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sleep(&mut self, ms: u64) {
|
|
||||||
let mut inner = self.inner();
|
|
||||||
inner.cb = None; // cancel any previous request
|
|
||||||
self.inner = Some(inner);
|
|
||||||
|
|
||||||
let mut to_sleep = libc::timespec {
|
|
||||||
tv_sec: (ms / 1000) as libc::time_t,
|
|
||||||
tv_nsec: ((ms % 1000) * 1000000) as libc::c_long,
|
|
||||||
};
|
|
||||||
while unsafe { libc::nanosleep(&to_sleep, &mut to_sleep) } != 0 {
|
|
||||||
if os::errno() as isize != libc::EINTR as isize {
|
|
||||||
panic!("failed to sleep, but not because of EINTR?");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn oneshot(&mut self, msecs: u64, cb: Box<Callback + Send>) {
|
|
||||||
let now = now();
|
|
||||||
let mut inner = self.inner();
|
|
||||||
|
|
||||||
inner.repeat = false;
|
|
||||||
inner.cb = Some(cb);
|
|
||||||
inner.interval = msecs;
|
|
||||||
inner.target = now + msecs;
|
|
||||||
|
|
||||||
HELPER.send(NewTimer(inner));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn period(&mut self, msecs: u64, cb: Box<Callback + Send>) {
|
|
||||||
let now = now();
|
|
||||||
let mut inner = self.inner();
|
|
||||||
|
|
||||||
inner.repeat = true;
|
|
||||||
inner.cb = Some(cb);
|
|
||||||
inner.interval = msecs;
|
|
||||||
inner.target = now + msecs;
|
|
||||||
|
|
||||||
HELPER.send(NewTimer(inner));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inner(&mut self) -> Box<Inner> {
|
|
||||||
match self.inner.take() {
|
|
||||||
Some(i) => i,
|
|
||||||
None => {
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
HELPER.send(RemoveTimer(self.id, tx));
|
|
||||||
rx.recv().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Timer {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.inner = Some(self.inner());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,81 +0,0 @@
|
|||||||
// Copyright 2014 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.
|
|
||||||
|
|
||||||
#![allow(deprecated)]
|
|
||||||
|
|
||||||
use prelude::v1::*;
|
|
||||||
|
|
||||||
use sys::fs::FileDesc;
|
|
||||||
use libc::{self, c_int, c_ulong};
|
|
||||||
use old_io::{self, IoResult, IoError};
|
|
||||||
use sys::c;
|
|
||||||
use sys_common;
|
|
||||||
|
|
||||||
pub struct TTY {
|
|
||||||
pub fd: FileDesc,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos",
|
|
||||||
target_os = "ios",
|
|
||||||
target_os = "dragonfly",
|
|
||||||
target_os = "freebsd",
|
|
||||||
target_os = "bitrig",
|
|
||||||
target_os = "openbsd"))]
|
|
||||||
const TIOCGWINSZ: c_ulong = 0x40087468;
|
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
||||||
const TIOCGWINSZ: c_ulong = 0x00005413;
|
|
||||||
|
|
||||||
impl TTY {
|
|
||||||
pub fn new(fd: c_int) -> IoResult<TTY> {
|
|
||||||
if unsafe { libc::isatty(fd) } != 0 {
|
|
||||||
Ok(TTY { fd: FileDesc::new(fd, true) })
|
|
||||||
} else {
|
|
||||||
Err(IoError {
|
|
||||||
kind: old_io::MismatchedFileTypeForOperation,
|
|
||||||
desc: "file descriptor is not a TTY",
|
|
||||||
detail: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
|
||||||
self.fd.read(buf)
|
|
||||||
}
|
|
||||||
pub fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
|
||||||
self.fd.write(buf)
|
|
||||||
}
|
|
||||||
pub fn set_raw(&mut self, _raw: bool) -> IoResult<()> {
|
|
||||||
Err(sys_common::unimpl())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_winsize(&mut self) -> IoResult<(isize, isize)> {
|
|
||||||
unsafe {
|
|
||||||
#[repr(C)]
|
|
||||||
struct winsize {
|
|
||||||
ws_row: u16,
|
|
||||||
ws_col: u16,
|
|
||||||
ws_xpixel: u16,
|
|
||||||
ws_ypixel: u16
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut size = winsize { ws_row: 0, ws_col: 0, ws_xpixel: 0, ws_ypixel: 0 };
|
|
||||||
if c::ioctl(self.fd.fd(), TIOCGWINSZ, &mut size) == -1 {
|
|
||||||
Err(IoError {
|
|
||||||
kind: old_io::OtherIoError,
|
|
||||||
desc: "Size of terminal could not be determined",
|
|
||||||
detail: None,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Ok((size.ws_col as isize, size.ws_row as isize))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
// Copyright 2014 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.
|
|
||||||
|
|
||||||
pub use sys_common::net::UdpSocket;
|
|
@ -24,9 +24,6 @@ pub mod io {
|
|||||||
use sys_common::{net2, AsInner, FromInner};
|
use sys_common::{net2, AsInner, FromInner};
|
||||||
use sys;
|
use sys;
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
use old_io;
|
|
||||||
|
|
||||||
/// Raw HANDLEs.
|
/// Raw HANDLEs.
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
pub type RawHandle = libc::HANDLE;
|
pub type RawHandle = libc::HANDLE;
|
||||||
@ -61,14 +58,6 @@ pub mod io {
|
|||||||
unsafe fn from_raw_handle(handle: RawHandle) -> Self;
|
unsafe fn from_raw_handle(handle: RawHandle) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl AsRawHandle for old_io::fs::File {
|
|
||||||
fn as_raw_handle(&self) -> RawHandle {
|
|
||||||
self.as_inner().handle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
impl AsRawHandle for fs::File {
|
impl AsRawHandle for fs::File {
|
||||||
fn as_raw_handle(&self) -> RawHandle {
|
fn as_raw_handle(&self) -> RawHandle {
|
||||||
@ -83,38 +72,6 @@ pub mod io {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl AsRawHandle for old_io::pipe::PipeStream {
|
|
||||||
fn as_raw_handle(&self) -> RawHandle {
|
|
||||||
self.as_inner().handle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl AsRawHandle for old_io::net::pipe::UnixStream {
|
|
||||||
fn as_raw_handle(&self) -> RawHandle {
|
|
||||||
self.as_inner().handle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl AsRawHandle for old_io::net::pipe::UnixListener {
|
|
||||||
fn as_raw_handle(&self) -> RawHandle {
|
|
||||||
self.as_inner().handle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl AsRawHandle for old_io::net::pipe::UnixAcceptor {
|
|
||||||
fn as_raw_handle(&self) -> RawHandle {
|
|
||||||
self.as_inner().handle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extract raw sockets.
|
/// Extract raw sockets.
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
pub trait AsRawSocket {
|
pub trait AsRawSocket {
|
||||||
@ -139,38 +96,6 @@ pub mod io {
|
|||||||
unsafe fn from_raw_socket(sock: RawSocket) -> Self;
|
unsafe fn from_raw_socket(sock: RawSocket) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl AsRawSocket for old_io::net::tcp::TcpStream {
|
|
||||||
fn as_raw_socket(&self) -> RawSocket {
|
|
||||||
self.as_inner().fd()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl AsRawSocket for old_io::net::tcp::TcpListener {
|
|
||||||
fn as_raw_socket(&self) -> RawSocket {
|
|
||||||
self.as_inner().socket()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl AsRawSocket for old_io::net::tcp::TcpAcceptor {
|
|
||||||
fn as_raw_socket(&self) -> RawSocket {
|
|
||||||
self.as_inner().socket()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
impl AsRawSocket for old_io::net::udp::UdpSocket {
|
|
||||||
fn as_raw_socket(&self) -> RawSocket {
|
|
||||||
self.as_inner().fd()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
impl AsRawSocket for net::TcpStream {
|
impl AsRawSocket for net::TcpStream {
|
||||||
fn as_raw_socket(&self) -> RawSocket {
|
fn as_raw_socket(&self) -> RawSocket {
|
||||||
|
@ -1,452 +0,0 @@
|
|||||||
// Copyright 2014 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.
|
|
||||||
|
|
||||||
//! Blocking Windows-based file I/O
|
|
||||||
|
|
||||||
#![allow(deprecated)] // this module itself is essentially deprecated
|
|
||||||
|
|
||||||
use libc::{self, c_int};
|
|
||||||
|
|
||||||
use mem;
|
|
||||||
use ptr;
|
|
||||||
use old_io;
|
|
||||||
|
|
||||||
use prelude::v1::*;
|
|
||||||
use sys;
|
|
||||||
use sys_common::{self, mkerr_libc};
|
|
||||||
|
|
||||||
use old_path::{Path, GenericPath};
|
|
||||||
use old_io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode};
|
|
||||||
use old_io::{IoResult, IoError, FileStat, SeekStyle};
|
|
||||||
use old_io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append};
|
|
||||||
|
|
||||||
pub type fd_t = libc::c_int;
|
|
||||||
|
|
||||||
pub struct FileDesc {
|
|
||||||
/// The underlying C file descriptor.
|
|
||||||
pub fd: fd_t,
|
|
||||||
|
|
||||||
/// Whether to close the file descriptor on drop.
|
|
||||||
close_on_drop: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FileDesc {
|
|
||||||
pub fn new(fd: fd_t, close_on_drop: bool) -> FileDesc {
|
|
||||||
FileDesc { fd: fd, close_on_drop: close_on_drop }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read(&self, buf: &mut [u8]) -> IoResult<usize> {
|
|
||||||
let mut read = 0;
|
|
||||||
let ret = unsafe {
|
|
||||||
libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID,
|
|
||||||
buf.len() as libc::DWORD, &mut read,
|
|
||||||
ptr::null_mut())
|
|
||||||
};
|
|
||||||
if ret != 0 {
|
|
||||||
Ok(read as usize)
|
|
||||||
} else {
|
|
||||||
Err(super::last_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write(&self, buf: &[u8]) -> IoResult<()> {
|
|
||||||
let mut cur = buf.as_ptr();
|
|
||||||
let mut remaining = buf.len();
|
|
||||||
while remaining > 0 {
|
|
||||||
let mut amt = 0;
|
|
||||||
let ret = unsafe {
|
|
||||||
libc::WriteFile(self.handle(), cur as libc::LPVOID,
|
|
||||||
remaining as libc::DWORD, &mut amt,
|
|
||||||
ptr::null_mut())
|
|
||||||
};
|
|
||||||
if ret != 0 {
|
|
||||||
remaining -= amt as usize;
|
|
||||||
cur = unsafe { cur.offset(amt as isize) };
|
|
||||||
} else {
|
|
||||||
return Err(super::last_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fd(&self) -> fd_t { self.fd }
|
|
||||||
|
|
||||||
pub fn handle(&self) -> libc::HANDLE {
|
|
||||||
unsafe { libc::get_osfhandle(self.fd()) as libc::HANDLE }
|
|
||||||
}
|
|
||||||
|
|
||||||
// A version of seek that takes &self so that tell can call it
|
|
||||||
// - the private seek should of course take &mut self.
|
|
||||||
fn seek_common(&self, pos: i64, style: SeekStyle) -> IoResult<u64> {
|
|
||||||
let whence = match style {
|
|
||||||
SeekSet => libc::FILE_BEGIN,
|
|
||||||
SeekEnd => libc::FILE_END,
|
|
||||||
SeekCur => libc::FILE_CURRENT,
|
|
||||||
};
|
|
||||||
unsafe {
|
|
||||||
let mut newpos = 0;
|
|
||||||
match libc::SetFilePointerEx(self.handle(), pos, &mut newpos, whence) {
|
|
||||||
0 => Err(super::last_error()),
|
|
||||||
_ => Ok(newpos as u64),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<u64> {
|
|
||||||
self.seek_common(pos, style)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tell(&self) -> IoResult<u64> {
|
|
||||||
self.seek_common(0, SeekCur)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fsync(&mut self) -> IoResult<()> {
|
|
||||||
super::mkerr_winbool(unsafe {
|
|
||||||
libc::FlushFileBuffers(self.handle())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn datasync(&mut self) -> IoResult<()> { return self.fsync(); }
|
|
||||||
|
|
||||||
pub fn truncate(&mut self, offset: i64) -> IoResult<()> {
|
|
||||||
let orig_pos = try!(self.tell());
|
|
||||||
let _ = try!(self.seek(offset, SeekSet));
|
|
||||||
let ret = unsafe {
|
|
||||||
match libc::SetEndOfFile(self.handle()) {
|
|
||||||
0 => Err(super::last_error()),
|
|
||||||
_ => Ok(())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let _ = self.seek(orig_pos as i64, SeekSet);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fstat(&self) -> IoResult<old_io::FileStat> {
|
|
||||||
let mut stat: libc::stat = unsafe { mem::zeroed() };
|
|
||||||
match unsafe { libc::fstat(self.fd(), &mut stat) } {
|
|
||||||
0 => Ok(mkstat(&stat)),
|
|
||||||
_ => Err(super::last_error()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn unwrap(self) -> fd_t {
|
|
||||||
let fd = self.fd;
|
|
||||||
unsafe { mem::forget(self) };
|
|
||||||
fd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for FileDesc {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
// closing stdio file handles makes no sense, so never do it. Also, note
|
|
||||||
// that errors are ignored when closing a file descriptor. The reason
|
|
||||||
// for this is that if an error occurs we don't actually know if the
|
|
||||||
// file descriptor was closed or not, and if we retried (for something
|
|
||||||
// like EINTR), we might close another valid file descriptor (opened
|
|
||||||
// after we closed ours.
|
|
||||||
if self.close_on_drop && self.fd > libc::STDERR_FILENO {
|
|
||||||
let n = unsafe { libc::close(self.fd) };
|
|
||||||
if n != 0 {
|
|
||||||
println!("error {} when closing file descriptor {}", n, self.fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_utf16(s: &Path) -> IoResult<Vec<u16>> {
|
|
||||||
sys::to_utf16(s.as_str())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn open(path: &Path, fm: FileMode, fa: FileAccess) -> IoResult<FileDesc> {
|
|
||||||
// Flags passed to open_osfhandle
|
|
||||||
let flags = match fm {
|
|
||||||
Open => 0,
|
|
||||||
Append => libc::O_APPEND,
|
|
||||||
Truncate => libc::O_TRUNC,
|
|
||||||
};
|
|
||||||
let flags = match fa {
|
|
||||||
Read => flags | libc::O_RDONLY,
|
|
||||||
Write => flags | libc::O_WRONLY | libc::O_CREAT,
|
|
||||||
ReadWrite => flags | libc::O_RDWR | libc::O_CREAT,
|
|
||||||
};
|
|
||||||
let mut dwDesiredAccess = match fa {
|
|
||||||
Read => libc::FILE_GENERIC_READ,
|
|
||||||
Write => libc::FILE_GENERIC_WRITE,
|
|
||||||
ReadWrite => libc::FILE_GENERIC_READ | libc::FILE_GENERIC_WRITE
|
|
||||||
};
|
|
||||||
|
|
||||||
// libuv has a good comment about this, but the basic idea is what we try to
|
|
||||||
// emulate unix semantics by enabling all sharing by allowing things such as
|
|
||||||
// deleting a file while it's still open.
|
|
||||||
let dwShareMode = libc::FILE_SHARE_READ | libc::FILE_SHARE_WRITE |
|
|
||||||
libc::FILE_SHARE_DELETE;
|
|
||||||
|
|
||||||
let dwCreationDisposition = match (fm, fa) {
|
|
||||||
(Truncate, Read) => libc::TRUNCATE_EXISTING,
|
|
||||||
(Truncate, _) => libc::CREATE_ALWAYS,
|
|
||||||
(Open, Read) => libc::OPEN_EXISTING,
|
|
||||||
(Open, _) => libc::OPEN_ALWAYS,
|
|
||||||
(Append, Read) => {
|
|
||||||
dwDesiredAccess |= libc::FILE_APPEND_DATA;
|
|
||||||
libc::OPEN_EXISTING
|
|
||||||
}
|
|
||||||
(Append, _) => {
|
|
||||||
dwDesiredAccess &= !libc::FILE_WRITE_DATA;
|
|
||||||
dwDesiredAccess |= libc::FILE_APPEND_DATA;
|
|
||||||
libc::OPEN_ALWAYS
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut dwFlagsAndAttributes = libc::FILE_ATTRIBUTE_NORMAL;
|
|
||||||
// Compat with unix, this allows opening directories (see libuv)
|
|
||||||
dwFlagsAndAttributes |= libc::FILE_FLAG_BACKUP_SEMANTICS;
|
|
||||||
|
|
||||||
let path = try!(to_utf16(path));
|
|
||||||
let handle = unsafe {
|
|
||||||
libc::CreateFileW(path.as_ptr(),
|
|
||||||
dwDesiredAccess,
|
|
||||||
dwShareMode,
|
|
||||||
ptr::null_mut(),
|
|
||||||
dwCreationDisposition,
|
|
||||||
dwFlagsAndAttributes,
|
|
||||||
ptr::null_mut())
|
|
||||||
};
|
|
||||||
if handle == libc::INVALID_HANDLE_VALUE {
|
|
||||||
Err(super::last_error())
|
|
||||||
} else {
|
|
||||||
let fd = unsafe {
|
|
||||||
libc::open_osfhandle(handle as libc::intptr_t, flags)
|
|
||||||
};
|
|
||||||
if fd < 0 {
|
|
||||||
let _ = unsafe { libc::CloseHandle(handle) };
|
|
||||||
Err(super::last_error())
|
|
||||||
} else {
|
|
||||||
Ok(FileDesc::new(fd, true))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mkdir(p: &Path, _mode: usize) -> IoResult<()> {
|
|
||||||
let p = try!(to_utf16(p));
|
|
||||||
super::mkerr_winbool(unsafe {
|
|
||||||
// FIXME: turn mode into something useful? #2623
|
|
||||||
libc::CreateDirectoryW(p.as_ptr(), ptr::null_mut())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn readdir(p: &Path) -> IoResult<Vec<Path>> {
|
|
||||||
fn prune(root: &Path, dirs: Vec<Path>) -> Vec<Path> {
|
|
||||||
dirs.into_iter().filter(|path| {
|
|
||||||
path.as_vec() != b"." && path.as_vec() != b".."
|
|
||||||
}).map(|path| root.join(path)).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
let star = p.join("*");
|
|
||||||
let path = try!(to_utf16(&star));
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let mut wfd = mem::zeroed();
|
|
||||||
let find_handle = libc::FindFirstFileW(path.as_ptr(), &mut wfd);
|
|
||||||
if find_handle != libc::INVALID_HANDLE_VALUE {
|
|
||||||
let mut paths = vec![];
|
|
||||||
let mut more_files = 1 as libc::BOOL;
|
|
||||||
while more_files != 0 {
|
|
||||||
{
|
|
||||||
let filename = super::truncate_utf16_at_nul(&wfd.cFileName);
|
|
||||||
match String::from_utf16(filename) {
|
|
||||||
Ok(filename) => paths.push(Path::new(filename)),
|
|
||||||
Err(..) => {
|
|
||||||
assert!(libc::FindClose(find_handle) != 0);
|
|
||||||
return Err(IoError {
|
|
||||||
kind: old_io::InvalidInput,
|
|
||||||
desc: "path was not valid UTF-16",
|
|
||||||
detail: Some(format!("path was not valid UTF-16: {:?}", filename)),
|
|
||||||
})
|
|
||||||
}, // FIXME #12056: Convert the UCS-2 to invalid utf-8 instead of erroring
|
|
||||||
}
|
|
||||||
}
|
|
||||||
more_files = libc::FindNextFileW(find_handle, &mut wfd);
|
|
||||||
}
|
|
||||||
assert!(libc::FindClose(find_handle) != 0);
|
|
||||||
Ok(prune(p, paths))
|
|
||||||
} else {
|
|
||||||
Err(super::last_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unlink(p: &Path) -> IoResult<()> {
|
|
||||||
fn do_unlink(p_utf16: &Vec<u16>) -> IoResult<()> {
|
|
||||||
super::mkerr_winbool(unsafe { libc::DeleteFileW(p_utf16.as_ptr()) })
|
|
||||||
}
|
|
||||||
|
|
||||||
let p_utf16 = try!(to_utf16(p));
|
|
||||||
let res = do_unlink(&p_utf16);
|
|
||||||
match res {
|
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(e) => {
|
|
||||||
// FIXME: change the code below to use more direct calls
|
|
||||||
// than `stat` and `chmod`, to avoid re-conversion to
|
|
||||||
// utf16 etc.
|
|
||||||
|
|
||||||
// On unix, a readonly file can be successfully removed. On windows,
|
|
||||||
// however, it cannot. To keep the two platforms in line with
|
|
||||||
// respect to their behavior, catch this case on windows, attempt to
|
|
||||||
// change it to read-write, and then remove the file.
|
|
||||||
if e.kind == old_io::PermissionDenied {
|
|
||||||
let stat = match stat(p) {
|
|
||||||
Ok(stat) => stat,
|
|
||||||
Err(..) => return Err(e),
|
|
||||||
};
|
|
||||||
if stat.perm.intersects(old_io::USER_WRITE) { return Err(e) }
|
|
||||||
|
|
||||||
match chmod(p, (stat.perm | old_io::USER_WRITE).bits() as usize) {
|
|
||||||
Ok(()) => do_unlink(&p_utf16),
|
|
||||||
Err(..) => {
|
|
||||||
// Try to put it back as we found it
|
|
||||||
let _ = chmod(p, stat.perm.bits() as usize);
|
|
||||||
Err(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rename(old: &Path, new: &Path) -> IoResult<()> {
|
|
||||||
let old = try!(to_utf16(old));
|
|
||||||
let new = try!(to_utf16(new));
|
|
||||||
super::mkerr_winbool(unsafe {
|
|
||||||
libc::MoveFileExW(old.as_ptr(), new.as_ptr(), libc::MOVEFILE_REPLACE_EXISTING)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn chmod(p: &Path, mode: usize) -> IoResult<()> {
|
|
||||||
let p = try!(to_utf16(p));
|
|
||||||
mkerr_libc(unsafe {
|
|
||||||
libc::wchmod(p.as_ptr(), mode as libc::c_int)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rmdir(p: &Path) -> IoResult<()> {
|
|
||||||
let p = try!(to_utf16(p));
|
|
||||||
super::mkerr_winbool(unsafe { libc::RemoveDirectoryW(p.as_ptr()) })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn chown(_p: &Path, _uid: isize, _gid: isize) -> IoResult<()> {
|
|
||||||
// libuv has this as a no-op, so seems like this should as well?
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn readlink(p: &Path) -> IoResult<Path> {
|
|
||||||
// FIXME: I have a feeling that this reads intermediate symlinks as well.
|
|
||||||
use sys::c::compat::kernel32::GetFinalPathNameByHandleW;
|
|
||||||
let p = try!(to_utf16(p));
|
|
||||||
let handle = unsafe {
|
|
||||||
libc::CreateFileW(p.as_ptr(),
|
|
||||||
libc::GENERIC_READ,
|
|
||||||
libc::FILE_SHARE_READ,
|
|
||||||
ptr::null_mut(),
|
|
||||||
libc::OPEN_EXISTING,
|
|
||||||
libc::FILE_ATTRIBUTE_NORMAL,
|
|
||||||
ptr::null_mut())
|
|
||||||
};
|
|
||||||
if handle == libc::INVALID_HANDLE_VALUE {
|
|
||||||
return Err(super::last_error())
|
|
||||||
}
|
|
||||||
// Specify (sz - 1) because the documentation states that it's the size
|
|
||||||
// without the null pointer
|
|
||||||
let ret = super::fill_utf16_buf(|buf, sz| unsafe {
|
|
||||||
GetFinalPathNameByHandleW(handle,
|
|
||||||
buf as *const u16,
|
|
||||||
sz - 1,
|
|
||||||
libc::VOLUME_NAME_DOS)
|
|
||||||
}, |data| {
|
|
||||||
Path::new(String::from_utf16(data).unwrap())
|
|
||||||
});
|
|
||||||
assert!(unsafe { libc::CloseHandle(handle) } != 0);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
|
|
||||||
use sys::c::compat::kernel32::CreateSymbolicLinkW;
|
|
||||||
let src = try!(to_utf16(src));
|
|
||||||
let dst = try!(to_utf16(dst));
|
|
||||||
super::mkerr_winbool(unsafe {
|
|
||||||
CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), 0) as libc::BOOL
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn link(src: &Path, dst: &Path) -> IoResult<()> {
|
|
||||||
let src = try!(to_utf16(src));
|
|
||||||
let dst = try!(to_utf16(dst));
|
|
||||||
super::mkerr_winbool(unsafe {
|
|
||||||
libc::CreateHardLinkW(dst.as_ptr(), src.as_ptr(), ptr::null_mut())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mkstat(stat: &libc::stat) -> FileStat {
|
|
||||||
FileStat {
|
|
||||||
size: stat.st_size as u64,
|
|
||||||
kind: match (stat.st_mode as libc::c_int) & libc::S_IFMT {
|
|
||||||
libc::S_IFREG => old_io::FileType::RegularFile,
|
|
||||||
libc::S_IFDIR => old_io::FileType::Directory,
|
|
||||||
libc::S_IFIFO => old_io::FileType::NamedPipe,
|
|
||||||
libc::S_IFBLK => old_io::FileType::BlockSpecial,
|
|
||||||
libc::S_IFLNK => old_io::FileType::Symlink,
|
|
||||||
_ => old_io::FileType::Unknown,
|
|
||||||
},
|
|
||||||
perm: FilePermission::from_bits_truncate(stat.st_mode as u32),
|
|
||||||
created: stat.st_ctime as u64,
|
|
||||||
modified: stat.st_mtime as u64,
|
|
||||||
accessed: stat.st_atime as u64,
|
|
||||||
unstable: UnstableFileStat {
|
|
||||||
device: stat.st_dev as u64,
|
|
||||||
inode: stat.st_ino as u64,
|
|
||||||
rdev: stat.st_rdev as u64,
|
|
||||||
nlink: stat.st_nlink as u64,
|
|
||||||
uid: stat.st_uid as u64,
|
|
||||||
gid: stat.st_gid as u64,
|
|
||||||
blksize:0,
|
|
||||||
blocks: 0,
|
|
||||||
flags: 0,
|
|
||||||
gen: 0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn stat(p: &Path) -> IoResult<FileStat> {
|
|
||||||
let mut stat: libc::stat = unsafe { mem::zeroed() };
|
|
||||||
let p = try!(to_utf16(p));
|
|
||||||
match unsafe { libc::wstat(p.as_ptr(), &mut stat) } {
|
|
||||||
0 => Ok(mkstat(&stat)),
|
|
||||||
_ => Err(super::last_error()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: move this to platform-specific modules (for now)?
|
|
||||||
pub fn lstat(_p: &Path) -> IoResult<FileStat> {
|
|
||||||
// FIXME: implementation is missing
|
|
||||||
Err(sys_common::unimpl())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn utime(p: &Path, atime: u64, mtime: u64) -> IoResult<()> {
|
|
||||||
let mut buf = libc::utimbuf {
|
|
||||||
actime: atime as libc::time64_t,
|
|
||||||
modtime: mtime as libc::time64_t,
|
|
||||||
};
|
|
||||||
let p = try!(to_utf16(p));
|
|
||||||
mkerr_libc(unsafe {
|
|
||||||
libc::wutime(p.as_ptr(), &mut buf)
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
// Copyright 2014 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 libc::{self, BOOL, LPCSTR, HANDLE, LPSECURITY_ATTRIBUTES, CloseHandle};
|
|
||||||
use ptr;
|
|
||||||
|
|
||||||
pub type signal = HANDLE;
|
|
||||||
|
|
||||||
pub fn new() -> (HANDLE, HANDLE) {
|
|
||||||
unsafe {
|
|
||||||
let handle = CreateEventA(ptr::null_mut(), libc::FALSE, libc::FALSE,
|
|
||||||
ptr::null());
|
|
||||||
(handle, handle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn signal(handle: HANDLE) {
|
|
||||||
assert!(unsafe { SetEvent(handle) != 0 });
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn close(handle: HANDLE) {
|
|
||||||
assert!(unsafe { CloseHandle(handle) != 0 });
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "system" {
|
|
||||||
fn CreateEventA(lpSecurityAttributes: LPSECURITY_ATTRIBUTES,
|
|
||||||
bManualReset: BOOL,
|
|
||||||
bInitialState: BOOL,
|
|
||||||
lpName: LPCSTR) -> HANDLE;
|
|
||||||
fn SetEvent(hEvent: HANDLE) -> BOOL;
|
|
||||||
}
|
|
@ -17,136 +17,31 @@ use prelude::v1::*;
|
|||||||
use ffi::{OsStr, OsString};
|
use ffi::{OsStr, OsString};
|
||||||
use io::{self, ErrorKind};
|
use io::{self, ErrorKind};
|
||||||
use libc;
|
use libc;
|
||||||
use mem;
|
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
use num::Int;
|
use num::Int;
|
||||||
use old_io::{self, IoResult, IoError};
|
|
||||||
use os::windows::ffi::{OsStrExt, OsStringExt};
|
use os::windows::ffi::{OsStrExt, OsStringExt};
|
||||||
use path::PathBuf;
|
use path::PathBuf;
|
||||||
use sync::{Once, ONCE_INIT};
|
|
||||||
|
|
||||||
pub mod backtrace;
|
pub mod backtrace;
|
||||||
pub mod c;
|
pub mod c;
|
||||||
pub mod condvar;
|
pub mod condvar;
|
||||||
pub mod ext;
|
pub mod ext;
|
||||||
pub mod fs;
|
|
||||||
pub mod fs2;
|
pub mod fs2;
|
||||||
pub mod handle;
|
pub mod handle;
|
||||||
pub mod helper_signal;
|
|
||||||
pub mod mutex;
|
pub mod mutex;
|
||||||
pub mod net;
|
pub mod net;
|
||||||
pub mod os;
|
pub mod os;
|
||||||
pub mod os_str;
|
pub mod os_str;
|
||||||
pub mod pipe;
|
|
||||||
pub mod pipe2;
|
pub mod pipe2;
|
||||||
pub mod process;
|
|
||||||
pub mod process2;
|
pub mod process2;
|
||||||
pub mod rwlock;
|
pub mod rwlock;
|
||||||
pub mod stack_overflow;
|
pub mod stack_overflow;
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
pub mod tcp;
|
|
||||||
pub mod thread;
|
pub mod thread;
|
||||||
pub mod thread_local;
|
pub mod thread_local;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
pub mod timer;
|
|
||||||
pub mod tty;
|
|
||||||
pub mod udp;
|
|
||||||
pub mod stdio;
|
pub mod stdio;
|
||||||
|
|
||||||
pub mod addrinfo {
|
|
||||||
pub use sys_common::net::get_host_addresses;
|
|
||||||
pub use sys_common::net::get_address_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: move these to c module
|
|
||||||
pub type sock_t = libc::SOCKET;
|
|
||||||
pub type wrlen = libc::c_int;
|
|
||||||
pub type msglen_t = libc::c_int;
|
|
||||||
pub unsafe fn close_sock(sock: sock_t) { let _ = libc::closesocket(sock); }
|
|
||||||
|
|
||||||
// windows has zero values as errors
|
|
||||||
#[allow(deprecated)]
|
|
||||||
fn mkerr_winbool(ret: libc::c_int) -> IoResult<()> {
|
|
||||||
if ret == 0 {
|
|
||||||
Err(last_error())
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn last_error() -> IoError {
|
|
||||||
let errno = os::errno() as i32;
|
|
||||||
let mut err = decode_error(errno);
|
|
||||||
err.detail = Some(os::error_string(errno));
|
|
||||||
err
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn last_net_error() -> IoError {
|
|
||||||
let errno = unsafe { c::WSAGetLastError() as i32 };
|
|
||||||
let mut err = decode_error(errno);
|
|
||||||
err.detail = Some(os::error_string(errno));
|
|
||||||
err
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn last_gai_error(_errno: i32) -> IoError {
|
|
||||||
last_net_error()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert an `errno` value into a high-level error variant and description.
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn decode_error(errno: i32) -> IoError {
|
|
||||||
let (kind, desc) = match errno {
|
|
||||||
libc::EOF => (old_io::EndOfFile, "end of file"),
|
|
||||||
libc::ERROR_NO_DATA => (old_io::BrokenPipe, "the pipe is being closed"),
|
|
||||||
libc::ERROR_FILE_NOT_FOUND => (old_io::FileNotFound, "file not found"),
|
|
||||||
libc::ERROR_INVALID_NAME => (old_io::InvalidInput, "invalid file name"),
|
|
||||||
libc::WSAECONNREFUSED => (old_io::ConnectionRefused, "connection refused"),
|
|
||||||
libc::WSAECONNRESET => (old_io::ConnectionReset, "connection reset"),
|
|
||||||
libc::ERROR_ACCESS_DENIED | libc::WSAEACCES =>
|
|
||||||
(old_io::PermissionDenied, "permission denied"),
|
|
||||||
libc::WSAEWOULDBLOCK => {
|
|
||||||
(old_io::ResourceUnavailable, "resource temporarily unavailable")
|
|
||||||
}
|
|
||||||
libc::WSAENOTCONN => (old_io::NotConnected, "not connected"),
|
|
||||||
libc::WSAECONNABORTED => (old_io::ConnectionAborted, "connection aborted"),
|
|
||||||
libc::WSAEADDRNOTAVAIL => (old_io::ConnectionRefused, "address not available"),
|
|
||||||
libc::WSAEADDRINUSE => (old_io::ConnectionRefused, "address in use"),
|
|
||||||
libc::ERROR_BROKEN_PIPE => (old_io::EndOfFile, "the pipe has ended"),
|
|
||||||
libc::ERROR_OPERATION_ABORTED =>
|
|
||||||
(old_io::TimedOut, "operation timed out"),
|
|
||||||
libc::WSAEINVAL => (old_io::InvalidInput, "invalid argument"),
|
|
||||||
libc::ERROR_CALL_NOT_IMPLEMENTED =>
|
|
||||||
(old_io::IoUnavailable, "function not implemented"),
|
|
||||||
libc::ERROR_INVALID_HANDLE =>
|
|
||||||
(old_io::MismatchedFileTypeForOperation,
|
|
||||||
"invalid handle provided to function"),
|
|
||||||
libc::ERROR_NOTHING_TO_TERMINATE =>
|
|
||||||
(old_io::InvalidInput, "no process to kill"),
|
|
||||||
libc::ERROR_ALREADY_EXISTS =>
|
|
||||||
(old_io::PathAlreadyExists, "path already exists"),
|
|
||||||
|
|
||||||
// libuv maps this error code to EISDIR. we do too. if it is found
|
|
||||||
// to be incorrect, we can add in some more machinery to only
|
|
||||||
// return this message when ERROR_INVALID_FUNCTION after certain
|
|
||||||
// Windows calls.
|
|
||||||
libc::ERROR_INVALID_FUNCTION => (old_io::InvalidInput,
|
|
||||||
"illegal operation on a directory"),
|
|
||||||
|
|
||||||
_ => (old_io::OtherIoError, "unknown error")
|
|
||||||
};
|
|
||||||
IoError { kind: kind, desc: desc, detail: None }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn decode_error_detailed(errno: i32) -> IoError {
|
|
||||||
let mut err = decode_error(errno);
|
|
||||||
err.detail = Some(os::error_string(errno));
|
|
||||||
err
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn decode_error_kind(errno: i32) -> ErrorKind {
|
pub fn decode_error_kind(errno: i32) -> ErrorKind {
|
||||||
match errno as libc::c_int {
|
match errno as libc::c_int {
|
||||||
libc::ERROR_ACCESS_DENIED => ErrorKind::PermissionDenied,
|
libc::ERROR_ACCESS_DENIED => ErrorKind::PermissionDenied,
|
||||||
@ -170,58 +65,6 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn retry<I, F>(f: F) -> I where F: FnOnce() -> I { f() } // PR rust-lang/rust/#17020
|
|
||||||
|
|
||||||
pub fn ms_to_timeval(ms: u64) -> libc::timeval {
|
|
||||||
libc::timeval {
|
|
||||||
tv_sec: (ms / 1000) as libc::c_long,
|
|
||||||
tv_usec: ((ms % 1000) * 1000) as libc::c_long,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn wouldblock() -> bool {
|
|
||||||
let err = os::errno();
|
|
||||||
err == libc::WSAEWOULDBLOCK as i32
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn set_nonblocking(fd: sock_t, nb: bool) {
|
|
||||||
let mut set = nb as libc::c_ulong;
|
|
||||||
if unsafe { c::ioctlsocket(fd, c::FIONBIO, &mut set) } != 0 {
|
|
||||||
// The above function should not return an error unless we passed it
|
|
||||||
// invalid parameters. Panic on errors.
|
|
||||||
panic!("set_nonblocking called with invalid parameters: {}", last_error());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init_net() {
|
|
||||||
unsafe {
|
|
||||||
static START: Once = ONCE_INIT;
|
|
||||||
|
|
||||||
START.call_once(|| {
|
|
||||||
let mut data: c::WSADATA = mem::zeroed();
|
|
||||||
let ret = c::WSAStartup(0x202, // version 2.2
|
|
||||||
&mut data);
|
|
||||||
assert_eq!(ret, 0);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn to_utf16(s: Option<&str>) -> IoResult<Vec<u16>> {
|
|
||||||
match s {
|
|
||||||
Some(s) => Ok(to_utf16_os(OsStr::from_str(s))),
|
|
||||||
None => Err(IoError {
|
|
||||||
kind: old_io::InvalidInput,
|
|
||||||
desc: "valid unicode input required",
|
|
||||||
detail: None,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_utf16_os(s: &OsStr) -> Vec<u16> {
|
fn to_utf16_os(s: &OsStr) -> Vec<u16> {
|
||||||
let mut v: Vec<_> = s.encode_wide().collect();
|
let mut v: Vec<_> = s.encode_wide().collect();
|
||||||
v.push(0);
|
v.push(0);
|
||||||
@ -242,7 +85,7 @@ fn to_utf16_os(s: &OsStr) -> Vec<u16> {
|
|||||||
// Once the syscall has completed (errors bail out early) the second closure is
|
// Once the syscall has completed (errors bail out early) the second closure is
|
||||||
// yielded the data which has been read from the syscall. The return value
|
// yielded the data which has been read from the syscall. The return value
|
||||||
// from this closure is then the return value of the function.
|
// from this closure is then the return value of the function.
|
||||||
fn fill_utf16_buf_base<F1, F2, T>(mut f1: F1, f2: F2) -> Result<T, ()>
|
fn fill_utf16_buf<F1, F2, T>(mut f1: F1, f2: F2) -> io::Result<T>
|
||||||
where F1: FnMut(*mut u16, libc::DWORD) -> libc::DWORD,
|
where F1: FnMut(*mut u16, libc::DWORD) -> libc::DWORD,
|
||||||
F2: FnOnce(&[u16]) -> T
|
F2: FnOnce(&[u16]) -> T
|
||||||
{
|
{
|
||||||
@ -274,7 +117,7 @@ fn fill_utf16_buf_base<F1, F2, T>(mut f1: F1, f2: F2) -> Result<T, ()>
|
|||||||
c::SetLastError(0);
|
c::SetLastError(0);
|
||||||
let k = match f1(buf.as_mut_ptr(), n as libc::DWORD) {
|
let k = match f1(buf.as_mut_ptr(), n as libc::DWORD) {
|
||||||
0 if libc::GetLastError() == 0 => 0,
|
0 if libc::GetLastError() == 0 => 0,
|
||||||
0 => return Err(()),
|
0 => return Err(io::Error::last_os_error()),
|
||||||
n => n,
|
n => n,
|
||||||
} as usize;
|
} as usize;
|
||||||
if k == n && libc::GetLastError() ==
|
if k == n && libc::GetLastError() ==
|
||||||
@ -289,21 +132,6 @@ fn fill_utf16_buf_base<F1, F2, T>(mut f1: F1, f2: F2) -> Result<T, ()>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
fn fill_utf16_buf<F1, F2, T>(f1: F1, f2: F2) -> IoResult<T>
|
|
||||||
where F1: FnMut(*mut u16, libc::DWORD) -> libc::DWORD,
|
|
||||||
F2: FnOnce(&[u16]) -> T
|
|
||||||
{
|
|
||||||
fill_utf16_buf_base(f1, f2).map_err(|()| IoError::last_error())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fill_utf16_buf_new<F1, F2, T>(f1: F1, f2: F2) -> io::Result<T>
|
|
||||||
where F1: FnMut(*mut u16, libc::DWORD) -> libc::DWORD,
|
|
||||||
F2: FnOnce(&[u16]) -> T
|
|
||||||
{
|
|
||||||
fill_utf16_buf_base(f1, f2).map_err(|()| io::Error::last_os_error())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn os2path(s: &[u16]) -> PathBuf {
|
fn os2path(s: &[u16]) -> PathBuf {
|
||||||
PathBuf::from(OsString::from_wide(s))
|
PathBuf::from(OsString::from_wide(s))
|
||||||
}
|
}
|
||||||
|
@ -22,15 +22,12 @@ use io;
|
|||||||
use libc::types::os::arch::extra::LPWCH;
|
use libc::types::os::arch::extra::LPWCH;
|
||||||
use libc::{self, c_int, c_void};
|
use libc::{self, c_int, c_void};
|
||||||
use mem;
|
use mem;
|
||||||
#[allow(deprecated)]
|
|
||||||
use old_io::{IoError, IoResult};
|
|
||||||
use ops::Range;
|
use ops::Range;
|
||||||
use os::windows::ffi::EncodeWide;
|
use os::windows::ffi::EncodeWide;
|
||||||
use path::{self, PathBuf};
|
use path::{self, PathBuf};
|
||||||
use ptr;
|
use ptr;
|
||||||
use slice;
|
use slice;
|
||||||
use sys::c;
|
use sys::c;
|
||||||
use sys::fs::FileDesc;
|
|
||||||
use sys::handle::Handle;
|
use sys::handle::Handle;
|
||||||
|
|
||||||
use libc::funcs::extra::kernel32::{
|
use libc::funcs::extra::kernel32::{
|
||||||
@ -233,13 +230,13 @@ impl StdError for JoinPathsError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_exe() -> io::Result<PathBuf> {
|
pub fn current_exe() -> io::Result<PathBuf> {
|
||||||
super::fill_utf16_buf_new(|buf, sz| unsafe {
|
super::fill_utf16_buf(|buf, sz| unsafe {
|
||||||
libc::GetModuleFileNameW(ptr::null_mut(), buf, sz)
|
libc::GetModuleFileNameW(ptr::null_mut(), buf, sz)
|
||||||
}, super::os2path)
|
}, super::os2path)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getcwd() -> io::Result<PathBuf> {
|
pub fn getcwd() -> io::Result<PathBuf> {
|
||||||
super::fill_utf16_buf_new(|buf, sz| unsafe {
|
super::fill_utf16_buf(|buf, sz| unsafe {
|
||||||
libc::GetCurrentDirectoryW(sz, buf)
|
libc::GetCurrentDirectoryW(sz, buf)
|
||||||
}, super::os2path)
|
}, super::os2path)
|
||||||
}
|
}
|
||||||
@ -259,7 +256,7 @@ pub fn chdir(p: &path::Path) -> io::Result<()> {
|
|||||||
|
|
||||||
pub fn getenv(k: &OsStr) -> Option<OsString> {
|
pub fn getenv(k: &OsStr) -> Option<OsString> {
|
||||||
let k = super::to_utf16_os(k);
|
let k = super::to_utf16_os(k);
|
||||||
super::fill_utf16_buf_new(|buf, sz| unsafe {
|
super::fill_utf16_buf(|buf, sz| unsafe {
|
||||||
libc::GetEnvironmentVariableW(k.as_ptr(), buf, sz)
|
libc::GetEnvironmentVariableW(k.as_ptr(), buf, sz)
|
||||||
}, |buf| {
|
}, |buf| {
|
||||||
OsStringExt::from_wide(buf)
|
OsStringExt::from_wide(buf)
|
||||||
@ -336,27 +333,8 @@ pub fn page_size() -> usize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> {
|
|
||||||
// Windows pipes work subtly differently than unix pipes, and their
|
|
||||||
// inheritance has to be handled in a different way that I do not
|
|
||||||
// fully understand. Here we explicitly make the pipe non-inheritable,
|
|
||||||
// which means to pass it to a subprocess they need to be duplicated
|
|
||||||
// first, as in std::run.
|
|
||||||
let mut fds = [0; 2];
|
|
||||||
match libc::pipe(fds.as_mut_ptr(), 1024 as ::libc::c_uint,
|
|
||||||
(libc::O_BINARY | libc::O_NOINHERIT) as c_int) {
|
|
||||||
0 => {
|
|
||||||
assert!(fds[0] != -1 && fds[0] != 0);
|
|
||||||
assert!(fds[1] != -1 && fds[1] != 0);
|
|
||||||
Ok((FileDesc::new(fds[0], true), FileDesc::new(fds[1], true)))
|
|
||||||
}
|
|
||||||
_ => Err(IoError::last_error()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn temp_dir() -> PathBuf {
|
pub fn temp_dir() -> PathBuf {
|
||||||
super::fill_utf16_buf_new(|buf, sz| unsafe {
|
super::fill_utf16_buf(|buf, sz| unsafe {
|
||||||
c::GetTempPathW(sz, buf)
|
c::GetTempPathW(sz, buf)
|
||||||
}, super::os2path).unwrap()
|
}, super::os2path).unwrap()
|
||||||
}
|
}
|
||||||
@ -371,7 +349,7 @@ pub fn home_dir() -> Option<PathBuf> {
|
|||||||
return None
|
return None
|
||||||
}
|
}
|
||||||
let _handle = Handle::new(token);
|
let _handle = Handle::new(token);
|
||||||
super::fill_utf16_buf_new(|buf, mut sz| {
|
super::fill_utf16_buf(|buf, mut sz| {
|
||||||
match c::GetUserProfileDirectoryW(token, buf, &mut sz) {
|
match c::GetUserProfileDirectoryW(token, buf, &mut sz) {
|
||||||
0 if libc::GetLastError() != 0 => 0,
|
0 if libc::GetLastError() != 0 => 0,
|
||||||
0 => sz,
|
0 => sz,
|
||||||
|
@ -1,775 +0,0 @@
|
|||||||
// Copyright 2014 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.
|
|
||||||
|
|
||||||
//! Named pipes implementation for windows
|
|
||||||
//!
|
|
||||||
//! If are unfortunate enough to be reading this code, I would like to first
|
|
||||||
//! apologize. This was my first encounter with windows named pipes, and it
|
|
||||||
//! didn't exactly turn out very cleanly. If you, too, are new to named pipes,
|
|
||||||
//! read on as I'll try to explain some fun things that I ran into.
|
|
||||||
//!
|
|
||||||
//! # Unix pipes vs Named pipes
|
|
||||||
//!
|
|
||||||
//! As with everything else, named pipes on windows are pretty different from
|
|
||||||
//! unix pipes on unix. On unix, you use one "server pipe" to accept new client
|
|
||||||
//! pipes. So long as this server pipe is active, new children pipes can
|
|
||||||
//! connect. On windows, you instead have a number of "server pipes", and each
|
|
||||||
//! of these server pipes can throughout their lifetime be attached to a client
|
|
||||||
//! or not. Once attached to a client, a server pipe may then disconnect at a
|
|
||||||
//! later date.
|
|
||||||
//!
|
|
||||||
//! # Accepting clients
|
|
||||||
//!
|
|
||||||
//! As with most other I/O interfaces, our Listener/Acceptor/Stream interfaces
|
|
||||||
//! are built around the unix flavors. This means that we have one "server
|
|
||||||
//! pipe" to which many clients can connect. In order to make this compatible
|
|
||||||
//! with the windows model, each connected client consumes ownership of a server
|
|
||||||
//! pipe, and then a new server pipe is created for the next client.
|
|
||||||
//!
|
|
||||||
//! Note that the server pipes attached to clients are never given back to the
|
|
||||||
//! listener for recycling. This could possibly be implemented with a channel so
|
|
||||||
//! the listener half can re-use server pipes, but for now I err'd on the simple
|
|
||||||
//! side of things. Each stream accepted by a listener will destroy the server
|
|
||||||
//! pipe after the stream is dropped.
|
|
||||||
//!
|
|
||||||
//! This model ends up having a small race or two, and you can find more details
|
|
||||||
//! on the `native_accept` method.
|
|
||||||
//!
|
|
||||||
//! # Simultaneous reads and writes
|
|
||||||
//!
|
|
||||||
//! In testing, I found that two simultaneous writes and two simultaneous reads
|
|
||||||
//! on a pipe ended up working out just fine, but problems were encountered when
|
|
||||||
//! a read was executed simultaneously with a write. After some googling around,
|
|
||||||
//! it sounded like named pipes just weren't built for this kind of interaction,
|
|
||||||
//! and the suggested solution was to use overlapped I/O.
|
|
||||||
//!
|
|
||||||
//! I don't really know what overlapped I/O is, but my basic understanding after
|
|
||||||
//! reading about it is that you have an external Event which is used to signal
|
|
||||||
//! I/O completion, passed around in some OVERLAPPED structures. As to what this
|
|
||||||
//! is, I'm not exactly sure.
|
|
||||||
//!
|
|
||||||
//! This problem implies that all named pipes are created with the
|
|
||||||
//! FILE_FLAG_OVERLAPPED option. This means that all of their I/O is
|
|
||||||
//! asynchronous. Each I/O operation has an associated OVERLAPPED structure, and
|
|
||||||
//! inside of this structure is a HANDLE from CreateEvent. After the I/O is
|
|
||||||
//! determined to be pending (may complete in the future), the
|
|
||||||
//! GetOverlappedResult function is used to block on the event, waiting for the
|
|
||||||
//! I/O to finish.
|
|
||||||
//!
|
|
||||||
//! This scheme ended up working well enough. There were two snags that I ran
|
|
||||||
//! into, however:
|
|
||||||
//!
|
|
||||||
//! * Each UnixStream instance needs its own read/write events to wait on. These
|
|
||||||
//! can't be shared among clones of the same stream because the documentation
|
|
||||||
//! states that it unsets the event when the I/O is started (would possibly
|
|
||||||
//! corrupt other events simultaneously waiting). For convenience's sake,
|
|
||||||
//! these events are lazily initialized.
|
|
||||||
//!
|
|
||||||
//! * Each server pipe needs to be created with FILE_FLAG_OVERLAPPED in addition
|
|
||||||
//! to all pipes created through `connect`. Notably this means that the
|
|
||||||
//! ConnectNamedPipe function is nonblocking, implying that the Listener needs
|
|
||||||
//! to have yet another event to do the actual blocking.
|
|
||||||
//!
|
|
||||||
//! # Conclusion
|
|
||||||
//!
|
|
||||||
//! The conclusion here is that I probably don't know the best way to work with
|
|
||||||
//! windows named pipes, but the solution here seems to work well enough to get
|
|
||||||
//! the test suite passing (the suite is in libstd), and that's good enough for
|
|
||||||
//! me!
|
|
||||||
|
|
||||||
#![allow(deprecated)]
|
|
||||||
|
|
||||||
use prelude::v1::*;
|
|
||||||
|
|
||||||
use libc;
|
|
||||||
use ffi::CString;
|
|
||||||
use old_io::{self, IoError, IoResult};
|
|
||||||
use mem;
|
|
||||||
use ptr;
|
|
||||||
use str;
|
|
||||||
use sync::atomic::{AtomicBool, Ordering};
|
|
||||||
use sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
use sys_common::{self, eof};
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
impl Event {
|
|
||||||
fn new(manual_reset: bool, initial_state: bool) -> IoResult<Event> {
|
|
||||||
let event = unsafe {
|
|
||||||
libc::CreateEventW(ptr::null_mut(),
|
|
||||||
manual_reset as libc::BOOL,
|
|
||||||
initial_state as libc::BOOL,
|
|
||||||
ptr::null())
|
|
||||||
};
|
|
||||||
if event as usize == 0 {
|
|
||||||
Err(super::last_error())
|
|
||||||
} else {
|
|
||||||
Ok(Event(event))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle(&self) -> libc::HANDLE { let Event(handle) = *self; handle }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Event {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe { let _ = libc::CloseHandle(self.handle()); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for Event {}
|
|
||||||
unsafe impl Sync for Event {}
|
|
||||||
|
|
||||||
struct Inner {
|
|
||||||
handle: libc::HANDLE,
|
|
||||||
lock: Mutex<()>,
|
|
||||||
read_closed: AtomicBool,
|
|
||||||
write_closed: AtomicBool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Inner {
|
|
||||||
fn new(handle: libc::HANDLE) -> Inner {
|
|
||||||
Inner {
|
|
||||||
handle: handle,
|
|
||||||
lock: Mutex::new(()),
|
|
||||||
read_closed: AtomicBool::new(false),
|
|
||||||
write_closed: AtomicBool::new(false),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Inner {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
let _ = libc::FlushFileBuffers(self.handle);
|
|
||||||
let _ = libc::CloseHandle(self.handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for Inner {}
|
|
||||||
unsafe impl Sync for Inner {}
|
|
||||||
|
|
||||||
unsafe fn pipe(name: *const u16, init: bool) -> libc::HANDLE {
|
|
||||||
libc::CreateNamedPipeW(
|
|
||||||
name,
|
|
||||||
libc::PIPE_ACCESS_DUPLEX |
|
|
||||||
if init {libc::FILE_FLAG_FIRST_PIPE_INSTANCE} else {0} |
|
|
||||||
libc::FILE_FLAG_OVERLAPPED,
|
|
||||||
libc::PIPE_TYPE_BYTE | libc::PIPE_READMODE_BYTE |
|
|
||||||
libc::PIPE_WAIT,
|
|
||||||
libc::PIPE_UNLIMITED_INSTANCES,
|
|
||||||
65536,
|
|
||||||
65536,
|
|
||||||
0,
|
|
||||||
ptr::null_mut()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn await(handle: libc::HANDLE, deadline: u64,
|
|
||||||
events: &[libc::HANDLE]) -> IoResult<usize> {
|
|
||||||
use libc::consts::os::extra::{WAIT_FAILED, WAIT_TIMEOUT, WAIT_OBJECT_0};
|
|
||||||
|
|
||||||
// If we've got a timeout, use WaitForSingleObject in tandem with CancelIo
|
|
||||||
// to figure out if we should indeed get the result.
|
|
||||||
let ms = if deadline == 0 {
|
|
||||||
libc::INFINITE as u64
|
|
||||||
} else {
|
|
||||||
let now = timer::now();
|
|
||||||
if deadline < now {0} else {deadline - now}
|
|
||||||
};
|
|
||||||
let ret = unsafe {
|
|
||||||
c::WaitForMultipleObjects(events.len() as libc::DWORD,
|
|
||||||
events.as_ptr(),
|
|
||||||
libc::FALSE,
|
|
||||||
ms as libc::DWORD)
|
|
||||||
};
|
|
||||||
match ret {
|
|
||||||
WAIT_FAILED => Err(super::last_error()),
|
|
||||||
WAIT_TIMEOUT => unsafe {
|
|
||||||
let _ = c::CancelIo(handle);
|
|
||||||
Err(sys_common::timeout("operation timed out"))
|
|
||||||
},
|
|
||||||
n => Ok((n - WAIT_OBJECT_0) as usize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn epipe() -> IoError {
|
|
||||||
IoError {
|
|
||||||
kind: old_io::EndOfFile,
|
|
||||||
desc: "the pipe has ended",
|
|
||||||
detail: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Unix Streams
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
pub struct UnixStream {
|
|
||||||
inner: Arc<Inner>,
|
|
||||||
write: Option<Event>,
|
|
||||||
read: Option<Event>,
|
|
||||||
read_deadline: u64,
|
|
||||||
write_deadline: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UnixStream {
|
|
||||||
fn try_connect(p: *const u16) -> Option<libc::HANDLE> {
|
|
||||||
// Note that most of this is lifted from the libuv implementation.
|
|
||||||
// The idea is that if we fail to open a pipe in read/write mode
|
|
||||||
// that we try afterwards in just read or just write
|
|
||||||
let mut result = unsafe {
|
|
||||||
libc::CreateFileW(p,
|
|
||||||
libc::GENERIC_READ | libc::GENERIC_WRITE,
|
|
||||||
0,
|
|
||||||
ptr::null_mut(),
|
|
||||||
libc::OPEN_EXISTING,
|
|
||||||
libc::FILE_FLAG_OVERLAPPED,
|
|
||||||
ptr::null_mut())
|
|
||||||
};
|
|
||||||
if result != libc::INVALID_HANDLE_VALUE {
|
|
||||||
return Some(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
let err = unsafe { libc::GetLastError() };
|
|
||||||
if err == libc::ERROR_ACCESS_DENIED as libc::DWORD {
|
|
||||||
result = unsafe {
|
|
||||||
libc::CreateFileW(p,
|
|
||||||
libc::GENERIC_READ | libc::FILE_WRITE_ATTRIBUTES,
|
|
||||||
0,
|
|
||||||
ptr::null_mut(),
|
|
||||||
libc::OPEN_EXISTING,
|
|
||||||
libc::FILE_FLAG_OVERLAPPED,
|
|
||||||
ptr::null_mut())
|
|
||||||
};
|
|
||||||
if result != libc::INVALID_HANDLE_VALUE {
|
|
||||||
return Some(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let err = unsafe { libc::GetLastError() };
|
|
||||||
if err == libc::ERROR_ACCESS_DENIED as libc::DWORD {
|
|
||||||
result = unsafe {
|
|
||||||
libc::CreateFileW(p,
|
|
||||||
libc::GENERIC_WRITE | libc::FILE_READ_ATTRIBUTES,
|
|
||||||
0,
|
|
||||||
ptr::null_mut(),
|
|
||||||
libc::OPEN_EXISTING,
|
|
||||||
libc::FILE_FLAG_OVERLAPPED,
|
|
||||||
ptr::null_mut())
|
|
||||||
};
|
|
||||||
if result != libc::INVALID_HANDLE_VALUE {
|
|
||||||
return Some(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn connect(addr: &CString, timeout: Option<u64>) -> IoResult<UnixStream> {
|
|
||||||
let addr = try!(to_utf16(addr));
|
|
||||||
let start = timer::now();
|
|
||||||
loop {
|
|
||||||
match UnixStream::try_connect(addr.as_ptr()) {
|
|
||||||
Some(handle) => {
|
|
||||||
let inner = Inner::new(handle);
|
|
||||||
let mut mode = libc::PIPE_TYPE_BYTE |
|
|
||||||
libc::PIPE_READMODE_BYTE |
|
|
||||||
libc::PIPE_WAIT;
|
|
||||||
let ret = unsafe {
|
|
||||||
libc::SetNamedPipeHandleState(inner.handle,
|
|
||||||
&mut mode,
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut())
|
|
||||||
};
|
|
||||||
return if ret == 0 {
|
|
||||||
Err(super::last_error())
|
|
||||||
} else {
|
|
||||||
Ok(UnixStream {
|
|
||||||
inner: Arc::new(inner),
|
|
||||||
read: None,
|
|
||||||
write: None,
|
|
||||||
read_deadline: 0,
|
|
||||||
write_deadline: 0,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// On windows, if you fail to connect, you may need to call the
|
|
||||||
// `WaitNamedPipe` function, and this is indicated with an error
|
|
||||||
// code of ERROR_PIPE_BUSY.
|
|
||||||
let code = unsafe { libc::GetLastError() };
|
|
||||||
if code as isize != libc::ERROR_PIPE_BUSY as isize {
|
|
||||||
return Err(super::last_error())
|
|
||||||
}
|
|
||||||
|
|
||||||
match timeout {
|
|
||||||
Some(timeout) => {
|
|
||||||
let now = timer::now();
|
|
||||||
let timed_out = (now - start) >= timeout || unsafe {
|
|
||||||
let ms = (timeout - (now - start)) as libc::DWORD;
|
|
||||||
libc::WaitNamedPipeW(addr.as_ptr(), ms) == 0
|
|
||||||
};
|
|
||||||
if timed_out {
|
|
||||||
return Err(sys_common::timeout("connect timed out"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// An example I found on Microsoft's website used 20
|
|
||||||
// seconds, libuv uses 30 seconds, hence we make the
|
|
||||||
// obvious choice of waiting for 25 seconds.
|
|
||||||
None => {
|
|
||||||
if unsafe { libc::WaitNamedPipeW(addr.as_ptr(), 25000) } == 0 {
|
|
||||||
return Err(super::last_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle(&self) -> libc::HANDLE { self.inner.handle }
|
|
||||||
|
|
||||||
fn read_closed(&self) -> bool {
|
|
||||||
self.inner.read_closed.load(Ordering::SeqCst)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_closed(&self) -> bool {
|
|
||||||
self.inner.write_closed.load(Ordering::SeqCst)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cancel_io(&self) -> IoResult<()> {
|
|
||||||
match unsafe { c::CancelIoEx(self.handle(), ptr::null_mut()) } {
|
|
||||||
0 if os::errno() == libc::ERROR_NOT_FOUND as i32 => {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
0 => Err(super::last_error()),
|
|
||||||
_ => Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
|
||||||
if self.read.is_none() {
|
|
||||||
self.read = Some(try!(Event::new(true, false)));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut bytes_read = 0;
|
|
||||||
let mut overlapped: libc::OVERLAPPED = unsafe { mem::zeroed() };
|
|
||||||
overlapped.hEvent = self.read.as_ref().unwrap().handle();
|
|
||||||
|
|
||||||
// Pre-flight check to see if the reading half has been closed. This
|
|
||||||
// must be done before issuing the ReadFile request, but after we
|
|
||||||
// acquire the lock.
|
|
||||||
//
|
|
||||||
// See comments in close_read() about why this lock is necessary.
|
|
||||||
let guard = self.inner.lock.lock();
|
|
||||||
if self.read_closed() {
|
|
||||||
return Err(eof())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Issue a nonblocking requests, succeeding quickly if it happened to
|
|
||||||
// succeed.
|
|
||||||
let ret = unsafe {
|
|
||||||
libc::ReadFile(self.handle(),
|
|
||||||
buf.as_ptr() as libc::LPVOID,
|
|
||||||
buf.len() as libc::DWORD,
|
|
||||||
&mut bytes_read,
|
|
||||||
&mut overlapped)
|
|
||||||
};
|
|
||||||
if ret != 0 { return Ok(bytes_read as usize) }
|
|
||||||
|
|
||||||
// If our errno doesn't say that the I/O is pending, then we hit some
|
|
||||||
// legitimate error and return immediately.
|
|
||||||
if os::errno() != libc::ERROR_IO_PENDING as i32 {
|
|
||||||
return Err(super::last_error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now that we've issued a successful nonblocking request, we need to
|
|
||||||
// wait for it to finish. This can all be done outside the lock because
|
|
||||||
// we'll see any invocation of CancelIoEx. We also call this in a loop
|
|
||||||
// because we're woken up if the writing half is closed, we just need to
|
|
||||||
// realize that the reading half wasn't closed and we go right back to
|
|
||||||
// sleep.
|
|
||||||
drop(guard);
|
|
||||||
loop {
|
|
||||||
// Process a timeout if one is pending
|
|
||||||
let wait_succeeded = await(self.handle(), self.read_deadline,
|
|
||||||
&[overlapped.hEvent]);
|
|
||||||
|
|
||||||
let ret = unsafe {
|
|
||||||
libc::GetOverlappedResult(self.handle(),
|
|
||||||
&mut overlapped,
|
|
||||||
&mut bytes_read,
|
|
||||||
libc::TRUE)
|
|
||||||
};
|
|
||||||
// If we succeeded, or we failed for some reason other than
|
|
||||||
// CancelIoEx, return immediately
|
|
||||||
if ret != 0 { return Ok(bytes_read as usize) }
|
|
||||||
if os::errno() != libc::ERROR_OPERATION_ABORTED as i32 {
|
|
||||||
return Err(super::last_error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the reading half is now closed, then we're done. If we woke up
|
|
||||||
// because the writing half was closed, keep trying.
|
|
||||||
if wait_succeeded.is_err() {
|
|
||||||
return Err(sys_common::timeout("read timed out"))
|
|
||||||
}
|
|
||||||
if self.read_closed() {
|
|
||||||
return Err(eof())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
|
||||||
if self.write.is_none() {
|
|
||||||
self.write = Some(try!(Event::new(true, false)));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut offset = 0;
|
|
||||||
let mut overlapped: libc::OVERLAPPED = unsafe { mem::zeroed() };
|
|
||||||
overlapped.hEvent = self.write.as_ref().unwrap().handle();
|
|
||||||
|
|
||||||
while offset < buf.len() {
|
|
||||||
let mut bytes_written = 0;
|
|
||||||
|
|
||||||
// This sequence below is quite similar to the one found in read().
|
|
||||||
// Some careful looping is done to ensure that if close_write() is
|
|
||||||
// invoked we bail out early, and if close_read() is invoked we keep
|
|
||||||
// going after we woke up.
|
|
||||||
//
|
|
||||||
// See comments in close_read() about why this lock is necessary.
|
|
||||||
let guard = self.inner.lock.lock();
|
|
||||||
if self.write_closed() {
|
|
||||||
return Err(epipe())
|
|
||||||
}
|
|
||||||
let ret = unsafe {
|
|
||||||
libc::WriteFile(self.handle(),
|
|
||||||
buf[offset..].as_ptr() as libc::LPVOID,
|
|
||||||
(buf.len() - offset) as libc::DWORD,
|
|
||||||
&mut bytes_written,
|
|
||||||
&mut overlapped)
|
|
||||||
};
|
|
||||||
let err = os::errno();
|
|
||||||
drop(guard);
|
|
||||||
|
|
||||||
if ret == 0 {
|
|
||||||
if err != libc::ERROR_IO_PENDING as i32 {
|
|
||||||
return Err(decode_error_detailed(err as i32))
|
|
||||||
}
|
|
||||||
// Process a timeout if one is pending
|
|
||||||
let wait_succeeded = await(self.handle(), self.write_deadline,
|
|
||||||
&[overlapped.hEvent]);
|
|
||||||
let ret = unsafe {
|
|
||||||
libc::GetOverlappedResult(self.handle(),
|
|
||||||
&mut overlapped,
|
|
||||||
&mut bytes_written,
|
|
||||||
libc::TRUE)
|
|
||||||
};
|
|
||||||
// If we weren't aborted, this was a legit error, if we were
|
|
||||||
// aborted, then check to see if the write half was actually
|
|
||||||
// closed or whether we woke up from the read half closing.
|
|
||||||
if ret == 0 {
|
|
||||||
if os::errno() != libc::ERROR_OPERATION_ABORTED as i32 {
|
|
||||||
return Err(super::last_error())
|
|
||||||
}
|
|
||||||
if !wait_succeeded.is_ok() {
|
|
||||||
let amt = offset + bytes_written as usize;
|
|
||||||
return if amt > 0 {
|
|
||||||
Err(IoError {
|
|
||||||
kind: old_io::ShortWrite(amt),
|
|
||||||
desc: "short write during write",
|
|
||||||
detail: None,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(sys_common::timeout("write timed out"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if self.write_closed() {
|
|
||||||
return Err(epipe())
|
|
||||||
}
|
|
||||||
continue // retry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
offset += bytes_written as usize;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn close_read(&mut self) -> IoResult<()> {
|
|
||||||
// On windows, there's no actual shutdown() method for pipes, so we're
|
|
||||||
// forced to emulate the behavior manually at the application level. To
|
|
||||||
// do this, we need to both cancel any pending requests, as well as
|
|
||||||
// prevent all future requests from succeeding. These two operations are
|
|
||||||
// not atomic with respect to one another, so we must use a lock to do
|
|
||||||
// so.
|
|
||||||
//
|
|
||||||
// The read() code looks like:
|
|
||||||
//
|
|
||||||
// 1. Make sure the pipe is still open
|
|
||||||
// 2. Submit a read request
|
|
||||||
// 3. Wait for the read request to finish
|
|
||||||
//
|
|
||||||
// The race this lock is preventing is if another thread invokes
|
|
||||||
// close_read() between steps 1 and 2. By atomically executing steps 1
|
|
||||||
// and 2 with a lock with respect to close_read(), we're guaranteed that
|
|
||||||
// no thread will erroneously sit in a read forever.
|
|
||||||
let _guard = self.inner.lock.lock();
|
|
||||||
self.inner.read_closed.store(true, Ordering::SeqCst);
|
|
||||||
self.cancel_io()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn close_write(&mut self) -> IoResult<()> {
|
|
||||||
// see comments in close_read() for why this lock is necessary
|
|
||||||
let _guard = self.inner.lock.lock();
|
|
||||||
self.inner.write_closed.store(true, Ordering::SeqCst);
|
|
||||||
self.cancel_io()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_timeout(&mut self, timeout: Option<u64>) {
|
|
||||||
let deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
|
|
||||||
self.read_deadline = deadline;
|
|
||||||
self.write_deadline = deadline;
|
|
||||||
}
|
|
||||||
pub fn set_read_timeout(&mut self, timeout: Option<u64>) {
|
|
||||||
self.read_deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
|
|
||||||
}
|
|
||||||
pub fn set_write_timeout(&mut self, timeout: Option<u64>) {
|
|
||||||
self.write_deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for UnixStream {
|
|
||||||
fn clone(&self) -> UnixStream {
|
|
||||||
UnixStream {
|
|
||||||
inner: self.inner.clone(),
|
|
||||||
read: None,
|
|
||||||
write: None,
|
|
||||||
read_deadline: 0,
|
|
||||||
write_deadline: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Unix Listener
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
pub struct UnixListener {
|
|
||||||
handle: libc::HANDLE,
|
|
||||||
name: CString,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for UnixListener {}
|
|
||||||
unsafe impl Sync for UnixListener {}
|
|
||||||
|
|
||||||
impl UnixListener {
|
|
||||||
pub fn bind(addr: &CString) -> IoResult<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));
|
|
||||||
let ret = unsafe { pipe(addr_v.as_ptr(), true) };
|
|
||||||
if ret == libc::INVALID_HANDLE_VALUE {
|
|
||||||
Err(super::last_error())
|
|
||||||
} else {
|
|
||||||
Ok(UnixListener { handle: ret, name: addr.clone() })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn listen(self) -> IoResult<UnixAcceptor> {
|
|
||||||
Ok(UnixAcceptor {
|
|
||||||
listener: self,
|
|
||||||
event: try!(Event::new(true, false)),
|
|
||||||
deadline: 0,
|
|
||||||
inner: Arc::new(AcceptorState {
|
|
||||||
abort: try!(Event::new(true, false)),
|
|
||||||
closed: AtomicBool::new(false),
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle(&self) -> libc::HANDLE {
|
|
||||||
self.handle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for UnixListener {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe { let _ = libc::CloseHandle(self.handle); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UnixAcceptor {
|
|
||||||
inner: Arc<AcceptorState>,
|
|
||||||
listener: UnixListener,
|
|
||||||
event: Event,
|
|
||||||
deadline: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AcceptorState {
|
|
||||||
abort: Event,
|
|
||||||
closed: AtomicBool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UnixAcceptor {
|
|
||||||
pub fn accept(&mut self) -> IoResult<UnixStream> {
|
|
||||||
// This function has some funky implementation details when working with
|
|
||||||
// unix pipes. On windows, each server named pipe handle can be
|
|
||||||
// connected to a one or zero clients. To the best of my knowledge, a
|
|
||||||
// named server is considered active and present if there exists at
|
|
||||||
// least one server named pipe for it.
|
|
||||||
//
|
|
||||||
// The model of this function is to take the current known server
|
|
||||||
// handle, connect a client to it, and then transfer ownership to the
|
|
||||||
// UnixStream instance. The next time accept() is invoked, it'll need a
|
|
||||||
// different server handle to connect a client to.
|
|
||||||
//
|
|
||||||
// Note that there is a possible race here. Once our server pipe is
|
|
||||||
// handed off to a `UnixStream` object, the stream could be closed,
|
|
||||||
// meaning that there would be no active server pipes, hence even though
|
|
||||||
// we have a valid `UnixAcceptor`, no one can connect to it. For this
|
|
||||||
// reason, we generate the next accept call's server pipe at the end of
|
|
||||||
// this function call.
|
|
||||||
//
|
|
||||||
// This provides us an invariant that we always have at least one server
|
|
||||||
// connection open at a time, meaning that all connects to this acceptor
|
|
||||||
// should succeed while this is active.
|
|
||||||
//
|
|
||||||
// The actual implementation of doing this is a little tricky. Once a
|
|
||||||
// server pipe is created, a client can connect to it at any time. I
|
|
||||||
// assume that which server a client connects to is nondeterministic, so
|
|
||||||
// we also need to guarantee that the only server able to be connected
|
|
||||||
// to is the one that we're calling ConnectNamedPipe on. This means that
|
|
||||||
// we have to create the second server pipe *after* we've already
|
|
||||||
// accepted a connection. In order to at least somewhat gracefully
|
|
||||||
// handle errors, this means that if the second server pipe creation
|
|
||||||
// fails that we disconnect the connected client and then just keep
|
|
||||||
// using the original server pipe.
|
|
||||||
let handle = self.listener.handle;
|
|
||||||
|
|
||||||
// If we've had an artificial call to close_accept, be sure to never
|
|
||||||
// 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));
|
|
||||||
|
|
||||||
// Once we've got a "server handle", we need to wait for a client to
|
|
||||||
// connect. The ConnectNamedPipe function will block this thread until
|
|
||||||
// someone on the other end connects. This function can "fail" if a
|
|
||||||
// client connects after we created the pipe but before we got down
|
|
||||||
// here. Thanks windows.
|
|
||||||
let mut overlapped: libc::OVERLAPPED = unsafe { mem::zeroed() };
|
|
||||||
overlapped.hEvent = self.event.handle();
|
|
||||||
if unsafe { libc::ConnectNamedPipe(handle, &mut overlapped) == 0 } {
|
|
||||||
let mut err = unsafe { libc::GetLastError() };
|
|
||||||
|
|
||||||
if err == libc::ERROR_IO_PENDING as libc::DWORD {
|
|
||||||
// Process a timeout if one is pending
|
|
||||||
let wait_succeeded = await(handle, self.deadline,
|
|
||||||
&[self.inner.abort.handle(),
|
|
||||||
overlapped.hEvent]);
|
|
||||||
|
|
||||||
// This will block until the overlapped I/O is completed. The
|
|
||||||
// timeout was previously handled, so this will either block in
|
|
||||||
// the normal case or succeed very quickly in the timeout case.
|
|
||||||
let ret = unsafe {
|
|
||||||
let mut transfer = 0;
|
|
||||||
libc::GetOverlappedResult(handle,
|
|
||||||
&mut overlapped,
|
|
||||||
&mut transfer,
|
|
||||||
libc::TRUE)
|
|
||||||
};
|
|
||||||
if ret == 0 {
|
|
||||||
if wait_succeeded.is_ok() {
|
|
||||||
err = unsafe { libc::GetLastError() };
|
|
||||||
} else {
|
|
||||||
return Err(sys_common::timeout("accept timed out"))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// we succeeded, bypass the check below
|
|
||||||
err = libc::ERROR_PIPE_CONNECTED as libc::DWORD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != libc::ERROR_PIPE_CONNECTED as libc::DWORD {
|
|
||||||
return Err(super::last_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now that we've got a connected client to our handle, we need to
|
|
||||||
// create a second server pipe. If this fails, we disconnect the
|
|
||||||
// connected client and return an error (see comments above).
|
|
||||||
let new_handle = unsafe { pipe(name.as_ptr(), false) };
|
|
||||||
if new_handle == libc::INVALID_HANDLE_VALUE {
|
|
||||||
let ret = Err(super::last_error());
|
|
||||||
// If our disconnection fails, then there's not really a whole lot
|
|
||||||
// that we can do, so panic
|
|
||||||
let err = unsafe { libc::DisconnectNamedPipe(handle) };
|
|
||||||
assert!(err != 0);
|
|
||||||
return ret;
|
|
||||||
} else {
|
|
||||||
self.listener.handle = new_handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transfer ownership of our handle into this stream
|
|
||||||
Ok(UnixStream {
|
|
||||||
inner: Arc::new(Inner::new(handle)),
|
|
||||||
read: None,
|
|
||||||
write: None,
|
|
||||||
read_deadline: 0,
|
|
||||||
write_deadline: 0,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_timeout(&mut self, timeout: Option<u64>) {
|
|
||||||
self.deadline = timeout.map(|i| i + timer::now()).unwrap_or(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn close_accept(&mut self) -> IoResult<()> {
|
|
||||||
self.inner.closed.store(true, Ordering::SeqCst);
|
|
||||||
let ret = unsafe {
|
|
||||||
c::SetEvent(self.inner.abort.handle())
|
|
||||||
};
|
|
||||||
if ret == 0 {
|
|
||||||
Err(super::last_error())
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle(&self) -> libc::HANDLE {
|
|
||||||
self.listener.handle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for UnixAcceptor {
|
|
||||||
fn clone(&self) -> UnixAcceptor {
|
|
||||||
let name = to_utf16(&self.listener.name).unwrap();
|
|
||||||
UnixAcceptor {
|
|
||||||
inner: self.inner.clone(),
|
|
||||||
event: Event::new(true, false).unwrap(),
|
|
||||||
deadline: 0,
|
|
||||||
listener: UnixListener {
|
|
||||||
name: self.listener.name.clone(),
|
|
||||||
handle: unsafe {
|
|
||||||
let p = pipe(name.as_ptr(), false) ;
|
|
||||||
assert!(p != libc::INVALID_HANDLE_VALUE as libc::HANDLE);
|
|
||||||
p
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,518 +0,0 @@
|
|||||||
// Copyright 2012-2014 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.
|
|
||||||
|
|
||||||
#![allow(deprecated)] // this module itself is essentially deprecated
|
|
||||||
|
|
||||||
use prelude::v1::*;
|
|
||||||
|
|
||||||
use collections;
|
|
||||||
use env;
|
|
||||||
use ffi::CString;
|
|
||||||
use hash::Hash;
|
|
||||||
use libc::{pid_t, c_void};
|
|
||||||
use libc;
|
|
||||||
use mem;
|
|
||||||
#[allow(deprecated)] use old_io::fs::PathExtensions;
|
|
||||||
use old_io::process::{ProcessExit, ExitStatus};
|
|
||||||
use old_io::{IoResult, IoError};
|
|
||||||
use old_io;
|
|
||||||
use fs::PathExt;
|
|
||||||
use old_path::{BytesContainer, GenericPath};
|
|
||||||
use ptr;
|
|
||||||
use str;
|
|
||||||
use sync::{StaticMutex, MUTEX_INIT};
|
|
||||||
use sys::fs::FileDesc;
|
|
||||||
use sys::timer;
|
|
||||||
use sys_common::{AsInner, timeout};
|
|
||||||
|
|
||||||
pub use sys_common::ProcessConfig;
|
|
||||||
|
|
||||||
// `CreateProcess` is racy!
|
|
||||||
// http://support.microsoft.com/kb/315939
|
|
||||||
static CREATE_PROCESS_LOCK: StaticMutex = MUTEX_INIT;
|
|
||||||
|
|
||||||
/// A value representing a child process.
|
|
||||||
///
|
|
||||||
/// The lifetime of this value is linked to the lifetime of the actual
|
|
||||||
/// process - the Process destructor calls self.finish() which waits
|
|
||||||
/// for the process to terminate.
|
|
||||||
pub struct Process {
|
|
||||||
/// The unique id of the process (this should never be negative).
|
|
||||||
pid: pid_t,
|
|
||||||
|
|
||||||
/// A HANDLE to the process, which will prevent the pid being
|
|
||||||
/// re-used until the handle is closed.
|
|
||||||
handle: *mut (),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Process {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
free_handle(self.handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Process {
|
|
||||||
pub fn id(&self) -> pid_t {
|
|
||||||
self.pid
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn kill(&self, signal: isize) -> IoResult<()> {
|
|
||||||
Process::killpid(self.pid, signal)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn killpid(pid: pid_t, signal: isize) -> IoResult<()> {
|
|
||||||
let handle = libc::OpenProcess(libc::PROCESS_TERMINATE |
|
|
||||||
libc::PROCESS_QUERY_INFORMATION,
|
|
||||||
libc::FALSE, pid as libc::DWORD);
|
|
||||||
if handle.is_null() {
|
|
||||||
return Err(super::last_error())
|
|
||||||
}
|
|
||||||
let ret = match signal {
|
|
||||||
// test for existence on signal 0
|
|
||||||
0 => {
|
|
||||||
let mut status = 0;
|
|
||||||
let ret = libc::GetExitCodeProcess(handle, &mut status);
|
|
||||||
if ret == 0 {
|
|
||||||
Err(super::last_error())
|
|
||||||
} else if status != libc::STILL_ACTIVE {
|
|
||||||
Err(IoError {
|
|
||||||
kind: old_io::InvalidInput,
|
|
||||||
desc: "no process to kill",
|
|
||||||
detail: None,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
15 | 9 => { // sigterm or sigkill
|
|
||||||
let ret = libc::TerminateProcess(handle, 1);
|
|
||||||
super::mkerr_winbool(ret)
|
|
||||||
}
|
|
||||||
_ => Err(IoError {
|
|
||||||
kind: old_io::IoUnavailable,
|
|
||||||
desc: "unsupported signal on windows",
|
|
||||||
detail: None,
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let _ = libc::CloseHandle(handle);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn spawn<K, V, C, P>(cfg: &C, in_fd: Option<P>,
|
|
||||||
out_fd: Option<P>, err_fd: Option<P>)
|
|
||||||
-> IoResult<Process>
|
|
||||||
where C: ProcessConfig<K, V>, P: AsInner<FileDesc>,
|
|
||||||
K: BytesContainer + Eq + Hash, V: BytesContainer
|
|
||||||
{
|
|
||||||
use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO};
|
|
||||||
use libc::consts::os::extra::{
|
|
||||||
TRUE, FALSE,
|
|
||||||
STARTF_USESTDHANDLES,
|
|
||||||
INVALID_HANDLE_VALUE,
|
|
||||||
DUPLICATE_SAME_ACCESS
|
|
||||||
};
|
|
||||||
use libc::funcs::extra::kernel32::{
|
|
||||||
GetCurrentProcess,
|
|
||||||
DuplicateHandle,
|
|
||||||
CloseHandle,
|
|
||||||
CreateProcessW
|
|
||||||
};
|
|
||||||
use libc::funcs::extra::msvcrt::get_osfhandle;
|
|
||||||
|
|
||||||
use mem;
|
|
||||||
|
|
||||||
if cfg.gid().is_some() || cfg.uid().is_some() {
|
|
||||||
return Err(IoError {
|
|
||||||
kind: old_io::IoUnavailable,
|
|
||||||
desc: "unsupported gid/uid requested on windows",
|
|
||||||
detail: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// To have the spawning semantics of unix/windows stay the same, we need to
|
|
||||||
// read the *child's* PATH if one is provided. See #15149 for more details.
|
|
||||||
let program = cfg.env().and_then(|env| {
|
|
||||||
for (key, v) in env {
|
|
||||||
if b"PATH" != key.container_as_bytes() { continue }
|
|
||||||
let v = match ::str::from_utf8(v.container_as_bytes()) {
|
|
||||||
Ok(s) => s,
|
|
||||||
Err(..) => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Split the value and test each path to see if the
|
|
||||||
// program exists.
|
|
||||||
for path in ::env::split_paths(v) {
|
|
||||||
let program = str::from_utf8(cfg.program().as_bytes()).unwrap();
|
|
||||||
let path = path.join(program)
|
|
||||||
.with_extension(env::consts::EXE_EXTENSION);
|
|
||||||
if path.exists() {
|
|
||||||
return Some(CString::new(path.to_str().unwrap()).unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
None
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let mut si = zeroed_startupinfo();
|
|
||||||
si.cb = mem::size_of::<STARTUPINFO>() as DWORD;
|
|
||||||
si.dwFlags = STARTF_USESTDHANDLES;
|
|
||||||
|
|
||||||
let cur_proc = GetCurrentProcess();
|
|
||||||
|
|
||||||
// Similarly to unix, we don't actually leave holes for the stdio file
|
|
||||||
// descriptors, but rather open up /dev/null equivalents. These
|
|
||||||
// equivalents are drawn from libuv's windows process spawning.
|
|
||||||
let set_fd = |fd: &Option<P>, slot: &mut HANDLE,
|
|
||||||
is_stdin: bool| {
|
|
||||||
match *fd {
|
|
||||||
None => {
|
|
||||||
let access = if is_stdin {
|
|
||||||
libc::FILE_GENERIC_READ
|
|
||||||
} else {
|
|
||||||
libc::FILE_GENERIC_WRITE | libc::FILE_READ_ATTRIBUTES
|
|
||||||
};
|
|
||||||
let size = mem::size_of::<libc::SECURITY_ATTRIBUTES>();
|
|
||||||
let mut sa = libc::SECURITY_ATTRIBUTES {
|
|
||||||
nLength: size as libc::DWORD,
|
|
||||||
lpSecurityDescriptor: ptr::null_mut(),
|
|
||||||
bInheritHandle: 1,
|
|
||||||
};
|
|
||||||
let mut filename: Vec<u16> = "NUL".utf16_units().collect();
|
|
||||||
filename.push(0);
|
|
||||||
*slot = libc::CreateFileW(filename.as_ptr(),
|
|
||||||
access,
|
|
||||||
libc::FILE_SHARE_READ |
|
|
||||||
libc::FILE_SHARE_WRITE,
|
|
||||||
&mut sa,
|
|
||||||
libc::OPEN_EXISTING,
|
|
||||||
0,
|
|
||||||
ptr::null_mut());
|
|
||||||
if *slot == INVALID_HANDLE_VALUE {
|
|
||||||
return Err(super::last_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(ref fd) => {
|
|
||||||
let orig = get_osfhandle(fd.as_inner().fd()) as HANDLE;
|
|
||||||
if orig == INVALID_HANDLE_VALUE {
|
|
||||||
return Err(super::last_error())
|
|
||||||
}
|
|
||||||
if DuplicateHandle(cur_proc, orig, cur_proc, slot,
|
|
||||||
0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
|
|
||||||
return Err(super::last_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
|
|
||||||
try!(set_fd(&in_fd, &mut si.hStdInput, true));
|
|
||||||
try!(set_fd(&out_fd, &mut si.hStdOutput, false));
|
|
||||||
try!(set_fd(&err_fd, &mut si.hStdError, false));
|
|
||||||
|
|
||||||
let cmd_str = make_command_line(program.as_ref().unwrap_or(cfg.program()),
|
|
||||||
cfg.args());
|
|
||||||
let mut pi = zeroed_process_information();
|
|
||||||
let mut create_err = None;
|
|
||||||
|
|
||||||
// stolen from the libuv code.
|
|
||||||
let mut flags = libc::CREATE_UNICODE_ENVIRONMENT;
|
|
||||||
if cfg.detach() {
|
|
||||||
flags |= libc::DETACHED_PROCESS | libc::CREATE_NEW_PROCESS_GROUP;
|
|
||||||
}
|
|
||||||
|
|
||||||
with_envp(cfg.env(), |envp| {
|
|
||||||
with_dirp(cfg.cwd(), |dirp| {
|
|
||||||
let mut cmd_str: Vec<u16> = cmd_str.utf16_units().collect();
|
|
||||||
cmd_str.push(0);
|
|
||||||
let _lock = CREATE_PROCESS_LOCK.lock().unwrap();
|
|
||||||
let created = CreateProcessW(ptr::null(),
|
|
||||||
cmd_str.as_mut_ptr(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
TRUE,
|
|
||||||
flags, envp, dirp,
|
|
||||||
&mut si, &mut pi);
|
|
||||||
if created == FALSE {
|
|
||||||
create_err = Some(super::last_error());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
assert!(CloseHandle(si.hStdInput) != 0);
|
|
||||||
assert!(CloseHandle(si.hStdOutput) != 0);
|
|
||||||
assert!(CloseHandle(si.hStdError) != 0);
|
|
||||||
|
|
||||||
match create_err {
|
|
||||||
Some(err) => return Err(err),
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We close the thread handle because we don't care about keeping the
|
|
||||||
// thread id valid, and we aren't keeping the thread handle around to be
|
|
||||||
// able to close it later. We don't close the process handle however
|
|
||||||
// because std::we want the process id to stay valid at least until the
|
|
||||||
// calling code closes the process handle.
|
|
||||||
assert!(CloseHandle(pi.hThread) != 0);
|
|
||||||
|
|
||||||
Ok(Process {
|
|
||||||
pid: pi.dwProcessId as pid_t,
|
|
||||||
handle: pi.hProcess as *mut ()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Waits for a process to exit and returns the exit code, failing
|
|
||||||
/// if there is no process with the specified id.
|
|
||||||
///
|
|
||||||
/// Note that this is private to avoid race conditions on unix where if
|
|
||||||
/// a user calls waitpid(some_process.get_id()) then some_process.finish()
|
|
||||||
/// and some_process.destroy() and some_process.finalize() will then either
|
|
||||||
/// operate on a none-existent process or, even worse, on a newer process
|
|
||||||
/// with the same id.
|
|
||||||
pub fn wait(&self, deadline: u64) -> IoResult<ProcessExit> {
|
|
||||||
use libc::types::os::arch::extra::DWORD;
|
|
||||||
use libc::consts::os::extra::{
|
|
||||||
SYNCHRONIZE,
|
|
||||||
PROCESS_QUERY_INFORMATION,
|
|
||||||
FALSE,
|
|
||||||
STILL_ACTIVE,
|
|
||||||
INFINITE,
|
|
||||||
WAIT_TIMEOUT,
|
|
||||||
WAIT_OBJECT_0,
|
|
||||||
};
|
|
||||||
use libc::funcs::extra::kernel32::{
|
|
||||||
OpenProcess,
|
|
||||||
GetExitCodeProcess,
|
|
||||||
CloseHandle,
|
|
||||||
WaitForSingleObject,
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let process = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
|
|
||||||
FALSE,
|
|
||||||
self.pid as DWORD);
|
|
||||||
if process.is_null() {
|
|
||||||
return Err(super::last_error())
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let mut status = 0;
|
|
||||||
if GetExitCodeProcess(process, &mut status) == FALSE {
|
|
||||||
let err = Err(super::last_error());
|
|
||||||
assert!(CloseHandle(process) != 0);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
if status != STILL_ACTIVE {
|
|
||||||
assert!(CloseHandle(process) != 0);
|
|
||||||
return Ok(ExitStatus(status as isize));
|
|
||||||
}
|
|
||||||
let interval = if deadline == 0 {
|
|
||||||
INFINITE
|
|
||||||
} else {
|
|
||||||
let now = timer::now();
|
|
||||||
if deadline < now {0} else {(deadline - now) as u32}
|
|
||||||
};
|
|
||||||
match WaitForSingleObject(process, interval) {
|
|
||||||
WAIT_OBJECT_0 => {}
|
|
||||||
WAIT_TIMEOUT => {
|
|
||||||
assert!(CloseHandle(process) != 0);
|
|
||||||
return Err(timeout("process wait timed out"))
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let err = Err(super::last_error());
|
|
||||||
assert!(CloseHandle(process) != 0);
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn zeroed_startupinfo() -> libc::types::os::arch::extra::STARTUPINFO {
|
|
||||||
libc::types::os::arch::extra::STARTUPINFO {
|
|
||||||
cb: 0,
|
|
||||||
lpReserved: ptr::null_mut(),
|
|
||||||
lpDesktop: ptr::null_mut(),
|
|
||||||
lpTitle: ptr::null_mut(),
|
|
||||||
dwX: 0,
|
|
||||||
dwY: 0,
|
|
||||||
dwXSize: 0,
|
|
||||||
dwYSize: 0,
|
|
||||||
dwXCountChars: 0,
|
|
||||||
dwYCountCharts: 0,
|
|
||||||
dwFillAttribute: 0,
|
|
||||||
dwFlags: 0,
|
|
||||||
wShowWindow: 0,
|
|
||||||
cbReserved2: 0,
|
|
||||||
lpReserved2: ptr::null_mut(),
|
|
||||||
hStdInput: libc::INVALID_HANDLE_VALUE,
|
|
||||||
hStdOutput: libc::INVALID_HANDLE_VALUE,
|
|
||||||
hStdError: libc::INVALID_HANDLE_VALUE,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMATION {
|
|
||||||
libc::types::os::arch::extra::PROCESS_INFORMATION {
|
|
||||||
hProcess: ptr::null_mut(),
|
|
||||||
hThread: ptr::null_mut(),
|
|
||||||
dwProcessId: 0,
|
|
||||||
dwThreadId: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_command_line(prog: &CString, args: &[CString]) -> String {
|
|
||||||
let mut cmd = String::new();
|
|
||||||
append_arg(&mut cmd, str::from_utf8(prog.as_bytes()).ok()
|
|
||||||
.expect("expected program name to be utf-8 encoded"));
|
|
||||||
for arg in args {
|
|
||||||
cmd.push(' ');
|
|
||||||
append_arg(&mut cmd, str::from_utf8(arg.as_bytes()).ok()
|
|
||||||
.expect("expected argument to be utf-8 encoded"));
|
|
||||||
}
|
|
||||||
return cmd;
|
|
||||||
|
|
||||||
fn append_arg(cmd: &mut String, arg: &str) {
|
|
||||||
// If an argument has 0 characters then we need to quote it to ensure
|
|
||||||
// that it actually gets passed through on the command line or otherwise
|
|
||||||
// it will be dropped entirely when parsed on the other end.
|
|
||||||
let quote = arg.chars().any(|c| c == ' ' || c == '\t') || arg.len() == 0;
|
|
||||||
if quote {
|
|
||||||
cmd.push('"');
|
|
||||||
}
|
|
||||||
let argvec: Vec<char> = arg.chars().collect();
|
|
||||||
for i in 0..argvec.len() {
|
|
||||||
append_char_at(cmd, &argvec, i);
|
|
||||||
}
|
|
||||||
if quote {
|
|
||||||
cmd.push('"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn append_char_at(cmd: &mut String, arg: &[char], i: usize) {
|
|
||||||
match arg[i] {
|
|
||||||
'"' => {
|
|
||||||
// Escape quotes.
|
|
||||||
cmd.push_str("\\\"");
|
|
||||||
}
|
|
||||||
'\\' => {
|
|
||||||
if backslash_run_ends_in_quote(arg, i) {
|
|
||||||
// Double all backslashes that are in runs before quotes.
|
|
||||||
cmd.push_str("\\\\");
|
|
||||||
} else {
|
|
||||||
// Pass other backslashes through unescaped.
|
|
||||||
cmd.push('\\');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c => {
|
|
||||||
cmd.push(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn backslash_run_ends_in_quote(s: &[char], mut i: usize) -> bool {
|
|
||||||
while i < s.len() && s[i] == '\\' {
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
return i < s.len() && s[i] == '"';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_envp<K, V, T, F>(env: Option<&collections::HashMap<K, V>>, cb: F) -> T
|
|
||||||
where K: BytesContainer + Eq + Hash,
|
|
||||||
V: BytesContainer,
|
|
||||||
F: FnOnce(*mut c_void) -> T,
|
|
||||||
{
|
|
||||||
// On Windows we pass an "environment block" which is not a char**, but
|
|
||||||
// rather a concatenation of null-terminated k=v\0 sequences, with a final
|
|
||||||
// \0 to terminate.
|
|
||||||
match env {
|
|
||||||
Some(env) => {
|
|
||||||
let mut blk = Vec::new();
|
|
||||||
|
|
||||||
for pair in env {
|
|
||||||
let kv = format!("{}={}",
|
|
||||||
pair.0.container_as_str().unwrap(),
|
|
||||||
pair.1.container_as_str().unwrap());
|
|
||||||
blk.extend(kv.utf16_units());
|
|
||||||
blk.push(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
blk.push(0);
|
|
||||||
|
|
||||||
cb(blk.as_mut_ptr() as *mut c_void)
|
|
||||||
}
|
|
||||||
_ => cb(ptr::null_mut())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_dirp<T, F>(d: Option<&CString>, cb: F) -> T where
|
|
||||||
F: FnOnce(*const u16) -> T,
|
|
||||||
{
|
|
||||||
match d {
|
|
||||||
Some(dir) => {
|
|
||||||
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);
|
|
||||||
cb(dir_str.as_ptr())
|
|
||||||
},
|
|
||||||
None => cb(ptr::null())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn free_handle(handle: *mut ()) {
|
|
||||||
assert!(unsafe {
|
|
||||||
libc::CloseHandle(mem::transmute(handle)) != 0
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use prelude::v1::*;
|
|
||||||
use str;
|
|
||||||
use ffi::CString;
|
|
||||||
use super::make_command_line;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_make_command_line() {
|
|
||||||
fn test_wrapper(prog: &str, args: &[&str]) -> String {
|
|
||||||
make_command_line(&CString::new(prog).unwrap(),
|
|
||||||
&args.iter()
|
|
||||||
.map(|a| CString::new(*a).unwrap())
|
|
||||||
.collect::<Vec<CString>>())
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
test_wrapper("prog", &["aaa", "bbb", "ccc"]),
|
|
||||||
"prog aaa bbb ccc"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"]),
|
|
||||||
"\"C:\\Program Files\\blah\\blah.exe\" aaa"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
test_wrapper("C:\\Program Files\\test", &["aa\"bb"]),
|
|
||||||
"\"C:\\Program Files\\test\" aa\\\"bb"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
test_wrapper("echo", &["a b c"]),
|
|
||||||
"echo \"a b c\""
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[]),
|
|
||||||
"\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,230 +0,0 @@
|
|||||||
// Copyright 2014 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.
|
|
||||||
|
|
||||||
#![allow(deprecated)]
|
|
||||||
|
|
||||||
use prelude::v1::*;
|
|
||||||
|
|
||||||
use old_io::net::ip;
|
|
||||||
use old_io::IoResult;
|
|
||||||
use libc;
|
|
||||||
use libc::consts::os::extra::INVALID_SOCKET;
|
|
||||||
use mem;
|
|
||||||
use ptr;
|
|
||||||
use super::{last_error, last_net_error, sock_t};
|
|
||||||
use sync::Arc;
|
|
||||||
use sync::atomic::{AtomicBool, Ordering};
|
|
||||||
use sys::{self, c, set_nonblocking, wouldblock, timer};
|
|
||||||
use sys_common::{timeout, eof, net};
|
|
||||||
|
|
||||||
pub use sys_common::net::TcpStream;
|
|
||||||
|
|
||||||
pub struct Event(c::WSAEVENT);
|
|
||||||
|
|
||||||
unsafe impl Send for Event {}
|
|
||||||
unsafe impl Sync for Event {}
|
|
||||||
|
|
||||||
impl Event {
|
|
||||||
pub fn new() -> IoResult<Event> {
|
|
||||||
let event = unsafe { c::WSACreateEvent() };
|
|
||||||
if event == c::WSA_INVALID_EVENT {
|
|
||||||
Err(super::last_error())
|
|
||||||
} else {
|
|
||||||
Ok(Event(event))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle(&self) -> c::WSAEVENT { let Event(handle) = *self; handle }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Event {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe { let _ = c::WSACloseEvent(self.handle()); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// TCP listeners
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
pub struct TcpListener { sock: sock_t }
|
|
||||||
|
|
||||||
unsafe impl Send for TcpListener {}
|
|
||||||
unsafe impl Sync for TcpListener {}
|
|
||||||
|
|
||||||
impl TcpListener {
|
|
||||||
pub fn bind(addr: ip::SocketAddr) -> IoResult<TcpListener> {
|
|
||||||
sys::init_net();
|
|
||||||
|
|
||||||
let sock = try!(net::socket(addr, libc::SOCK_STREAM));
|
|
||||||
let ret = TcpListener { sock: sock };
|
|
||||||
|
|
||||||
let mut storage = unsafe { mem::zeroed() };
|
|
||||||
let len = net::addr_to_sockaddr(addr, &mut storage);
|
|
||||||
let addrp = &storage as *const _ as *const libc::sockaddr;
|
|
||||||
|
|
||||||
match unsafe { libc::bind(sock, addrp, len) } {
|
|
||||||
-1 => Err(last_net_error()),
|
|
||||||
_ => Ok(ret),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn socket(&self) -> sock_t { self.sock }
|
|
||||||
|
|
||||||
pub fn listen(self, backlog: isize) -> IoResult<TcpAcceptor> {
|
|
||||||
match unsafe { libc::listen(self.socket(), backlog as libc::c_int) } {
|
|
||||||
-1 => Err(last_net_error()),
|
|
||||||
|
|
||||||
_ => {
|
|
||||||
let accept = try!(Event::new());
|
|
||||||
let ret = unsafe {
|
|
||||||
c::WSAEventSelect(self.socket(), accept.handle(), c::FD_ACCEPT)
|
|
||||||
};
|
|
||||||
if ret != 0 {
|
|
||||||
return Err(last_net_error())
|
|
||||||
}
|
|
||||||
Ok(TcpAcceptor {
|
|
||||||
inner: Arc::new(AcceptorInner {
|
|
||||||
listener: self,
|
|
||||||
abort: try!(Event::new()),
|
|
||||||
accept: accept,
|
|
||||||
closed: AtomicBool::new(false),
|
|
||||||
}),
|
|
||||||
deadline: 0,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn socket_name(&mut self) -> IoResult<ip::SocketAddr> {
|
|
||||||
net::sockname(self.socket(), libc::getsockname)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for TcpListener {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe { super::close_sock(self.sock); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TcpAcceptor {
|
|
||||||
inner: Arc<AcceptorInner>,
|
|
||||||
deadline: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AcceptorInner {
|
|
||||||
listener: TcpListener,
|
|
||||||
abort: Event,
|
|
||||||
accept: Event,
|
|
||||||
closed: AtomicBool,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Sync for AcceptorInner {}
|
|
||||||
|
|
||||||
impl TcpAcceptor {
|
|
||||||
pub fn socket(&self) -> sock_t { self.inner.listener.socket() }
|
|
||||||
|
|
||||||
pub fn accept(&mut self) -> IoResult<TcpStream> {
|
|
||||||
// Unlink unix, windows cannot invoke `select` on arbitrary file
|
|
||||||
// descriptors like pipes, only sockets. Consequently, windows cannot
|
|
||||||
// use the same implementation as unix for accept() when close_accept()
|
|
||||||
// is considered.
|
|
||||||
//
|
|
||||||
// In order to implement close_accept() and timeouts, windows uses
|
|
||||||
// event handles. An acceptor-specific abort event is created which
|
|
||||||
// will only get set in close_accept(), and it will never be un-set.
|
|
||||||
// Additionally, another acceptor-specific event is associated with the
|
|
||||||
// FD_ACCEPT network event.
|
|
||||||
//
|
|
||||||
// These two events are then passed to WaitForMultipleEvents to see
|
|
||||||
// which one triggers first, and the timeout passed to this function is
|
|
||||||
// the local timeout for the acceptor.
|
|
||||||
//
|
|
||||||
// If the wait times out, then the accept timed out. If the wait
|
|
||||||
// succeeds with the abort event, then we were closed, and if the wait
|
|
||||||
// succeeds otherwise, then we do a nonblocking poll via `accept` to
|
|
||||||
// see if we can accept a connection. The connection is candidate to be
|
|
||||||
// stolen, so we do all of this in a loop as well.
|
|
||||||
let events = [self.inner.abort.handle(), self.inner.accept.handle()];
|
|
||||||
|
|
||||||
while !self.inner.closed.load(Ordering::SeqCst) {
|
|
||||||
let ms = if self.deadline == 0 {
|
|
||||||
c::WSA_INFINITE as u64
|
|
||||||
} else {
|
|
||||||
let now = timer::now();
|
|
||||||
if self.deadline < now {0} else {self.deadline - now}
|
|
||||||
};
|
|
||||||
let ret = unsafe {
|
|
||||||
c::WSAWaitForMultipleEvents(2, events.as_ptr(), libc::FALSE,
|
|
||||||
ms as libc::DWORD, libc::FALSE)
|
|
||||||
};
|
|
||||||
match ret {
|
|
||||||
c::WSA_WAIT_TIMEOUT => {
|
|
||||||
return Err(timeout("accept timed out"))
|
|
||||||
}
|
|
||||||
c::WSA_WAIT_FAILED => return Err(last_net_error()),
|
|
||||||
c::WSA_WAIT_EVENT_0 => break,
|
|
||||||
n => assert_eq!(n, c::WSA_WAIT_EVENT_0 + 1),
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut wsaevents: c::WSANETWORKEVENTS = unsafe { mem::zeroed() };
|
|
||||||
let ret = unsafe {
|
|
||||||
c::WSAEnumNetworkEvents(self.socket(), events[1], &mut wsaevents)
|
|
||||||
};
|
|
||||||
if ret != 0 { return Err(last_net_error()) }
|
|
||||||
|
|
||||||
if wsaevents.lNetworkEvents & c::FD_ACCEPT == 0 { continue }
|
|
||||||
match unsafe {
|
|
||||||
libc::accept(self.socket(), ptr::null_mut(), ptr::null_mut())
|
|
||||||
} {
|
|
||||||
INVALID_SOCKET if wouldblock() => {}
|
|
||||||
INVALID_SOCKET => return Err(last_net_error()),
|
|
||||||
|
|
||||||
// Accepted sockets inherit the same properties as the caller,
|
|
||||||
// so we need to deregister our event and switch the socket back
|
|
||||||
// to blocking mode
|
|
||||||
socket => {
|
|
||||||
let stream = TcpStream::new(socket);
|
|
||||||
let ret = unsafe {
|
|
||||||
c::WSAEventSelect(socket, events[1], 0)
|
|
||||||
};
|
|
||||||
if ret != 0 { return Err(last_net_error()) }
|
|
||||||
set_nonblocking(socket, false);
|
|
||||||
return Ok(stream)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(eof())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_timeout(&mut self, timeout: Option<u64>) {
|
|
||||||
self.deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn close_accept(&mut self) -> IoResult<()> {
|
|
||||||
self.inner.closed.store(true, Ordering::SeqCst);
|
|
||||||
let ret = unsafe { c::WSASetEvent(self.inner.abort.handle()) };
|
|
||||||
if ret == libc::TRUE {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(last_net_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for TcpAcceptor {
|
|
||||||
fn clone(&self) -> TcpAcceptor {
|
|
||||||
TcpAcceptor {
|
|
||||||
inner: self.inner.clone(),
|
|
||||||
deadline: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,214 +0,0 @@
|
|||||||
// Copyright 2013 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.
|
|
||||||
|
|
||||||
//! Timers based on Windows WaitableTimers
|
|
||||||
//!
|
|
||||||
//! This implementation is meant to be used solely on windows. As with other
|
|
||||||
//! implementations, there is a worker thread which is doing all the waiting on
|
|
||||||
//! a large number of timers for all active timers in the system. This worker
|
|
||||||
//! thread uses the select() equivalent, WaitForMultipleObjects. One of the
|
|
||||||
//! objects being waited on is a signal into the worker thread to notify that
|
|
||||||
//! the incoming channel should be looked at.
|
|
||||||
//!
|
|
||||||
//! Other than that, the implementation is pretty straightforward in terms of
|
|
||||||
//! the other two implementations of timers with nothing *that* new showing up.
|
|
||||||
|
|
||||||
#![allow(deprecated)]
|
|
||||||
|
|
||||||
use prelude::v1::*;
|
|
||||||
use self::Req::*;
|
|
||||||
|
|
||||||
use libc;
|
|
||||||
use ptr;
|
|
||||||
|
|
||||||
use old_io::IoResult;
|
|
||||||
use sys_common::helper_thread::Helper;
|
|
||||||
use sync::mpsc::{channel, TryRecvError, Sender, Receiver};
|
|
||||||
|
|
||||||
helper_init! { static HELPER: Helper<Req> }
|
|
||||||
|
|
||||||
pub trait Callback {
|
|
||||||
fn call(&mut self);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Timer {
|
|
||||||
obj: libc::HANDLE,
|
|
||||||
on_worker: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Req {
|
|
||||||
NewTimer(libc::HANDLE, Box<Callback + Send>, bool),
|
|
||||||
RemoveTimer(libc::HANDLE, Sender<()>),
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for Timer {}
|
|
||||||
unsafe impl Send for Req {}
|
|
||||||
|
|
||||||
fn helper(input: libc::HANDLE, messages: Receiver<Req>, _: ()) {
|
|
||||||
let mut objs = vec![input];
|
|
||||||
let mut chans = vec![];
|
|
||||||
|
|
||||||
'outer: loop {
|
|
||||||
let idx = unsafe {
|
|
||||||
imp::WaitForMultipleObjects(objs.len() as libc::DWORD,
|
|
||||||
objs.as_ptr(),
|
|
||||||
0 as libc::BOOL,
|
|
||||||
libc::INFINITE)
|
|
||||||
};
|
|
||||||
|
|
||||||
if idx == 0 {
|
|
||||||
loop {
|
|
||||||
match messages.try_recv() {
|
|
||||||
Ok(NewTimer(obj, c, one)) => {
|
|
||||||
objs.push(obj);
|
|
||||||
chans.push((c, one));
|
|
||||||
}
|
|
||||||
Ok(RemoveTimer(obj, c)) => {
|
|
||||||
c.send(()).unwrap();
|
|
||||||
match objs.iter().position(|&o| o == obj) {
|
|
||||||
Some(i) => {
|
|
||||||
drop(objs.remove(i));
|
|
||||||
drop(chans.remove(i - 1));
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// See the comment in unix::timer for why we don't have any
|
|
||||||
// asserts here and why we're likely just leaving timers on
|
|
||||||
// the floor as we exit.
|
|
||||||
Err(TryRecvError::Disconnected) => {
|
|
||||||
break 'outer;
|
|
||||||
}
|
|
||||||
Err(..) => break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let remove = {
|
|
||||||
match &mut chans[idx as usize - 1] {
|
|
||||||
&mut (ref mut c, oneshot) => { c.call(); oneshot }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if remove {
|
|
||||||
drop(objs.remove(idx as usize));
|
|
||||||
drop(chans.remove(idx as usize - 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns the current time (in milliseconds)
|
|
||||||
pub fn now() -> u64 {
|
|
||||||
let mut ticks_per_s = 0;
|
|
||||||
assert_eq!(unsafe { libc::QueryPerformanceFrequency(&mut ticks_per_s) }, 1);
|
|
||||||
let ticks_per_s = if ticks_per_s == 0 {1} else {ticks_per_s};
|
|
||||||
let mut ticks = 0;
|
|
||||||
assert_eq!(unsafe { libc::QueryPerformanceCounter(&mut ticks) }, 1);
|
|
||||||
|
|
||||||
return (ticks as u64 * 1000) / (ticks_per_s as u64);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Timer {
|
|
||||||
pub fn new() -> IoResult<Timer> {
|
|
||||||
HELPER.boot(|| {}, helper);
|
|
||||||
|
|
||||||
let obj = unsafe {
|
|
||||||
imp::CreateWaitableTimerA(ptr::null_mut(), 0, ptr::null())
|
|
||||||
};
|
|
||||||
if obj.is_null() {
|
|
||||||
Err(super::last_error())
|
|
||||||
} else {
|
|
||||||
Ok(Timer { obj: obj, on_worker: false, })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove(&mut self) {
|
|
||||||
if !self.on_worker { return }
|
|
||||||
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
HELPER.send(RemoveTimer(self.obj, tx));
|
|
||||||
rx.recv().unwrap();
|
|
||||||
|
|
||||||
self.on_worker = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sleep(&mut self, msecs: u64) {
|
|
||||||
self.remove();
|
|
||||||
|
|
||||||
// there are 10^6 nanoseconds in a millisecond, and the parameter is in
|
|
||||||
// 100ns intervals, so we multiply by 10^4.
|
|
||||||
let due = -(msecs as i64 * 10000) as libc::LARGE_INTEGER;
|
|
||||||
assert_eq!(unsafe {
|
|
||||||
imp::SetWaitableTimer(self.obj, &due, 0, ptr::null_mut(),
|
|
||||||
ptr::null_mut(), 0)
|
|
||||||
}, 1);
|
|
||||||
|
|
||||||
let _ = unsafe { imp::WaitForSingleObject(self.obj, libc::INFINITE) };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn oneshot(&mut self, msecs: u64, cb: Box<Callback + Send>) {
|
|
||||||
self.remove();
|
|
||||||
|
|
||||||
// see above for the calculation
|
|
||||||
let due = -(msecs as i64 * 10000) as libc::LARGE_INTEGER;
|
|
||||||
assert_eq!(unsafe {
|
|
||||||
imp::SetWaitableTimer(self.obj, &due, 0, ptr::null_mut(),
|
|
||||||
ptr::null_mut(), 0)
|
|
||||||
}, 1);
|
|
||||||
|
|
||||||
HELPER.send(NewTimer(self.obj, cb, true));
|
|
||||||
self.on_worker = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn period(&mut self, msecs: u64, cb: Box<Callback + Send>) {
|
|
||||||
self.remove();
|
|
||||||
|
|
||||||
// see above for the calculation
|
|
||||||
let due = -(msecs as i64 * 10000) as libc::LARGE_INTEGER;
|
|
||||||
assert_eq!(unsafe {
|
|
||||||
imp::SetWaitableTimer(self.obj, &due, msecs as libc::LONG,
|
|
||||||
ptr::null_mut(), ptr::null_mut(), 0)
|
|
||||||
}, 1);
|
|
||||||
|
|
||||||
HELPER.send(NewTimer(self.obj, cb, false));
|
|
||||||
self.on_worker = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Timer {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.remove();
|
|
||||||
assert!(unsafe { libc::CloseHandle(self.obj) != 0 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod imp {
|
|
||||||
use libc::{LPSECURITY_ATTRIBUTES, BOOL, LPCSTR, HANDLE, LARGE_INTEGER,
|
|
||||||
LONG, LPVOID, DWORD, c_void};
|
|
||||||
|
|
||||||
pub type PTIMERAPCROUTINE = *mut c_void;
|
|
||||||
|
|
||||||
extern "system" {
|
|
||||||
pub fn CreateWaitableTimerA(lpTimerAttributes: LPSECURITY_ATTRIBUTES,
|
|
||||||
bManualReset: BOOL,
|
|
||||||
lpTimerName: LPCSTR) -> HANDLE;
|
|
||||||
pub fn SetWaitableTimer(hTimer: HANDLE,
|
|
||||||
pDueTime: *const LARGE_INTEGER,
|
|
||||||
lPeriod: LONG,
|
|
||||||
pfnCompletionRoutine: PTIMERAPCROUTINE,
|
|
||||||
lpArgToCompletionRoutine: LPVOID,
|
|
||||||
fResume: BOOL) -> BOOL;
|
|
||||||
pub fn WaitForMultipleObjects(nCount: DWORD,
|
|
||||||
lpHandles: *const HANDLE,
|
|
||||||
bWaitAll: BOOL,
|
|
||||||
dwMilliseconds: DWORD) -> DWORD;
|
|
||||||
pub fn WaitForSingleObject(hHandle: HANDLE,
|
|
||||||
dwMilliseconds: DWORD) -> DWORD;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,169 +0,0 @@
|
|||||||
// Copyright 2014 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.
|
|
||||||
|
|
||||||
// ignore-lexer-test FIXME #15877
|
|
||||||
|
|
||||||
//! Windows specific console TTY implementation
|
|
||||||
//!
|
|
||||||
//! This module contains the implementation of a Windows specific console TTY.
|
|
||||||
//! Also converts between UTF-16 and UTF-8. Windows has very poor support for
|
|
||||||
//! UTF-8 and some functions will panic. In particular ReadFile and ReadConsole
|
|
||||||
//! will panic when the codepage is set to UTF-8 and a Unicode character is
|
|
||||||
//! entered.
|
|
||||||
//!
|
|
||||||
//! FIXME
|
|
||||||
//! This implementation does not account for codepoints that are split across
|
|
||||||
//! multiple reads and writes. Also, this implementation does not expose a way
|
|
||||||
//! to read/write UTF-16 directly. When/if Rust receives a Reader/Writer
|
|
||||||
//! wrapper that performs encoding/decoding, this implementation should switch
|
|
||||||
//! to working in raw UTF-16, with such a wrapper around it.
|
|
||||||
|
|
||||||
#![allow(deprecated)]
|
|
||||||
|
|
||||||
use prelude::v1::*;
|
|
||||||
|
|
||||||
use old_io::{self, IoError, IoResult, MemReader, Reader};
|
|
||||||
use iter::repeat;
|
|
||||||
use libc::types::os::arch::extra::LPCVOID;
|
|
||||||
use libc::{c_int, HANDLE, LPDWORD, DWORD, LPVOID};
|
|
||||||
use libc::{get_osfhandle, CloseHandle};
|
|
||||||
use mem;
|
|
||||||
use ptr;
|
|
||||||
use str::from_utf8;
|
|
||||||
use super::c::{ENABLE_ECHO_INPUT, ENABLE_EXTENDED_FLAGS};
|
|
||||||
use super::c::{ENABLE_INSERT_MODE, ENABLE_LINE_INPUT};
|
|
||||||
use super::c::{ENABLE_PROCESSED_INPUT, ENABLE_QUICK_EDIT_MODE};
|
|
||||||
use super::c::CONSOLE_SCREEN_BUFFER_INFO;
|
|
||||||
use super::c::{ReadConsoleW, WriteConsoleW, GetConsoleMode, SetConsoleMode};
|
|
||||||
use super::c::GetConsoleScreenBufferInfo;
|
|
||||||
|
|
||||||
fn invalid_encoding() -> IoError {
|
|
||||||
IoError {
|
|
||||||
kind: old_io::InvalidInput,
|
|
||||||
desc: "text was not valid unicode",
|
|
||||||
detail: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_tty(fd: c_int) -> bool {
|
|
||||||
let mut out: DWORD = 0;
|
|
||||||
// If this function doesn't return an error, then fd is a TTY
|
|
||||||
match unsafe { GetConsoleMode(get_osfhandle(fd) as HANDLE,
|
|
||||||
&mut out as LPDWORD) } {
|
|
||||||
0 => false,
|
|
||||||
_ => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TTY {
|
|
||||||
closeme: bool,
|
|
||||||
handle: HANDLE,
|
|
||||||
utf8: MemReader,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TTY {
|
|
||||||
pub fn new(fd: c_int) -> IoResult<TTY> {
|
|
||||||
if is_tty(fd) {
|
|
||||||
// If the file descriptor is one of stdin, stderr, or stdout
|
|
||||||
// then it should not be closed by us
|
|
||||||
let closeme = match fd {
|
|
||||||
0...2 => false,
|
|
||||||
_ => true,
|
|
||||||
};
|
|
||||||
let handle = unsafe { get_osfhandle(fd) as HANDLE };
|
|
||||||
Ok(TTY {
|
|
||||||
handle: handle,
|
|
||||||
utf8: MemReader::new(Vec::new()),
|
|
||||||
closeme: closeme,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(IoError {
|
|
||||||
kind: old_io::MismatchedFileTypeForOperation,
|
|
||||||
desc: "invalid handle provided to function",
|
|
||||||
detail: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
|
||||||
// Read more if the buffer is empty
|
|
||||||
if self.utf8.eof() {
|
|
||||||
let mut utf16: Vec<u16> = repeat(0u16).take(0x1000).collect();
|
|
||||||
let mut num: DWORD = 0;
|
|
||||||
match unsafe { ReadConsoleW(self.handle,
|
|
||||||
utf16.as_mut_ptr() as LPVOID,
|
|
||||||
utf16.len() as u32,
|
|
||||||
&mut num as LPDWORD,
|
|
||||||
ptr::null_mut()) } {
|
|
||||||
0 => return Err(super::last_error()),
|
|
||||||
_ => (),
|
|
||||||
};
|
|
||||||
utf16.truncate(num as usize);
|
|
||||||
let utf8 = match String::from_utf16(&utf16) {
|
|
||||||
Ok(utf8) => utf8.into_bytes(),
|
|
||||||
Err(..) => return Err(invalid_encoding()),
|
|
||||||
};
|
|
||||||
self.utf8 = MemReader::new(utf8);
|
|
||||||
}
|
|
||||||
// MemReader shouldn't error here since we just filled it
|
|
||||||
Ok(self.utf8.read(buf).unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
|
||||||
let utf16 = match from_utf8(buf).ok() {
|
|
||||||
Some(utf8) => {
|
|
||||||
utf8.utf16_units().collect::<Vec<u16>>()
|
|
||||||
}
|
|
||||||
None => return Err(invalid_encoding()),
|
|
||||||
};
|
|
||||||
let mut num: DWORD = 0;
|
|
||||||
match unsafe { WriteConsoleW(self.handle,
|
|
||||||
utf16.as_ptr() as LPCVOID,
|
|
||||||
utf16.len() as u32,
|
|
||||||
&mut num as LPDWORD,
|
|
||||||
ptr::null_mut()) } {
|
|
||||||
0 => Err(super::last_error()),
|
|
||||||
_ => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_raw(&mut self, raw: bool) -> IoResult<()> {
|
|
||||||
// FIXME
|
|
||||||
// Somebody needs to decide on which of these flags we want
|
|
||||||
match unsafe { SetConsoleMode(self.handle,
|
|
||||||
match raw {
|
|
||||||
true => 0,
|
|
||||||
false => ENABLE_ECHO_INPUT | ENABLE_EXTENDED_FLAGS |
|
|
||||||
ENABLE_INSERT_MODE | ENABLE_LINE_INPUT |
|
|
||||||
ENABLE_PROCESSED_INPUT | ENABLE_QUICK_EDIT_MODE,
|
|
||||||
}) } {
|
|
||||||
0 => Err(super::last_error()),
|
|
||||||
_ => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_winsize(&mut self) -> IoResult<(isize, isize)> {
|
|
||||||
let mut info: CONSOLE_SCREEN_BUFFER_INFO = unsafe { mem::zeroed() };
|
|
||||||
match unsafe { GetConsoleScreenBufferInfo(self.handle, &mut info as *mut _) } {
|
|
||||||
0 => Err(super::last_error()),
|
|
||||||
_ => Ok(((info.srWindow.Right + 1 - info.srWindow.Left) as isize,
|
|
||||||
(info.srWindow.Bottom + 1 - info.srWindow.Top) as isize)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for TTY {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if self.closeme {
|
|
||||||
// Nobody cares about the return value
|
|
||||||
let _ = unsafe { CloseHandle(self.handle) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
// Copyright 2014 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.
|
|
||||||
|
|
||||||
pub use sys_common::net::UdpSocket;
|
|
@ -30,7 +30,6 @@
|
|||||||
#![feature(collections)]
|
#![feature(collections)]
|
||||||
#![feature(core)]
|
#![feature(core)]
|
||||||
#![feature(libc)]
|
#![feature(libc)]
|
||||||
#![feature(old_path)]
|
|
||||||
#![feature(quote, unsafe_destructor)]
|
#![feature(quote, unsafe_destructor)]
|
||||||
#![feature(rustc_private)]
|
#![feature(rustc_private)]
|
||||||
#![feature(staged_api)]
|
#![feature(staged_api)]
|
||||||
|
@ -23,10 +23,7 @@ use util::interner;
|
|||||||
|
|
||||||
use serialize::{Decodable, Decoder, Encodable, Encoder};
|
use serialize::{Decodable, Decoder, Encodable, Encoder};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::mem;
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
#[allow(deprecated)]
|
|
||||||
use std::old_path::BytesContainer;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
@ -639,19 +636,6 @@ impl Deref for InternedString {
|
|||||||
fn deref(&self) -> &str { &*self.string }
|
fn deref(&self) -> &str { &*self.string }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
impl BytesContainer for InternedString {
|
|
||||||
fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
|
|
||||||
// FIXME #12938: This is a workaround for the incorrect signature
|
|
||||||
// of `BytesContainer`, which is itself a workaround for the lack of
|
|
||||||
// DST.
|
|
||||||
unsafe {
|
|
||||||
let this = &self[..];
|
|
||||||
mem::transmute::<&[u8],&[u8]>(this.container_as_bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for InternedString {
|
impl fmt::Debug for InternedString {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
fmt::Debug::fmt(&self.string, f)
|
fmt::Debug::fmt(&self.string, f)
|
||||||
|
Loading…
Reference in New Issue
Block a user