rollup merge of #24303: alexcrichton/remove-deprecated
This commit removes these long deprecated modules. Such a nice diff stat!
This commit is contained in:
commit
88a145ea36
@ -12,16 +12,16 @@
|
||||
|
||||
#![feature(box_syntax)]
|
||||
#![feature(collections)]
|
||||
#![feature(old_io)]
|
||||
#![feature(rustc_private)]
|
||||
#![feature(unboxed_closures)]
|
||||
#![feature(std_misc)]
|
||||
#![feature(test)]
|
||||
#![feature(path_ext)]
|
||||
#![feature(str_char)]
|
||||
#![feature(libc)]
|
||||
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate libc;
|
||||
extern crate test;
|
||||
extern crate getopts;
|
||||
|
||||
@ -42,6 +42,7 @@ pub mod header;
|
||||
pub mod runtest;
|
||||
pub mod common;
|
||||
pub mod errors;
|
||||
mod raise_fd_limit;
|
||||
|
||||
pub fn main() {
|
||||
let config = parse_config(env::args().collect());
|
||||
@ -245,11 +246,7 @@ pub fn run_tests(config: &Config) {
|
||||
// sadly osx needs some file descriptor limits raised for running tests in
|
||||
// parallel (especially when we have lots and lots of child processes).
|
||||
// For context, see #8904
|
||||
#[allow(deprecated)]
|
||||
fn raise_fd_limit() {
|
||||
std::old_io::test::raise_fd_limit();
|
||||
}
|
||||
raise_fd_limit();
|
||||
unsafe { raise_fd_limit::raise_fd_limit(); }
|
||||
// Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
|
||||
// If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
|
||||
env::set_var("__COMPAT_LAYER", "RunAsInvoker");
|
||||
|
79
src/compiletest/raise_fd_limit.rs
Normal file
79
src/compiletest/raise_fd_limit.rs
Normal file
@ -0,0 +1,79 @@
|
||||
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/// 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)]
|
||||
pub unsafe fn raise_fd_limit() {
|
||||
use libc;
|
||||
use std::cmp;
|
||||
use std::io;
|
||||
use std::mem::size_of_val;
|
||||
use std::ptr::null_mut;
|
||||
|
||||
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;
|
||||
|
||||
// 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.
|
||||
|
||||
// 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 _ as *mut _, &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")))]
|
||||
pub unsafe fn raise_fd_limit() {}
|
@ -29,7 +29,6 @@ use std::net::TcpStream;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Output, ExitStatus};
|
||||
use std::str;
|
||||
use std::time::Duration;
|
||||
use test::MetricMap;
|
||||
|
||||
pub fn run(config: Config, testfile: &Path) {
|
||||
@ -452,11 +451,7 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
|
||||
.expect(&format!("failed to exec `{:?}`", config.adb_path));
|
||||
loop {
|
||||
//waiting 1 second for gdbserver start
|
||||
#[allow(deprecated)]
|
||||
fn sleep() {
|
||||
::std::old_io::timer::sleep(Duration::milliseconds(1000));
|
||||
}
|
||||
sleep();
|
||||
::std::thread::sleep_ms(1000);
|
||||
if TcpStream::connect("127.0.0.1:5039").is_ok() {
|
||||
break
|
||||
}
|
||||
|
@ -943,7 +943,7 @@ mod test {
|
||||
use std::clone::Clone;
|
||||
use std::iter::Iterator;
|
||||
use std::option::Option::{Some, None, self};
|
||||
use std::rand;
|
||||
use std::__rand::{thread_rng, Rng};
|
||||
use std::thread;
|
||||
use std::vec::Vec;
|
||||
|
||||
@ -1095,7 +1095,7 @@ mod test {
|
||||
let mut v = vec![];
|
||||
for i in 0..sz {
|
||||
check_links(&m);
|
||||
let r: u8 = rand::random();
|
||||
let r: u8 = thread_rng().next_u32() as u8;
|
||||
match r % 6 {
|
||||
0 => {
|
||||
m.pop_back();
|
||||
|
@ -12,14 +12,13 @@ macro_rules! map_insert_rand_bench {
|
||||
($name: ident, $n: expr, $map: ident) => (
|
||||
#[bench]
|
||||
pub fn $name(b: &mut ::test::Bencher) {
|
||||
use std::rand;
|
||||
use std::rand::Rng;
|
||||
use std::__rand::{thread_rng, Rng};
|
||||
use test::black_box;
|
||||
|
||||
let n: usize = $n;
|
||||
let mut map = $map::new();
|
||||
// setup
|
||||
let mut rng = rand::weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
|
||||
for _ in 0..n {
|
||||
let i = rng.gen::<usize>() % n;
|
||||
@ -67,8 +66,7 @@ macro_rules! map_find_rand_bench {
|
||||
#[bench]
|
||||
pub fn $name(b: &mut ::test::Bencher) {
|
||||
use std::iter::Iterator;
|
||||
use std::rand::Rng;
|
||||
use std::rand;
|
||||
use std::__rand::{thread_rng, Rng};
|
||||
use std::vec::Vec;
|
||||
use test::black_box;
|
||||
|
||||
@ -76,7 +74,7 @@ macro_rules! map_find_rand_bench {
|
||||
let n: usize = $n;
|
||||
|
||||
// setup
|
||||
let mut rng = rand::weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
let mut keys: Vec<_> = (0..n).map(|_| rng.gen::<usize>() % n).collect();
|
||||
|
||||
for &k in &keys {
|
||||
|
@ -389,16 +389,15 @@ fn test_bit_vec_clone() {
|
||||
|
||||
mod bench {
|
||||
use std::collections::{BitSet, BitVec};
|
||||
use std::rand::{Rng, self};
|
||||
use std::__rand::{Rng, thread_rng, ThreadRng};
|
||||
use std::u32;
|
||||
|
||||
use test::{Bencher, black_box};
|
||||
|
||||
const BENCH_BITS : usize = 1 << 14;
|
||||
|
||||
fn rng() -> rand::IsaacRng {
|
||||
let seed: &[_] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
|
||||
rand::SeedableRng::from_seed(seed)
|
||||
fn rng() -> ThreadRng {
|
||||
thread_rng()
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
@ -633,15 +633,14 @@ fn test_bit_vec_extend() {
|
||||
mod bench {
|
||||
use std::collections::BitVec;
|
||||
use std::u32;
|
||||
use std::rand::{Rng, self};
|
||||
use std::__rand::{Rng, thread_rng, ThreadRng};
|
||||
|
||||
use test::{Bencher, black_box};
|
||||
|
||||
const BENCH_BITS : usize = 1 << 14;
|
||||
|
||||
fn rng() -> rand::IsaacRng {
|
||||
let seed: &[_] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
|
||||
rand::SeedableRng::from_seed(seed)
|
||||
fn rng() -> ThreadRng {
|
||||
thread_rng()
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
@ -251,7 +251,7 @@ fn test_entry(){
|
||||
|
||||
mod bench {
|
||||
use std::collections::BTreeMap;
|
||||
use std::rand::{Rng, weak_rng};
|
||||
use std::__rand::{Rng, thread_rng};
|
||||
|
||||
use test::{Bencher, black_box};
|
||||
|
||||
@ -269,7 +269,7 @@ mod bench {
|
||||
|
||||
fn bench_iter(b: &mut Bencher, size: i32) {
|
||||
let mut map = BTreeMap::<i32, i32>::new();
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
|
||||
for _ in 0..size {
|
||||
map.insert(rng.gen(), rng.gen());
|
||||
|
@ -12,7 +12,7 @@ use std::cmp::Ordering::{Equal, Greater, Less};
|
||||
use std::default::Default;
|
||||
use std::iter::RandomAccessIterator;
|
||||
use std::mem;
|
||||
use std::rand::{Rng, thread_rng};
|
||||
use std::__rand::{Rng, thread_rng};
|
||||
use std::rc::Rc;
|
||||
use std::slice::ElementSwaps;
|
||||
|
||||
@ -1296,7 +1296,7 @@ fn test_to_vec() {
|
||||
mod bench {
|
||||
use std::iter::repeat;
|
||||
use std::{mem, ptr};
|
||||
use std::rand::{Rng, weak_rng};
|
||||
use std::__rand::{Rng, thread_rng};
|
||||
|
||||
use test::{Bencher, black_box};
|
||||
|
||||
@ -1465,7 +1465,7 @@ mod bench {
|
||||
|
||||
#[bench]
|
||||
fn random_inserts(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| {
|
||||
let mut v: Vec<_> = repeat((0, 0)).take(30).collect();
|
||||
for _ in 0..100 {
|
||||
@ -1477,7 +1477,7 @@ mod bench {
|
||||
}
|
||||
#[bench]
|
||||
fn random_removes(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| {
|
||||
let mut v: Vec<_> = repeat((0, 0)).take(130).collect();
|
||||
for _ in 0..100 {
|
||||
@ -1489,7 +1489,7 @@ mod bench {
|
||||
|
||||
#[bench]
|
||||
fn sort_random_small(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| {
|
||||
let mut v: Vec<_> = rng.gen_iter::<u64>().take(5).collect();
|
||||
v.sort();
|
||||
@ -1499,7 +1499,7 @@ mod bench {
|
||||
|
||||
#[bench]
|
||||
fn sort_random_medium(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| {
|
||||
let mut v: Vec<_> = rng.gen_iter::<u64>().take(100).collect();
|
||||
v.sort();
|
||||
@ -1509,7 +1509,7 @@ mod bench {
|
||||
|
||||
#[bench]
|
||||
fn sort_random_large(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| {
|
||||
let mut v: Vec<_> = rng.gen_iter::<u64>().take(10000).collect();
|
||||
v.sort();
|
||||
@ -1530,7 +1530,7 @@ mod bench {
|
||||
|
||||
#[bench]
|
||||
fn sort_big_random_small(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| {
|
||||
let mut v = rng.gen_iter::<BigSortable>().take(5)
|
||||
.collect::<Vec<BigSortable>>();
|
||||
@ -1541,7 +1541,7 @@ mod bench {
|
||||
|
||||
#[bench]
|
||||
fn sort_big_random_medium(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| {
|
||||
let mut v = rng.gen_iter::<BigSortable>().take(100)
|
||||
.collect::<Vec<BigSortable>>();
|
||||
@ -1552,7 +1552,7 @@ mod bench {
|
||||
|
||||
#[bench]
|
||||
fn sort_big_random_large(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| {
|
||||
let mut v = rng.gen_iter::<BigSortable>().take(10000)
|
||||
.collect::<Vec<BigSortable>>();
|
||||
|
@ -85,35 +85,32 @@
|
||||
//! functions that may encounter errors but don't otherwise return a
|
||||
//! useful value.
|
||||
//!
|
||||
//! Consider the `write_line` method defined for I/O types
|
||||
//! by the [`Writer`](../old_io/trait.Writer.html) trait:
|
||||
//! Consider the `write_all` method defined for I/O types
|
||||
//! by the [`Write`](../io/trait.Write.html) trait:
|
||||
//!
|
||||
//! ```
|
||||
//! # #![feature(old_io)]
|
||||
//! use std::old_io::IoError;
|
||||
//! use std::io;
|
||||
//!
|
||||
//! trait Writer {
|
||||
//! fn write_line(&mut self, s: &str) -> Result<(), IoError>;
|
||||
//! fn write_all(&mut self, bytes: &[u8]) -> Result<(), io::Error>;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! *Note: The actual definition of `Writer` uses `IoResult`, which
|
||||
//! is just a synonym for `Result<T, IoError>`.*
|
||||
//! *Note: The actual definition of `Write` uses `io::Result`, which
|
||||
//! is just a synonym for `Result<T, io::Error>`.*
|
||||
//!
|
||||
//! This method doesn't produce a value, but the write may
|
||||
//! fail. It's crucial to handle the error case, and *not* write
|
||||
//! something like this:
|
||||
//!
|
||||
//! ```{.ignore}
|
||||
//! # #![feature(old_io)]
|
||||
//! use std::old_io::*;
|
||||
//! use std::old_path::Path;
|
||||
//! ```no_run
|
||||
//! use std::fs::File;
|
||||
//! use std::io::prelude::*;
|
||||
//!
|
||||
//! let mut file = File::open_mode(&Path::new("valuable_data.txt"), Open, Write);
|
||||
//! // If `write_line` errors, then we'll never know, because the return
|
||||
//! let mut file = File::create("valuable_data.txt").unwrap();
|
||||
//! // If `write_all` errors, then we'll never know, because the return
|
||||
//! // value is ignored.
|
||||
//! file.write_line("important message");
|
||||
//! drop(file);
|
||||
//! file.write_all(b"important message");
|
||||
//! ```
|
||||
//!
|
||||
//! If you *do* write that in Rust, the compiler will give you a
|
||||
@ -125,37 +122,31 @@
|
||||
//! a marginally useful message indicating why:
|
||||
//!
|
||||
//! ```{.no_run}
|
||||
//! # #![feature(old_io, old_path)]
|
||||
//! use std::old_io::*;
|
||||
//! use std::old_path::Path;
|
||||
//! use std::fs::File;
|
||||
//! use std::io::prelude::*;
|
||||
//!
|
||||
//! let mut file = File::open_mode(&Path::new("valuable_data.txt"), Open, Write);
|
||||
//! file.write_line("important message").ok().expect("failed to write message");
|
||||
//! drop(file);
|
||||
//! let mut file = File::create("valuable_data.txt").unwrap();
|
||||
//! file.write_all(b"important message").ok().expect("failed to write message");
|
||||
//! ```
|
||||
//!
|
||||
//! You might also simply assert success:
|
||||
//!
|
||||
//! ```{.no_run}
|
||||
//! # #![feature(old_io, old_path)]
|
||||
//! # use std::old_io::*;
|
||||
//! # use std::old_path::Path;
|
||||
//!
|
||||
//! # let mut file = File::open_mode(&Path::new("valuable_data.txt"), Open, Write);
|
||||
//! assert!(file.write_line("important message").is_ok());
|
||||
//! # drop(file);
|
||||
//! # use std::fs::File;
|
||||
//! # use std::io::prelude::*;
|
||||
//! # let mut file = File::create("valuable_data.txt").unwrap();
|
||||
//! assert!(file.write_all(b"important message").is_ok());
|
||||
//! ```
|
||||
//!
|
||||
//! Or propagate the error up the call stack with `try!`:
|
||||
//!
|
||||
//! ```
|
||||
//! # #![feature(old_io, old_path)]
|
||||
//! # use std::old_io::*;
|
||||
//! # use std::old_path::Path;
|
||||
//! fn write_message() -> Result<(), IoError> {
|
||||
//! let mut file = File::open_mode(&Path::new("valuable_data.txt"), Open, Write);
|
||||
//! try!(file.write_line("important message"));
|
||||
//! drop(file);
|
||||
//! # use std::fs::File;
|
||||
//! # use std::io::prelude::*;
|
||||
//! # use std::io;
|
||||
//! fn write_message() -> io::Result<()> {
|
||||
//! let mut file = try!(File::create("valuable_data.txt"));
|
||||
//! try!(file.write_all(b"important message"));
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
@ -170,9 +161,9 @@
|
||||
//! It replaces this:
|
||||
//!
|
||||
//! ```
|
||||
//! # #![feature(old_io, old_path)]
|
||||
//! use std::old_io::*;
|
||||
//! use std::old_path::Path;
|
||||
//! use std::fs::File;
|
||||
//! use std::io::prelude::*;
|
||||
//! use std::io;
|
||||
//!
|
||||
//! struct Info {
|
||||
//! name: String,
|
||||
@ -180,25 +171,28 @@
|
||||
//! rating: i32,
|
||||
//! }
|
||||
//!
|
||||
//! fn write_info(info: &Info) -> Result<(), IoError> {
|
||||
//! let mut file = File::open_mode(&Path::new("my_best_friends.txt"), Open, Write);
|
||||
//! fn write_info(info: &Info) -> io::Result<()> {
|
||||
//! let mut file = try!(File::create("my_best_friends.txt"));
|
||||
//! // Early return on error
|
||||
//! if let Err(e) = file.write_line(&format!("name: {}", info.name)) {
|
||||
//! if let Err(e) = file.write_all(format!("name: {}\n", info.name).as_bytes()) {
|
||||
//! return Err(e)
|
||||
//! }
|
||||
//! if let Err(e) = file.write_line(&format!("age: {}", info.age)) {
|
||||
//! if let Err(e) = file.write_all(format!("age: {}\n", info.age).as_bytes()) {
|
||||
//! return Err(e)
|
||||
//! }
|
||||
//! file.write_line(&format!("rating: {}", info.rating))
|
||||
//! if let Err(e) = file.write_all(format!("rating: {}\n", info.rating).as_bytes()) {
|
||||
//! return Err(e)
|
||||
//! }
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! With this:
|
||||
//!
|
||||
//! ```
|
||||
//! # #![feature(old_io, old_path)]
|
||||
//! use std::old_io::*;
|
||||
//! use std::old_path::Path;
|
||||
//! use std::fs::File;
|
||||
//! use std::io::prelude::*;
|
||||
//! use std::io;
|
||||
//!
|
||||
//! struct Info {
|
||||
//! name: String,
|
||||
@ -206,12 +200,12 @@
|
||||
//! rating: i32,
|
||||
//! }
|
||||
//!
|
||||
//! fn write_info(info: &Info) -> Result<(), IoError> {
|
||||
//! let mut file = File::open_mode(&Path::new("my_best_friends.txt"), Open, Write);
|
||||
//! fn write_info(info: &Info) -> io::Result<()> {
|
||||
//! let mut file = try!(File::create("my_best_friends.txt"));
|
||||
//! // Early return on error
|
||||
//! try!(file.write_line(&format!("name: {}", info.name)));
|
||||
//! try!(file.write_line(&format!("age: {}", info.age)));
|
||||
//! try!(file.write_line(&format!("rating: {}", info.rating)));
|
||||
//! try!(file.write_all(format!("name: {}\n", info.name).as_bytes()));
|
||||
//! try!(file.write_all(format!("age: {}\n", info.age).as_bytes()));
|
||||
//! try!(file.write_all(format!("rating: {}\n", info.rating).as_bytes()));
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
@ -464,29 +458,17 @@ impl<T, E> Result<T, E> {
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Sum the lines of a buffer by mapping strings to numbers,
|
||||
/// ignoring I/O and parse errors:
|
||||
/// Print the numbers on each line of a string multiplied by two.
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(old_io)]
|
||||
/// use std::old_io::*;
|
||||
/// let line = "1\n2\n3\n4\n";
|
||||
///
|
||||
/// let mut buffer: &[u8] = b"1\n2\n3\n4\n";
|
||||
/// let mut buffer = &mut buffer;
|
||||
///
|
||||
/// let mut sum = 0;
|
||||
///
|
||||
/// while !buffer.is_empty() {
|
||||
/// let line: IoResult<String> = buffer.read_line();
|
||||
/// // Convert the string line to a number using `map` and `from_str`
|
||||
/// let val: IoResult<i32> = line.map(|line| {
|
||||
/// line.trim_right().parse::<i32>().unwrap_or(0)
|
||||
/// });
|
||||
/// // Add the value if there were no errors, otherwise add 0
|
||||
/// sum += val.unwrap_or(0);
|
||||
/// for num in line.lines() {
|
||||
/// match num.parse::<i32>().map(|i| i * 2) {
|
||||
/// Ok(n) => println!("{}", n),
|
||||
/// Err(..) => {}
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// assert!(sum == 10);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
|
@ -169,42 +169,42 @@ fn test_radix_base_too_large() {
|
||||
mod u32 {
|
||||
use test::Bencher;
|
||||
use core::fmt::radix;
|
||||
use std::rand::{weak_rng, Rng};
|
||||
use std::__rand::{thread_rng, Rng};
|
||||
use std::io::{Write, sink};
|
||||
|
||||
#[bench]
|
||||
fn format_bin(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| { write!(&mut sink(), "{:b}", rng.gen::<u32>()) })
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn format_oct(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| { write!(&mut sink(), "{:o}", rng.gen::<u32>()) })
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn format_dec(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| { write!(&mut sink(), "{}", rng.gen::<u32>()) })
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn format_hex(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| { write!(&mut sink(), "{:x}", rng.gen::<u32>()) })
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn format_show(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| { write!(&mut sink(), "{:?}", rng.gen::<u32>()) })
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn format_base_36(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| { write!(&mut sink(), "{}", radix(rng.gen::<u32>(), 36)) })
|
||||
}
|
||||
}
|
||||
@ -212,42 +212,42 @@ mod u32 {
|
||||
mod i32 {
|
||||
use test::Bencher;
|
||||
use core::fmt::radix;
|
||||
use std::rand::{weak_rng, Rng};
|
||||
use std::__rand::{thread_rng, Rng};
|
||||
use std::io::{Write, sink};
|
||||
|
||||
#[bench]
|
||||
fn format_bin(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| { write!(&mut sink(), "{:b}", rng.gen::<i32>()) })
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn format_oct(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| { write!(&mut sink(), "{:o}", rng.gen::<i32>()) })
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn format_dec(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| { write!(&mut sink(), "{}", rng.gen::<i32>()) })
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn format_hex(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| { write!(&mut sink(), "{:x}", rng.gen::<i32>()) })
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn format_show(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| { write!(&mut sink(), "{:?}", rng.gen::<i32>()) })
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn format_base_36(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| { write!(&mut sink(), "{}", radix(rng.gen::<i32>(), 36)) })
|
||||
}
|
||||
}
|
||||
|
@ -155,12 +155,11 @@ pub fn inflate_bytes_zlib(bytes: &[u8]) -> Result<Bytes,Error> {
|
||||
mod tests {
|
||||
#![allow(deprecated)]
|
||||
use super::{inflate_bytes, deflate_bytes};
|
||||
use std::rand;
|
||||
use std::rand::Rng;
|
||||
use std::__rand::{thread_rng, Rng};
|
||||
|
||||
#[test]
|
||||
fn test_flate_round_trip() {
|
||||
let mut r = rand::thread_rng();
|
||||
let mut r = thread_rng();
|
||||
let mut words = vec![];
|
||||
for _ in 0..20 {
|
||||
let range = r.gen_range(1, 10);
|
||||
|
@ -56,18 +56,6 @@ impl Rand for Exp1 {
|
||||
///
|
||||
/// This distribution has density function: `f(x) = lambda *
|
||||
/// exp(-lambda * x)` for `x > 0`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(rand)]
|
||||
/// use std::rand;
|
||||
/// use std::rand::distributions::{Exp, IndependentSample};
|
||||
///
|
||||
/// let exp = Exp::new(2.0);
|
||||
/// let v = exp.ind_sample(&mut rand::thread_rng());
|
||||
/// println!("{} is from a Exp(2) distribution", v);
|
||||
/// ```
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Exp {
|
||||
/// `lambda` stored as `1/lambda`, since this is what we scale by.
|
||||
|
@ -37,18 +37,6 @@ use super::{IndependentSample, Sample, Exp};
|
||||
/// == 1`, and using the boosting technique described in [1] for
|
||||
/// `shape < 1`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(rand)]
|
||||
/// use std::rand;
|
||||
/// use std::rand::distributions::{IndependentSample, Gamma};
|
||||
///
|
||||
/// let gamma = Gamma::new(2.0, 5.0);
|
||||
/// let v = gamma.ind_sample(&mut rand::thread_rng());
|
||||
/// println!("{} is from a Gamma(2, 5) distribution", v);
|
||||
/// ```
|
||||
///
|
||||
/// [1]: George Marsaglia and Wai Wan Tsang. 2000. "A Simple Method
|
||||
/// for Generating Gamma Variables" *ACM Trans. Math. Softw.* 26, 3
|
||||
/// (September 2000),
|
||||
@ -184,18 +172,6 @@ impl IndependentSample<f64> for GammaLargeShape {
|
||||
/// of `k` independent standard normal random variables. For other
|
||||
/// `k`, this uses the equivalent characterisation `χ²(k) = Gamma(k/2,
|
||||
/// 2)`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(rand)]
|
||||
/// use std::rand;
|
||||
/// use std::rand::distributions::{ChiSquared, IndependentSample};
|
||||
///
|
||||
/// let chi = ChiSquared::new(11.0);
|
||||
/// let v = chi.ind_sample(&mut rand::thread_rng());
|
||||
/// println!("{} is from a χ²(11) distribution", v)
|
||||
/// ```
|
||||
pub struct ChiSquared {
|
||||
repr: ChiSquaredRepr,
|
||||
}
|
||||
@ -242,18 +218,6 @@ impl IndependentSample<f64> for ChiSquared {
|
||||
/// This distribution is equivalent to the ratio of two normalised
|
||||
/// chi-squared distributions, that is, `F(m,n) = (χ²(m)/m) /
|
||||
/// (χ²(n)/n)`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(rand)]
|
||||
/// use std::rand;
|
||||
/// use std::rand::distributions::{FisherF, IndependentSample};
|
||||
///
|
||||
/// let f = FisherF::new(2.0, 32.0);
|
||||
/// let v = f.ind_sample(&mut rand::thread_rng());
|
||||
/// println!("{} is from an F(2, 32) distribution", v)
|
||||
/// ```
|
||||
pub struct FisherF {
|
||||
numer: ChiSquared,
|
||||
denom: ChiSquared,
|
||||
@ -287,18 +251,6 @@ impl IndependentSample<f64> for FisherF {
|
||||
|
||||
/// The Student t distribution, `t(nu)`, where `nu` is the degrees of
|
||||
/// freedom.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(rand)]
|
||||
/// use std::rand;
|
||||
/// use std::rand::distributions::{StudentT, IndependentSample};
|
||||
///
|
||||
/// let t = StudentT::new(11.0);
|
||||
/// let v = t.ind_sample(&mut rand::thread_rng());
|
||||
/// println!("{} is from a t(11) distribution", v)
|
||||
/// ```
|
||||
pub struct StudentT {
|
||||
chi: ChiSquared,
|
||||
dof: f64
|
||||
|
@ -90,24 +90,6 @@ pub struct Weighted<T> {
|
||||
/// `IndependentSample` traits. Note that `&T` is (cheaply) `Clone` for
|
||||
/// all `T`, as is `usize`, so one can store references or indices into
|
||||
/// another vector.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(rand)]
|
||||
/// use std::rand;
|
||||
/// use std::rand::distributions::{Weighted, WeightedChoice, IndependentSample};
|
||||
///
|
||||
/// let mut items = vec!(Weighted { weight: 2, item: 'a' },
|
||||
/// Weighted { weight: 4, item: 'b' },
|
||||
/// Weighted { weight: 1, item: 'c' });
|
||||
/// let wc = WeightedChoice::new(&mut items[..]);
|
||||
/// let mut rng = rand::thread_rng();
|
||||
/// for _ in 0..16 {
|
||||
/// // on average prints 'a' 4 times, 'b' 8 and 'c' twice.
|
||||
/// println!("{}", wc.ind_sample(&mut rng));
|
||||
/// }
|
||||
/// ```
|
||||
pub struct WeightedChoice<'a, T:'a> {
|
||||
items: &'a mut [Weighted<T>],
|
||||
weight_range: Range<usize>
|
||||
|
@ -72,19 +72,6 @@ impl Rand for StandardNormal {
|
||||
///
|
||||
/// This uses the ZIGNOR variant of the Ziggurat method, see
|
||||
/// `StandardNormal` for more details.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(rand)]
|
||||
/// use std::rand;
|
||||
/// use std::rand::distributions::{Normal, IndependentSample};
|
||||
///
|
||||
/// // mean 2, standard deviation 3
|
||||
/// let normal = Normal::new(2.0, 3.0);
|
||||
/// let v = normal.ind_sample(&mut rand::thread_rng());
|
||||
/// println!("{} is from a N(2, 9) distribution", v)
|
||||
/// ```
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Normal {
|
||||
mean: f64,
|
||||
@ -121,19 +108,6 @@ impl IndependentSample<f64> for Normal {
|
||||
///
|
||||
/// If `X` is log-normal distributed, then `ln(X)` is `N(mean,
|
||||
/// std_dev**2)` distributed.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(rand)]
|
||||
/// use std::rand;
|
||||
/// use std::rand::distributions::{LogNormal, IndependentSample};
|
||||
///
|
||||
/// // mean 2, standard deviation 3
|
||||
/// let log_normal = LogNormal::new(2.0, 3.0);
|
||||
/// let v = log_normal.ind_sample(&mut rand::thread_rng());
|
||||
/// println!("{} is from an ln N(2, 9) distribution", v)
|
||||
/// ```
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct LogNormal {
|
||||
norm: Normal
|
||||
|
@ -32,23 +32,6 @@ use distributions::{Sample, IndependentSample};
|
||||
/// including `high`, but this may be very difficult. All the
|
||||
/// primitive integer types satisfy this property, and the float types
|
||||
/// normally satisfy it, but rounding may mean `high` can occur.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(rand)]
|
||||
/// use std::rand::distributions::{IndependentSample, Range};
|
||||
///
|
||||
/// fn main() {
|
||||
/// let between = Range::new(10, 10000);
|
||||
/// let mut rng = std::rand::thread_rng();
|
||||
/// let mut sum = 0;
|
||||
/// for _ in 0..1000 {
|
||||
/// sum += between.ind_sample(&mut rng);
|
||||
/// }
|
||||
/// println!("{}", sum);
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Range<X> {
|
||||
low: X,
|
||||
range: X,
|
||||
|
@ -24,15 +24,13 @@
|
||||
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
|
||||
html_root_url = "http://doc.rust-lang.org/nightly/",
|
||||
html_playground_url = "http://play.rust-lang.org/")]
|
||||
#![feature(no_std)]
|
||||
#![no_std]
|
||||
#![unstable(feature = "rand")]
|
||||
#![feature(staged_api)]
|
||||
#![staged_api]
|
||||
#![unstable(feature = "rand")]
|
||||
#![feature(core)]
|
||||
#![feature(no_std)]
|
||||
#![feature(staged_api)]
|
||||
#![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))]
|
||||
|
||||
@ -145,17 +143,6 @@ pub trait Rng : Sized {
|
||||
/// with new data, and may panic if this is impossible
|
||||
/// (e.g. reading past the end of a file that is being used as the
|
||||
/// source of randomness).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(rand, core)]
|
||||
/// use std::rand::{thread_rng, Rng};
|
||||
///
|
||||
/// let mut v = [0; 13579];
|
||||
/// thread_rng().fill_bytes(&mut v);
|
||||
/// println!("{:?}", &v[..]);
|
||||
/// ```
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
// this could, in theory, be done by transmuting dest to a
|
||||
// [u64], but this is (1) likely to be undefined behaviour for
|
||||
@ -181,18 +168,6 @@ pub trait Rng : Sized {
|
||||
}
|
||||
|
||||
/// Return a random value of a `Rand` type.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(rand)]
|
||||
/// use std::rand::{thread_rng, Rng};
|
||||
///
|
||||
/// let mut rng = thread_rng();
|
||||
/// let x: usize = rng.gen();
|
||||
/// println!("{}", x);
|
||||
/// println!("{:?}", rng.gen::<(f64, bool)>());
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
fn gen<T: Rand>(&mut self) -> T {
|
||||
Rand::rand(self)
|
||||
@ -200,19 +175,6 @@ pub trait Rng : Sized {
|
||||
|
||||
/// Return an iterator that will yield an infinite number of randomly
|
||||
/// generated items.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(rand)]
|
||||
/// use std::rand::{thread_rng, Rng};
|
||||
///
|
||||
/// let mut rng = thread_rng();
|
||||
/// let x = rng.gen_iter::<usize>().take(10).collect::<Vec<usize>>();
|
||||
/// println!("{:?}", x);
|
||||
/// println!("{:?}", rng.gen_iter::<(f64, bool)>().take(5)
|
||||
/// .collect::<Vec<(f64, bool)>>());
|
||||
/// ```
|
||||
fn gen_iter<'a, T: Rand>(&'a mut self) -> Generator<'a, T, Self> {
|
||||
Generator { rng: self, _marker: PhantomData }
|
||||
}
|
||||
@ -228,50 +190,17 @@ pub trait Rng : Sized {
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `low >= high`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(rand)]
|
||||
/// use std::rand::{thread_rng, Rng};
|
||||
///
|
||||
/// let mut rng = thread_rng();
|
||||
/// let n: usize = rng.gen_range(0, 10);
|
||||
/// println!("{}", n);
|
||||
/// let m: f64 = rng.gen_range(-40.0f64, 1.3e5f64);
|
||||
/// println!("{}", m);
|
||||
/// ```
|
||||
fn gen_range<T: PartialOrd + SampleRange>(&mut self, low: T, high: T) -> T {
|
||||
assert!(low < high, "Rng.gen_range called with low >= high");
|
||||
Range::new(low, high).ind_sample(self)
|
||||
}
|
||||
|
||||
/// Return a bool with a 1 in n chance of true
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(rand)]
|
||||
/// use std::rand::{thread_rng, Rng};
|
||||
///
|
||||
/// let mut rng = thread_rng();
|
||||
/// println!("{}", rng.gen_weighted_bool(3));
|
||||
/// ```
|
||||
fn gen_weighted_bool(&mut self, n: usize) -> bool {
|
||||
n <= 1 || self.gen_range(0, n) == 0
|
||||
}
|
||||
|
||||
/// Return an iterator of random characters from the set A-Z,a-z,0-9.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(rand)]
|
||||
/// use std::rand::{thread_rng, Rng};
|
||||
///
|
||||
/// let s: String = thread_rng().gen_ascii_chars().take(10).collect();
|
||||
/// println!("{}", s);
|
||||
/// ```
|
||||
fn gen_ascii_chars<'a>(&'a mut self) -> AsciiGenerator<'a, Self> {
|
||||
AsciiGenerator { rng: self }
|
||||
}
|
||||
@ -279,18 +208,6 @@ pub trait Rng : Sized {
|
||||
/// Return a random element from `values`.
|
||||
///
|
||||
/// Return `None` if `values` is empty.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(rand)]
|
||||
/// use std::rand::{thread_rng, Rng};
|
||||
///
|
||||
/// let choices = [1, 2, 4, 8, 16, 32];
|
||||
/// let mut rng = thread_rng();
|
||||
/// println!("{:?}", rng.choose(&choices));
|
||||
/// assert_eq!(rng.choose(&choices[..0]), None);
|
||||
/// ```
|
||||
fn choose<'a, T>(&mut self, values: &'a [T]) -> Option<&'a T> {
|
||||
if values.is_empty() {
|
||||
None
|
||||
@ -300,20 +217,6 @@ pub trait Rng : Sized {
|
||||
}
|
||||
|
||||
/// Shuffle a mutable slice in place.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(rand, core)]
|
||||
/// use std::rand::{thread_rng, Rng};
|
||||
///
|
||||
/// let mut rng = thread_rng();
|
||||
/// let mut y = [1, 2, 3];
|
||||
/// rng.shuffle(&mut y);
|
||||
/// println!("{:?}", y);
|
||||
/// rng.shuffle(&mut y);
|
||||
/// println!("{:?}", y);
|
||||
/// ```
|
||||
fn shuffle<T>(&mut self, values: &mut [T]) {
|
||||
let mut i = values.len();
|
||||
while i >= 2 {
|
||||
@ -364,33 +267,9 @@ impl<'a, R: Rng> Iterator for AsciiGenerator<'a, R> {
|
||||
/// the same stream of randomness multiple times.
|
||||
pub trait SeedableRng<Seed>: Rng {
|
||||
/// Reseed an RNG with the given seed.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(rand)]
|
||||
/// use std::rand::{Rng, SeedableRng, StdRng};
|
||||
///
|
||||
/// let seed: &[_] = &[1, 2, 3, 4];
|
||||
/// let mut rng: StdRng = SeedableRng::from_seed(seed);
|
||||
/// println!("{}", rng.gen::<f64>());
|
||||
/// rng.reseed(&[5, 6, 7, 8]);
|
||||
/// println!("{}", rng.gen::<f64>());
|
||||
/// ```
|
||||
fn reseed(&mut self, Seed);
|
||||
|
||||
/// Create a new RNG with the given seed.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(rand)]
|
||||
/// use std::rand::{Rng, SeedableRng, StdRng};
|
||||
///
|
||||
/// let seed: &[_] = &[1, 2, 3, 4];
|
||||
/// let mut rng: StdRng = SeedableRng::from_seed(seed);
|
||||
/// println!("{}", rng.gen::<f64>());
|
||||
/// ```
|
||||
fn from_seed(seed: Seed) -> Self;
|
||||
}
|
||||
|
||||
@ -486,16 +365,6 @@ impl Rand for XorShiftRng {
|
||||
/// Use `Closed01` for the closed interval `[0,1]`, and the default
|
||||
/// `Rand` implementation for `f32` and `f64` for the half-open
|
||||
/// `[0,1)`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(rand)]
|
||||
/// use std::rand::{random, Open01};
|
||||
///
|
||||
/// let Open01(val) = random::<Open01<f32>>();
|
||||
/// println!("f32 from (0,1): {}", val);
|
||||
/// ```
|
||||
pub struct Open01<F>(pub F);
|
||||
|
||||
/// A wrapper for generating floating point numbers uniformly in the
|
||||
@ -504,31 +373,17 @@ pub struct Open01<F>(pub F);
|
||||
/// Use `Open01` for the closed interval `(0,1)`, and the default
|
||||
/// `Rand` implementation of `f32` and `f64` for the half-open
|
||||
/// `[0,1)`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(rand)]
|
||||
/// use std::rand::{random, Closed01};
|
||||
///
|
||||
/// let Closed01(val) = random::<Closed01<f32>>();
|
||||
/// println!("f32 from [0,1]: {}", val);
|
||||
/// ```
|
||||
pub struct Closed01<F>(pub F);
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::rand;
|
||||
use std::__rand as rand;
|
||||
|
||||
pub struct MyRng<R> { inner: R }
|
||||
|
||||
impl<R: rand::Rng> ::Rng for MyRng<R> {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
fn next<T: rand::Rng>(t: &mut T) -> u32 {
|
||||
use std::rand::Rng;
|
||||
t.next_u32()
|
||||
}
|
||||
next(&mut self.inner)
|
||||
rand::Rng::next_u32(&mut self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
@ -536,7 +391,7 @@ mod test {
|
||||
MyRng { inner: rand::thread_rng() }
|
||||
}
|
||||
|
||||
pub fn weak_rng() -> MyRng<rand::XorShiftRng> {
|
||||
MyRng { inner: rand::weak_rng() }
|
||||
pub fn weak_rng() -> MyRng<rand::ThreadRng> {
|
||||
MyRng { inner: rand::thread_rng() }
|
||||
}
|
||||
}
|
||||
|
@ -211,55 +211,3 @@ impl<T:Rand> Rand for Option<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::rand::{Rng, thread_rng, Open01, Closed01};
|
||||
|
||||
struct ConstantRng(u64);
|
||||
impl Rng for ConstantRng {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
let ConstantRng(v) = *self;
|
||||
v as u32
|
||||
}
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
let ConstantRng(v) = *self;
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn floating_point_edge_cases() {
|
||||
// the test for exact equality is correct here.
|
||||
assert!(ConstantRng(0xffff_ffff).gen::<f32>() != 1.0);
|
||||
assert!(ConstantRng(0xffff_ffff_ffff_ffff).gen::<f64>() != 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rand_open() {
|
||||
// this is unlikely to catch an incorrect implementation that
|
||||
// generates exactly 0 or 1, but it keeps it sane.
|
||||
let mut rng = thread_rng();
|
||||
for _ in 0..1_000 {
|
||||
// strict inequalities
|
||||
let Open01(f) = rng.gen::<Open01<f64>>();
|
||||
assert!(0.0 < f && f < 1.0);
|
||||
|
||||
let Open01(f) = rng.gen::<Open01<f32>>();
|
||||
assert!(0.0 < f && f < 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rand_closed() {
|
||||
let mut rng = thread_rng();
|
||||
for _ in 0..1_000 {
|
||||
// strict inequalities
|
||||
let Closed01(f) = rng.gen::<Closed01<f64>>();
|
||||
assert!(0.0 <= f && f <= 1.0);
|
||||
|
||||
let Closed01(f) = rng.gen::<Closed01<f32>>();
|
||||
assert!(0.0 <= f && f <= 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -99,34 +99,6 @@ impl<S, R: SeedableRng<S>, Rsdr: Reseeder<R> + Default>
|
||||
}
|
||||
|
||||
/// Something that can be used to reseed an RNG via `ReseedingRng`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(rand)]
|
||||
/// use std::rand::{Rng, SeedableRng, StdRng};
|
||||
/// use std::rand::reseeding::{Reseeder, ReseedingRng};
|
||||
///
|
||||
/// struct TickTockReseeder { tick: bool }
|
||||
/// impl Reseeder<StdRng> for TickTockReseeder {
|
||||
/// fn reseed(&mut self, rng: &mut StdRng) {
|
||||
/// let val = if self.tick {0} else {1};
|
||||
/// rng.reseed(&[val]);
|
||||
/// self.tick = !self.tick;
|
||||
/// }
|
||||
/// }
|
||||
/// fn main() {
|
||||
/// let rsdr = TickTockReseeder { tick: true };
|
||||
///
|
||||
/// let inner = StdRng::new().unwrap();
|
||||
/// let mut rng = ReseedingRng::new(inner, 10, rsdr);
|
||||
///
|
||||
/// // this will repeat, because it gets reseeded very regularly.
|
||||
/// let s: String = rng.gen_ascii_chars().take(100).collect();
|
||||
/// println!("{}", s);
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
pub trait Reseeder<R> {
|
||||
/// Reseed the given RNG.
|
||||
fn reseed(&mut self, rng: &mut R);
|
||||
|
@ -9,69 +9,42 @@
|
||||
// except according to those terms.
|
||||
|
||||
use std::io;
|
||||
use std::env;
|
||||
#[allow(deprecated)] use std::old_path::{self, GenericPath};
|
||||
#[allow(deprecated)] use std::old_io;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// Returns an absolute path in the filesystem that `path` points to. The
|
||||
/// returned path does not contain any symlinks in its hierarchy.
|
||||
#[allow(deprecated)] // readlink is deprecated
|
||||
#[cfg(windows)]
|
||||
pub fn realpath(original: &Path) -> io::Result<PathBuf> {
|
||||
let old = old_path::Path::new(original.to_str().unwrap());
|
||||
match old_realpath(&old) {
|
||||
Ok(p) => Ok(PathBuf::from(p.as_str().unwrap())),
|
||||
Err(e) => Err(io::Error::new(io::ErrorKind::Other, e))
|
||||
}
|
||||
Ok(original.to_path_buf())
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
fn old_realpath(original: &old_path::Path) -> old_io::IoResult<old_path::Path> {
|
||||
use std::old_io::fs;
|
||||
const MAX_LINKS_FOLLOWED: usize = 256;
|
||||
let original = old_path::Path::new(env::current_dir().unwrap()
|
||||
.to_str().unwrap()).join(original);
|
||||
#[cfg(unix)]
|
||||
pub fn realpath(original: &Path) -> io::Result<PathBuf> {
|
||||
use libc;
|
||||
use std::ffi::{OsString, CString};
|
||||
use std::os::unix::prelude::*;
|
||||
|
||||
// Right now lstat on windows doesn't work quite well
|
||||
if cfg!(windows) {
|
||||
return Ok(original)
|
||||
extern {
|
||||
fn realpath(pathname: *const libc::c_char, resolved: *mut libc::c_char)
|
||||
-> *mut libc::c_char;
|
||||
}
|
||||
|
||||
let result = original.root_path();
|
||||
let mut result = result.expect("make_absolute has no root_path");
|
||||
let mut followed = 0;
|
||||
|
||||
for part in original.components() {
|
||||
result.push(part);
|
||||
|
||||
loop {
|
||||
if followed == MAX_LINKS_FOLLOWED {
|
||||
return Err(old_io::standard_error(old_io::InvalidInput))
|
||||
}
|
||||
|
||||
match fs::lstat(&result) {
|
||||
Err(..) => break,
|
||||
Ok(ref stat) if stat.kind != old_io::FileType::Symlink => break,
|
||||
Ok(..) => {
|
||||
followed += 1;
|
||||
let path = try!(fs::readlink(&result));
|
||||
result.pop();
|
||||
result.push(path);
|
||||
}
|
||||
}
|
||||
let path = try!(CString::new(original.as_os_str().as_bytes()));
|
||||
let mut buf = vec![0u8; 16 * 1024];
|
||||
unsafe {
|
||||
let r = realpath(path.as_ptr(), buf.as_mut_ptr() as *mut _);
|
||||
if r.is_null() {
|
||||
return Err(io::Error::last_os_error())
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(result);
|
||||
let p = buf.iter().position(|i| *i == 0).unwrap();
|
||||
buf.truncate(p);
|
||||
Ok(PathBuf::from(OsString::from_vec(buf)))
|
||||
}
|
||||
|
||||
#[cfg(all(not(windows), test))]
|
||||
mod test {
|
||||
use std::old_io;
|
||||
use std::old_io::fs::{File, symlink, mkdir, mkdir_recursive};
|
||||
use super::old_realpath as realpath;
|
||||
use std::old_io::TempDir;
|
||||
use std::old_path::{Path, GenericPath};
|
||||
use tempdir::TempDir;
|
||||
use std::fs::{self, File};
|
||||
use super::realpath;
|
||||
|
||||
#[test]
|
||||
fn realpath_works() {
|
||||
@ -83,15 +56,15 @@ mod test {
|
||||
let linkdir = tmpdir.join("test3");
|
||||
|
||||
File::create(&file).unwrap();
|
||||
mkdir(&dir, old_io::USER_RWX).unwrap();
|
||||
symlink(&file, &link).unwrap();
|
||||
symlink(&dir, &linkdir).unwrap();
|
||||
fs::create_dir(&dir).unwrap();
|
||||
fs::soft_link(&file, &link).unwrap();
|
||||
fs::soft_link(&dir, &linkdir).unwrap();
|
||||
|
||||
assert!(realpath(&tmpdir).unwrap() == tmpdir);
|
||||
assert!(realpath(&file).unwrap() == file);
|
||||
assert!(realpath(&link).unwrap() == file);
|
||||
assert!(realpath(&linkdir).unwrap() == dir);
|
||||
assert!(realpath(&linkdir.join("link")).unwrap() == file);
|
||||
assert_eq!(realpath(&tmpdir).unwrap(), tmpdir);
|
||||
assert_eq!(realpath(&file).unwrap(), file);
|
||||
assert_eq!(realpath(&link).unwrap(), file);
|
||||
assert_eq!(realpath(&linkdir).unwrap(), dir);
|
||||
assert_eq!(realpath(&linkdir.join("link")).unwrap(), file);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -106,13 +79,13 @@ mod test {
|
||||
let e = d.join("e");
|
||||
let f = a.join("f");
|
||||
|
||||
mkdir_recursive(&b, old_io::USER_RWX).unwrap();
|
||||
mkdir_recursive(&d, old_io::USER_RWX).unwrap();
|
||||
fs::create_dir_all(&b).unwrap();
|
||||
fs::create_dir_all(&d).unwrap();
|
||||
File::create(&f).unwrap();
|
||||
symlink(&Path::new("../d/e"), &c).unwrap();
|
||||
symlink(&Path::new("../f"), &e).unwrap();
|
||||
fs::soft_link("../d/e", &c).unwrap();
|
||||
fs::soft_link("../f", &e).unwrap();
|
||||
|
||||
assert!(realpath(&c).unwrap() == f);
|
||||
assert!(realpath(&e).unwrap() == f);
|
||||
assert_eq!(realpath(&c).unwrap(), f);
|
||||
assert_eq!(realpath(&e).unwrap(), f);
|
||||
}
|
||||
}
|
||||
|
@ -35,17 +35,16 @@
|
||||
#![feature(box_syntax)]
|
||||
#![feature(collections)]
|
||||
#![feature(core)]
|
||||
#![feature(old_fs)]
|
||||
#![feature(old_io)]
|
||||
#![feature(old_path)]
|
||||
#![feature(rustc_private)]
|
||||
#![feature(staged_api)]
|
||||
#![feature(rand)]
|
||||
#![feature(path_ext)]
|
||||
#![feature(step_by)]
|
||||
#![feature(libc)]
|
||||
#![cfg_attr(test, feature(test, rand))]
|
||||
|
||||
extern crate syntax;
|
||||
extern crate libc;
|
||||
extern crate serialize;
|
||||
#[macro_use] extern crate log;
|
||||
|
||||
|
@ -97,8 +97,9 @@ fn get_rpath_relative_to_output(config: &mut RPathConfig, lib: &Path) -> String
|
||||
let cwd = env::current_dir().unwrap();
|
||||
let mut lib = (config.realpath)(&cwd.join(lib)).unwrap();
|
||||
lib.pop();
|
||||
let mut output = (config.realpath)(&cwd.join(&config.out_filename)).unwrap();
|
||||
let mut output = cwd.join(&config.out_filename);
|
||||
output.pop();
|
||||
let output = (config.realpath)(&output).unwrap();
|
||||
let relative = path_relative_from(&lib, &output)
|
||||
.expect(&format!("couldn't create relative path from {:?} to {:?}", output, lib));
|
||||
// FIXME (#9639): This needs to handle non-utf8 paths
|
||||
|
@ -12,7 +12,7 @@ use std::env;
|
||||
use std::io::{self, Error, ErrorKind};
|
||||
use std::fs;
|
||||
use std::path::{self, PathBuf, Path};
|
||||
use std::rand::{thread_rng, Rng};
|
||||
use std::__rand::{thread_rng, Rng};
|
||||
|
||||
/// A wrapper for a path to temporary directory implementing automatic
|
||||
/// scope-based deletion.
|
||||
|
@ -2627,9 +2627,9 @@ mod tests {
|
||||
use super::{Json, from_str, DecodeResult, DecoderError, JsonEvent, Parser,
|
||||
StackElement, Stack, Decoder, Encoder, EncoderError};
|
||||
use std::{i64, u64, f32, f64};
|
||||
use std::io::prelude::*;
|
||||
use std::collections::BTreeMap;
|
||||
use std::string;
|
||||
use std::old_io::Writer;
|
||||
|
||||
#[derive(RustcDecodable, Eq, PartialEq, Debug)]
|
||||
struct OptionData {
|
||||
@ -3464,7 +3464,6 @@ mod tests {
|
||||
#[test]
|
||||
fn test_encode_hashmap_with_numeric_key() {
|
||||
use std::str::from_utf8;
|
||||
use std::old_io::Writer;
|
||||
use std::collections::HashMap;
|
||||
let mut hm: HashMap<usize, bool> = HashMap::new();
|
||||
hm.insert(1, true);
|
||||
@ -3480,7 +3479,6 @@ mod tests {
|
||||
#[test]
|
||||
fn test_prettyencode_hashmap_with_numeric_key() {
|
||||
use std::str::from_utf8;
|
||||
use std::old_io::Writer;
|
||||
use std::collections::HashMap;
|
||||
let mut hm: HashMap<usize, bool> = HashMap::new();
|
||||
hm.insert(1, true);
|
||||
|
@ -30,13 +30,12 @@ Core encoding and decoding interfaces.
|
||||
#![feature(box_syntax)]
|
||||
#![feature(collections)]
|
||||
#![feature(core)]
|
||||
#![feature(old_path)]
|
||||
#![feature(rustc_private)]
|
||||
#![feature(staged_api)]
|
||||
#![feature(std_misc)]
|
||||
#![feature(unicode)]
|
||||
#![feature(str_char)]
|
||||
#![cfg_attr(test, feature(test, old_io))]
|
||||
#![cfg_attr(test, feature(test))]
|
||||
|
||||
// test harness access
|
||||
#[cfg(test)] extern crate test;
|
||||
|
@ -14,8 +14,6 @@
|
||||
Core encoding and decoding interfaces.
|
||||
*/
|
||||
|
||||
#[allow(deprecated)]
|
||||
use std::old_path::{self, GenericPath};
|
||||
use std::path;
|
||||
use std::rc::Rc;
|
||||
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, }
|
||||
|
||||
#[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 {
|
||||
fn encode<S: Encoder>(&self, e: &mut S) -> Result<(), S::Error> {
|
||||
self.to_str().unwrap().encode(e)
|
||||
|
@ -1631,7 +1631,7 @@ mod test_map {
|
||||
use super::Entry::{Occupied, Vacant};
|
||||
use iter::{range_inclusive, range_step_inclusive, repeat};
|
||||
use cell::RefCell;
|
||||
use rand::{weak_rng, Rng};
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
#[test]
|
||||
fn test_create_capacity_zero() {
|
||||
@ -2290,7 +2290,7 @@ mod test_map {
|
||||
}
|
||||
|
||||
let mut m = HashMap::new();
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
|
||||
// Populate the map with some items.
|
||||
for _ in 0..50 {
|
||||
|
@ -18,8 +18,6 @@ use io;
|
||||
use iter::Iterator;
|
||||
use libc;
|
||||
use mem;
|
||||
#[allow(deprecated)]
|
||||
use old_io;
|
||||
use ops::Deref;
|
||||
use option::Option::{self, Some, None};
|
||||
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 {
|
||||
/// Cast a raw C string to a safe C string wrapper.
|
||||
///
|
||||
|
@ -42,7 +42,6 @@ use string::String;
|
||||
use ops;
|
||||
use cmp;
|
||||
use hash::{Hash, Hasher};
|
||||
use old_path::{Path, GenericPath};
|
||||
use vec::Vec;
|
||||
|
||||
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 {
|
||||
fn from_inner(buf: Buf) -> OsString {
|
||||
OsString { inner: buf }
|
||||
|
@ -123,7 +123,7 @@ pub struct WalkDir {
|
||||
/// Opening a file for both reading and writing, as well as creating it if it
|
||||
/// doesn't exist:
|
||||
///
|
||||
/// ```
|
||||
/// ```no_run
|
||||
/// use std::fs::OpenOptions;
|
||||
///
|
||||
/// let file = OpenOptions::new()
|
||||
@ -1195,7 +1195,8 @@ mod tests {
|
||||
|
||||
pub fn tmpdir() -> TempDir {
|
||||
let p = env::temp_dir();
|
||||
let ret = p.join(&format!("rust-{}", rand::random::<u32>()));
|
||||
let mut r = rand::thread_rng();
|
||||
let ret = p.join(&format!("rust-{}", r.next_u32()));
|
||||
check!(fs::create_dir(&ret));
|
||||
TempDir(ret)
|
||||
}
|
||||
|
@ -262,12 +262,9 @@ pub mod ffi;
|
||||
pub mod fs;
|
||||
pub mod io;
|
||||
pub mod net;
|
||||
pub mod old_io;
|
||||
pub mod old_path;
|
||||
pub mod os;
|
||||
pub mod path;
|
||||
pub mod process;
|
||||
pub mod rand;
|
||||
pub mod sync;
|
||||
pub mod time;
|
||||
|
||||
@ -281,6 +278,18 @@ pub mod time;
|
||||
|
||||
pub mod rt;
|
||||
mod panicking;
|
||||
mod rand;
|
||||
|
||||
// Some external utilities of the standard library rely on randomness (aka
|
||||
// rustc_back::TempDir and tests) and need a way to get at the OS rng we've got
|
||||
// here. This module is not at all intended for stabilization as-is, however,
|
||||
// but it may be stabilized long-term. As a result we're exposing a hidden,
|
||||
// unstable module so we can get our build working.
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "rand")]
|
||||
pub mod __rand {
|
||||
pub use rand::{thread_rng, ThreadRng, Rng};
|
||||
}
|
||||
|
||||
// Modules that exist purely to document + host impl docs for primitive types
|
||||
|
||||
@ -297,8 +306,6 @@ mod std {
|
||||
pub use sync; // used for select!()
|
||||
pub use error; // used for try!()
|
||||
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 rt; // used for panic!()
|
||||
pub use vec; // used for vec![]
|
||||
|
@ -464,7 +464,7 @@ mod bench {
|
||||
|
||||
mod usize {
|
||||
use super::test::Bencher;
|
||||
use rand::{weak_rng, Rng};
|
||||
use rand::{thread_rng, Rng};
|
||||
use std::fmt;
|
||||
|
||||
#[inline]
|
||||
@ -474,38 +474,38 @@ mod bench {
|
||||
|
||||
#[bench]
|
||||
fn to_str_bin(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| { to_string(rng.gen::<usize>(), 2); })
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn to_str_oct(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| { to_string(rng.gen::<usize>(), 8); })
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn to_str_dec(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| { to_string(rng.gen::<usize>(), 10); })
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn to_str_hex(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| { to_string(rng.gen::<usize>(), 16); })
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn to_str_base_36(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| { to_string(rng.gen::<usize>(), 36); })
|
||||
}
|
||||
}
|
||||
|
||||
mod isize {
|
||||
use super::test::Bencher;
|
||||
use rand::{weak_rng, Rng};
|
||||
use rand::{thread_rng, Rng};
|
||||
use std::fmt;
|
||||
|
||||
#[inline]
|
||||
@ -515,43 +515,43 @@ mod bench {
|
||||
|
||||
#[bench]
|
||||
fn to_str_bin(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| { to_string(rng.gen::<isize>(), 2); })
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn to_str_oct(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| { to_string(rng.gen::<isize>(), 8); })
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn to_str_dec(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| { to_string(rng.gen::<isize>(), 10); })
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn to_str_hex(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| { to_string(rng.gen::<isize>(), 16); })
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn to_str_base_36(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| { to_string(rng.gen::<isize>(), 36); })
|
||||
}
|
||||
}
|
||||
|
||||
mod f64 {
|
||||
use super::test::Bencher;
|
||||
use rand::{weak_rng, Rng};
|
||||
use rand::{thread_rng, Rng};
|
||||
use f64;
|
||||
|
||||
#[bench]
|
||||
fn float_to_string(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let mut rng = thread_rng();
|
||||
b.iter(|| { f64::to_string(rng.gen()); })
|
||||
}
|
||||
}
|
||||
|
@ -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
@ -47,6 +47,3 @@
|
||||
#[doc(no_inline)] pub use string::{String, ToString};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(no_inline)] pub use vec::Vec;
|
||||
|
||||
#[allow(deprecated)] pub use slice::AsSlice;
|
||||
#[allow(deprecated)] pub use str::Str;
|
||||
|
@ -534,8 +534,6 @@ mod tests {
|
||||
use io::prelude::*;
|
||||
|
||||
use io::ErrorKind;
|
||||
use old_path::{self, GenericPath};
|
||||
use old_io::fs::PathExtensions;
|
||||
use rt::running_on_valgrind;
|
||||
use str;
|
||||
use super::{Command, Output, Stdio};
|
||||
@ -748,43 +746,6 @@ mod tests {
|
||||
cmd
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "aarch64"))]
|
||||
#[test]
|
||||
fn test_keep_current_working_dir() {
|
||||
use os;
|
||||
let prog = pwd_cmd().spawn().unwrap();
|
||||
|
||||
let output = String::from_utf8(prog.wait_with_output().unwrap().stdout).unwrap();
|
||||
let parent_dir = ::env::current_dir().unwrap().to_str().unwrap().to_string();
|
||||
let parent_dir = old_path::Path::new(parent_dir);
|
||||
let child_dir = old_path::Path::new(output.trim());
|
||||
|
||||
let parent_stat = parent_dir.stat().unwrap();
|
||||
let child_stat = child_dir.stat().unwrap();
|
||||
|
||||
assert_eq!(parent_stat.unstable.device, child_stat.unstable.device);
|
||||
assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_change_working_directory() {
|
||||
use os;
|
||||
// test changing to the parent of os::getcwd() because we know
|
||||
// the path exists (and os::getcwd() is not expected to be root)
|
||||
let parent_dir = ::env::current_dir().unwrap().to_str().unwrap().to_string();
|
||||
let parent_dir = old_path::Path::new(parent_dir).dir_path();
|
||||
let result = pwd_cmd().current_dir(parent_dir.as_str().unwrap()).output().unwrap();
|
||||
|
||||
let output = String::from_utf8(result.stdout).unwrap();
|
||||
let child_dir = old_path::Path::new(output.trim());
|
||||
|
||||
let parent_stat = parent_dir.stat().unwrap();
|
||||
let child_stat = child_dir.stat().unwrap();
|
||||
|
||||
assert_eq!(parent_stat.unstable.device, child_stat.unstable.device);
|
||||
assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode);
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os="android")))]
|
||||
pub fn env_cmd() -> Command {
|
||||
Command::new("env")
|
||||
|
@ -54,197 +54,24 @@
|
||||
//! - On some systems (e.g. FreeBSD, OpenBSD and Mac OS X) there is no difference
|
||||
//! between the two sources. (Also note that, on some systems e.g. FreeBSD, both `/dev/random`
|
||||
//! and `/dev/urandom` may block once if the CSPRNG has not seeded yet.)
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```rust
|
||||
//! # #![feature(rand)]
|
||||
//! use std::rand;
|
||||
//! use std::rand::Rng;
|
||||
//!
|
||||
//! let mut rng = rand::thread_rng();
|
||||
//! if rng.gen() { // random bool
|
||||
//! println!("isize: {}, usize: {}", rng.gen::<isize>(), rng.gen::<usize>())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ```rust
|
||||
//! # #![feature(rand)]
|
||||
//! use std::rand;
|
||||
//!
|
||||
//! let tuple = rand::random::<(f64, char)>();
|
||||
//! println!("{:?}", tuple)
|
||||
//! ```
|
||||
//!
|
||||
//! ## Monte Carlo estimation of π
|
||||
//!
|
||||
//! For this example, imagine we have a square with sides of length 2 and a unit
|
||||
//! circle, both centered at the origin. Since the area of a unit circle is π,
|
||||
//! we have:
|
||||
//!
|
||||
//! ```text
|
||||
//! (area of unit circle) / (area of square) = π / 4
|
||||
//! ```
|
||||
//!
|
||||
//! So if we sample many points randomly from the square, roughly π / 4 of them
|
||||
//! should be inside the circle.
|
||||
//!
|
||||
//! We can use the above fact to estimate the value of π: pick many points in the
|
||||
//! square at random, calculate the fraction that fall within the circle, and
|
||||
//! multiply this fraction by 4.
|
||||
//!
|
||||
//! ```
|
||||
//! # #![feature(rand)]
|
||||
//! use std::rand;
|
||||
//! use std::rand::distributions::{IndependentSample, Range};
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let between = Range::new(-1f64, 1.);
|
||||
//! let mut rng = rand::thread_rng();
|
||||
//!
|
||||
//! let total = 1_000_000;
|
||||
//! let mut in_circle = 0;
|
||||
//!
|
||||
//! for _ in 0..total {
|
||||
//! let a = between.ind_sample(&mut rng);
|
||||
//! let b = between.ind_sample(&mut rng);
|
||||
//! if a*a + b*b <= 1. {
|
||||
//! in_circle += 1;
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! // prints something close to 3.14159...
|
||||
//! println!("{}", 4. * (in_circle as f64) / (total as f64));
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Monty Hall Problem
|
||||
//!
|
||||
//! This is a simulation of the [Monty Hall Problem][]:
|
||||
//!
|
||||
//! > Suppose you're on a game show, and you're given the choice of three doors:
|
||||
//! > Behind one door is a car; behind the others, goats. You pick a door, say No. 1,
|
||||
//! > and the host, who knows what's behind the doors, opens another door, say No. 3,
|
||||
//! > which has a goat. He then says to you, "Do you want to pick door No. 2?"
|
||||
//! > Is it to your advantage to switch your choice?
|
||||
//!
|
||||
//! The rather unintuitive answer is that you will have a 2/3 chance of winning if
|
||||
//! you switch and a 1/3 chance of winning if you don't, so it's better to switch.
|
||||
//!
|
||||
//! This program will simulate the game show and with large enough simulation steps
|
||||
//! it will indeed confirm that it is better to switch.
|
||||
//!
|
||||
//! [Monty Hall Problem]: http://en.wikipedia.org/wiki/Monty_Hall_problem
|
||||
//!
|
||||
//! ```
|
||||
//! # #![feature(rand)]
|
||||
//! use std::rand;
|
||||
//! use std::rand::Rng;
|
||||
//! use std::rand::distributions::{IndependentSample, Range};
|
||||
//!
|
||||
//! struct SimulationResult {
|
||||
//! win: bool,
|
||||
//! switch: bool,
|
||||
//! }
|
||||
//!
|
||||
//! // Run a single simulation of the Monty Hall problem.
|
||||
//! fn simulate<R: Rng>(random_door: &Range<usize>, rng: &mut R) -> SimulationResult {
|
||||
//! let car = random_door.ind_sample(rng);
|
||||
//!
|
||||
//! // This is our initial choice
|
||||
//! let mut choice = random_door.ind_sample(rng);
|
||||
//!
|
||||
//! // The game host opens a door
|
||||
//! let open = game_host_open(car, choice, rng);
|
||||
//!
|
||||
//! // Shall we switch?
|
||||
//! let switch = rng.gen();
|
||||
//! if switch {
|
||||
//! choice = switch_door(choice, open);
|
||||
//! }
|
||||
//!
|
||||
//! SimulationResult { win: choice == car, switch: switch }
|
||||
//! }
|
||||
//!
|
||||
//! // Returns the door the game host opens given our choice and knowledge of
|
||||
//! // where the car is. The game host will never open the door with the car.
|
||||
//! fn game_host_open<R: Rng>(car: usize, choice: usize, rng: &mut R) -> usize {
|
||||
//! let choices = free_doors(&[car, choice]);
|
||||
//! rand::sample(rng, choices.into_iter(), 1)[0]
|
||||
//! }
|
||||
//!
|
||||
//! // Returns the door we switch to, given our current choice and
|
||||
//! // the open door. There will only be one valid door.
|
||||
//! fn switch_door(choice: usize, open: usize) -> usize {
|
||||
//! free_doors(&[choice, open])[0]
|
||||
//! }
|
||||
//!
|
||||
//! fn free_doors(blocked: &[usize]) -> Vec<usize> {
|
||||
//! (0..3).filter(|x| !blocked.contains(x)).collect()
|
||||
//! }
|
||||
//!
|
||||
//! fn main() {
|
||||
//! // The estimation will be more accurate with more simulations
|
||||
//! let num_simulations = 10000;
|
||||
//!
|
||||
//! let mut rng = rand::thread_rng();
|
||||
//! let random_door = Range::new(0, 3);
|
||||
//!
|
||||
//! let (mut switch_wins, mut switch_losses) = (0, 0);
|
||||
//! let (mut keep_wins, mut keep_losses) = (0, 0);
|
||||
//!
|
||||
//! println!("Running {} simulations...", num_simulations);
|
||||
//! for _ in 0..num_simulations {
|
||||
//! let result = simulate(&random_door, &mut rng);
|
||||
//!
|
||||
//! match (result.win, result.switch) {
|
||||
//! (true, true) => switch_wins += 1,
|
||||
//! (true, false) => keep_wins += 1,
|
||||
//! (false, true) => switch_losses += 1,
|
||||
//! (false, false) => keep_losses += 1,
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! let total_switches = switch_wins + switch_losses;
|
||||
//! let total_keeps = keep_wins + keep_losses;
|
||||
//!
|
||||
//! println!("Switched door {} times with {} wins and {} losses",
|
||||
//! total_switches, switch_wins, switch_losses);
|
||||
//!
|
||||
//! println!("Kept our choice {} times with {} wins and {} losses",
|
||||
//! total_keeps, keep_wins, keep_losses);
|
||||
//!
|
||||
//! // With a large number of simulations, the values should converge to
|
||||
//! // 0.667 and 0.333 respectively.
|
||||
//! println!("Estimated chance to win if we switch: {}",
|
||||
//! switch_wins as f32 / total_switches as f32);
|
||||
//! println!("Estimated chance to win if we don't: {}",
|
||||
//! keep_wins as f32 / total_keeps as f32);
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
#![unstable(feature = "rand")]
|
||||
#![deprecated(reason = "use the crates.io `rand` library instead",
|
||||
since = "1.0.0-alpha")]
|
||||
#![allow(deprecated)]
|
||||
|
||||
use prelude::v1::*;
|
||||
|
||||
use cell::RefCell;
|
||||
use clone::Clone;
|
||||
use old_io::IoResult;
|
||||
use iter::Iterator;
|
||||
use io;
|
||||
use mem;
|
||||
use rc::Rc;
|
||||
use result::Result::{Ok, Err};
|
||||
use vec::Vec;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
use core_rand::IsaacRng as IsaacWordRng;
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
use core_rand::Isaac64Rng as IsaacWordRng;
|
||||
|
||||
pub use core_rand::{Rand, Rng, SeedableRng, Open01, Closed01};
|
||||
pub use core_rand::{XorShiftRng, IsaacRng, Isaac64Rng, ChaChaRng};
|
||||
pub use core_rand::{distributions, reseeding};
|
||||
pub use core_rand::{Rand, Rng, SeedableRng};
|
||||
pub use core_rand::{XorShiftRng, IsaacRng, Isaac64Rng};
|
||||
pub use core_rand::reseeding;
|
||||
pub use rand::os::OsRng;
|
||||
|
||||
pub mod os;
|
||||
@ -269,7 +96,7 @@ impl StdRng {
|
||||
///
|
||||
/// Reading the randomness from the OS may fail, and any error is
|
||||
/// 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() })
|
||||
}
|
||||
}
|
||||
@ -298,22 +125,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.
|
||||
struct ThreadRngReseeder;
|
||||
|
||||
@ -375,338 +186,3 @@ impl Rng for ThreadRng {
|
||||
self.rng.borrow_mut().fill_bytes(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)]
|
||||
mod test {
|
||||
use prelude::v1::*;
|
||||
use super::{Rng, thread_rng, random, SeedableRng, StdRng, sample};
|
||||
use iter::{order, repeat};
|
||||
|
||||
struct ConstRng { i: u64 }
|
||||
impl Rng for ConstRng {
|
||||
fn next_u32(&mut self) -> u32 { self.i as u32 }
|
||||
fn next_u64(&mut self) -> u64 { self.i }
|
||||
|
||||
// no fill_bytes on purpose
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fill_bytes_default() {
|
||||
let mut r = ConstRng { i: 0x11_22_33_44_55_66_77_88 };
|
||||
|
||||
// check every remainder mod 8, both in small and big vectors.
|
||||
let lengths = [0, 1, 2, 3, 4, 5, 6, 7,
|
||||
80, 81, 82, 83, 84, 85, 86, 87];
|
||||
for &n in &lengths {
|
||||
let mut v = repeat(0).take(n).collect::<Vec<_>>();
|
||||
r.fill_bytes(&mut v);
|
||||
|
||||
// use this to get nicer error messages.
|
||||
for (i, &byte) in v.iter().enumerate() {
|
||||
if byte == 0 {
|
||||
panic!("byte {} of {} is zero", i, n)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gen_range() {
|
||||
let mut r = thread_rng();
|
||||
for _ in 0..1000 {
|
||||
let a = r.gen_range(-3, 42);
|
||||
assert!(a >= -3 && a < 42);
|
||||
assert_eq!(r.gen_range(0, 1), 0);
|
||||
assert_eq!(r.gen_range(-12, -11), -12);
|
||||
}
|
||||
|
||||
for _ in 0..1000 {
|
||||
let a = r.gen_range(10, 42);
|
||||
assert!(a >= 10 && a < 42);
|
||||
assert_eq!(r.gen_range(0, 1), 0);
|
||||
assert_eq!(r.gen_range(3_000_000, 3_000_001), 3_000_000);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_gen_range_panic_int() {
|
||||
let mut r = thread_rng();
|
||||
r.gen_range(5, -2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_gen_range_panic_uint() {
|
||||
let mut r = thread_rng();
|
||||
r.gen_range(5, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gen_f64() {
|
||||
let mut r = thread_rng();
|
||||
let a = r.gen::<f64>();
|
||||
let b = r.gen::<f64>();
|
||||
debug!("{:?}", (a, b));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gen_weighted_bool() {
|
||||
let mut r = thread_rng();
|
||||
assert_eq!(r.gen_weighted_bool(0), true);
|
||||
assert_eq!(r.gen_weighted_bool(1), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gen_ascii_str() {
|
||||
let mut r = thread_rng();
|
||||
assert_eq!(r.gen_ascii_chars().take(0).count(), 0);
|
||||
assert_eq!(r.gen_ascii_chars().take(10).count(), 10);
|
||||
assert_eq!(r.gen_ascii_chars().take(16).count(), 16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gen_vec() {
|
||||
let mut r = thread_rng();
|
||||
assert_eq!(r.gen_iter::<u8>().take(0).count(), 0);
|
||||
assert_eq!(r.gen_iter::<u8>().take(10).count(), 10);
|
||||
assert_eq!(r.gen_iter::<f64>().take(16).count(), 16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_choose() {
|
||||
let mut r = thread_rng();
|
||||
assert_eq!(r.choose(&[1, 1, 1]).cloned(), Some(1));
|
||||
|
||||
let v: &[isize] = &[];
|
||||
assert_eq!(r.choose(v), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shuffle() {
|
||||
let mut r = thread_rng();
|
||||
let empty: &mut [isize] = &mut [];
|
||||
r.shuffle(empty);
|
||||
let mut one = [1];
|
||||
r.shuffle(&mut one);
|
||||
let b: &[_] = &[1];
|
||||
assert_eq!(one, b);
|
||||
|
||||
let mut two = [1, 2];
|
||||
r.shuffle(&mut two);
|
||||
assert!(two == [1, 2] || two == [2, 1]);
|
||||
|
||||
let mut x = [1, 1, 1];
|
||||
r.shuffle(&mut x);
|
||||
let b: &[_] = &[1, 1, 1];
|
||||
assert_eq!(x, b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_thread_rng() {
|
||||
let mut r = thread_rng();
|
||||
r.gen::<isize>();
|
||||
let mut v = [1, 1, 1];
|
||||
r.shuffle(&mut v);
|
||||
let b: &[_] = &[1, 1, 1];
|
||||
assert_eq!(v, b);
|
||||
assert_eq!(r.gen_range(0, 1), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_random() {
|
||||
// not sure how to test this aside from just getting some values
|
||||
let _n : usize = random();
|
||||
let _f : f32 = random();
|
||||
let _o : Option<Option<i8>> = random();
|
||||
let _many : ((),
|
||||
(usize,
|
||||
isize,
|
||||
Option<(u32, (bool,))>),
|
||||
(u8, i8, u16, i16, u32, i32, u64, i64),
|
||||
(f32, (f64, (f64,)))) = random();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sample() {
|
||||
let min_val = 1;
|
||||
let max_val = 100;
|
||||
|
||||
let mut r = thread_rng();
|
||||
let vals = (min_val..max_val).collect::<Vec<isize>>();
|
||||
let small_sample = sample(&mut r, vals.iter(), 5);
|
||||
let large_sample = sample(&mut r, vals.iter(), vals.len() + 5);
|
||||
|
||||
assert_eq!(small_sample.len(), 5);
|
||||
assert_eq!(large_sample.len(), vals.len());
|
||||
|
||||
assert!(small_sample.iter().all(|e| {
|
||||
**e >= min_val && **e <= max_val
|
||||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_std_rng_seeded() {
|
||||
let s = thread_rng().gen_iter::<usize>().take(256).collect::<Vec<usize>>();
|
||||
let mut ra: StdRng = SeedableRng::from_seed(&*s);
|
||||
let mut rb: StdRng = SeedableRng::from_seed(&*s);
|
||||
assert!(order::equals(ra.gen_ascii_chars().take(100),
|
||||
rb.gen_ascii_chars().take(100)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_std_rng_reseed() {
|
||||
let s = thread_rng().gen_iter::<usize>().take(256).collect::<Vec<usize>>();
|
||||
let mut r: StdRng = SeedableRng::from_seed(&*s);
|
||||
let string1 = r.gen_ascii_chars().take(100).collect::<String>();
|
||||
|
||||
r.reseed(&s);
|
||||
|
||||
let string2 = r.gen_ascii_chars().take(100).collect::<String>();
|
||||
assert_eq!(string1, string2);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod bench {
|
||||
extern crate test;
|
||||
use prelude::v1::*;
|
||||
|
||||
use self::test::Bencher;
|
||||
use super::{XorShiftRng, StdRng, IsaacRng, Isaac64Rng, Rng};
|
||||
use super::{OsRng, weak_rng};
|
||||
use mem::size_of;
|
||||
|
||||
const RAND_BENCH_N: u64 = 100;
|
||||
|
||||
#[bench]
|
||||
fn rand_xorshift(b: &mut Bencher) {
|
||||
let mut rng: XorShiftRng = OsRng::new().unwrap().gen();
|
||||
b.iter(|| {
|
||||
for _ in 0..RAND_BENCH_N {
|
||||
rng.gen::<usize>();
|
||||
}
|
||||
});
|
||||
b.bytes = size_of::<usize>() as u64 * RAND_BENCH_N;
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn rand_isaac(b: &mut Bencher) {
|
||||
let mut rng: IsaacRng = OsRng::new().unwrap().gen();
|
||||
b.iter(|| {
|
||||
for _ in 0..RAND_BENCH_N {
|
||||
rng.gen::<usize>();
|
||||
}
|
||||
});
|
||||
b.bytes = size_of::<usize>() as u64 * RAND_BENCH_N;
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn rand_isaac64(b: &mut Bencher) {
|
||||
let mut rng: Isaac64Rng = OsRng::new().unwrap().gen();
|
||||
b.iter(|| {
|
||||
for _ in 0..RAND_BENCH_N {
|
||||
rng.gen::<usize>();
|
||||
}
|
||||
});
|
||||
b.bytes = size_of::<usize>() as u64 * RAND_BENCH_N;
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn rand_std(b: &mut Bencher) {
|
||||
let mut rng = StdRng::new().unwrap();
|
||||
b.iter(|| {
|
||||
for _ in 0..RAND_BENCH_N {
|
||||
rng.gen::<usize>();
|
||||
}
|
||||
});
|
||||
b.bytes = size_of::<usize>() as u64 * RAND_BENCH_N;
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn rand_shuffle_100(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let x : &mut[usize] = &mut [1; 100];
|
||||
b.iter(|| {
|
||||
rng.shuffle(x);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -18,10 +18,10 @@ mod imp {
|
||||
use prelude::v1::*;
|
||||
use self::OsRngInner::*;
|
||||
|
||||
use fs::File;
|
||||
use io;
|
||||
use libc;
|
||||
use mem;
|
||||
use old_io::{IoResult, File};
|
||||
use old_path::Path;
|
||||
use rand::Rng;
|
||||
use rand::reader::ReaderRng;
|
||||
use sys::os::errno;
|
||||
@ -147,12 +147,12 @@ mod imp {
|
||||
|
||||
impl OsRng {
|
||||
/// Create a new `OsRng`.
|
||||
pub fn new() -> IoResult<OsRng> {
|
||||
pub fn new() -> io::Result<OsRng> {
|
||||
if is_getrandom_available() {
|
||||
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);
|
||||
|
||||
Ok(OsRng { inner: OsReaderRng(reader_rng) })
|
||||
@ -186,7 +186,6 @@ mod imp {
|
||||
use prelude::v1::*;
|
||||
|
||||
use io;
|
||||
use old_io::IoResult;
|
||||
use mem;
|
||||
use rand::Rng;
|
||||
use libc::{c_int, size_t};
|
||||
@ -202,7 +201,8 @@ mod imp {
|
||||
///
|
||||
/// This does not block.
|
||||
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: (),
|
||||
}
|
||||
|
||||
@ -220,7 +220,7 @@ mod imp {
|
||||
|
||||
impl OsRng {
|
||||
/// Create a new `OsRng`.
|
||||
pub fn new() -> IoResult<OsRng> {
|
||||
pub fn new() -> io::Result<OsRng> {
|
||||
Ok(OsRng { _dummy: () })
|
||||
}
|
||||
}
|
||||
@ -238,10 +238,12 @@ mod imp {
|
||||
}
|
||||
fn fill_bytes(&mut self, v: &mut [u8]) {
|
||||
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 {
|
||||
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 mem;
|
||||
use old_io::{IoResult, IoError};
|
||||
use rand::Rng;
|
||||
use libc::types::os::arch::extra::{LONG_PTR};
|
||||
use libc::{DWORD, BYTE, LPCSTR, BOOL};
|
||||
@ -293,7 +294,7 @@ mod imp {
|
||||
|
||||
impl OsRng {
|
||||
/// Create a new `OsRng`.
|
||||
pub fn new() -> IoResult<OsRng> {
|
||||
pub fn new() -> io::Result<OsRng> {
|
||||
let mut hcp = 0;
|
||||
let ret = unsafe {
|
||||
CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
|
||||
@ -302,7 +303,7 @@ mod imp {
|
||||
};
|
||||
|
||||
if ret == 0 {
|
||||
Err(IoError::last_error())
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(OsRng { hcryptprov: hcp })
|
||||
}
|
||||
|
@ -8,35 +8,26 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// 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 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.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// 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> {
|
||||
reader: R
|
||||
}
|
||||
|
||||
impl<R: Reader> ReaderRng<R> {
|
||||
/// Create a new `ReaderRng` from a `Reader`.
|
||||
impl<R: Read> ReaderRng<R> {
|
||||
/// Create a new `ReaderRng` from a `Read`.
|
||||
pub fn new(r: R) -> ReaderRng<R> {
|
||||
ReaderRng {
|
||||
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 {
|
||||
// This is designed for speed: reading a LE integer on a LE
|
||||
// platform just involves blitting the bytes into the memory
|
||||
// of the u32, similarly for BE on BE; avoiding byteswapping.
|
||||
if cfg!(target_endian="little") {
|
||||
self.reader.read_le_u32().unwrap()
|
||||
} else {
|
||||
self.reader.read_be_u32().unwrap()
|
||||
}
|
||||
let mut bytes = [0; 4];
|
||||
self.fill_bytes(&mut bytes);
|
||||
unsafe { *(bytes.as_ptr() as *const u32) }
|
||||
}
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
// see above for explanation.
|
||||
if cfg!(target_endian="little") {
|
||||
self.reader.read_le_u64().unwrap()
|
||||
} else {
|
||||
self.reader.read_be_u64().unwrap()
|
||||
}
|
||||
let mut bytes = [0; 8];
|
||||
self.fill_bytes(&mut bytes);
|
||||
unsafe { *(bytes.as_ptr() as *const u64) }
|
||||
}
|
||||
fn fill_bytes(&mut self, v: &mut [u8]) {
|
||||
if v.len() == 0 { return }
|
||||
match self.reader.read_at_least(v.len(), v) {
|
||||
Ok(_) => {}
|
||||
Err(e) => panic!("ReaderRng.fill_bytes error: {:?}", e)
|
||||
fn fill_bytes(&mut self, mut v: &mut [u8]) {
|
||||
while v.len() > 0 {
|
||||
let t = v;
|
||||
match self.reader.read(t) {
|
||||
Ok(0) => panic!("ReaderRng.fill_bytes: EOF reached"),
|
||||
Ok(n) => v = t.split_at_mut(n).1,
|
||||
Err(e) => panic!("ReaderRng.fill_bytes: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -77,17 +67,16 @@ mod test {
|
||||
use prelude::v1::*;
|
||||
|
||||
use super::ReaderRng;
|
||||
use old_io::MemReader;
|
||||
use num::Int;
|
||||
use rand::Rng;
|
||||
|
||||
#[test]
|
||||
fn test_reader_rng_u64() {
|
||||
// transmute from the target to avoid endianness concerns.
|
||||
let v = vec![0, 0, 0, 0, 0, 0, 0, 1,
|
||||
0 , 0, 0, 0, 0, 0, 0, 2,
|
||||
0, 0, 0, 0, 0, 0, 0, 3];
|
||||
let mut rng = ReaderRng::new(MemReader::new(v));
|
||||
let v = &[0, 0, 0, 0, 0, 0, 0, 1,
|
||||
0 , 0, 0, 0, 0, 0, 0, 2,
|
||||
0, 0, 0, 0, 0, 0, 0, 3][..];
|
||||
let mut rng = ReaderRng::new(v);
|
||||
|
||||
assert_eq!(rng.next_u64(), 1.to_be());
|
||||
assert_eq!(rng.next_u64(), 2.to_be());
|
||||
@ -95,8 +84,8 @@ mod test {
|
||||
}
|
||||
#[test]
|
||||
fn test_reader_rng_u32() {
|
||||
let v = vec![0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3];
|
||||
let mut rng = ReaderRng::new(MemReader::new(v));
|
||||
let v = &[0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3][..];
|
||||
let mut rng = ReaderRng::new(v);
|
||||
|
||||
assert_eq!(rng.next_u32(), 1.to_be());
|
||||
assert_eq!(rng.next_u32(), 2.to_be());
|
||||
@ -107,7 +96,7 @@ mod test {
|
||||
let v = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||
let mut w = [0; 8];
|
||||
|
||||
let mut rng = ReaderRng::new(MemReader::new(v.to_vec()));
|
||||
let mut rng = ReaderRng::new(&v[..]);
|
||||
rng.fill_bytes(&mut w);
|
||||
|
||||
assert!(v == w);
|
||||
@ -116,7 +105,7 @@ mod test {
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_reader_rng_insufficient_bytes() {
|
||||
let mut rng = ReaderRng::new(MemReader::new(vec!()));
|
||||
let mut rng = ReaderRng::new(&[][..]);
|
||||
let mut v = [0; 3];
|
||||
rng.fill_bytes(&mut v);
|
||||
}
|
||||
|
@ -10,24 +10,11 @@
|
||||
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use old_io::{self, IoError, IoResult};
|
||||
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 condvar;
|
||||
pub mod mutex;
|
||||
pub mod net;
|
||||
pub mod net2;
|
||||
pub mod poison;
|
||||
pub mod remutex;
|
||||
@ -40,72 +27,6 @@ pub mod wtf8;
|
||||
|
||||
// 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
|
||||
#[doc(hidden)]
|
||||
pub trait AsInner<Inner: ?Sized> {
|
||||
@ -129,15 +50,3 @@ pub trait IntoInner<Inner> {
|
||||
pub trait FromInner<Inner> {
|
||||
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
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
//! #![feature(globs)]
|
||||
//!
|
||||
//! use std::old_io::fs::File;
|
||||
//! ```no_run
|
||||
//! use std::fs::File;
|
||||
//! use std::os::unix::prelude::*;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let f = File::create(&Path::new("foo.txt")).unwrap();
|
||||
//! let f = File::create("foo.txt").unwrap();
|
||||
//! let fd = f.as_raw_fd();
|
||||
//!
|
||||
//! // use fd with native unix bindings
|
||||
@ -34,7 +32,6 @@
|
||||
/// Unix-specific extensions to general I/O primitives
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub mod io {
|
||||
#[allow(deprecated)] use old_io;
|
||||
use fs;
|
||||
use libc;
|
||||
use net;
|
||||
@ -82,14 +79,6 @@ pub mod io {
|
||||
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")]
|
||||
impl AsRawFd for fs::File {
|
||||
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")]
|
||||
impl AsRawFd for net::TcpStream {
|
||||
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 ffi::CStr;
|
||||
use io::{self, ErrorKind};
|
||||
use libc;
|
||||
use num::{Int, SignedInt};
|
||||
use old_io::{self, IoError};
|
||||
use str;
|
||||
use sys_common::mkerr_libc;
|
||||
|
||||
pub mod backtrace;
|
||||
pub mod c;
|
||||
pub mod condvar;
|
||||
pub mod ext;
|
||||
pub mod fd;
|
||||
pub mod fs; // support for std::old_io
|
||||
pub mod fs2; // support for std::fs
|
||||
pub mod helper_signal;
|
||||
pub mod fs2;
|
||||
pub mod mutex;
|
||||
pub mod net;
|
||||
pub mod os;
|
||||
pub mod os_str;
|
||||
pub mod pipe;
|
||||
pub mod pipe2;
|
||||
pub mod process;
|
||||
pub mod process2;
|
||||
pub mod rwlock;
|
||||
pub mod stack_overflow;
|
||||
pub mod sync;
|
||||
pub mod tcp;
|
||||
pub mod thread;
|
||||
pub mod thread_local;
|
||||
pub mod time;
|
||||
pub mod timer;
|
||||
pub mod tty;
|
||||
pub mod udp;
|
||||
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 {
|
||||
match errno as libc::c_int {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
#[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 libc::{self, c_int, c_char, c_void};
|
||||
use mem;
|
||||
#[allow(deprecated)] use old_io::{IoError, IoResult};
|
||||
use ptr;
|
||||
use path::{self, PathBuf};
|
||||
use slice;
|
||||
use str;
|
||||
use sys::c;
|
||||
use sys::fd;
|
||||
use sys::fs::FileDesc;
|
||||
use vec;
|
||||
|
||||
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 {
|
||||
unsafe {
|
||||
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)),
|
||||
0 => None,
|
||||
n => panic!("unknown waitpid error `{:?}`: {:?}", n,
|
||||
super::last_error()),
|
||||
n => panic!("unknown waitpid error `{}`: {}", n,
|
||||
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;
|
||||
|
||||
#[allow(deprecated)]
|
||||
use old_io;
|
||||
|
||||
/// Raw HANDLEs.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub type RawHandle = libc::HANDLE;
|
||||
@ -61,14 +58,6 @@ pub mod io {
|
||||
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")]
|
||||
impl AsRawHandle for fs::File {
|
||||
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.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub trait AsRawSocket {
|
||||
@ -139,38 +96,6 @@ pub mod io {
|
||||
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")]
|
||||
impl AsRawSocket for net::TcpStream {
|
||||
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 io::{self, ErrorKind};
|
||||
use libc;
|
||||
use mem;
|
||||
#[allow(deprecated)]
|
||||
use num::Int;
|
||||
use old_io::{self, IoResult, IoError};
|
||||
use os::windows::ffi::{OsStrExt, OsStringExt};
|
||||
use path::PathBuf;
|
||||
use sync::{Once, ONCE_INIT};
|
||||
|
||||
pub mod backtrace;
|
||||
pub mod c;
|
||||
pub mod condvar;
|
||||
pub mod ext;
|
||||
pub mod fs;
|
||||
pub mod fs2;
|
||||
pub mod handle;
|
||||
pub mod helper_signal;
|
||||
pub mod mutex;
|
||||
pub mod net;
|
||||
pub mod os;
|
||||
pub mod os_str;
|
||||
pub mod pipe;
|
||||
pub mod pipe2;
|
||||
pub mod process;
|
||||
pub mod process2;
|
||||
pub mod rwlock;
|
||||
pub mod stack_overflow;
|
||||
pub mod sync;
|
||||
pub mod tcp;
|
||||
pub mod thread;
|
||||
pub mod thread_local;
|
||||
pub mod time;
|
||||
pub mod timer;
|
||||
pub mod tty;
|
||||
pub mod udp;
|
||||
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 {
|
||||
match errno as libc::c_int {
|
||||
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> {
|
||||
let mut v: Vec<_> = s.encode_wide().collect();
|
||||
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
|
||||
// yielded the data which has been read from the syscall. The return value
|
||||
// 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,
|
||||
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);
|
||||
let k = match f1(buf.as_mut_ptr(), n as libc::DWORD) {
|
||||
0 if libc::GetLastError() == 0 => 0,
|
||||
0 => return Err(()),
|
||||
0 => return Err(io::Error::last_os_error()),
|
||||
n => n,
|
||||
} as usize;
|
||||
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 {
|
||||
PathBuf::from(OsString::from_wide(s))
|
||||
}
|
||||
|
@ -22,15 +22,12 @@ use io;
|
||||
use libc::types::os::arch::extra::LPWCH;
|
||||
use libc::{self, c_int, c_void};
|
||||
use mem;
|
||||
#[allow(deprecated)]
|
||||
use old_io::{IoError, IoResult};
|
||||
use ops::Range;
|
||||
use os::windows::ffi::EncodeWide;
|
||||
use path::{self, PathBuf};
|
||||
use ptr;
|
||||
use slice;
|
||||
use sys::c;
|
||||
use sys::fs::FileDesc;
|
||||
use sys::handle::Handle;
|
||||
|
||||
use libc::funcs::extra::kernel32::{
|
||||
@ -233,13 +230,13 @@ impl StdError for JoinPathsError {
|
||||
}
|
||||
|
||||
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)
|
||||
}, super::os2path)
|
||||
}
|
||||
|
||||
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)
|
||||
}, super::os2path)
|
||||
}
|
||||
@ -259,7 +256,7 @@ pub fn chdir(p: &path::Path) -> io::Result<()> {
|
||||
|
||||
pub fn getenv(k: &OsStr) -> Option<OsString> {
|
||||
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)
|
||||
}, |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 {
|
||||
super::fill_utf16_buf_new(|buf, sz| unsafe {
|
||||
super::fill_utf16_buf(|buf, sz| unsafe {
|
||||
c::GetTempPathW(sz, buf)
|
||||
}, super::os2path).unwrap()
|
||||
}
|
||||
@ -371,7 +349,7 @@ pub fn home_dir() -> Option<PathBuf> {
|
||||
return None
|
||||
}
|
||||
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) {
|
||||
0 if libc::GetLastError() != 0 => 0,
|
||||
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;
|
@ -728,7 +728,6 @@ mod test {
|
||||
use any::Any;
|
||||
use sync::mpsc::{channel, Sender};
|
||||
use result;
|
||||
use std::old_io::{ChanReader, ChanWriter};
|
||||
use super::{Builder};
|
||||
use thread;
|
||||
use thunk::Thunk;
|
||||
@ -967,13 +966,11 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_park_timeout_unpark_called_other_thread() {
|
||||
use std::old_io;
|
||||
|
||||
for _ in 0..10 {
|
||||
let th = thread::current();
|
||||
|
||||
let _guard = thread::spawn(move || {
|
||||
old_io::timer::sleep(Duration::milliseconds(50));
|
||||
super::sleep_ms(50);
|
||||
th.unpark();
|
||||
});
|
||||
|
||||
|
@ -61,7 +61,6 @@ pub mod clone;
|
||||
pub mod encodable;
|
||||
pub mod decodable;
|
||||
pub mod hash;
|
||||
pub mod rand;
|
||||
pub mod show;
|
||||
pub mod default;
|
||||
pub mod primitive;
|
||||
@ -168,8 +167,6 @@ derive_traits! {
|
||||
"PartialOrd" => ord::expand_deriving_ord,
|
||||
"Ord" => totalord::expand_deriving_totalord,
|
||||
|
||||
"Rand" => rand::expand_deriving_rand,
|
||||
|
||||
"Debug" => show::expand_deriving_show,
|
||||
|
||||
"Default" => default::expand_deriving_default,
|
||||
|
@ -1,175 +0,0 @@
|
||||
// Copyright 2012-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 ast;
|
||||
use ast::{MetaItem, Item, Expr};
|
||||
use codemap::Span;
|
||||
use ext::base::ExtCtxt;
|
||||
use ext::build::AstBuilder;
|
||||
use ext::deriving::generic::*;
|
||||
use ext::deriving::generic::ty::*;
|
||||
use ptr::P;
|
||||
|
||||
pub fn expand_deriving_rand<F>(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
mitem: &MetaItem,
|
||||
item: &Item,
|
||||
push: F) where
|
||||
F: FnOnce(P<Item>),
|
||||
{
|
||||
cx.span_warn(span,
|
||||
"`#[derive(Rand)]` is deprecated in favour of `#[derive_Rand]` from \
|
||||
`rand_macros` on crates.io");
|
||||
|
||||
if !cx.use_std {
|
||||
// FIXME(#21880): lift this requirement.
|
||||
cx.span_err(span, "this trait cannot be derived with #![no_std]");
|
||||
return;
|
||||
}
|
||||
|
||||
let trait_def = TraitDef {
|
||||
span: span,
|
||||
attributes: Vec::new(),
|
||||
path: path!(std::rand::Rand),
|
||||
additional_bounds: Vec::new(),
|
||||
generics: LifetimeBounds::empty(),
|
||||
methods: vec!(
|
||||
MethodDef {
|
||||
name: "rand",
|
||||
generics: LifetimeBounds {
|
||||
lifetimes: Vec::new(),
|
||||
bounds: vec!(("R",
|
||||
vec!( path!(std::rand::Rng) ))),
|
||||
},
|
||||
explicit_self: None,
|
||||
args: vec!(
|
||||
Ptr(box Literal(Path::new_local("R")),
|
||||
Borrowed(None, ast::MutMutable))
|
||||
),
|
||||
ret_ty: Self_,
|
||||
attributes: Vec::new(),
|
||||
combine_substructure: combine_substructure(Box::new(|a, b, c| {
|
||||
rand_substructure(a, b, c)
|
||||
}))
|
||||
}
|
||||
),
|
||||
associated_types: Vec::new(),
|
||||
};
|
||||
trait_def.expand(cx, mitem, item, push)
|
||||
}
|
||||
|
||||
fn rand_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> {
|
||||
let rng = match substr.nonself_args {
|
||||
[ref rng] => rng,
|
||||
_ => cx.bug("Incorrect number of arguments to `rand` in `derive(Rand)`")
|
||||
};
|
||||
let rand_ident = vec!(
|
||||
cx.ident_of("std"),
|
||||
cx.ident_of("rand"),
|
||||
cx.ident_of("Rand"),
|
||||
cx.ident_of("rand")
|
||||
);
|
||||
let rand_call = |cx: &mut ExtCtxt, span| {
|
||||
cx.expr_call_global(span,
|
||||
rand_ident.clone(),
|
||||
vec!(rng.clone()))
|
||||
};
|
||||
|
||||
return match *substr.fields {
|
||||
StaticStruct(_, ref summary) => {
|
||||
let path = cx.path_ident(trait_span, substr.type_ident);
|
||||
rand_thing(cx, trait_span, path, summary, rand_call)
|
||||
}
|
||||
StaticEnum(_, ref variants) => {
|
||||
if variants.is_empty() {
|
||||
cx.span_err(trait_span, "`Rand` cannot be derived for enums with no variants");
|
||||
// let compilation continue
|
||||
return cx.expr_usize(trait_span, 0);
|
||||
}
|
||||
|
||||
let variant_count = cx.expr_usize(trait_span, variants.len());
|
||||
|
||||
let rand_name = cx.path_all(trait_span,
|
||||
true,
|
||||
rand_ident.clone(),
|
||||
Vec::new(),
|
||||
Vec::new(),
|
||||
Vec::new());
|
||||
let rand_name = cx.expr_path(rand_name);
|
||||
|
||||
// ::rand::Rand::rand(rng)
|
||||
let rv_call = cx.expr_call(trait_span,
|
||||
rand_name,
|
||||
vec!(rng.clone()));
|
||||
|
||||
// need to specify the usize-ness of the random number
|
||||
let usize_ty = cx.ty_ident(trait_span, cx.ident_of("usize"));
|
||||
let value_ident = cx.ident_of("__value");
|
||||
let let_statement = cx.stmt_let_typed(trait_span,
|
||||
false,
|
||||
value_ident,
|
||||
usize_ty,
|
||||
rv_call);
|
||||
|
||||
// rand() % variants.len()
|
||||
let value_ref = cx.expr_ident(trait_span, value_ident);
|
||||
let rand_variant = cx.expr_binary(trait_span,
|
||||
ast::BiRem,
|
||||
value_ref,
|
||||
variant_count);
|
||||
|
||||
let mut arms = variants.iter().enumerate().map(|(i, &(ident, v_span, ref summary))| {
|
||||
let i_expr = cx.expr_usize(v_span, i);
|
||||
let pat = cx.pat_lit(v_span, i_expr);
|
||||
|
||||
let path = cx.path(v_span, vec![substr.type_ident, ident]);
|
||||
let thing = rand_thing(cx, v_span, path, summary, |cx, sp| rand_call(cx, sp));
|
||||
cx.arm(v_span, vec!( pat ), thing)
|
||||
}).collect::<Vec<ast::Arm> >();
|
||||
|
||||
// _ => {} at the end. Should never occur
|
||||
arms.push(cx.arm_unreachable(trait_span));
|
||||
|
||||
let match_expr = cx.expr_match(trait_span, rand_variant, arms);
|
||||
|
||||
let block = cx.block(trait_span, vec!( let_statement ), Some(match_expr));
|
||||
cx.expr_block(block)
|
||||
}
|
||||
_ => cx.bug("Non-static method in `derive(Rand)`")
|
||||
};
|
||||
|
||||
fn rand_thing<F>(cx: &mut ExtCtxt,
|
||||
trait_span: Span,
|
||||
ctor_path: ast::Path,
|
||||
summary: &StaticFields,
|
||||
mut rand_call: F)
|
||||
-> P<Expr> where
|
||||
F: FnMut(&mut ExtCtxt, Span) -> P<Expr>,
|
||||
{
|
||||
let path = cx.expr_path(ctor_path.clone());
|
||||
match *summary {
|
||||
Unnamed(ref fields) => {
|
||||
if fields.is_empty() {
|
||||
path
|
||||
} else {
|
||||
let exprs = fields.iter().map(|span| rand_call(cx, *span)).collect();
|
||||
cx.expr_call(trait_span, path, exprs)
|
||||
}
|
||||
}
|
||||
Named(ref fields) => {
|
||||
let rand_fields = fields.iter().map(|&(ident, span)| {
|
||||
let e = rand_call(cx, span);
|
||||
cx.field_imm(span, ident, e)
|
||||
}).collect();
|
||||
cx.expr_struct(trait_span, ctor_path, rand_fields)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -30,7 +30,6 @@
|
||||
#![feature(collections)]
|
||||
#![feature(core)]
|
||||
#![feature(libc)]
|
||||
#![feature(old_path)]
|
||||
#![feature(quote, unsafe_destructor)]
|
||||
#![feature(rustc_private)]
|
||||
#![feature(staged_api)]
|
||||
|
@ -23,10 +23,7 @@ use util::interner;
|
||||
|
||||
use serialize::{Decodable, Decoder, Encodable, Encoder};
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
#[allow(deprecated)]
|
||||
use std::old_path::BytesContainer;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
@ -639,19 +636,6 @@ impl Deref for InternedString {
|
||||
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 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self.string, f)
|
||||
|
@ -43,7 +43,6 @@ pub trait Stats <T: Float + FromPrimitive> {
|
||||
/// Depends on IEEE-754 arithmetic guarantees. See proof of correctness at:
|
||||
/// ["Adaptive Precision Floating-Point Arithmetic and Fast Robust Geometric Predicates"]
|
||||
/// (http://www.cs.cmu.edu/~quake-papers/robust-arithmetic.ps)
|
||||
/// *Discrete & Computational Geometry 18*, 3 (Oct 1997), 305-363, Shewchuk J.R.
|
||||
fn sum(&self) -> T;
|
||||
|
||||
/// Minimum value of the samples.
|
||||
@ -334,8 +333,9 @@ pub fn winsorize<T: Float + FromPrimitive>(samples: &mut [T], pct: T) {
|
||||
mod tests {
|
||||
use stats::Stats;
|
||||
use stats::Summary;
|
||||
use std::old_io::{self, Writer};
|
||||
use std::f64;
|
||||
use std::io::prelude::*;
|
||||
use std::io;
|
||||
|
||||
macro_rules! assert_approx_eq {
|
||||
($a:expr, $b:expr) => ({
|
||||
@ -350,7 +350,7 @@ mod tests {
|
||||
|
||||
let summ2 = Summary::new(samples);
|
||||
|
||||
let mut w = old_io::stdout();
|
||||
let mut w = io::sink();
|
||||
let w = &mut w;
|
||||
(write!(w, "\n")).unwrap();
|
||||
|
||||
|
@ -8,11 +8,11 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(unboxed_closures, std_misc, rand)]
|
||||
#![feature(std_misc, rand)]
|
||||
|
||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||
use std::env;
|
||||
use std::rand::{Rng, IsaacRng, SeedableRng};
|
||||
use std::__rand::{Rng, thread_rng};
|
||||
use std::time::Duration;
|
||||
|
||||
fn timed<F>(label: &str, f: F) where F: FnMut() {
|
||||
@ -114,7 +114,7 @@ fn main() {
|
||||
|
||||
{
|
||||
let seed: &[_] = &[1, 1, 1, 1, 1, 1, 1];
|
||||
let mut rng: IsaacRng = SeedableRng::from_seed(seed);
|
||||
let mut rng = thread_rng();
|
||||
let mut set = HashSet::new();
|
||||
while set.len() != n_keys {
|
||||
let next = rng.gen();
|
||||
|
@ -11,18 +11,14 @@
|
||||
// ignore-lexer-test FIXME #15679
|
||||
// Microbenchmarks for various functions in std and extra
|
||||
|
||||
#![feature(unboxed_closures, rand, old_io, old_path, std_misc, collections)]
|
||||
#![feature(rand, collections, std_misc)]
|
||||
|
||||
use std::old_io::*;
|
||||
use std::old_path::{Path, GenericPath};
|
||||
use std::iter::repeat;
|
||||
use std::mem::swap;
|
||||
use std::env;
|
||||
use std::rand::Rng;
|
||||
use std::rand;
|
||||
use std::__rand::{thread_rng, Rng};
|
||||
use std::str;
|
||||
use std::time::Duration;
|
||||
use std::vec;
|
||||
|
||||
fn main() {
|
||||
let argv: Vec<String> = env::args().collect();
|
||||
@ -35,7 +31,6 @@ fn main() {
|
||||
}
|
||||
|
||||
bench!(shift_push);
|
||||
bench!(read_line);
|
||||
bench!(vec_plus);
|
||||
bench!(vec_append);
|
||||
bench!(vec_push_all);
|
||||
@ -70,21 +65,8 @@ fn shift_push() {
|
||||
}
|
||||
}
|
||||
|
||||
fn read_line() {
|
||||
use std::old_io::BufferedReader;
|
||||
|
||||
let mut path = Path::new(env!("CFG_SRC_DIR"));
|
||||
path.push("src/test/bench/shootout-k-nucleotide.data");
|
||||
|
||||
for _ in 0..3 {
|
||||
let mut reader = BufferedReader::new(File::open(&path).unwrap());
|
||||
for _line in reader.lines() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn vec_plus() {
|
||||
let mut r = rand::thread_rng();
|
||||
let mut r = thread_rng();
|
||||
|
||||
let mut v = Vec::new();
|
||||
let mut i = 0;
|
||||
@ -102,7 +84,7 @@ fn vec_plus() {
|
||||
}
|
||||
|
||||
fn vec_append() {
|
||||
let mut r = rand::thread_rng();
|
||||
let mut r = thread_rng();
|
||||
|
||||
let mut v = Vec::new();
|
||||
let mut i = 0;
|
||||
@ -123,7 +105,7 @@ fn vec_append() {
|
||||
}
|
||||
|
||||
fn vec_push_all() {
|
||||
let mut r = rand::thread_rng();
|
||||
let mut r = thread_rng();
|
||||
|
||||
let mut v = Vec::new();
|
||||
for i in 0..1500 {
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
use std::f32::consts::PI;
|
||||
use std::num::Float;
|
||||
use std::rand::{Rng, StdRng};
|
||||
use std::__rand::{Rng, thread_rng};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Vec2 {
|
||||
@ -44,7 +44,7 @@ struct Noise2DContext {
|
||||
|
||||
impl Noise2DContext {
|
||||
fn new() -> Noise2DContext {
|
||||
let mut rng = StdRng::new().unwrap();
|
||||
let mut rng = thread_rng();
|
||||
|
||||
let mut rgradients = [Vec2 { x: 0.0, y: 0.0 }; 256];
|
||||
for x in &mut rgradients[..] {
|
||||
|
@ -38,13 +38,11 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
// OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#![feature(core, old_io, io, core)]
|
||||
|
||||
use std::cmp::min;
|
||||
use std::old_io::*;
|
||||
use std::iter::repeat;
|
||||
use std::env;
|
||||
use std::slice::bytes::copy_memory;
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
use std::iter::repeat;
|
||||
|
||||
const LINE_LEN: usize = 60;
|
||||
const LOOKUP_SIZE: usize = 4 * 1024;
|
||||
@ -116,27 +114,31 @@ struct RepeatFasta<'a, W:'a> {
|
||||
out: &'a mut W
|
||||
}
|
||||
|
||||
impl<'a, W: Writer> RepeatFasta<'a, W> {
|
||||
impl<'a, W: Write> RepeatFasta<'a, W> {
|
||||
fn new(alu: &'static str, w: &'a mut W) -> RepeatFasta<'a, W> {
|
||||
RepeatFasta { alu: alu, out: w }
|
||||
}
|
||||
|
||||
fn make(&mut self, n: usize) -> IoResult<()> {
|
||||
fn make(&mut self, n: usize) -> io::Result<()> {
|
||||
let alu_len = self.alu.len();
|
||||
let mut buf = repeat(0).take(alu_len + LINE_LEN).collect::<Vec<_>>();
|
||||
let alu: &[u8] = self.alu.as_bytes();
|
||||
|
||||
copy_memory(alu, &mut buf);
|
||||
for (slot, val) in buf.iter_mut().zip(alu.iter()) {
|
||||
*slot = *val;
|
||||
}
|
||||
let buf_len = buf.len();
|
||||
copy_memory(&alu[..LINE_LEN], &mut buf[alu_len..buf_len]);
|
||||
for (slot, val) in buf[alu_len..buf_len].iter_mut().zip(alu[..LINE_LEN].iter()) {
|
||||
*slot = *val;
|
||||
}
|
||||
|
||||
let mut pos = 0;
|
||||
let mut bytes;
|
||||
let mut n = n;
|
||||
while n > 0 {
|
||||
bytes = min(LINE_LEN, n);
|
||||
try!(self.out.write(&buf[pos..pos + bytes]));
|
||||
try!(self.out.write_u8('\n' as u8));
|
||||
try!(self.out.write_all(&buf[pos..pos + bytes]));
|
||||
try!(self.out.write_all(&[b'\n']));
|
||||
pos += bytes;
|
||||
if pos > alu_len {
|
||||
pos -= alu_len;
|
||||
@ -165,7 +167,7 @@ struct RandomFasta<'a, W:'a> {
|
||||
out: &'a mut W,
|
||||
}
|
||||
|
||||
impl<'a, W: Writer> RandomFasta<'a, W> {
|
||||
impl<'a, W: Write> RandomFasta<'a, W> {
|
||||
fn new(w: &'a mut W, a: &[AminoAcid]) -> RandomFasta<'a, W> {
|
||||
RandomFasta {
|
||||
seed: 42,
|
||||
@ -189,7 +191,7 @@ impl<'a, W: Writer> RandomFasta<'a, W> {
|
||||
0
|
||||
}
|
||||
|
||||
fn make(&mut self, n: usize) -> IoResult<()> {
|
||||
fn make(&mut self, n: usize) -> io::Result<()> {
|
||||
let lines = n / LINE_LEN;
|
||||
let chars_left = n % LINE_LEN;
|
||||
let mut buf = [0;LINE_LEN + 1];
|
||||
@ -204,7 +206,7 @@ impl<'a, W: Writer> RandomFasta<'a, W> {
|
||||
for i in 0..chars_left {
|
||||
buf[i] = self.nextc();
|
||||
}
|
||||
self.out.write(&buf[..chars_left])
|
||||
self.out.write_all(&buf[..chars_left])
|
||||
}
|
||||
}
|
||||
|
||||
@ -216,23 +218,23 @@ fn main() {
|
||||
5
|
||||
};
|
||||
|
||||
let mut out = stdout();
|
||||
let mut out = io::stdout();
|
||||
|
||||
out.write_line(">ONE Homo sapiens alu").unwrap();
|
||||
out.write_all(b">ONE Homo sapiens alu\n").unwrap();
|
||||
{
|
||||
let mut repeat = RepeatFasta::new(ALU, &mut out);
|
||||
repeat.make(n * 2).unwrap();
|
||||
}
|
||||
|
||||
out.write_line(">TWO IUB ambiguity codes").unwrap();
|
||||
out.write_all(b">TWO IUB ambiguity codes\n").unwrap();
|
||||
let iub = sum_and_scale(&IUB);
|
||||
let mut random = RandomFasta::new(&mut out, &iub);
|
||||
random.make(n * 3).unwrap();
|
||||
|
||||
random.out.write_line(">THREE Homo sapiens frequency").unwrap();
|
||||
random.out.write_all(b">THREE Homo sapiens frequency\n").unwrap();
|
||||
let homo_sapiens = sum_and_scale(&HOMO_SAPIENS);
|
||||
random.lookup = make_lookup(&homo_sapiens);
|
||||
random.make(n * 5).unwrap();
|
||||
|
||||
random.out.write_str("\n").unwrap();
|
||||
random.out.write_all(b"\n").unwrap();
|
||||
}
|
||||
|
@ -38,14 +38,12 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
// OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#![feature(old_io, old_path, io, core)]
|
||||
|
||||
use std::cmp::min;
|
||||
use std::old_io::*;
|
||||
use std::old_io;
|
||||
use std::old_path::Path;
|
||||
use std::num::Float;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::{self, BufWriter};
|
||||
use std::io::prelude::*;
|
||||
use std::num::Float;
|
||||
|
||||
const LINE_LENGTH: usize = 60;
|
||||
const IM: u32 = 139968;
|
||||
@ -87,9 +85,9 @@ impl<'a> Iterator for AAGen<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn make_fasta<W: Writer, I: Iterator<Item=u8>>(
|
||||
fn make_fasta<W: Write, I: Iterator<Item=u8>>(
|
||||
wr: &mut W, header: &str, mut it: I, mut n: usize)
|
||||
-> std::old_io::IoResult<()>
|
||||
-> io::Result<()>
|
||||
{
|
||||
try!(wr.write(header.as_bytes()));
|
||||
let mut line = [0; LINE_LENGTH + 1];
|
||||
@ -105,7 +103,7 @@ fn make_fasta<W: Writer, I: Iterator<Item=u8>>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run<W: Writer>(writer: &mut W) -> std::old_io::IoResult<()> {
|
||||
fn run<W: Write>(writer: &mut W) -> io::Result<()> {
|
||||
let mut args = env::args();
|
||||
let n = if env::var_os("RUST_BENCH").is_some() {
|
||||
25000000
|
||||
@ -146,10 +144,10 @@ fn run<W: Writer>(writer: &mut W) -> std::old_io::IoResult<()> {
|
||||
|
||||
fn main() {
|
||||
let res = if env::var_os("RUST_BENCH").is_some() {
|
||||
let mut file = BufferedWriter::new(File::create(&Path::new("./shootout-fasta.data")));
|
||||
let mut file = BufWriter::new(File::create("./shootout-fasta.data").unwrap());
|
||||
run(&mut file)
|
||||
} else {
|
||||
run(&mut old_io::stdout())
|
||||
run(&mut io::stdout())
|
||||
};
|
||||
res.unwrap()
|
||||
}
|
||||
|
@ -13,18 +13,17 @@
|
||||
|
||||
// multi tasking k-nucleotide
|
||||
|
||||
#![feature(box_syntax, std_misc, old_io, collections, os)]
|
||||
#![allow(bad_style)]
|
||||
|
||||
use std::ascii::{AsciiExt, OwnedAsciiExt};
|
||||
use std::ascii::AsciiExt;
|
||||
use std::cmp::Ordering::{self, Less, Greater, Equal};
|
||||
use std::collections::HashMap;
|
||||
use std::mem::replace;
|
||||
use std::num::Float;
|
||||
use std::option;
|
||||
use std::os;
|
||||
use std::env;
|
||||
use std::sync::mpsc::{channel, Sender, Receiver};
|
||||
use std::thread;
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
|
||||
fn f64_cmp(x: f64, y: f64) -> Ordering {
|
||||
// arbitrarily decide that NaNs are larger than everything.
|
||||
@ -75,10 +74,10 @@ fn sort_and_fmt(mm: &HashMap<Vec<u8> , usize>, total: usize) -> String {
|
||||
|
||||
// given a map, search for the frequency of a pattern
|
||||
fn find(mm: &HashMap<Vec<u8> , usize>, key: String) -> usize {
|
||||
let key = key.into_ascii_lowercase();
|
||||
let key = key.to_ascii_lowercase();
|
||||
match mm.get(key.as_bytes()) {
|
||||
option::Option::None => { return 0; }
|
||||
option::Option::Some(&num) => { return num; }
|
||||
None => 0,
|
||||
Some(&num) => num,
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,7 +122,7 @@ fn make_sequence_processor(sz: usize,
|
||||
line = from_parent.recv().unwrap();
|
||||
if line == Vec::new() { break; }
|
||||
|
||||
carry.push_all(&line);
|
||||
carry.extend(line);
|
||||
carry = windows_with_carry(&carry, sz, |window| {
|
||||
update_freq(&mut freqs, window);
|
||||
total += 1;
|
||||
@ -147,15 +146,13 @@ fn make_sequence_processor(sz: usize,
|
||||
|
||||
// given a FASTA file on stdin, process sequence THREE
|
||||
fn main() {
|
||||
use std::old_io::*;
|
||||
|
||||
let input = io::stdin();
|
||||
let rdr = if env::var_os("RUST_BENCH").is_some() {
|
||||
let foo = include_bytes!("shootout-k-nucleotide.data");
|
||||
box MemReader::new(foo.to_vec()) as Box<Reader>
|
||||
let foo: &[u8] = include_bytes!("shootout-k-nucleotide.data");
|
||||
Box::new(foo) as Box<BufRead>
|
||||
} else {
|
||||
box stdio::stdin() as Box<Reader>
|
||||
Box::new(input.lock()) as Box<BufRead>
|
||||
};
|
||||
let mut rdr = BufferedReader::new(rdr);
|
||||
|
||||
// initialize each sequence sorter
|
||||
let sizes: Vec<usize> = vec!(1,2,3,4,6,12,18);
|
||||
|
@ -38,13 +38,13 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
// OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#![feature(simd, old_io, core, io)]
|
||||
#![feature(simd, core)]
|
||||
|
||||
// ignore-pretty very bad with line comments
|
||||
|
||||
use std::old_io;
|
||||
use std::old_io::*;
|
||||
use std::env;
|
||||
use std::io::prelude::*;
|
||||
use std::io;
|
||||
use std::simd::f64x2;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
@ -53,8 +53,7 @@ const ITER: usize = 50;
|
||||
const LIMIT: f64 = 2.0;
|
||||
const WORKERS: usize = 16;
|
||||
|
||||
#[inline(always)]
|
||||
fn mandelbrot<W: old_io::Writer>(w: usize, mut out: W) -> old_io::IoResult<()> {
|
||||
fn mandelbrot<W: Write>(w: usize, mut out: W) -> io::Result<()> {
|
||||
assert!(WORKERS % 2 == 0);
|
||||
|
||||
// Ensure w and h are multiples of 8.
|
||||
@ -142,9 +141,9 @@ fn mandelbrot<W: old_io::Writer>(w: usize, mut out: W) -> old_io::IoResult<()> {
|
||||
})
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
try!(writeln!(&mut out as &mut Writer, "P4\n{} {}", w, h));
|
||||
try!(writeln!(&mut out, "P4\n{} {}", w, h));
|
||||
for res in data {
|
||||
try!(out.write(&res.join()));
|
||||
try!(out.write_all(&res.join()));
|
||||
}
|
||||
out.flush()
|
||||
}
|
||||
@ -202,9 +201,9 @@ fn main() {
|
||||
let res = if args.len() < 2 {
|
||||
println!("Test mode: do not dump the image because it's not utf8, \
|
||||
which interferes with the test runner.");
|
||||
mandelbrot(1000, old_io::util::NullWriter)
|
||||
mandelbrot(1000, io::sink())
|
||||
} else {
|
||||
mandelbrot(args.nth(1).unwrap().parse().unwrap(), old_io::stdout())
|
||||
mandelbrot(args.nth(1).unwrap().parse().unwrap(), io::stdout())
|
||||
};
|
||||
res.unwrap();
|
||||
}
|
||||
|
@ -40,18 +40,18 @@
|
||||
|
||||
// ignore-android see #10393 #13206
|
||||
|
||||
#![feature(unboxed_closures, libc, old_io, collections, io, core)]
|
||||
#![feature(libc)]
|
||||
|
||||
extern crate libc;
|
||||
|
||||
use std::old_io::stdio::{stdin_raw, stdout_raw};
|
||||
use std::old_io::*;
|
||||
use std::ptr::{copy, Unique};
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
use std::ptr::copy;
|
||||
use std::thread;
|
||||
|
||||
struct Tables {
|
||||
table8: [u8;1 << 8],
|
||||
table16: [u16;1 << 16]
|
||||
table8: [u8; 1 << 8],
|
||||
table16: [u16; 1 << 16]
|
||||
}
|
||||
|
||||
impl Tables {
|
||||
@ -101,36 +101,6 @@ impl Tables {
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads all remaining bytes from the stream.
|
||||
fn read_to_end<R: Reader>(r: &mut R) -> IoResult<Vec<u8>> {
|
||||
// As reading the input stream in memory is a bottleneck, we tune
|
||||
// Reader::read_to_end() with a fast growing policy to limit
|
||||
// recopies. If MREMAP_RETAIN is implemented in the linux kernel
|
||||
// and jemalloc use it, this trick will become useless.
|
||||
const CHUNK: usize = 64 * 1024;
|
||||
|
||||
let mut vec = Vec::with_capacity(CHUNK);
|
||||
loop {
|
||||
// workaround: very fast growing
|
||||
let len = vec.len();
|
||||
if vec.capacity() - len < CHUNK {
|
||||
let cap = vec.capacity();
|
||||
let mult = if cap < 256 * 1024 * 1024 {
|
||||
16
|
||||
} else {
|
||||
2
|
||||
};
|
||||
vec.reserve_exact(mult * cap - len);
|
||||
}
|
||||
match r.push_at_least(1, CHUNK, &mut vec) {
|
||||
Ok(_) => {}
|
||||
Err(ref e) if e.kind == EndOfFile => break,
|
||||
Err(e) => return Err(e)
|
||||
}
|
||||
}
|
||||
Ok(vec)
|
||||
}
|
||||
|
||||
/// Finds the first position at which `b` occurs in `s`.
|
||||
fn memchr(h: &[u8], n: u8) -> Option<usize> {
|
||||
use libc::{c_void, c_int, size_t};
|
||||
@ -175,7 +145,8 @@ const LINE_LEN: usize = 60;
|
||||
|
||||
/// Compute the reverse complement.
|
||||
fn reverse_complement(seq: &mut [u8], tables: &Tables) {
|
||||
let seq = seq.init_mut();// Drop the last newline
|
||||
let len = seq.len();
|
||||
let seq = &mut seq[..len - 1]; // Drop the last newline
|
||||
let len = seq.len();
|
||||
let off = LINE_LEN - len % (LINE_LEN + 1);
|
||||
let mut i = LINE_LEN;
|
||||
@ -222,26 +193,20 @@ fn reverse_complement(seq: &mut [u8], tables: &Tables) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct Racy<T>(T);
|
||||
|
||||
unsafe impl<T: 'static> Send for Racy<T> {}
|
||||
|
||||
/// Executes a closure in parallel over the given iterator over mutable slice.
|
||||
/// The closure `f` is run in parallel with an element of `iter`.
|
||||
fn parallel<'a, I: Iterator, F>(iter: I, ref f: F)
|
||||
where I::Item: Send + 'a,
|
||||
F: Fn(I::Item) + Sync + 'a {
|
||||
fn parallel<I: Iterator, F>(iter: I, ref f: F)
|
||||
where I::Item: Send,
|
||||
F: Fn(I::Item) + Sync, {
|
||||
iter.map(|x| {
|
||||
thread::scoped(move|| {
|
||||
f(x)
|
||||
})
|
||||
thread::scoped(move || f(x))
|
||||
}).collect::<Vec<_>>();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut data = read_to_end(&mut stdin_raw()).unwrap();
|
||||
let mut data = Vec::with_capacity(1024 * 1024);
|
||||
io::stdin().read_to_end(&mut data);
|
||||
let tables = &Tables::new();
|
||||
parallel(mut_dna_seqs(&mut data), |seq| reverse_complement(seq, tables));
|
||||
stdout_raw().write(&data).unwrap();
|
||||
io::stdout().write_all(&data).unwrap();
|
||||
}
|
||||
|
@ -15,12 +15,6 @@ extern crate core;
|
||||
extern crate rand;
|
||||
extern crate serialize as rustc_serialize;
|
||||
|
||||
#[derive(Rand)] //~ ERROR this trait cannot be derived
|
||||
//~^ WARNING `#[derive(Rand)]` is deprecated
|
||||
struct Foo {
|
||||
x: u32,
|
||||
}
|
||||
|
||||
#[derive(RustcEncodable)] //~ ERROR this trait cannot be derived
|
||||
struct Bar {
|
||||
x: u32,
|
||||
|
@ -10,15 +10,15 @@
|
||||
|
||||
// min-lldb-version: 310
|
||||
|
||||
// This test case checks if function arguments already have the correct value when breaking at the
|
||||
// first line of the function, that is if the function prologue has already been executed at the
|
||||
// first line. Note that because of the __morestack part of the prologue GDB incorrectly breaks at
|
||||
// before the arguments have been properly loaded when setting the breakpoint via the function name.
|
||||
// This test case checks if function arguments already have the correct value
|
||||
// when breaking at the first line of the function, that is if the function
|
||||
// prologue has already been executed at the first line. Note that because of
|
||||
// the __morestack part of the prologue GDB incorrectly breaks at before the
|
||||
// arguments have been properly loaded when setting the breakpoint via the
|
||||
// function name.
|
||||
|
||||
// compile-flags:-g
|
||||
|
||||
#![feature(old_io)]
|
||||
|
||||
// === GDB TESTS ===================================================================================
|
||||
|
||||
// gdb-command:run
|
||||
@ -227,7 +227,7 @@
|
||||
#![omit_gdb_pretty_printer_section]
|
||||
|
||||
fn immediate_args(a: isize, b: bool, c: f64) {
|
||||
::std::old_io::print("") // #break
|
||||
println!("") // #break
|
||||
}
|
||||
|
||||
struct BigStruct {
|
||||
@ -242,21 +242,21 @@ struct BigStruct {
|
||||
}
|
||||
|
||||
fn non_immediate_args(a: BigStruct, b: BigStruct) {
|
||||
::std::old_io::print("") // #break
|
||||
println!("") // #break
|
||||
}
|
||||
|
||||
fn binding(a: i64, b: u64, c: f64) {
|
||||
let x = 0; // #break
|
||||
::std::old_io::print("")
|
||||
println!("")
|
||||
}
|
||||
|
||||
fn assignment(mut a: u64, b: u64, c: f64) {
|
||||
a = b; // #break
|
||||
::std::old_io::print("")
|
||||
println!("")
|
||||
}
|
||||
|
||||
fn function_call(x: u64, y: u64, z: f64) {
|
||||
std::old_io::stdio::print("Hi!") // #break
|
||||
println!("Hi!") // #break
|
||||
}
|
||||
|
||||
fn identifier(x: u64, y: u64, z: f64) -> u64 {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user