std: Implement stdio for std::io

This is an implementation of RFC 899 and adds stdio functionality to the new
`std::io` module. Details of the API can be found on the RFC, but from a high
level:

* `io::{stdin, stdout, stderr}` constructors are now available. There are also
  `*_raw` variants for unbuffered and unlocked access.
* All handles are globally shared (excluding raw variants).
* The stderr handle is no longer buffered.
* All handles can be explicitly locked (excluding the raw variants).

The `print!` and `println!` machinery has not yet been hooked up to these
streams just yet. The `std::fmt::output` module has also not yet been
implemented as part of this commit.
This commit is contained in:
Alex Crichton 2015-02-24 23:27:20 -08:00
parent 8a69110c3b
commit 94d71f8836
20 changed files with 732 additions and 108 deletions

View file

@ -43,12 +43,16 @@
#![feature(box_syntax)]
use std::ascii::OwnedAsciiExt;
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io;
use std::slice;
use std::sync::Arc;
use std::thread;
static TABLE: [u8;4] = [ 'A' as u8, 'C' as u8, 'G' as u8, 'T' as u8 ];
static TABLE_SIZE: uint = 2 << 16;
static TABLE_SIZE: usize = 2 << 16;
static OCCURRENCES: [&'static str;5] = [
"GGT",
@ -73,7 +77,7 @@ impl Code {
Code((self.hash() << 2) + (pack_symbol(c) as u64))
}
fn rotate(&self, c: u8, frame: uint) -> Code {
fn rotate(&self, c: u8, frame: usize) -> Code {
Code(self.push_char(c).hash() & ((1u64 << (2 * frame)) - 1))
}
@ -81,7 +85,7 @@ impl Code {
string.bytes().fold(Code(0u64), |a, b| a.push_char(b))
}
fn unpack(&self, frame: uint) -> String {
fn unpack(&self, frame: usize) -> String {
let mut key = self.hash();
let mut result = Vec::new();
for _ in 0..frame {
@ -113,13 +117,13 @@ struct PrintCallback(&'static str);
impl TableCallback for PrintCallback {
fn f(&self, entry: &mut Entry) {
let PrintCallback(s) = *self;
println!("{}\t{}", entry.count as int, s);
println!("{}\t{}", entry.count, s);
}
}
struct Entry {
code: Code,
count: uint,
count: usize,
next: Option<Box<Entry>>,
}
@ -165,20 +169,20 @@ impl Table {
let index = key.hash() % (TABLE_SIZE as u64);
{
if self.items[index as uint].is_none() {
if self.items[index as usize].is_none() {
let mut entry = box Entry {
code: key,
count: 0,
next: None,
};
c.f(&mut *entry);
self.items[index as uint] = Some(entry);
self.items[index as usize] = Some(entry);
return;
}
}
{
let entry = self.items[index as uint].as_mut().unwrap();
let entry = self.items[index as usize].as_mut().unwrap();
if entry.code == key {
c.f(&mut **entry);
return;
@ -233,10 +237,10 @@ fn pack_symbol(c: u8) -> u8 {
}
fn unpack_symbol(c: u8) -> u8 {
TABLE[c as uint]
TABLE[c as usize]
}
fn generate_frequencies(mut input: &[u8], frame: uint) -> Table {
fn generate_frequencies(mut input: &[u8], frame: usize) -> Table {
let mut frequencies = Table::new();
if input.len() < frame { return frequencies; }
let mut code = Code(0);
@ -256,7 +260,7 @@ fn generate_frequencies(mut input: &[u8], frame: uint) -> Table {
frequencies
}
fn print_frequencies(frequencies: &Table, frame: uint) {
fn print_frequencies(frequencies: &Table, frame: usize) {
let mut vector = Vec::new();
for entry in frequencies.iter() {
vector.push((entry.count, entry.code));
@ -280,9 +284,9 @@ fn print_occurrences(frequencies: &mut Table, occurrence: &'static str) {
frequencies.lookup(Code::pack(occurrence), PrintCallback(occurrence))
}
fn get_sequence<R: Buffer>(r: &mut R, key: &str) -> Vec<u8> {
fn get_sequence<R: BufRead>(r: &mut R, key: &str) -> Vec<u8> {
let mut res = Vec::new();
for l in r.lines().map(|l| l.ok().unwrap())
for l in r.lines().map(|l| l.unwrap())
.skip_while(|l| key != &l[..key.len()]).skip(1)
{
res.push_all(l.trim().as_bytes());
@ -291,13 +295,13 @@ fn get_sequence<R: Buffer>(r: &mut R, key: &str) -> Vec<u8> {
}
fn main() {
let input = if std::env::var_os("RUST_BENCH").is_some() {
let fd = std::old_io::File::open(&Path::new("shootout-k-nucleotide.data"));
get_sequence(&mut std::old_io::BufferedReader::new(fd), ">THREE")
let input = if env::var_os("RUST_BENCH").is_some() {
let f = File::open("shootout-k-nucleotide.data").unwrap();
get_sequence(&mut io::BufReader::new(f), ">THREE")
} else {
let mut stdin = std::old_io::stdin();
let stdin = io::stdin();
let mut stdin = stdin.lock();
get_sequence(&mut *stdin, ">THREE")
get_sequence(&mut stdin, ">THREE")
};
let input = Arc::new(input);

View file

@ -13,9 +13,8 @@
#![feature(box_syntax)]
#![allow(non_snake_case)]
use std::old_io::BufferedReader;
use std::old_io::stdio::StdReader;
use std::old_io;
use std::io::prelude::*;
use std::io;
use std::iter::repeat;
use std::num::Int;
use std::env;
@ -37,7 +36,7 @@ use std::env;
//
// internal type of sudoku grids
type grid = Vec<Vec<u8> > ;
type grid = Vec<Vec<u8>>;
struct Sudoku {
grid: grid
@ -55,9 +54,11 @@ impl Sudoku {
return Sudoku::new(g)
}
pub fn read(mut reader: &mut BufferedReader<StdReader>) -> Sudoku {
pub fn read(reader: &mut BufRead) -> Sudoku {
/* assert first line is exactly "9,9" */
assert!(reader.read_line().unwrap() == "9,9".to_string());
let mut s = String::new();
reader.read_line(&mut s).unwrap();
assert_eq!(s, "9,9\n");
let mut g = repeat(vec![0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8])
.take(10).collect::<Vec<_>>();
@ -71,7 +72,7 @@ impl Sudoku {
if comps.len() == 3 {
let row = comps[0].parse::<u8>().unwrap();
let col = comps[1].parse::<u8>().unwrap();
g[row as uint][col as uint] = comps[2].parse().unwrap();
g[row as usize][col as usize] = comps[2].parse().unwrap();
}
else {
panic!("Invalid sudoku file");
@ -80,11 +81,11 @@ impl Sudoku {
return Sudoku::new(g)
}
pub fn write(&self, writer: &mut old_io::Writer) {
pub fn write(&self, writer: &mut Write) {
for row in 0u8..9u8 {
write!(writer, "{}", self.grid[row as uint][0]);
write!(writer, "{}", self.grid[row as usize][0]);
for col in 1u8..9u8 {
write!(writer, " {}", self.grid[row as uint][col as uint]);
write!(writer, " {}", self.grid[row as usize][col as usize]);
}
write!(writer, "\n");
}
@ -95,7 +96,7 @@ impl Sudoku {
let mut work: Vec<(u8, u8)> = Vec::new(); /* queue of uncolored fields */
for row in 0u8..9u8 {
for col in 0u8..9u8 {
let color = self.grid[row as uint][col as uint];
let color = self.grid[row as usize][col as usize];
if color == 0u8 {
work.push((row, col));
}
@ -107,7 +108,7 @@ impl Sudoku {
while ptr < end {
let (row, col) = work[ptr];
// is there another color to try?
let the_color = self.grid[row as uint][col as uint] +
let the_color = self.grid[row as usize][col as usize] +
(1 as u8);
if self.next_color(row, col, the_color) {
// yes: advance work list
@ -130,10 +131,10 @@ impl Sudoku {
// find first remaining color that is available
let next = avail.next();
self.grid[row as uint][col as uint] = next;
self.grid[row as usize][col as usize] = next;
return 0u8 != next;
}
self.grid[row as uint][col as uint] = 0u8;
self.grid[row as usize][col as usize] = 0u8;
return false;
}
@ -141,9 +142,9 @@ impl Sudoku {
fn drop_colors(&mut self, avail: &mut Colors, row: u8, col: u8) {
for idx in 0u8..9u8 {
/* check same column fields */
avail.remove(self.grid[idx as uint][col as uint]);
avail.remove(self.grid[idx as usize][col as usize]);
/* check same row fields */
avail.remove(self.grid[row as uint][idx as uint]);
avail.remove(self.grid[row as usize][idx as usize]);
}
// check same block fields
@ -151,7 +152,7 @@ impl Sudoku {
let col0 = (col / 3u8) * 3u8;
for alt_row in row0..row0 + 3u8 {
for alt_col in col0..col0 + 3u8 {
avail.remove(self.grid[alt_row as uint][alt_col as uint]);
avail.remove(self.grid[alt_row as usize][alt_col as usize]);
}
}
}
@ -165,7 +166,7 @@ static HEADS: u16 = (1u16 << 10) - 1; /* bits 9..0 */
impl Colors {
fn new(start_color: u8) -> Colors {
// Sets bits 9..start_color
let tails = !0u16 << start_color as uint;
let tails = !0u16 << start_color as usize;
return Colors(HEADS & tails);
}
@ -182,7 +183,7 @@ impl Colors {
fn remove(&mut self, color: u8) {
if color != 0u8 {
let Colors(val) = *self;
let mask = !(1u16 << color as uint);
let mask = !(1u16 << color as usize);
*self = Colors(val & mask);
}
}
@ -269,15 +270,16 @@ fn check_DEFAULT_SUDOKU_solution() {
}
fn main() {
let args = env::args();
let args = env::args();
let use_default = args.len() == 1;
let mut sudoku = if use_default {
Sudoku::from_vec(&DEFAULT_SUDOKU)
} else {
let mut stdin = old_io::stdin();
let mut stdin = stdin.lock();
Sudoku::read(&mut *stdin)
let stdin = io::stdin();
let mut locked = stdin.lock();
Sudoku::read(&mut locked)
};
sudoku.solve();
sudoku.write(&mut old_io::stdout());
let out = io::stdout();
sudoku.write(&mut out.lock());
}

View file

@ -10,4 +10,5 @@
fn main() {
let _ = std::old_io::stdin();
let _ = std::io::stdin();
}

View file

@ -8,10 +8,10 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-fast
use std::env;
use std::old_io;
use std::io::prelude::*;
use std::io;
use std::process::{Command, Stdio};
use std::str;
fn main() {
@ -25,17 +25,19 @@ fn main() {
fn parent() {
let args: Vec<String> = env::args().collect();
let mut p = old_io::process::Command::new(&args[0])
.arg("child").spawn().unwrap();
p.stdin.as_mut().unwrap().write_str("test1\ntest2\ntest3").unwrap();
let mut p = Command::new(&args[0]).arg("child")
.stdout(Stdio::capture())
.stdin(Stdio::capture())
.spawn().unwrap();
p.stdin.as_mut().unwrap().write_all(b"test1\ntest2\ntest3").unwrap();
let out = p.wait_with_output().unwrap();
assert!(out.status.success());
let s = str::from_utf8(&out.output).unwrap();
assert_eq!(s, "test1\n\ntest2\n\ntest3\n");
let s = str::from_utf8(&out.stdout).unwrap();
assert_eq!(s, "test1\ntest2\ntest3\n");
}
fn child() {
let mut stdin = old_io::stdin();
let mut stdin = io::stdin();
for line in stdin.lock().lines() {
println!("{}", line.unwrap());
}

View file

@ -8,11 +8,10 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::old_io::process;
use std::old_io::Command;
use std::old_io;
use std::env;
use std::io::prelude::*;
use std::io;
use std::process::{Command, Stdio};
fn main() {
let args: Vec<String> = env::args().collect();
@ -21,22 +20,23 @@ fn main() {
}
test();
}
fn child() {
old_io::stdout().write_line("foo").unwrap();
old_io::stderr().write_line("bar").unwrap();
let mut stdin = old_io::stdin();
assert_eq!(stdin.lock().read_line().err().unwrap().kind, old_io::EndOfFile);
writeln!(&mut io::stdout(), "foo").unwrap();
writeln!(&mut io::stderr(), "bar").unwrap();
let mut stdin = io::stdin();
let mut s = String::new();
stdin.lock().read_line(&mut s).unwrap();
assert_eq!(s.len(), 0);
}
fn test() {
let args: Vec<String> = env::args().collect();
let mut p = Command::new(&args[0]).arg("child")
.stdin(process::Ignored)
.stdout(process::Ignored)
.stderr(process::Ignored)
.stdin(Stdio::capture())
.stdout(Stdio::capture())
.stderr(Stdio::capture())
.spawn().unwrap();
assert!(p.wait().unwrap().success());
}

View file

@ -17,11 +17,13 @@
// A var moved into a proc, that has a mutable loan path should
// not trigger a misleading unused_mut warning.
use std::io::prelude::*;
use std::thread;
pub fn main() {
let mut stdin = std::old_io::stdin();
let mut stdin = std::io::stdin();
thread::spawn(move|| {
let _ = stdin.read_to_end();
});
let mut v = Vec::new();
let _ = stdin.read_to_end(&mut v);
}).join().ok().unwrap();
}

View file

@ -11,13 +11,13 @@
#![allow(unknown_features)]
#![feature(box_syntax)]
use std::old_io;
use std::io::{self, Write};
fn f(wr: &mut Writer) {
wr.write_str("hello").ok().expect("failed");
fn f(wr: &mut Write) {
wr.write_all(b"hello").ok().expect("failed");
}
fn main() {
let mut wr = box old_io::stdout() as Box<Writer + 'static>;
let mut wr = box io::stdout() as Box<Write>;
f(&mut wr);
}

View file

@ -8,9 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::old_io;
use std::io;
pub fn main() {
let stdout = &mut old_io::stdout() as &mut old_io::Writer;
let stdout = &mut io::stdout() as &mut io::Write;
stdout.write(b"Hello!");
}

View file

@ -16,30 +16,31 @@
// non-ASCII characters. The child process ensures all the strings are
// intact.
use std::old_io;
use std::old_io::fs;
use std::old_io::Command;
use std::io::prelude::*;
use std::io;
use std::fs;
use std::process::Command;
use std::os;
use std::env;
use std::old_path::Path;
use std::path::{Path, PathBuf};
fn main() {
let my_args = env::args().collect::<Vec<_>>();
let my_cwd = os::getcwd().unwrap();
let my_cwd = PathBuf::new(os::getcwd().unwrap().as_str().unwrap());
let my_env = env::vars().collect::<Vec<_>>();
let my_path = Path::new(os::self_exe_name().unwrap());
let my_dir = my_path.dir_path();
let my_ext = my_path.extension_str().unwrap_or("");
let my_path = PathBuf::new(os::self_exe_name().unwrap().as_str().unwrap());
let my_dir = my_path.parent().unwrap();
let my_ext = my_path.extension().and_then(|s| s.to_str()).unwrap_or("");
// some non-ASCII characters
let blah = "\u03c0\u042f\u97f3\u00e6\u221e";
let blah = "\u{3c0}\u{42f}\u{97f3}\u{e6}\u{221e}";
let child_name = "child";
let child_dir = format!("process-spawn-with-unicode-params-{}", blah);
// parameters sent to child / expected to be received from parent
let arg = blah;
let cwd = my_dir.join(Path::new(child_dir.clone()));
let cwd = my_dir.join(&child_dir);
let env = ("RUST_TEST_PROC_SPAWN_UNICODE".to_string(), blah.to_string());
// am I the parent or the child?
@ -47,24 +48,22 @@ fn main() {
let child_filestem = Path::new(child_name);
let child_filename = child_filestem.with_extension(my_ext);
let child_path = cwd.join(child_filename);
let child_path = cwd.join(&child_filename);
// make a separate directory for the child
drop(fs::mkdir(&cwd, old_io::USER_RWX).is_ok());
assert!(fs::copy(&my_path, &child_path).is_ok());
let mut my_env = my_env;
my_env.push(env);
let _ = fs::create_dir(&cwd);
fs::copy(&my_path, &child_path).unwrap();
// run child
let p = Command::new(&child_path)
.arg(arg)
.cwd(&cwd)
.env_set_all(&my_env)
.current_dir(&cwd)
.env(&env.0, &env.1)
.spawn().unwrap().wait_with_output().unwrap();
// display the output
assert!(old_io::stdout().write(&p.output).is_ok());
assert!(old_io::stderr().write(&p.error).is_ok());
io::stdout().write_all(&p.stdout).unwrap();
io::stderr().write_all(&p.stderr).unwrap();
// make sure the child succeeded
assert!(p.status.success());
@ -72,7 +71,7 @@ fn main() {
} else { // child
// check working directory (don't try to compare with `cwd` here!)
assert!(my_cwd.ends_with_path(&Path::new(child_dir)));
assert!(my_cwd.ends_with(&child_dir));
// check arguments
assert_eq!(&*my_args[1], arg);