diff options
| author | Brian Anderson <banderson@mozilla.com> | 2015-01-27 09:38:30 -0800 |
|---|---|---|
| committer | Brian Anderson <banderson@mozilla.com> | 2015-01-27 15:05:04 -0800 |
| commit | 71223050538939ed758fcd3b9114f71abff20bb2 (patch) | |
| tree | 43ddd18223904fa86601f1a0e16ebcbaddead270 /src/libstd/old_io | |
| parent | 3c172392cf0c86ffd1d7b39d3f44de98f77afc44 (diff) | |
| parent | 777435990e0e91df6b72ce80c9b6fa485eeb5daa (diff) | |
| download | rust-71223050538939ed758fcd3b9114f71abff20bb2.tar.gz rust-71223050538939ed758fcd3b9114f71abff20bb2.zip | |
Merge remote-tracking branch 'rust-lang/master'
Conflicts: src/libcore/cell.rs src/librustc_driver/test.rs src/libstd/old_io/net/tcp.rs src/libstd/old_io/process.rs
Diffstat (limited to 'src/libstd/old_io')
| -rw-r--r-- | src/libstd/old_io/buffered.rs | 697 | ||||
| -rw-r--r-- | src/libstd/old_io/comm_adapters.rs | 248 | ||||
| -rw-r--r-- | src/libstd/old_io/extensions.rs | 562 | ||||
| -rw-r--r-- | src/libstd/old_io/fs.rs | 1572 | ||||
| -rw-r--r-- | src/libstd/old_io/mem.rs | 759 | ||||
| -rw-r--r-- | src/libstd/old_io/mod.rs | 1961 | ||||
| -rw-r--r-- | src/libstd/old_io/net/addrinfo.rs | 137 | ||||
| -rw-r--r-- | src/libstd/old_io/net/ip.rs | 700 | ||||
| -rw-r--r-- | src/libstd/old_io/net/mod.rs | 46 | ||||
| -rw-r--r-- | src/libstd/old_io/net/pipe.rs | 869 | ||||
| -rw-r--r-- | src/libstd/old_io/net/tcp.rs | 1477 | ||||
| -rw-r--r-- | src/libstd/old_io/net/udp.rs | 459 | ||||
| -rw-r--r-- | src/libstd/old_io/pipe.rs | 139 | ||||
| -rw-r--r-- | src/libstd/old_io/process.rs | 1230 | ||||
| -rw-r--r-- | src/libstd/old_io/result.rs | 129 | ||||
| -rw-r--r-- | src/libstd/old_io/stdio.rs | 566 | ||||
| -rw-r--r-- | src/libstd/old_io/tempfile.rs | 182 | ||||
| -rw-r--r-- | src/libstd/old_io/test.rs | 175 | ||||
| -rw-r--r-- | src/libstd/old_io/timer.rs | 481 | ||||
| -rw-r--r-- | src/libstd/old_io/util.rs | 444 |
20 files changed, 12833 insertions, 0 deletions
diff --git a/src/libstd/old_io/buffered.rs b/src/libstd/old_io/buffered.rs new file mode 100644 index 00000000000..1590598c0b8 --- /dev/null +++ b/src/libstd/old_io/buffered.rs @@ -0,0 +1,697 @@ +// 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::{IteratorExt, ExactSizeIterator, repeat}; +use ops::Drop; +use option::Option; +use option::Option::{Some, None}; +use result::Result::Ok; +use slice::{SliceExt}; +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. +/// +/// # Example +/// +/// ```rust +/// use std::old_io::{BufferedReader, File}; +/// +/// 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: uint, + cap: uint, +} + +#[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: uint, 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(self.buf.as_mut_slice())); + self.pos = 0; + } + Ok(&self.buf[self.pos..self.cap]) + } + + fn consume(&mut self, amt: uint) { + self.pos += amt; + assert!(self.pos <= self.cap); + } +} + +impl<R: Reader> Reader for BufferedReader<R> { + fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { + if self.pos == self.cap && buf.len() >= self.buf.capacity() { + 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(buf, &available[..nread]); + 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. +/// +/// # Example +/// +/// ```rust +/// use std::old_io::{BufferedWriter, File}; +/// +/// 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> { + inner: Option<W>, + buf: Vec<u8>, + pos: uint +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<W> 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: uint, 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(dst, buf); + 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> { + inner: BufferedWriter<W>, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<W> 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>(BufferedWriter<W>); + +impl<W> InternalBufferedWriter<W> { + fn get_mut<'a>(&'a mut self) -> &'a mut BufferedWriter<W> { + let InternalBufferedWriter(ref mut w) = *self; + return w; + } +} + +impl<W: Reader> Reader for InternalBufferedWriter<W> { + fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { + 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. +/// +/// # Example +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// use std::old_io::{BufferedStream, File}; +/// +/// 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> { + inner: BufferedReader<InternalBufferedWriter<S>> +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<S> 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: uint, writer_cap: uint, 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: uint) { self.inner.consume(amt) } +} + +impl<S: Stream> Reader for BufferedStream<S> { + fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { + 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; + 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<uint> { + 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<uint>, + } + + impl Reader for ShortReader { + fn read(&mut self, _: &mut [u8]) -> old_io::IoResult<uint> { + 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!(a, &w.get_ref()[]); + 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<uint> { + 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 = [195u8, 159u8]; + let mut reader = BufferedReader::with_capacity(1, &buf[]); + assert_eq!(reader.read_char(), Ok('ß')); + } + + #[test] + fn test_chars() { + let buf = [195u8, 159u8, 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_fail] + 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); + }); + } +} diff --git a/src/libstd/old_io/comm_adapters.rs b/src/libstd/old_io/comm_adapters.rs new file mode 100644 index 00000000000..feb2ef6f4f3 --- /dev/null +++ b/src/libstd/old_io/comm_adapters.rs @@ -0,0 +1,248 @@ +// 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, SliceExt}; +use super::{Buffer, Reader, Writer, IoResult}; +use vec::Vec; + +/// Allows reading from a rx. +/// +/// # Example +/// +/// ``` +/// use std::sync::mpsc::channel; +/// use std::old_io::ChanReader; +/// +/// let (tx, rx) = channel(); +/// # drop(tx); +/// let mut reader = ChanReader::new(rx); +/// +/// let mut buf = [0u8; 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: uint, // 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: uint) { + self.pos += amt; + assert!(self.pos <= self.buf.len()); + } +} + +impl Reader for ChanReader { + fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { + 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(dst, &src[..count]); + 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. +/// +/// # Example +/// +/// ``` +/// # #![allow(unused_must_use)] +/// use std::sync::mpsc::channel; +/// use std::old_io::ChanWriter; +/// +/// 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; + use thread::Thread; + + #[test] + fn test_rx_reader() { + let (tx, rx) = channel(); + Thread::spawn(move|| { + tx.send(vec![1u8, 2u8]).unwrap(); + tx.send(vec![]).unwrap(); + tx.send(vec![3u8, 4u8]).unwrap(); + tx.send(vec![5u8, 6u8]).unwrap(); + tx.send(vec![7u8, 8u8]).unwrap(); + }); + + let mut reader = ChanReader::new(rx); + let mut buf = [0u8; 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(buf.as_mut_slice()) { + 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(buf.as_mut_slice()) { + 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![0u8, 0u8, 0u8, 42u8]; + let got = match Thread::scoped(move|| { rx.recv().unwrap() }).join() { + Ok(got) => got, + Err(_) => panic!(), + }; + assert_eq!(wanted, got); + + match writer.write_u8(1) { + Ok(..) => panic!(), + Err(e) => assert_eq!(e.kind, old_io::BrokenPipe), + } + } +} diff --git a/src/libstd/old_io/extensions.rs b/src/libstd/old_io/extensions.rs new file mode 100644 index 00000000000..826271a9f83 --- /dev/null +++ b/src/libstd/old_io/extensions.rs @@ -0,0 +1,562 @@ +// 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)] + +// 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 ptr::PtrExt; +use result::Result::{Ok, Err}; +use slice::{SliceExt, AsSlice}; + +/// 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: uint, 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 <= 8u); + match size { + 1u => f(&[n as u8]), + 2u => f(unsafe { & transmute::<_, [u8; 2]>((n as u16).to_le()) }), + 4u => f(unsafe { & transmute::<_, [u8; 4]>((n as u32).to_le()) }), + 8u => f(unsafe { & transmute::<_, [u8; 8]>(n.to_le()) }), + _ => { + + let mut bytes = vec!(); + let mut i = size; + let mut n = n; + while i > 0u { + bytes.push((n & 255_u64) as u8); + n >>= 8; + i -= 1u; + } + f(bytes.as_slice()) + } + } +} + +/// 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: uint, 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 <= 8u); + match size { + 1u => f(&[n as u8]), + 2u => f(unsafe { & transmute::<_, [u8; 2]>((n as u16).to_be()) }), + 4u => f(unsafe { & transmute::<_, [u8; 4]>((n as u32).to_be()) }), + 8u => f(unsafe { & transmute::<_, [u8; 8]>(n.to_be()) }), + _ => { + let mut bytes = vec!(); + let mut i = size; + while i > 0u { + let shift = (i - 1u) * 8u; + bytes.push((n >> shift) as u8); + i -= 1u; + } + f(bytes.as_slice()) + } + } +} + +/// 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: uint, size: uint) -> u64 { + use ptr::{copy_nonoverlapping_memory}; + use slice::SliceExt; + + assert!(size <= 8u); + + if data.len() - start < size { + panic!("index out of bounds"); + } + + let mut buf = [0u8; 8]; + unsafe { + let ptr = data.as_ptr().offset(start as int); + let out = buf.as_mut_ptr(); + copy_nonoverlapping_memory(out.offset((8 - size) as int), ptr, size); + (*(out as *const u64)).to_be() + } +} + +#[cfg(test)] +mod test { + use prelude::v1::*; + use old_io; + use old_io::{MemReader, BytesReader}; + + struct InitialZeroByteReader { + count: int, + } + + impl Reader for InitialZeroByteReader { + fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<uint> { + 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<uint> { + Err(old_io::standard_error(old_io::EndOfFile)) + } + } + + struct ErroringReader; + + impl Reader for ErroringReader { + fn read(&mut self, _: &mut [u8]) -> old_io::IoResult<uint> { + Err(old_io::standard_error(old_io::InvalidInput)) + } + } + + struct PartialReader { + count: int, + } + + impl Reader for PartialReader { + fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<uint> { + 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: int, + } + + impl Reader for ErroringLaterReader { + fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<uint> { + if self.count == 0 { + self.count = 1; + buf[0] = 10; + Ok(1) + } else { + Err(old_io::standard_error(old_io::InvalidInput)) + } + } + } + + struct ThreeChunkReader { + count: int, + } + + impl Reader for ThreeChunkReader { + fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<uint> { + 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!(bytes == vec!(10, 11, 12, 13)); + } + + #[test] + fn read_bytes_partial() { + let mut reader = PartialReader { + count: 0, + }; + let bytes = reader.read_exact(4).unwrap(); + assert!(bytes == vec!(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!(buf == vec![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!(buf == vec![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!(buf == vec![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!(buf == vec![8, 9, 10]); + } + + #[test] + fn read_to_end() { + let mut reader = ThreeChunkReader { + count: 0, + }; + let buf = reader.read_to_end().unwrap(); + assert!(buf == vec!(10, 11, 12, 13)); + } + + #[test] + #[should_fail] + fn read_to_end_error() { + let mut reader = ThreeChunkReader { + count: 0, + }; + let buf = reader.read_to_end().unwrap(); + assert!(buf == vec!(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.iter() { + writer.write_le_u64(*i).unwrap(); + } + + let mut reader = MemReader::new(writer); + for i in uints.iter() { + 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.iter() { + writer.write_be_u64(*i).unwrap(); + } + + let mut reader = MemReader::new(writer); + for i in uints.iter() { + 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.iter() { + writer.write_be_i32(*i).unwrap(); + } + + let mut reader = MemReader::new(writer); + for i in ints.iter() { + // 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.as_slice()).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 data = range(0u8, $stride*100+$start_index).collect::<Vec<_>>(); + let mut sum = 0u64; + $b.iter(|| { + let mut i = $start_index; + while i < data.len() { + sum += u64_from_be_bytes(data.as_slice(), 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); + } +} diff --git a/src/libstd/old_io/fs.rs b/src/libstd/old_io/fs.rs new file mode 100644 index 00000000000..a1ac5d2eab0 --- /dev/null +++ b/src/libstd/old_io/fs.rs @@ -0,0 +1,1572 @@ +// 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. +// +// ignore-lexer-test FIXME #15679 + +//! Synchronous File I/O +//! +//! This module provides a set of functions and traits for working +//! with regular files & directories on a filesystem. +//! +//! At the top-level of the module are a set of freestanding functions, associated +//! with various filesystem operations. They all operate on `Path` objects. +//! +//! All operations in this module, including those as part of `File` et al block +//! the task during execution. In the event of failure, all functions/methods +//! will return an `IoResult` type with an `Err` value. +//! +//! Also included in this module is an implementation block on the `Path` object +//! defined in `std::path::Path`. The impl adds useful methods about inspecting +//! the metadata of a file. This includes getting the `stat` information, +//! reading off particular bits of it, etc. +//! +//! # Example +//! +//! ```rust +//! # #![allow(unused_must_use)] +//! use std::old_io::fs::PathExtensions; +//! use std::old_io::{File, fs}; +//! +//! let path = Path::new("foo.txt"); +//! +//! // create the file, whether it exists or not +//! let mut file = File::create(&path); +//! file.write(b"foobar"); +//! # drop(file); +//! +//! // open the file in read-only mode +//! let mut file = File::open(&path); +//! file.read_to_end(); +//! +//! println!("{}", path.stat().unwrap().size); +//! # drop(file); +//! fs::unlink(&path); +//! ``` + +use clone::Clone; +use old_io::standard_error; +use old_io::{FilePermission, Write, Open, FileAccess, FileMode, FileType}; +use old_io::{IoResult, IoError, InvalidInput}; +use old_io::{FileStat, SeekStyle, Seek, Writer, Reader}; +use old_io::{Read, Truncate, ReadWrite, Append}; +use old_io::UpdateIoError; +use old_io; +use iter::{Iterator, Extend}; +use option::Option; +use option::Option::{Some, None}; +use path::{Path, GenericPath}; +use path; +use result::Result::{Err, Ok}; +use slice::SliceExt; +use string::String; +use vec::Vec; + +use sys::fs as fs_imp; +use sys_common; + +/// Unconstrained file access type that exposes read and write operations +/// +/// Can be constructed via `File::open()`, `File::create()`, and +/// `File::open_mode()`. +/// +/// # Error +/// +/// This type will return errors as an `IoResult<T>` if operations are +/// attempted against it for which its underlying file descriptor was not +/// configured at creation time, via the `FileAccess` parameter to +/// `File::open_mode()`. +pub struct File { + fd: fs_imp::FileDesc, + path: Path, + last_nread: int, +} + +impl sys_common::AsInner<fs_imp::FileDesc> for File { + fn as_inner(&self) -> &fs_imp::FileDesc { + &self.fd + } +} + +impl File { + /// Open a file at `path` in the mode specified by the `mode` and `access` + /// arguments + /// + /// # Example + /// + /// ```rust,should_fail + /// use std::old_io::{File, Open, ReadWrite}; + /// + /// let p = Path::new("/some/file/path.txt"); + /// + /// let file = match File::open_mode(&p, Open, ReadWrite) { + /// Ok(f) => f, + /// Err(e) => panic!("file error: {}", e), + /// }; + /// // do some stuff with that file + /// + /// // the file will be closed at the end of this block + /// ``` + /// + /// `FileMode` and `FileAccess` provide information about the permissions + /// context in which a given stream is created. More information about them + /// can be found in `std::io`'s docs. If a file is opened with `Write` + /// or `ReadWrite` access, then it will be created if it does not already + /// exist. + /// + /// Note that, with this function, a `File` is returned regardless of the + /// access-limitations indicated by `FileAccess` (e.g. calling `write` on a + /// `File` opened as `Read` will return an error at runtime). + /// + /// # Error + /// + /// This function will return an error under a number of different + /// circumstances, to include but not limited to: + /// + /// * Opening a file that does not exist with `Read` access. + /// * Attempting to open a file with a `FileAccess` that the user lacks + /// permissions for + /// * Filesystem-level errors (full disk, etc) + pub fn open_mode(path: &Path, + mode: FileMode, + access: FileAccess) -> IoResult<File> { + fs_imp::open(path, mode, access).and_then(|fd| { + // On *BSD systems, we can open a directory as a file and read from it: + // fd=open("/tmp", O_RDONLY); read(fd, buf, N); + // due to an old tradition before the introduction of opendir(3). + // We explicitly reject it because there are few use cases. + if cfg!(not(any(windows, target_os = "linux", target_os = "android"))) && + try!(fd.fstat()).kind == FileType::Directory { + Err(IoError { + kind: InvalidInput, + desc: "is a directory", + detail: None + }) + } else { + Ok(File { + path: path.clone(), + fd: fd, + last_nread: -1 + }) + } + }).update_err("couldn't open path as file", |e| { + format!("{}; path={}; mode={}; access={}", e, path.display(), + mode_string(mode), access_string(access)) + }) + } + + /// Attempts to open a file in read-only mode. This function is equivalent to + /// `File::open_mode(path, Open, Read)`, and will raise all of the same + /// errors that `File::open_mode` does. + /// + /// For more information, see the `File::open_mode` function. + /// + /// # Example + /// + /// ```rust + /// use std::old_io::File; + /// + /// let contents = File::open(&Path::new("foo.txt")).read_to_end(); + /// ``` + pub fn open(path: &Path) -> IoResult<File> { + File::open_mode(path, Open, Read) + } + + /// Attempts to create a file in write-only mode. This function is + /// equivalent to `File::open_mode(path, Truncate, Write)`, and will + /// raise all of the same errors that `File::open_mode` does. + /// + /// For more information, see the `File::open_mode` function. + /// + /// # Example + /// + /// ```rust + /// # #![allow(unused_must_use)] + /// use std::old_io::File; + /// + /// let mut f = File::create(&Path::new("foo.txt")); + /// f.write(b"This is a sample file"); + /// # drop(f); + /// # ::std::old_io::fs::unlink(&Path::new("foo.txt")); + /// ``` + pub fn create(path: &Path) -> IoResult<File> { + File::open_mode(path, Truncate, Write) + .update_desc("couldn't create file") + } + + /// Returns the original path that was used to open this file. + pub fn path<'a>(&'a self) -> &'a Path { + &self.path + } + + /// Synchronizes all modifications to this file to its permanent storage + /// device. This will flush any internal buffers necessary to perform this + /// operation. + pub fn fsync(&mut self) -> IoResult<()> { + self.fd.fsync() + .update_err("couldn't fsync file", + |e| format!("{}; path={}", e, self.path.display())) + } + + /// This function is similar to `fsync`, except that it may not synchronize + /// file metadata to the filesystem. This is intended for use cases that + /// must synchronize content, but don't need the metadata on disk. The goal + /// of this method is to reduce disk operations. + pub fn datasync(&mut self) -> IoResult<()> { + self.fd.datasync() + .update_err("couldn't datasync file", + |e| format!("{}; path={}", e, self.path.display())) + } + + /// Either truncates or extends the underlying file, updating the size of + /// this file to become `size`. This is equivalent to unix's `truncate` + /// function. + /// + /// If the `size` is less than the current file's size, then the file will + /// be shrunk. If it is greater than the current file's size, then the file + /// will be extended to `size` and have all of the intermediate data filled + /// in with 0s. + pub fn truncate(&mut self, size: i64) -> IoResult<()> { + self.fd.truncate(size) + .update_err("couldn't truncate file", |e| + format!("{}; path={}; size={}", e, self.path.display(), size)) + } + + /// Returns true if the stream has reached the end of the file. + /// + /// If true, then this file will no longer continue to return data via + /// `read`. + /// + /// Note that the operating system will not return an `EOF` indicator + /// until you have attempted to read past the end of the file, so if + /// you've read _exactly_ the number of bytes in the file, this will + /// return `false`, not `true`. + pub fn eof(&self) -> bool { + self.last_nread == 0 + } + + /// Queries information about the underlying file. + pub fn stat(&self) -> IoResult<FileStat> { + self.fd.fstat() + .update_err("couldn't fstat file", |e| + format!("{}; path={}", e, self.path.display())) + } +} + +/// Unlink a file from the underlying filesystem. +/// +/// # Example +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// use std::old_io::fs; +/// +/// let p = Path::new("/some/file/path.txt"); +/// fs::unlink(&p); +/// ``` +/// +/// Note that, just because an unlink call was successful, it is not +/// guaranteed that a file is immediately deleted (e.g. depending on +/// platform, other open file descriptors may prevent immediate removal) +/// +/// # Error +/// +/// This function will return an error if `path` points to a directory, if the +/// user lacks permissions to remove the file, or if some other filesystem-level +/// error occurs. +pub fn unlink(path: &Path) -> IoResult<()> { + fs_imp::unlink(path) + .update_err("couldn't unlink path", |e| + format!("{}; path={}", e, path.display())) +} + +/// Given a path, query the file system to get information about a file, +/// directory, etc. This function will traverse symlinks to query +/// information about the destination file. +/// +/// # Example +/// +/// ```rust +/// use std::old_io::fs; +/// +/// let p = Path::new("/some/file/path.txt"); +/// match fs::stat(&p) { +/// Ok(stat) => { /* ... */ } +/// Err(e) => { /* handle error */ } +/// } +/// ``` +/// +/// # Error +/// +/// This function will return an error if the user lacks the requisite permissions +/// to perform a `stat` call on the given `path` or if there is no entry in the +/// filesystem at the provided path. +pub fn stat(path: &Path) -> IoResult<FileStat> { + fs_imp::stat(path) + .update_err("couldn't stat path", |e| + format!("{}; path={}", e, path.display())) +} + +/// Perform the same operation as the `stat` function, except that this +/// function does not traverse through symlinks. This will return +/// information about the symlink file instead of the file that it points +/// to. +/// +/// # Error +/// +/// See `stat` +pub fn lstat(path: &Path) -> IoResult<FileStat> { + fs_imp::lstat(path) + .update_err("couldn't lstat path", |e| + format!("{}; path={}", e, path.display())) +} + +/// Rename a file or directory to a new name. +/// +/// # Example +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// use std::old_io::fs; +/// +/// fs::rename(&Path::new("foo"), &Path::new("bar")); +/// ``` +/// +/// # Error +/// +/// This function will return an error if the provided `from` doesn't exist, if +/// the process lacks permissions to view the contents, or if some other +/// intermittent I/O error occurs. +pub fn rename(from: &Path, to: &Path) -> IoResult<()> { + fs_imp::rename(from, to) + .update_err("couldn't rename path", |e| + format!("{}; from={:?}; to={:?}", e, from.display(), to.display())) +} + +/// Copies the contents of one file to another. This function will also +/// copy the permission bits of the original file to the destination file. +/// +/// Note that if `from` and `to` both point to the same file, then the file +/// will likely get truncated by this operation. +/// +/// # Example +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// use std::old_io::fs; +/// +/// fs::copy(&Path::new("foo.txt"), &Path::new("bar.txt")); +/// ``` +/// +/// # Error +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * The `from` path is not a file +/// * The `from` file does not exist +/// * The current process does not have the permission rights to access +/// `from` or write `to` +/// +/// Note that this copy is not atomic in that once the destination is +/// ensured to not exist, there is nothing preventing the destination from +/// being created and then destroyed by this operation. +pub fn copy(from: &Path, to: &Path) -> IoResult<()> { + fn update_err<T>(result: IoResult<T>, from: &Path, to: &Path) -> IoResult<T> { + result.update_err("couldn't copy path", |e| { + format!("{}; from={:?}; to={:?}", e, from.display(), to.display()) + }) + } + + if !from.is_file() { + return update_err(Err(IoError { + kind: old_io::MismatchedFileTypeForOperation, + desc: "the source path is not an existing file", + detail: None + }), from, to) + } + + let mut reader = try!(File::open(from)); + let mut writer = try!(File::create(to)); + + try!(update_err(super::util::copy(&mut reader, &mut writer), from, to)); + + chmod(to, try!(update_err(from.stat(), from, to)).perm) +} + +/// Changes the permission mode bits found on a file or a directory. This +/// function takes a mask from the `io` module +/// +/// # Example +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// use std::old_io; +/// use std::old_io::fs; +/// +/// fs::chmod(&Path::new("file.txt"), old_io::USER_FILE); +/// fs::chmod(&Path::new("file.txt"), old_io::USER_READ | old_io::USER_WRITE); +/// fs::chmod(&Path::new("dir"), old_io::USER_DIR); +/// fs::chmod(&Path::new("file.exe"), old_io::USER_EXEC); +/// ``` +/// +/// # Error +/// +/// This function will return an error if the provided `path` doesn't exist, if +/// the process lacks permissions to change the attributes of the file, or if +/// some other I/O error is encountered. +pub fn chmod(path: &Path, mode: old_io::FilePermission) -> IoResult<()> { + fs_imp::chmod(path, mode.bits() as uint) + .update_err("couldn't chmod path", |e| + format!("{}; path={}; mode={:?}", e, path.display(), mode)) +} + +/// Change the user and group owners of a file at the specified path. +pub fn chown(path: &Path, uid: int, gid: int) -> IoResult<()> { + fs_imp::chown(path, uid, gid) + .update_err("couldn't chown path", |e| + format!("{}; path={}; uid={}; gid={}", e, path.display(), uid, gid)) +} + +/// Creates a new hard link on the filesystem. The `dst` path will be a +/// link pointing to the `src` path. Note that systems often require these +/// two paths to both be located on the same filesystem. +pub fn link(src: &Path, dst: &Path) -> IoResult<()> { + fs_imp::link(src, dst) + .update_err("couldn't link path", |e| + format!("{}; src={:?}; dest={:?}", e, src.display(), dst.display())) +} + +/// Creates a new symbolic link on the filesystem. The `dst` path will be a +/// symlink pointing to the `src` path. +pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> { + fs_imp::symlink(src, dst) + .update_err("couldn't symlink path", |e| + format!("{}; src={:?}; dest={:?}", e, src.display(), dst.display())) +} + +/// Reads a symlink, returning the file that the symlink points to. +/// +/// # Error +/// +/// This function will return an error on failure. Failure conditions include +/// reading a file that does not exist or reading a file that is not a symlink. +pub fn readlink(path: &Path) -> IoResult<Path> { + fs_imp::readlink(path) + .update_err("couldn't resolve symlink for path", |e| + format!("{}; path={}", e, path.display())) +} + +/// Create a new, empty directory at the provided path +/// +/// # Example +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// use std::old_io; +/// use std::old_io::fs; +/// +/// let p = Path::new("/some/dir"); +/// fs::mkdir(&p, old_io::USER_RWX); +/// ``` +/// +/// # Error +/// +/// This function will return an error if the user lacks permissions to make a +/// new directory at the provided `path`, or if the directory already exists. +pub fn mkdir(path: &Path, mode: FilePermission) -> IoResult<()> { + fs_imp::mkdir(path, mode.bits() as uint) + .update_err("couldn't create directory", |e| + format!("{}; path={}; mode={}", e, path.display(), mode)) +} + +/// Remove an existing, empty directory +/// +/// # Example +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// use std::old_io::fs; +/// +/// let p = Path::new("/some/dir"); +/// fs::rmdir(&p); +/// ``` +/// +/// # Error +/// +/// This function will return an error if the user lacks permissions to remove +/// the directory at the provided `path`, or if the directory isn't empty. +pub fn rmdir(path: &Path) -> IoResult<()> { + fs_imp::rmdir(path) + .update_err("couldn't remove directory", |e| + format!("{}; path={}", e, path.display())) +} + +/// Retrieve a vector containing all entries within a provided directory +/// +/// # Example +/// +/// ```rust +/// use std::old_io::fs::PathExtensions; +/// use std::old_io::fs; +/// use std::old_io; +/// +/// // one possible implementation of fs::walk_dir only visiting files +/// fn visit_dirs<F>(dir: &Path, cb: &mut F) -> old_io::IoResult<()> where +/// F: FnMut(&Path), +/// { +/// if dir.is_dir() { +/// let contents = try!(fs::readdir(dir)); +/// for entry in contents.iter() { +/// if entry.is_dir() { +/// try!(visit_dirs(entry, cb)); +/// } else { +/// (*cb)(entry); +/// } +/// } +/// Ok(()) +/// } else { +/// Err(old_io::standard_error(old_io::InvalidInput)) +/// } +/// } +/// ``` +/// +/// # Error +/// +/// This function will return an error if the provided `path` doesn't exist, if +/// the process lacks permissions to view the contents or if the `path` points +/// at a non-directory file +pub fn readdir(path: &Path) -> IoResult<Vec<Path>> { + fs_imp::readdir(path) + .update_err("couldn't read directory", + |e| format!("{}; path={}", e, path.display())) +} + +/// Returns an iterator that will recursively walk the directory structure +/// rooted at `path`. The path given will not be iterated over, and this will +/// perform iteration in some top-down order. The contents of unreadable +/// subdirectories are ignored. +pub fn walk_dir(path: &Path) -> IoResult<Directories> { + Ok(Directories { + stack: try!(readdir(path).update_err("couldn't walk directory", + |e| format!("{}; path={}", e, path.display()))) + }) +} + +/// An iterator that walks over a directory +#[derive(Clone)] +pub struct Directories { + stack: Vec<Path>, +} + +impl Iterator for Directories { + type Item = Path; + + fn next(&mut self) -> Option<Path> { + match self.stack.pop() { + Some(path) => { + if path.is_dir() { + match readdir(&path) { + Ok(dirs) => { self.stack.extend(dirs.into_iter()); } + Err(..) => {} + } + } + Some(path) + } + None => None + } + } +} + +/// Recursively create a directory and all of its parent components if they +/// are missing. +/// +/// # Error +/// +/// See `fs::mkdir`. +pub fn mkdir_recursive(path: &Path, mode: FilePermission) -> IoResult<()> { + // tjc: if directory exists but with different permissions, + // should we return false? + if path.is_dir() { + return Ok(()) + } + + let mut comps = path.components(); + let mut curpath = path.root_path().unwrap_or(Path::new(".")); + + for c in comps { + curpath.push(c); + + let result = mkdir(&curpath, mode) + .update_err("couldn't recursively mkdir", + |e| format!("{}; path={}", e, path.display())); + + match result { + Err(mkdir_err) => { + // already exists ? + if try!(stat(&curpath)).kind != FileType::Directory { + return Err(mkdir_err); + } + } + Ok(()) => () + } + } + + Ok(()) +} + +/// Removes a directory at this path, after removing all its contents. Use +/// carefully! +/// +/// # Error +/// +/// See `file::unlink` and `fs::readdir` +pub fn rmdir_recursive(path: &Path) -> IoResult<()> { + let mut rm_stack = Vec::new(); + rm_stack.push(path.clone()); + + fn rmdir_failed(err: &IoError, path: &Path) -> String { + format!("rmdir_recursive failed; path={}; cause={}", + path.display(), err) + } + + fn update_err<T>(err: IoResult<T>, path: &Path) -> IoResult<T> { + err.update_err("couldn't recursively rmdir", + |e| rmdir_failed(e, path)) + } + + while !rm_stack.is_empty() { + let children = try!(readdir(rm_stack.last().unwrap()) + .update_detail(|e| rmdir_failed(e, path))); + + let mut has_child_dir = false; + + // delete all regular files in the way and push subdirs + // on the stack + for child in children.into_iter() { + // FIXME(#12795) we should use lstat in all cases + let child_type = match cfg!(windows) { + true => try!(update_err(stat(&child), path)), + false => try!(update_err(lstat(&child), path)) + }; + + if child_type.kind == FileType::Directory { + rm_stack.push(child); + has_child_dir = true; + } else { + // we can carry on safely if the file is already gone + // (eg: deleted by someone else since readdir) + match update_err(unlink(&child), path) { + Ok(()) => (), + Err(ref e) if e.kind == old_io::FileNotFound => (), + Err(e) => return Err(e) + } + } + } + + // if no subdir was found, let's pop and delete + if !has_child_dir { + let result = update_err(rmdir(&rm_stack.pop().unwrap()), path); + match result { + Ok(()) => (), + Err(ref e) if e.kind == old_io::FileNotFound => (), + Err(e) => return Err(e) + } + } + } + + Ok(()) +} + +/// Changes the timestamps for a file's last modification and access time. +/// The file at the path specified will have its last access time set to +/// `atime` and its modification time set to `mtime`. The times specified should +/// be in milliseconds. +// FIXME(#10301) these arguments should not be u64 +pub fn change_file_times(path: &Path, atime: u64, mtime: u64) -> IoResult<()> { + fs_imp::utime(path, atime, mtime) + .update_err("couldn't change_file_times", |e| + format!("{}; path={}", e, path.display())) +} + +impl Reader for File { + fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { + fn update_err<T>(result: IoResult<T>, file: &File) -> IoResult<T> { + result.update_err("couldn't read file", + |e| format!("{}; path={}", + e, file.path.display())) + } + + let result = update_err(self.fd.read(buf), self); + + match result { + Ok(read) => { + self.last_nread = read as int; + match read { + 0 => update_err(Err(standard_error(old_io::EndOfFile)), self), + _ => Ok(read as uint) + } + }, + Err(e) => Err(e) + } + } +} + +impl Writer for File { + fn write_all(&mut self, buf: &[u8]) -> IoResult<()> { + self.fd.write(buf) + .update_err("couldn't write to file", + |e| format!("{}; path={}", e, self.path.display())) + } +} + +impl Seek for File { + fn tell(&self) -> IoResult<u64> { + self.fd.tell() + .update_err("couldn't retrieve file cursor (`tell`)", + |e| format!("{}; path={}", e, self.path.display())) + } + + fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> { + let err = match self.fd.seek(pos, style) { + Ok(_) => { + // successful seek resets EOF indicator + self.last_nread = -1; + Ok(()) + } + Err(e) => Err(e), + }; + err.update_err("couldn't seek in file", + |e| format!("{}; path={}", e, self.path.display())) + } +} + +/// Utility methods for paths. +pub trait PathExtensions { + /// Get information on the file, directory, etc at this path. + /// + /// Consult the `fs::stat` documentation for more info. + /// + /// This call preserves identical runtime/error semantics with `file::stat`. + fn stat(&self) -> IoResult<FileStat>; + + /// Get information on the file, directory, etc at this path, not following + /// symlinks. + /// + /// Consult the `fs::lstat` documentation for more info. + /// + /// This call preserves identical runtime/error semantics with `file::lstat`. + fn lstat(&self) -> IoResult<FileStat>; + + /// Boolean value indicator whether the underlying file exists on the local + /// filesystem. Returns false in exactly the cases where `fs::stat` fails. + fn exists(&self) -> bool; + + /// Whether the underlying implementation (be it a file path, or something + /// else) points at a "regular file" on the FS. Will return false for paths + /// to non-existent locations or directories or other non-regular files + /// (named pipes, etc). Follows links when making this determination. + fn is_file(&self) -> bool; + + /// Whether the underlying implementation (be it a file path, or something + /// else) is pointing at a directory in the underlying FS. Will return + /// false for paths to non-existent locations or if the item is not a + /// directory (eg files, named pipes, etc). Follows links when making this + /// determination. + fn is_dir(&self) -> bool; +} + +impl PathExtensions for path::Path { + fn stat(&self) -> IoResult<FileStat> { stat(self) } + fn lstat(&self) -> IoResult<FileStat> { lstat(self) } + fn exists(&self) -> bool { + self.stat().is_ok() + } + fn is_file(&self) -> bool { + match self.stat() { + Ok(s) => s.kind == FileType::RegularFile, + Err(..) => false + } + } + fn is_dir(&self) -> bool { + match self.stat() { + Ok(s) => s.kind == FileType::Directory, + Err(..) => false + } + } +} + +fn mode_string(mode: FileMode) -> &'static str { + match mode { + super::Open => "open", + super::Append => "append", + super::Truncate => "truncate" + } +} + +fn access_string(access: FileAccess) -> &'static str { + match access { + super::Read => "read", + super::Write => "write", + super::ReadWrite => "readwrite" + } +} + +#[cfg(test)] +#[allow(unused_imports)] +#[allow(unused_variables)] +#[allow(unused_mut)] +mod test { + use prelude::v1::*; + use old_io::{SeekSet, SeekCur, SeekEnd, Read, Open, ReadWrite, FileType}; + use old_io; + use str; + use old_io::fs::*; + + macro_rules! check { ($e:expr) => ( + match $e { + Ok(t) => t, + Err(e) => panic!("{} failed with: {:?}", stringify!($e), e), + } + ) } + + macro_rules! error { ($e:expr, $s:expr) => ( + match $e { + Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s), + Err(ref err) => assert!(err.to_string().contains($s.as_slice()), + format!("`{}` did not contain `{}`", err, $s)) + } + ) } + + pub struct TempDir(Path); + + impl TempDir { + fn join(&self, path: &str) -> Path { + let TempDir(ref p) = *self; + p.join(path) + } + + fn path<'a>(&'a self) -> &'a Path { + let TempDir(ref p) = *self; + p + } + } + + impl Drop for TempDir { + fn drop(&mut self) { + // Gee, seeing how we're testing the fs module I sure hope that we + // at least implement this correctly! + let TempDir(ref p) = *self; + check!(old_io::fs::rmdir_recursive(p)); + } + } + + pub fn tmpdir() -> TempDir { + use os; + use rand; + let ret = os::tmpdir().join(format!("rust-{}", rand::random::<u32>())); + check!(old_io::fs::mkdir(&ret, old_io::USER_RWX)); + TempDir(ret) + } + + #[test] + fn file_test_io_smoke_test() { + let message = "it's alright. have a good time"; + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_rt_io_file_test.txt"); + { + let mut write_stream = File::open_mode(filename, Open, ReadWrite); + check!(write_stream.write(message.as_bytes())); + } + { + let mut read_stream = File::open_mode(filename, Open, Read); + let mut read_buf = [0; 1028]; + let read_str = match check!(read_stream.read(&mut read_buf)) { + -1|0 => panic!("shouldn't happen"), + n => str::from_utf8(&read_buf[..n]).unwrap().to_string() + }; + assert_eq!(read_str.as_slice(), message); + } + check!(unlink(filename)); + } + + #[test] + fn invalid_path_raises() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_that_does_not_exist.txt"); + let result = File::open_mode(filename, Open, Read); + + error!(result, "couldn't open path as file"); + if cfg!(unix) { + error!(result, "no such file or directory"); + } + error!(result, format!("path={}; mode=open; access=read", filename.display())); + } + + #[test] + fn file_test_iounlinking_invalid_path_should_raise_condition() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt"); + + let result = unlink(filename); + + error!(result, "couldn't unlink path"); + if cfg!(unix) { + error!(result, "no such file or directory"); + } + error!(result, format!("path={}", filename.display())); + } + + #[test] + fn file_test_io_non_positional_read() { + let message: &str = "ten-four"; + let mut read_mem = [0; 8]; + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_rt_io_file_test_positional.txt"); + { + let mut rw_stream = File::open_mode(filename, Open, ReadWrite); + check!(rw_stream.write(message.as_bytes())); + } + { + let mut read_stream = File::open_mode(filename, Open, Read); + { + let read_buf = read_mem.slice_mut(0, 4); + check!(read_stream.read(read_buf)); + } + { + let read_buf = read_mem.slice_mut(4, 8); + check!(read_stream.read(read_buf)); + } + } + check!(unlink(filename)); + let read_str = str::from_utf8(&read_mem).unwrap(); + assert_eq!(read_str, message); + } + + #[test] + fn file_test_io_seek_and_tell_smoke_test() { + let message = "ten-four"; + let mut read_mem = [0; 4]; + let set_cursor = 4 as u64; + let mut tell_pos_pre_read; + let mut tell_pos_post_read; + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_rt_io_file_test_seeking.txt"); + { + let mut rw_stream = File::open_mode(filename, Open, ReadWrite); + check!(rw_stream.write(message.as_bytes())); + } + { + let mut read_stream = File::open_mode(filename, Open, Read); + check!(read_stream.seek(set_cursor as i64, SeekSet)); + tell_pos_pre_read = check!(read_stream.tell()); + check!(read_stream.read(&mut read_mem)); + tell_pos_post_read = check!(read_stream.tell()); + } + check!(unlink(filename)); + let read_str = str::from_utf8(&read_mem).unwrap(); + assert_eq!(read_str, message.slice(4, 8)); + assert_eq!(tell_pos_pre_read, set_cursor); + assert_eq!(tell_pos_post_read, message.len() as u64); + } + + #[test] + fn file_test_io_seek_and_write() { + let initial_msg = "food-is-yummy"; + let overwrite_msg = "-the-bar!!"; + let final_msg = "foo-the-bar!!"; + let seek_idx = 3i; + let mut read_mem = [0; 13]; + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_rt_io_file_test_seek_and_write.txt"); + { + let mut rw_stream = File::open_mode(filename, Open, ReadWrite); + check!(rw_stream.write(initial_msg.as_bytes())); + check!(rw_stream.seek(seek_idx as i64, SeekSet)); + check!(rw_stream.write(overwrite_msg.as_bytes())); + } + { + let mut read_stream = File::open_mode(filename, Open, Read); + check!(read_stream.read(&mut read_mem)); + } + check!(unlink(filename)); + let read_str = str::from_utf8(&read_mem).unwrap(); + assert!(read_str == final_msg); + } + + #[test] + fn file_test_io_seek_shakedown() { + use str; // 01234567890123 + let initial_msg = "qwer-asdf-zxcv"; + let chunk_one: &str = "qwer"; + let chunk_two: &str = "asdf"; + let chunk_three: &str = "zxcv"; + let mut read_mem = [0; 4]; + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_rt_io_file_test_seek_shakedown.txt"); + { + let mut rw_stream = File::open_mode(filename, Open, ReadWrite); + check!(rw_stream.write(initial_msg.as_bytes())); + } + { + let mut read_stream = File::open_mode(filename, Open, Read); + + check!(read_stream.seek(-4, SeekEnd)); + check!(read_stream.read(&mut read_mem)); + assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_three); + + check!(read_stream.seek(-9, SeekCur)); + check!(read_stream.read(&mut read_mem)); + assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_two); + + check!(read_stream.seek(0, SeekSet)); + check!(read_stream.read(&mut read_mem)); + assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_one); + } + check!(unlink(filename)); + } + + #[test] + fn file_test_stat_is_correct_on_is_file() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_stat_correct_on_is_file.txt"); + { + let mut fs = check!(File::open_mode(filename, Open, ReadWrite)); + let msg = "hw"; + fs.write(msg.as_bytes()).unwrap(); + + let fstat_res = check!(fs.stat()); + assert_eq!(fstat_res.kind, FileType::RegularFile); + } + let stat_res_fn = check!(stat(filename)); + assert_eq!(stat_res_fn.kind, FileType::RegularFile); + let stat_res_meth = check!(filename.stat()); + assert_eq!(stat_res_meth.kind, FileType::RegularFile); + check!(unlink(filename)); + } + + #[test] + fn file_test_stat_is_correct_on_is_dir() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_stat_correct_on_is_dir"); + check!(mkdir(filename, old_io::USER_RWX)); + let stat_res_fn = check!(stat(filename)); + assert!(stat_res_fn.kind == FileType::Directory); + let stat_res_meth = check!(filename.stat()); + assert!(stat_res_meth.kind == FileType::Directory); + check!(rmdir(filename)); + } + + #[test] + fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { + let tmpdir = tmpdir(); + let dir = &tmpdir.join("fileinfo_false_on_dir"); + check!(mkdir(dir, old_io::USER_RWX)); + assert!(dir.is_file() == false); + check!(rmdir(dir)); + } + + #[test] + fn file_test_fileinfo_check_exists_before_and_after_file_creation() { + let tmpdir = tmpdir(); + let file = &tmpdir.join("fileinfo_check_exists_b_and_a.txt"); + check!(File::create(file).write(b"foo")); + assert!(file.exists()); + check!(unlink(file)); + assert!(!file.exists()); + } + + #[test] + fn file_test_directoryinfo_check_exists_before_and_after_mkdir() { + let tmpdir = tmpdir(); + let dir = &tmpdir.join("before_and_after_dir"); + assert!(!dir.exists()); + check!(mkdir(dir, old_io::USER_RWX)); + assert!(dir.exists()); + assert!(dir.is_dir()); + check!(rmdir(dir)); + assert!(!dir.exists()); + } + + #[test] + fn file_test_directoryinfo_readdir() { + use str; + let tmpdir = tmpdir(); + let dir = &tmpdir.join("di_readdir"); + check!(mkdir(dir, old_io::USER_RWX)); + let prefix = "foo"; + for n in range(0i,3) { + let f = dir.join(format!("{}.txt", n)); + let mut w = check!(File::create(&f)); + let msg_str = format!("{}{}", prefix, n.to_string()); + let msg = msg_str.as_bytes(); + check!(w.write(msg)); + } + let files = check!(readdir(dir)); + let mut mem = [0u8; 4]; + for f in files.iter() { + { + let n = f.filestem_str(); + check!(File::open(f).read(&mut mem)); + let read_str = str::from_utf8(&mem).unwrap(); + let expected = match n { + None|Some("") => panic!("really shouldn't happen.."), + Some(n) => format!("{}{}", prefix, n), + }; + assert_eq!(expected.as_slice(), read_str); + } + check!(unlink(f)); + } + check!(rmdir(dir)); + } + + #[test] + fn file_test_walk_dir() { + let tmpdir = tmpdir(); + let dir = &tmpdir.join("walk_dir"); + check!(mkdir(dir, old_io::USER_RWX)); + + let dir1 = &dir.join("01/02/03"); + check!(mkdir_recursive(dir1, old_io::USER_RWX)); + check!(File::create(&dir1.join("04"))); + + let dir2 = &dir.join("11/12/13"); + check!(mkdir_recursive(dir2, old_io::USER_RWX)); + check!(File::create(&dir2.join("14"))); + + let mut files = check!(walk_dir(dir)); + let mut cur = [0u8; 2]; + for f in files { + let stem = f.filestem_str().unwrap(); + let root = stem.as_bytes()[0] - b'0'; + let name = stem.as_bytes()[1] - b'0'; + assert!(cur[root as uint] < name); + cur[root as uint] = name; + } + + check!(rmdir_recursive(dir)); + } + + #[test] + fn mkdir_path_already_exists_error() { + use old_io::{IoError, PathAlreadyExists}; + + let tmpdir = tmpdir(); + let dir = &tmpdir.join("mkdir_error_twice"); + check!(mkdir(dir, old_io::USER_RWX)); + match mkdir(dir, old_io::USER_RWX) { + Err(IoError{kind:PathAlreadyExists,..}) => (), + _ => assert!(false) + }; + } + + #[test] + fn recursive_mkdir() { + let tmpdir = tmpdir(); + let dir = tmpdir.join("d1/d2"); + check!(mkdir_recursive(&dir, old_io::USER_RWX)); + assert!(dir.is_dir()) + } + + #[test] + fn recursive_mkdir_failure() { + let tmpdir = tmpdir(); + let dir = tmpdir.join("d1"); + let file = dir.join("f1"); + + check!(mkdir_recursive(&dir, old_io::USER_RWX)); + check!(File::create(&file)); + + let result = mkdir_recursive(&file, old_io::USER_RWX); + + error!(result, "couldn't recursively mkdir"); + error!(result, "couldn't create directory"); + error!(result, "mode=0700"); + error!(result, format!("path={}", file.display())); + } + + #[test] + fn recursive_mkdir_slash() { + check!(mkdir_recursive(&Path::new("/"), old_io::USER_RWX)); + } + + // FIXME(#12795) depends on lstat to work on windows + #[cfg(not(windows))] + #[test] + fn recursive_rmdir() { + let tmpdir = tmpdir(); + let d1 = tmpdir.join("d1"); + let dt = d1.join("t"); + let dtt = dt.join("t"); + let d2 = tmpdir.join("d2"); + let canary = d2.join("do_not_delete"); + check!(mkdir_recursive(&dtt, old_io::USER_RWX)); + check!(mkdir_recursive(&d2, old_io::USER_RWX)); + check!(File::create(&canary).write(b"foo")); + check!(symlink(&d2, &dt.join("d2"))); + check!(rmdir_recursive(&d1)); + + assert!(!d1.is_dir()); + assert!(canary.exists()); + } + + #[test] + fn unicode_path_is_dir() { + assert!(Path::new(".").is_dir()); + assert!(!Path::new("test/stdtest/fs.rs").is_dir()); + + let tmpdir = tmpdir(); + + let mut dirpath = tmpdir.path().clone(); + dirpath.push(format!("test-가一ー你好")); + check!(mkdir(&dirpath, old_io::USER_RWX)); + assert!(dirpath.is_dir()); + + let mut filepath = dirpath; + filepath.push("unicode-file-\u{ac00}\u{4e00}\u{30fc}\u{4f60}\u{597d}.rs"); + check!(File::create(&filepath)); // ignore return; touch only + assert!(!filepath.is_dir()); + assert!(filepath.exists()); + } + + #[test] + fn unicode_path_exists() { + assert!(Path::new(".").exists()); + assert!(!Path::new("test/nonexistent-bogus-path").exists()); + + let tmpdir = tmpdir(); + let unicode = tmpdir.path(); + let unicode = unicode.join(format!("test-각丁ー再见")); + check!(mkdir(&unicode, old_io::USER_RWX)); + assert!(unicode.exists()); + assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists()); + } + + #[test] + fn copy_file_does_not_exist() { + let from = Path::new("test/nonexistent-bogus-path"); + let to = Path::new("test/other-bogus-path"); + + error!(copy(&from, &to), + format!("couldn't copy path (the source path is not an \ + existing file; from={:?}; to={:?})", + from.display(), to.display())); + + match copy(&from, &to) { + Ok(..) => panic!(), + Err(..) => { + assert!(!from.exists()); + assert!(!to.exists()); + } + } + } + + #[test] + fn copy_file_ok() { + let tmpdir = tmpdir(); + let input = tmpdir.join("in.txt"); + let out = tmpdir.join("out.txt"); + + check!(File::create(&input).write(b"hello")); + check!(copy(&input, &out)); + let contents = check!(File::open(&out).read_to_end()); + assert_eq!(contents.as_slice(), b"hello"); + + assert_eq!(check!(input.stat()).perm, check!(out.stat()).perm); + } + + #[test] + fn copy_file_dst_dir() { + let tmpdir = tmpdir(); + let out = tmpdir.join("out"); + + check!(File::create(&out)); + match copy(&out, tmpdir.path()) { + Ok(..) => panic!(), Err(..) => {} + } + } + + #[test] + fn copy_file_dst_exists() { + let tmpdir = tmpdir(); + let input = tmpdir.join("in"); + let output = tmpdir.join("out"); + + check!(File::create(&input).write("foo".as_bytes())); + check!(File::create(&output).write("bar".as_bytes())); + check!(copy(&input, &output)); + + assert_eq!(check!(File::open(&output).read_to_end()), + b"foo".to_vec()); + } + + #[test] + fn copy_file_src_dir() { + let tmpdir = tmpdir(); + let out = tmpdir.join("out"); + + match copy(tmpdir.path(), &out) { + Ok(..) => panic!(), Err(..) => {} + } + assert!(!out.exists()); + } + + #[test] + fn copy_file_preserves_perm_bits() { + let tmpdir = tmpdir(); + let input = tmpdir.join("in.txt"); + let out = tmpdir.join("out.txt"); + + check!(File::create(&input)); + check!(chmod(&input, old_io::USER_READ)); + check!(copy(&input, &out)); + assert!(!check!(out.stat()).perm.intersects(old_io::USER_WRITE)); + + check!(chmod(&input, old_io::USER_FILE)); + check!(chmod(&out, old_io::USER_FILE)); + } + + #[cfg(not(windows))] // FIXME(#10264) operation not permitted? + #[test] + fn symlinks_work() { + let tmpdir = tmpdir(); + let input = tmpdir.join("in.txt"); + let out = tmpdir.join("out.txt"); + + check!(File::create(&input).write("foobar".as_bytes())); + check!(symlink(&input, &out)); + if cfg!(not(windows)) { + assert_eq!(check!(lstat(&out)).kind, FileType::Symlink); + assert_eq!(check!(out.lstat()).kind, FileType::Symlink); + } + assert_eq!(check!(stat(&out)).size, check!(stat(&input)).size); + assert_eq!(check!(File::open(&out).read_to_end()), + b"foobar".to_vec()); + } + + #[cfg(not(windows))] // apparently windows doesn't like symlinks + #[test] + fn symlink_noexist() { + let tmpdir = tmpdir(); + // symlinks can point to things that don't exist + check!(symlink(&tmpdir.join("foo"), &tmpdir.join("bar"))); + assert!(check!(readlink(&tmpdir.join("bar"))) == tmpdir.join("foo")); + } + + #[test] + fn readlink_not_symlink() { + let tmpdir = tmpdir(); + match readlink(tmpdir.path()) { + Ok(..) => panic!("wanted a failure"), + Err(..) => {} + } + } + + #[test] + fn links_work() { + let tmpdir = tmpdir(); + let input = tmpdir.join("in.txt"); + let out = tmpdir.join("out.txt"); + + check!(File::create(&input).write("foobar".as_bytes())); + check!(link(&input, &out)); + if cfg!(not(windows)) { + assert_eq!(check!(lstat(&out)).kind, FileType::RegularFile); + assert_eq!(check!(out.lstat()).kind, FileType::RegularFile); + assert_eq!(check!(stat(&out)).unstable.nlink, 2); + assert_eq!(check!(out.stat()).unstable.nlink, 2); + } + assert_eq!(check!(stat(&out)).size, check!(stat(&input)).size); + assert_eq!(check!(stat(&out)).size, check!(input.stat()).size); + assert_eq!(check!(File::open(&out).read_to_end()), + b"foobar".to_vec()); + + // can't link to yourself + match link(&input, &input) { + Ok(..) => panic!("wanted a failure"), + Err(..) => {} + } + // can't link to something that doesn't exist + match link(&tmpdir.join("foo"), &tmpdir.join("bar")) { + Ok(..) => panic!("wanted a failure"), + Err(..) => {} + } + } + + #[test] + fn chmod_works() { + let tmpdir = tmpdir(); + let file = tmpdir.join("in.txt"); + + check!(File::create(&file)); + assert!(check!(stat(&file)).perm.contains(old_io::USER_WRITE)); + check!(chmod(&file, old_io::USER_READ)); + assert!(!check!(stat(&file)).perm.contains(old_io::USER_WRITE)); + + match chmod(&tmpdir.join("foo"), old_io::USER_RWX) { + Ok(..) => panic!("wanted a panic"), + Err(..) => {} + } + + check!(chmod(&file, old_io::USER_FILE)); + } + + #[test] + fn sync_doesnt_kill_anything() { + let tmpdir = tmpdir(); + let path = tmpdir.join("in.txt"); + + let mut file = check!(File::open_mode(&path, old_io::Open, old_io::ReadWrite)); + check!(file.fsync()); + check!(file.datasync()); + check!(file.write(b"foo")); + check!(file.fsync()); + check!(file.datasync()); + drop(file); + } + + #[test] + fn truncate_works() { + let tmpdir = tmpdir(); + let path = tmpdir.join("in.txt"); + + let mut file = check!(File::open_mode(&path, old_io::Open, old_io::ReadWrite)); + check!(file.write(b"foo")); + check!(file.fsync()); + + // Do some simple things with truncation + assert_eq!(check!(file.stat()).size, 3); + check!(file.truncate(10)); + assert_eq!(check!(file.stat()).size, 10); + check!(file.write(b"bar")); + check!(file.fsync()); + assert_eq!(check!(file.stat()).size, 10); + assert_eq!(check!(File::open(&path).read_to_end()), + b"foobar\0\0\0\0".to_vec()); + + // Truncate to a smaller length, don't seek, and then write something. + // Ensure that the intermediate zeroes are all filled in (we're seeked + // past the end of the file). + check!(file.truncate(2)); + assert_eq!(check!(file.stat()).size, 2); + check!(file.write(b"wut")); + check!(file.fsync()); + assert_eq!(check!(file.stat()).size, 9); + assert_eq!(check!(File::open(&path).read_to_end()), + b"fo\0\0\0\0wut".to_vec()); + drop(file); + } + + #[test] + fn open_flavors() { + let tmpdir = tmpdir(); + + match File::open_mode(&tmpdir.join("a"), old_io::Open, old_io::Read) { + Ok(..) => panic!(), Err(..) => {} + } + + // Perform each one twice to make sure that it succeeds the second time + // (where the file exists) + check!(File::open_mode(&tmpdir.join("b"), old_io::Open, old_io::Write)); + assert!(tmpdir.join("b").exists()); + check!(File::open_mode(&tmpdir.join("b"), old_io::Open, old_io::Write)); + + check!(File::open_mode(&tmpdir.join("c"), old_io::Open, old_io::ReadWrite)); + assert!(tmpdir.join("c").exists()); + check!(File::open_mode(&tmpdir.join("c"), old_io::Open, old_io::ReadWrite)); + + check!(File::open_mode(&tmpdir.join("d"), old_io::Append, old_io::Write)); + assert!(tmpdir.join("d").exists()); + check!(File::open_mode(&tmpdir.join("d"), old_io::Append, old_io::Write)); + + check!(File::open_mode(&tmpdir.join("e"), old_io::Append, old_io::ReadWrite)); + assert!(tmpdir.join("e").exists()); + check!(File::open_mode(&tmpdir.join("e"), old_io::Append, old_io::ReadWrite)); + + check!(File::open_mode(&tmpdir.join("f"), old_io::Truncate, old_io::Write)); + assert!(tmpdir.join("f").exists()); + check!(File::open_mode(&tmpdir.join("f"), old_io::Truncate, old_io::Write)); + + check!(File::open_mode(&tmpdir.join("g"), old_io::Truncate, old_io::ReadWrite)); + assert!(tmpdir.join("g").exists()); + check!(File::open_mode(&tmpdir.join("g"), old_io::Truncate, old_io::ReadWrite)); + + check!(File::create(&tmpdir.join("h")).write("foo".as_bytes())); + check!(File::open_mode(&tmpdir.join("h"), old_io::Open, old_io::Read)); + { + let mut f = check!(File::open_mode(&tmpdir.join("h"), old_io::Open, + old_io::Read)); + match f.write("wut".as_bytes()) { + Ok(..) => panic!(), Err(..) => {} + } + } + assert!(check!(stat(&tmpdir.join("h"))).size == 3, + "write/stat failed"); + { + let mut f = check!(File::open_mode(&tmpdir.join("h"), old_io::Append, + old_io::Write)); + check!(f.write("bar".as_bytes())); + } + assert!(check!(stat(&tmpdir.join("h"))).size == 6, + "append didn't append"); + { + let mut f = check!(File::open_mode(&tmpdir.join("h"), old_io::Truncate, + old_io::Write)); + check!(f.write("bar".as_bytes())); + } + assert!(check!(stat(&tmpdir.join("h"))).size == 3, + "truncate didn't truncate"); + } + + #[test] + fn utime() { + let tmpdir = tmpdir(); + let path = tmpdir.join("a"); + check!(File::create(&path)); + // These numbers have to be bigger than the time in the day to account + // for timezones Windows in particular will fail in certain timezones + // with small enough values + check!(change_file_times(&path, 100000, 200000)); + assert_eq!(check!(path.stat()).accessed, 100000); + assert_eq!(check!(path.stat()).modified, 200000); + } + + #[test] + fn utime_noexist() { + let tmpdir = tmpdir(); + + match change_file_times(&tmpdir.join("a"), 100, 200) { + Ok(..) => panic!(), + Err(..) => {} + } + } + + #[test] + fn binary_file() { + use rand::{StdRng, Rng}; + + let mut bytes = [0; 1024]; + StdRng::new().ok().unwrap().fill_bytes(&mut bytes); + + let tmpdir = tmpdir(); + + check!(File::create(&tmpdir.join("test")).write(&bytes)); + let actual = check!(File::open(&tmpdir.join("test")).read_to_end()); + assert!(actual == bytes.as_slice()); + } + + #[test] + fn unlink_readonly() { + let tmpdir = tmpdir(); + let path = tmpdir.join("file"); + check!(File::create(&path)); + check!(chmod(&path, old_io::USER_READ)); + check!(unlink(&path)); + } +} diff --git a/src/libstd/old_io/mem.rs b/src/libstd/old_io/mem.rs new file mode 100644 index 00000000000..254daf3202a --- /dev/null +++ b/src/libstd/old_io/mem.rs @@ -0,0 +1,759 @@ +// 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::{self, AsSlice, SliceExt}; +use vec::Vec; + +const BUF_CAPACITY: uint = 128; + +fn combine(seek: SeekStyle, cur: uint, end: uint, 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 +/// +/// # Example +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// use std::old_io::MemWriter; +/// +/// let mut w = MemWriter::new(); +/// w.write(&[0, 1, 2]); +/// +/// assert_eq!(w.into_inner(), vec!(0, 1, 2)); +/// ``` +#[unstable(feature = "io")] +#[deprecated(since = "1.0.0", + reason = "use the Vec<u8> Writer implementation directly")] +#[derive(Clone)] +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: uint) -> 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.as_slice() } + + /// Unwraps this `MemWriter`, returning the underlying buffer + #[inline] + pub fn into_inner(self) -> Vec<u8> { self.buf } +} + +impl Writer for MemWriter { + #[inline] + fn write_all(&mut self, buf: &[u8]) -> IoResult<()> { + self.buf.push_all(buf); + Ok(()) + } +} + +/// Reads from an owned byte vector +/// +/// # Example +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// use std::old_io::MemReader; +/// +/// let mut r = MemReader::new(vec!(0, 1, 2)); +/// +/// assert_eq!(r.read_to_end().unwrap(), vec!(0, 1, 2)); +/// ``` +pub struct MemReader { + buf: Vec<u8>, + pos: uint +} + +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.as_slice() } + + /// 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<uint> { + 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(output, input); + } + 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 uint; + 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: uint) { self.pos += amt; } +} + +impl<'a> Reader for &'a [u8] { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { + 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(output, input); + } + + *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: uint) { + *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. +/// +/// # Example +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// use std::old_io::BufWriter; +/// +/// 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: uint +} + +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(dst, src); + + self.pos += src_len; + + Ok(()) + } else { + slice::bytes::copy_memory(dst, &src[..dst_len]); + + 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 uint, self.buf.len()); + Ok(()) + } +} + +/// Reads from a fixed-size byte slice +/// +/// # Example +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// use std::old_io::BufReader; +/// +/// let buf = [0, 1, 2, 3]; +/// let mut r = BufReader::new(&buf); +/// +/// assert_eq!(r.read_to_end().unwrap(), vec![0, 1, 2, 3]); +/// ``` +pub struct BufReader<'a> { + buf: &'a [u8], + pos: uint +} + +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<uint> { + 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(output, input); + } + 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 uint; + 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: uint) { self.pos += amt; } +} + +#[cfg(test)] +mod test { + extern crate "test" as test_crate; + use old_io::{SeekSet, SeekCur, SeekEnd, Reader, Writer, Seek}; + use prelude::v1::{Ok, Err, range, Vec, Buffer, AsSlice, SliceExt}; + use prelude::v1::IteratorExt; + 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.as_slice(), 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(), vec!(0, 1, 2, 3)); + assert_eq!(reader.read_until(3).unwrap(), vec!(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.as_slice(); + 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.as_slice(), 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.as_slice(), 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.as_slice(); + assert_eq!(reader.read_until(3).unwrap(), vec!(0, 1, 2, 3)); + assert_eq!(reader.read_until(3).unwrap(), vec!(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.as_slice()); + 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.as_slice()); + assert_eq!(reader.read_until(3).unwrap(), vec!(0, 1, 2, 3)); + assert_eq!(reader.read_until(3).unwrap(), vec!(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, buf.slice_to_mut(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: uint, len: uint) { + let src: Vec<u8> = repeat(5).take(len).collect(); + + b.bytes = (times * len) as u64; + b.iter(|| { + let mut wr = MemWriter::new(); + for _ in range(0, times) { + wr.write(src.as_slice()).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 range(0u, 10) { + let mut buf = [0 as u8; 10]; + rdr.read(&mut buf).unwrap(); + assert_eq!(buf.as_slice(), [5; 10].as_slice()); + } + } + }); + } + + #[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 range(0u, 10) { + wr.write(&[5; 10]).unwrap(); + } + } + assert_eq!(buf.as_slice(), [5; 100].as_slice()); + }); + } + + #[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 range(0u, 10) { + let mut buf = [0 as u8; 10]; + rdr.read(&mut buf).unwrap(); + assert_eq!(buf, [5; 10]); + } + } + }); + } +} diff --git a/src/libstd/old_io/mod.rs b/src/libstd/old_io/mod.rs new file mode 100644 index 00000000000..6c5ce129a33 --- /dev/null +++ b/src/libstd/old_io/mod.rs @@ -0,0 +1,1961 @@ +// 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. +// +// ignore-lexer-test FIXME #15883 + +// FIXME: cover these topics: +// path, reader, writer, stream, raii (close not needed), +// stdio, print!, println!, file access, process spawning, +// error handling + + +//! I/O, including files, networking, timers, and processes +//! +//! > **Warning**: This module is currently called `old_io` for a reason! The +//! > module is currently being redesigned in a number of RFCs. For more details +//! > follow the RFC repository in connection with [RFC 517][base] or follow +//! > some of these sub-RFCs +//! > +//! > * [String handling][osstr] +//! > * [Core I/O support][core] +//! > * [Deadlines][deadlines] +//! > * [std::env][env] +//! > * [std::process][process] +//! +//! [base]: https://github.com/rust-lang/rfcs/blob/master/text/0517-io-os-reform.md +//! [osstr]: https://github.com/rust-lang/rfcs/pull/575 +//! [core]: https://github.com/rust-lang/rfcs/pull/576 +//! [deadlines]: https://github.com/rust-lang/rfcs/pull/577 +//! [env]: https://github.com/rust-lang/rfcs/pull/578 +//! [process]: https://github.com/rust-lang/rfcs/pull/579 +//! +//! `std::io` provides Rust's basic I/O types, +//! for reading and writing to files, TCP, UDP, +//! and other types of sockets and pipes, +//! manipulating the file system, spawning processes. +//! +//! # Examples +//! +//! Some examples of obvious things you might want to do +//! +//! * Read lines from stdin +//! +//! ```rust +//! use std::old_io as io; +//! +//! for line in io::stdin().lock().lines() { +//! print!("{}", line.unwrap()); +//! } +//! ``` +//! +//! * Read a complete file +//! +//! ```rust +//! use std::old_io::File; +//! +//! let contents = File::open(&Path::new("message.txt")).read_to_end(); +//! ``` +//! +//! * Write a line to a file +//! +//! ```rust +//! # #![allow(unused_must_use)] +//! use std::old_io::File; +//! +//! let mut file = File::create(&Path::new("message.txt")); +//! file.write_all(b"hello, file!\n"); +//! # drop(file); +//! # ::std::old_io::fs::unlink(&Path::new("message.txt")); +//! ``` +//! +//! * Iterate over the lines of a file +//! +//! ```rust,no_run +//! use std::old_io::BufferedReader; +//! use std::old_io::File; +//! +//! let path = Path::new("message.txt"); +//! let mut file = BufferedReader::new(File::open(&path)); +//! for line in file.lines() { +//! print!("{}", line.unwrap()); +//! } +//! ``` +//! +//! * Pull the lines of a file into a vector of strings +//! +//! ```rust,no_run +//! use std::old_io::BufferedReader; +//! use std::old_io::File; +//! +//! let path = Path::new("message.txt"); +//! let mut file = BufferedReader::new(File::open(&path)); +//! let lines: Vec<String> = file.lines().map(|x| x.unwrap()).collect(); +//! ``` +//! +//! * Make a simple TCP client connection and request +//! +//! ```rust +//! # #![allow(unused_must_use)] +//! use std::old_io::TcpStream; +//! +//! # // connection doesn't fail if a server is running on 8080 +//! # // locally, we still want to be type checking this code, so lets +//! # // just stop it running (#11576) +//! # if false { +//! let mut socket = TcpStream::connect("127.0.0.1:8080").unwrap(); +//! socket.write_all(b"GET / HTTP/1.0\n\n"); +//! let response = socket.read_to_end(); +//! # } +//! ``` +//! +//! * Make a simple TCP server +//! +//! ```rust +//! # fn main() { } +//! # fn foo() { +//! # #![allow(dead_code)] +//! use std::old_io::{TcpListener, TcpStream}; +//! use std::old_io::{Acceptor, Listener}; +//! use std::thread::Thread; +//! +//! let listener = TcpListener::bind("127.0.0.1:80"); +//! +//! // bind the listener to the specified address +//! let mut acceptor = listener.listen(); +//! +//! fn handle_client(mut stream: TcpStream) { +//! // ... +//! # &mut stream; // silence unused mutability/variable warning +//! } +//! // accept connections and process them, spawning a new tasks for each one +//! for stream in acceptor.incoming() { +//! match stream { +//! Err(e) => { /* connection failed */ } +//! Ok(stream) => { +//! Thread::spawn(move|| { +//! // connection succeeded +//! handle_client(stream) +//! }); +//! } +//! } +//! } +//! +//! // close the socket server +//! drop(acceptor); +//! # } +//! ``` +//! +//! +//! # Error Handling +//! +//! I/O is an area where nearly every operation can result in unexpected +//! errors. Errors should be painfully visible when they happen, and handling them +//! should be easy to work with. It should be convenient to handle specific I/O +//! errors, and it should also be convenient to not deal with I/O errors. +//! +//! Rust's I/O employs a combination of techniques to reduce boilerplate +//! while still providing feedback about errors. The basic strategy: +//! +//! * All I/O operations return `IoResult<T>` which is equivalent to +//! `Result<T, IoError>`. The `Result` type is defined in the `std::result` +//! module. +//! * If the `Result` type goes unused, then the compiler will by default emit a +//! warning about the unused result. This is because `Result` has the +//! `#[must_use]` attribute. +//! * Common traits are implemented for `IoResult`, e.g. +//! `impl<R: Reader> Reader for IoResult<R>`, so that error values do not have +//! to be 'unwrapped' before use. +//! +//! These features combine in the API to allow for expressions like +//! `File::create(&Path::new("diary.txt")).write_all(b"Met a girl.\n")` +//! without having to worry about whether "diary.txt" exists or whether +//! the write succeeds. As written, if either `new` or `write_line` +//! encounters an error then the result of the entire expression will +//! be an error. +//! +//! If you wanted to handle the error though you might write: +//! +//! ```rust +//! # #![allow(unused_must_use)] +//! use std::old_io::File; +//! +//! match File::create(&Path::new("diary.txt")).write_all(b"Met a girl.\n") { +//! Ok(()) => (), // succeeded +//! Err(e) => println!("failed to write to my diary: {}", e), +//! } +//! +//! # ::std::old_io::fs::unlink(&Path::new("diary.txt")); +//! ``` +//! +//! So what actually happens if `create` encounters an error? +//! It's important to know that what `new` returns is not a `File` +//! but an `IoResult<File>`. If the file does not open, then `new` will simply +//! return `Err(..)`. Because there is an implementation of `Writer` (the trait +//! required ultimately required for types to implement `write_line`) there is no +//! need to inspect or unwrap the `IoResult<File>` and we simply call `write_line` +//! on it. If `new` returned an `Err(..)` then the followup call to `write_line` +//! will also return an error. +//! +//! ## `try!` +//! +//! Explicit pattern matching on `IoResult`s can get quite verbose, especially +//! when performing many I/O operations. Some examples (like those above) are +//! alleviated with extra methods implemented on `IoResult`, but others have more +//! complex interdependencies among each I/O operation. +//! +//! The `try!` macro from `std::macros` is provided as a method of early-return +//! inside `Result`-returning functions. It expands to an early-return on `Err` +//! and otherwise unwraps the contained `Ok` value. +//! +//! If you wanted to read several `u32`s from a file and return their product: +//! +//! ```rust +//! use std::old_io::{File, IoResult}; +//! +//! fn file_product(p: &Path) -> IoResult<u32> { +//! let mut f = File::open(p); +//! let x1 = try!(f.read_le_u32()); +//! let x2 = try!(f.read_le_u32()); +//! +//! Ok(x1 * x2) +//! } +//! +//! match file_product(&Path::new("numbers.bin")) { +//! Ok(x) => println!("{}", x), +//! Err(e) => println!("Failed to read numbers!") +//! } +//! ``` +//! +//! With `try!` in `file_product`, each `read_le_u32` need not be directly +//! concerned with error handling; instead its caller is responsible for +//! responding to errors that may occur while attempting to read the numbers. + +#![unstable(feature = "io")] +#![deny(unused_must_use)] + +pub use self::SeekStyle::*; +pub use self::FileMode::*; +pub use self::FileAccess::*; +pub use self::IoErrorKind::*; + +use char::CharExt; +use default::Default; +use error::Error; +use fmt; +use int; +use iter::{Iterator, IteratorExt}; +use marker::Sized; +use mem::transmute; +use ops::FnOnce; +use option::Option; +use option::Option::{Some, None}; +use os; +use boxed::Box; +use result::Result; +use result::Result::{Ok, Err}; +use sys; +use slice::SliceExt; +use str::StrExt; +use str; +use string::String; +use uint; +use unicode; +use vec::Vec; + +// Reexports +pub use self::stdio::stdin; +pub use self::stdio::stdout; +pub use self::stdio::stderr; +pub use self::stdio::print; +pub use self::stdio::println; + +pub use self::fs::File; +pub use self::timer::Timer; +pub use self::net::ip::IpAddr; +pub use self::net::tcp::TcpListener; +pub use self::net::tcp::TcpStream; +pub use self::pipe::PipeStream; +pub use self::process::{Process, Command}; +pub use self::tempfile::TempDir; + +pub use self::mem::{MemReader, BufReader, MemWriter, BufWriter}; +pub use self::buffered::{BufferedReader, BufferedWriter, BufferedStream, + LineBufferedWriter}; +pub use self::comm_adapters::{ChanReader, ChanWriter}; + +mod buffered; +mod comm_adapters; +mod mem; +mod result; +mod tempfile; +pub mod extensions; +pub mod fs; +pub mod net; +pub mod pipe; +pub mod process; +pub mod stdio; +pub mod timer; +pub mod util; + +#[macro_use] +pub mod test; + +/// The default buffer size for various I/O operations +// libuv recommends 64k buffers to maximize throughput +// https://groups.google.com/forum/#!topic/libuv/oQO1HJAIDdA +const DEFAULT_BUF_SIZE: uint = 1024 * 64; + +/// A convenient typedef of the return value of any I/O action. +pub type IoResult<T> = Result<T, IoError>; + +/// The type passed to I/O condition handlers to indicate error +/// +/// # FIXME +/// +/// Is something like this sufficient? It's kind of archaic +#[derive(PartialEq, Eq, Clone, Show)] +pub struct IoError { + /// An enumeration which can be matched against for determining the flavor + /// of error. + pub kind: IoErrorKind, + /// A human-readable description about the error + pub desc: &'static str, + /// Detailed information about this error, not always available + pub detail: Option<String> +} + +impl IoError { + /// Convert an `errno` value into an `IoError`. + /// + /// If `detail` is `true`, the `detail` field of the `IoError` + /// struct is filled with an allocated string describing the error + /// in more detail, retrieved from the operating system. + pub fn from_errno(errno: uint, detail: bool) -> IoError { + let mut err = sys::decode_error(errno as i32); + if detail && err.kind == OtherIoError { + err.detail = Some(os::error_string(errno).chars() + .map(|c| c.to_lowercase()).collect()) + } + err + } + + /// Retrieve the last error to occur as a (detailed) IoError. + /// + /// This uses the OS `errno`, and so there should not be any task + /// descheduling or migration (other than that performed by the + /// operating system) between the call(s) for which errors are + /// being checked and the call of this function. + pub fn last_error() -> IoError { + IoError::from_errno(os::errno() as uint, true) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for IoError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match *self { + IoError { kind: OtherIoError, desc: "unknown error", detail: Some(ref detail) } => + write!(fmt, "{}", detail), + IoError { detail: None, desc, .. } => + write!(fmt, "{}", desc), + IoError { detail: Some(ref detail), desc, .. } => + write!(fmt, "{} ({})", desc, detail) + } + } +} + +impl Error for IoError { + fn description(&self) -> &str { self.desc } +} + +/// A list specifying general categories of I/O error. +#[derive(Copy, PartialEq, Eq, Clone, Show)] +pub enum IoErrorKind { + /// Any I/O error not part of this list. + OtherIoError, + /// The operation could not complete because end of file was reached. + EndOfFile, + /// The file was not found. + FileNotFound, + /// The file permissions disallowed access to this file. + PermissionDenied, + /// A network connection failed for some reason not specified in this list. + ConnectionFailed, + /// The network operation failed because the network connection was closed. + Closed, + /// The connection was refused by the remote server. + ConnectionRefused, + /// The connection was reset by the remote server. + ConnectionReset, + /// The connection was aborted (terminated) by the remote server. + ConnectionAborted, + /// The network operation failed because it was not connected yet. + NotConnected, + /// The operation failed because a pipe was closed. + BrokenPipe, + /// A file already existed with that name. + PathAlreadyExists, + /// No file exists at that location. + PathDoesntExist, + /// The path did not specify the type of file that this operation required. For example, + /// attempting to copy a directory with the `fs::copy()` operation will fail with this error. + MismatchedFileTypeForOperation, + /// The operation temporarily failed (for example, because a signal was received), and retrying + /// may succeed. + ResourceUnavailable, + /// No I/O functionality is available for this task. + IoUnavailable, + /// A parameter was incorrect in a way that caused an I/O error not part of this list. + InvalidInput, + /// The I/O operation's timeout expired, causing it to be canceled. + TimedOut, + /// This write operation failed to write all of its data. + /// + /// Normally the write() method on a Writer guarantees that all of its data + /// has been written, but some operations may be terminated after only + /// partially writing some data. An example of this is a timed out write + /// which successfully wrote a known number of bytes, but bailed out after + /// doing so. + /// + /// The payload contained as part of this variant is the number of bytes + /// which are known to have been successfully written. + ShortWrite(uint), + /// The Reader returned 0 bytes from `read()` too many times. + NoProgress, +} + +/// A trait that lets you add a `detail` to an IoError easily +trait UpdateIoError<T> { + /// Returns an IoError with updated description and detail + fn update_err<D>(self, desc: &'static str, detail: D) -> Self where + D: FnOnce(&IoError) -> String; + + /// Returns an IoError with updated detail + fn update_detail<D>(self, detail: D) -> Self where + D: FnOnce(&IoError) -> String; + + /// Returns an IoError with update description + fn update_desc(self, desc: &'static str) -> Self; +} + +impl<T> UpdateIoError<T> for IoResult<T> { + fn update_err<D>(self, desc: &'static str, detail: D) -> IoResult<T> where + D: FnOnce(&IoError) -> String, + { + self.map_err(move |mut e| { + let detail = detail(&e); + e.desc = desc; + e.detail = Some(detail); + e + }) + } + + fn update_detail<D>(self, detail: D) -> IoResult<T> where + D: FnOnce(&IoError) -> String, + { + self.map_err(move |mut e| { e.detail = Some(detail(&e)); e }) + } + + fn update_desc(self, desc: &'static str) -> IoResult<T> { + self.map_err(|mut e| { e.desc = desc; e }) + } +} + +static NO_PROGRESS_LIMIT: uint = 1000; + +/// A trait for objects which are byte-oriented streams. Readers are defined by +/// one method, `read`. This function will block until data is available, +/// filling in the provided buffer with any data read. +/// +/// Readers are intended to be composable with one another. Many objects +/// throughout the I/O and related libraries take and provide types which +/// implement the `Reader` trait. +pub trait Reader { + + // Only method which need to get implemented for this trait + + /// Read bytes, up to the length of `buf` and place them in `buf`. + /// Returns the number of bytes read. The number of bytes read may + /// be less than the number requested, even 0. Returns `Err` on EOF. + /// + /// # Error + /// + /// If an error occurs during this I/O operation, then it is returned as + /// `Err(IoError)`. Note that end-of-file is considered an error, and can be + /// inspected for in the error's `kind` field. Also note that reading 0 + /// bytes is not considered an error in all circumstances + /// + /// # Implementation Note + /// + /// When implementing this method on a new Reader, you are strongly encouraged + /// not to return 0 if you can avoid it. + fn read(&mut self, buf: &mut [u8]) -> IoResult<uint>; + + // Convenient helper methods based on the above methods + + /// Reads at least `min` bytes and places them in `buf`. + /// Returns the number of bytes read. + /// + /// This will continue to call `read` until at least `min` bytes have been + /// read. If `read` returns 0 too many times, `NoProgress` will be + /// returned. + /// + /// # Error + /// + /// If an error occurs at any point, that error is returned, and no further + /// bytes are read. + fn read_at_least(&mut self, min: uint, buf: &mut [u8]) -> IoResult<uint> { + if min > buf.len() { + return Err(IoError { + detail: Some(String::from_str("the buffer is too short")), + ..standard_error(InvalidInput) + }); + } + let mut read = 0; + while read < min { + let mut zeroes = 0; + loop { + match self.read(&mut buf[read..]) { + Ok(0) => { + zeroes += 1; + if zeroes >= NO_PROGRESS_LIMIT { + return Err(standard_error(NoProgress)); + } + } + Ok(n) => { + read += n; + break; + } + err@Err(_) => return err + } + } + } + Ok(read) + } + + /// Reads a single byte. Returns `Err` on EOF. + fn read_byte(&mut self) -> IoResult<u8> { + let mut buf = [0]; + try!(self.read_at_least(1, &mut buf)); + Ok(buf[0]) + } + + /// Reads up to `len` bytes and appends them to a vector. + /// Returns the number of bytes read. The number of bytes read may be + /// less than the number requested, even 0. Returns Err on EOF. + /// + /// # Error + /// + /// If an error occurs during this I/O operation, then it is returned + /// as `Err(IoError)`. See `read()` for more details. + fn push(&mut self, len: uint, buf: &mut Vec<u8>) -> IoResult<uint> { + let start_len = buf.len(); + buf.reserve(len); + + let n = { + let s = unsafe { slice_vec_capacity(buf, start_len, start_len + len) }; + try!(self.read(s)) + }; + unsafe { buf.set_len(start_len + n) }; + Ok(n) + } + + /// Reads at least `min` bytes, but no more than `len`, and appends them to + /// a vector. + /// Returns the number of bytes read. + /// + /// This will continue to call `read` until at least `min` bytes have been + /// read. If `read` returns 0 too many times, `NoProgress` will be + /// returned. + /// + /// # Error + /// + /// If an error occurs at any point, that error is returned, and no further + /// bytes are read. + fn push_at_least(&mut self, min: uint, len: uint, buf: &mut Vec<u8>) -> IoResult<uint> { + if min > len { + return Err(IoError { + detail: Some(String::from_str("the buffer is too short")), + ..standard_error(InvalidInput) + }); + } + + let start_len = buf.len(); + buf.reserve(len); + + // we can't just use self.read_at_least(min, slice) because we need to push + // successful reads onto the vector before any returned errors. + + let mut read = 0; + while read < min { + read += { + let s = unsafe { slice_vec_capacity(buf, start_len + read, start_len + len) }; + try!(self.read_at_least(1, s)) + }; + unsafe { buf.set_len(start_len + read) }; + } + Ok(read) + } + + /// Reads exactly `len` bytes and gives you back a new vector of length + /// `len` + /// + /// # Error + /// + /// Fails with the same conditions as `read`. Additionally returns error + /// on EOF. Note that if an error is returned, then some number of bytes may + /// have already been consumed from the underlying reader, and they are lost + /// (not returned as part of the error). If this is unacceptable, then it is + /// recommended to use the `push_at_least` or `read` methods. + fn read_exact(&mut self, len: uint) -> IoResult<Vec<u8>> { + let mut buf = Vec::with_capacity(len); + match self.push_at_least(len, len, &mut buf) { + Ok(_) => Ok(buf), + Err(e) => Err(e), + } + } + + /// Reads all remaining bytes from the stream. + /// + /// # Error + /// + /// Returns any non-EOF error immediately. Previously read bytes are + /// discarded when an error is returned. + /// + /// When EOF is encountered, all bytes read up to that point are returned. + fn read_to_end(&mut self) -> IoResult<Vec<u8>> { + let mut buf = Vec::with_capacity(DEFAULT_BUF_SIZE); + loop { + match self.push_at_least(1, DEFAULT_BUF_SIZE, &mut buf) { + Ok(_) => {} + Err(ref e) if e.kind == EndOfFile => break, + Err(e) => return Err(e) + } + } + return Ok(buf); + } + + /// Reads all of the remaining bytes of this stream, interpreting them as a + /// UTF-8 encoded stream. The corresponding string is returned. + /// + /// # Error + /// + /// This function returns all of the same errors as `read_to_end` with an + /// additional error if the reader's contents are not a valid sequence of + /// UTF-8 bytes. + fn read_to_string(&mut self) -> IoResult<String> { + self.read_to_end().and_then(|s| { + match String::from_utf8(s) { + Ok(s) => Ok(s), + Err(_) => Err(standard_error(InvalidInput)), + } + }) + } + + // Byte conversion helpers + + /// Reads `n` little-endian unsigned integer bytes. + /// + /// `n` must be between 1 and 8, inclusive. + fn read_le_uint_n(&mut self, nbytes: uint) -> IoResult<u64> { + assert!(nbytes > 0 && nbytes <= 8); + + let mut val = 0u64; + let mut pos = 0; + let mut i = nbytes; + while i > 0 { + val += (try!(self.read_u8()) as u64) << pos; + pos += 8; + i -= 1; + } + Ok(val) + } + + /// Reads `n` little-endian signed integer bytes. + /// + /// `n` must be between 1 and 8, inclusive. + fn read_le_int_n(&mut self, nbytes: uint) -> IoResult<i64> { + self.read_le_uint_n(nbytes).map(|i| extend_sign(i, nbytes)) + } + + /// Reads `n` big-endian unsigned integer bytes. + /// + /// `n` must be between 1 and 8, inclusive. + fn read_be_uint_n(&mut self, nbytes: uint) -> IoResult<u64> { + assert!(nbytes > 0 && nbytes <= 8); + + let mut val = 0u64; + let mut i = nbytes; + while i > 0 { + i -= 1; + val += (try!(self.read_u8()) as u64) << i * 8; + } + Ok(val) + } + + /// Reads `n` big-endian signed integer bytes. + /// + /// `n` must be between 1 and 8, inclusive. + fn read_be_int_n(&mut self, nbytes: uint) -> IoResult<i64> { + self.read_be_uint_n(nbytes).map(|i| extend_sign(i, nbytes)) + } + + /// Reads a little-endian unsigned integer. + /// + /// The number of bytes returned is system-dependent. + fn read_le_uint(&mut self) -> IoResult<uint> { + self.read_le_uint_n(uint::BYTES).map(|i| i as uint) + } + + /// Reads a little-endian integer. + /// + /// The number of bytes returned is system-dependent. + fn read_le_int(&mut self) -> IoResult<int> { + self.read_le_int_n(int::BYTES).map(|i| i as int) + } + + /// Reads a big-endian unsigned integer. + /// + /// The number of bytes returned is system-dependent. + fn read_be_uint(&mut self) -> IoResult<uint> { + self.read_be_uint_n(uint::BYTES).map(|i| i as uint) + } + + /// Reads a big-endian integer. + /// + /// The number of bytes returned is system-dependent. + fn read_be_int(&mut self) -> IoResult<int> { + self.read_be_int_n(int::BYTES).map(|i| i as int) + } + + /// Reads a big-endian `u64`. + /// + /// `u64`s are 8 bytes long. + fn read_be_u64(&mut self) -> IoResult<u64> { + self.read_be_uint_n(8) + } + + /// Reads a big-endian `u32`. + /// + /// `u32`s are 4 bytes long. + fn read_be_u32(&mut self) -> IoResult<u32> { + self.read_be_uint_n(4).map(|i| i as u32) + } + + /// Reads a big-endian `u16`. + /// + /// `u16`s are 2 bytes long. + fn read_be_u16(&mut self) -> IoResult<u16> { + self.read_be_uint_n(2).map(|i| i as u16) + } + + /// Reads a big-endian `i64`. + /// + /// `i64`s are 8 bytes long. + fn read_be_i64(&mut self) -> IoResult<i64> { + self.read_be_int_n(8) + } + + /// Reads a big-endian `i32`. + /// + /// `i32`s are 4 bytes long. + fn read_be_i32(&mut self) -> IoResult<i32> { + self.read_be_int_n(4).map(|i| i as i32) + } + + /// Reads a big-endian `i16`. + /// + /// `i16`s are 2 bytes long. + fn read_be_i16(&mut self) -> IoResult<i16> { + self.read_be_int_n(2).map(|i| i as i16) + } + + /// Reads a big-endian `f64`. + /// + /// `f64`s are 8 byte, IEEE754 double-precision floating point numbers. + fn read_be_f64(&mut self) -> IoResult<f64> { + self.read_be_u64().map(|i| unsafe { + transmute::<u64, f64>(i) + }) + } + + /// Reads a big-endian `f32`. + /// + /// `f32`s are 4 byte, IEEE754 single-precision floating point numbers. + fn read_be_f32(&mut self) -> IoResult<f32> { + self.read_be_u32().map(|i| unsafe { + transmute::<u32, f32>(i) + }) + } + + /// Reads a little-endian `u64`. + /// + /// `u64`s are 8 bytes long. + fn read_le_u64(&mut self) -> IoResult<u64> { + self.read_le_uint_n(8) + } + + /// Reads a little-endian `u32`. + /// + /// `u32`s are 4 bytes long. + fn read_le_u32(&mut self) -> IoResult<u32> { + self.read_le_uint_n(4).map(|i| i as u32) + } + + /// Reads a little-endian `u16`. + /// + /// `u16`s are 2 bytes long. + fn read_le_u16(&mut self) -> IoResult<u16> { + self.read_le_uint_n(2).map(|i| i as u16) + } + + /// Reads a little-endian `i64`. + /// + /// `i64`s are 8 bytes long. + fn read_le_i64(&mut self) -> IoResult<i64> { + self.read_le_int_n(8) + } + + /// Reads a little-endian `i32`. + /// + /// `i32`s are 4 bytes long. + fn read_le_i32(&mut self) -> IoResult<i32> { + self.read_le_int_n(4).map(|i| i as i32) + } + + /// Reads a little-endian `i16`. + /// + /// `i16`s are 2 bytes long. + fn read_le_i16(&mut self) -> IoResult<i16> { + self.read_le_int_n(2).map(|i| i as i16) + } + + /// Reads a little-endian `f64`. + /// + /// `f64`s are 8 byte, IEEE754 double-precision floating point numbers. + fn read_le_f64(&mut self) -> IoResult<f64> { + self.read_le_u64().map(|i| unsafe { + transmute::<u64, f64>(i) + }) + } + + /// Reads a little-endian `f32`. + /// + /// `f32`s are 4 byte, IEEE754 single-precision floating point numbers. + fn read_le_f32(&mut self) -> IoResult<f32> { + self.read_le_u32().map(|i| unsafe { + transmute::<u32, f32>(i) + }) + } + + /// Read a u8. + /// + /// `u8`s are 1 byte. + fn read_u8(&mut self) -> IoResult<u8> { + self.read_byte() + } + + /// Read an i8. + /// + /// `i8`s are 1 byte. + fn read_i8(&mut self) -> IoResult<i8> { + self.read_byte().map(|i| i as i8) + } +} + +/// A reader which can be converted to a RefReader. +pub trait ByRefReader { + /// Creates a wrapper around a mutable reference to the reader. + /// + /// This is useful to allow applying adaptors while still + /// retaining ownership of the original value. + fn by_ref<'a>(&'a mut self) -> RefReader<'a, Self>; +} + +impl<T: Reader> ByRefReader for T { + fn by_ref<'a>(&'a mut self) -> RefReader<'a, T> { + RefReader { inner: self } + } +} + +/// A reader which can be converted to bytes. +pub trait BytesReader { + /// Create an iterator that reads a single byte on + /// each iteration, until EOF. + /// + /// # 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. + fn bytes<'r>(&'r mut self) -> extensions::Bytes<'r, Self>; +} + +impl<T: Reader> BytesReader for T { + fn bytes<'r>(&'r mut self) -> extensions::Bytes<'r, T> { + extensions::Bytes::new(self) + } +} + +impl<'a> Reader for Box<Reader+'a> { + fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { + let reader: &mut Reader = &mut **self; + reader.read(buf) + } +} + +impl<'a> Reader for &'a mut (Reader+'a) { + fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { (*self).read(buf) } +} + +/// Returns a slice of `v` between `start` and `end`. +/// +/// Similar to `slice()` except this function only bounds the slice on the +/// capacity of `v`, not the length. +/// +/// # Panics +/// +/// Panics when `start` or `end` point outside the capacity of `v`, or when +/// `start` > `end`. +// Private function here because we aren't sure if we want to expose this as +// API yet. If so, it should be a method on Vec. +unsafe fn slice_vec_capacity<'a, T>(v: &'a mut Vec<T>, start: uint, end: uint) -> &'a mut [T] { + use raw::Slice; + use ptr::PtrExt; + + assert!(start <= end); + assert!(end <= v.capacity()); + transmute(Slice { + data: v.as_ptr().offset(start as int), + len: end - start + }) +} + +/// A `RefReader` is a struct implementing `Reader` which contains a reference +/// to another reader. This is often useful when composing streams. +/// +/// # Examples +/// +/// ``` +/// use std::old_io as io; +/// use std::old_io::ByRefReader; +/// use std::old_io::util::LimitReader; +/// +/// fn process_input<R: Reader>(r: R) {} +/// +/// let mut stream = io::stdin(); +/// +/// // Only allow the function to process at most one kilobyte of input +/// { +/// let stream = LimitReader::new(stream.by_ref(), 1024); +/// process_input(stream); +/// } +/// +/// // 'stream' is still available for use here +/// ``` +pub struct RefReader<'a, R:'a> { + /// The underlying reader which this is referencing + inner: &'a mut R +} + +impl<'a, R: Reader> Reader for RefReader<'a, R> { + fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { self.inner.read(buf) } +} + +impl<'a, R: Buffer> Buffer for RefReader<'a, R> { + fn fill_buf(&mut self) -> IoResult<&[u8]> { self.inner.fill_buf() } + fn consume(&mut self, amt: uint) { self.inner.consume(amt) } +} + +fn extend_sign(val: u64, nbytes: uint) -> i64 { + let shift = (8 - nbytes) * 8; + (val << shift) as i64 >> shift +} + +/// A trait for objects which are byte-oriented streams. Writers are defined by +/// one method, `write`. This function will block until the provided buffer of +/// bytes has been entirely written, and it will return any failures which occur. +/// +/// Another commonly overridden method is the `flush` method for writers such as +/// buffered writers. +/// +/// Writers are intended to be composable with one another. Many objects +/// throughout the I/O and related libraries take and provide types which +/// implement the `Writer` trait. +pub trait Writer { + /// Write the entirety of a given buffer + /// + /// # Errors + /// + /// If an error happens during the I/O operation, the error is returned as + /// `Err`. Note that it is considered an error if the entire buffer could + /// not be written, and if an error is returned then it is unknown how much + /// data (if any) was actually written. + fn write_all(&mut self, buf: &[u8]) -> IoResult<()>; + + /// Deprecated, this method was renamed to `write_all` + #[unstable(feature = "io")] + #[deprecated(since = "1.0.0", reason = "renamed to `write_all`")] + fn write(&mut self, buf: &[u8]) -> IoResult<()> { self.write_all(buf) } + + /// Flush this output stream, ensuring that all intermediately buffered + /// contents reach their destination. + /// + /// This is by default a no-op and implementers of the `Writer` trait should + /// decide whether their stream needs to be buffered or not. + fn flush(&mut self) -> IoResult<()> { Ok(()) } + + /// Writes a formatted string into this writer, returning any error + /// encountered. + /// + /// This method is primarily used to interface with the `format_args!` + /// macro, but it is rare that this should explicitly be called. The + /// `write!` macro should be favored to invoke this method instead. + /// + /// # Errors + /// + /// This function will return any I/O error reported while formatting. + fn write_fmt(&mut self, fmt: fmt::Arguments) -> IoResult<()> { + // Create a shim which translates a Writer to a fmt::Writer and saves + // off I/O errors. instead of discarding them + struct Adaptor<'a, T: ?Sized +'a> { + inner: &'a mut T, + error: IoResult<()>, + } + + impl<'a, T: ?Sized + Writer> fmt::Writer for Adaptor<'a, T> { + fn write_str(&mut self, s: &str) -> fmt::Result { + match self.inner.write_all(s.as_bytes()) { + Ok(()) => Ok(()), + Err(e) => { + self.error = Err(e); + Err(fmt::Error) + } + } + } + } + + let mut output = Adaptor { inner: self, error: Ok(()) }; + match fmt::write(&mut output, fmt) { + Ok(()) => Ok(()), + Err(..) => output.error + } + } + + + /// Write a rust string into this sink. + /// + /// The bytes written will be the UTF-8 encoded version of the input string. + /// If other encodings are desired, it is recommended to compose this stream + /// with another performing the conversion, or to use `write` with a + /// converted byte-array instead. + #[inline] + fn write_str(&mut self, s: &str) -> IoResult<()> { + self.write_all(s.as_bytes()) + } + + /// Writes a string into this sink, and then writes a literal newline (`\n`) + /// byte afterwards. Note that the writing of the newline is *not* atomic in + /// the sense that the call to `write` is invoked twice (once with the + /// string and once with a newline character). + /// + /// If other encodings or line ending flavors are desired, it is recommended + /// that the `write` method is used specifically instead. + #[inline] + fn write_line(&mut self, s: &str) -> IoResult<()> { + self.write_str(s).and_then(|()| self.write_all(&[b'\n'])) + } + + /// Write a single char, encoded as UTF-8. + #[inline] + fn write_char(&mut self, c: char) -> IoResult<()> { + let mut buf = [0u8; 4]; + let n = c.encode_utf8(buf.as_mut_slice()).unwrap_or(0); + self.write_all(&buf[..n]) + } + + /// Write the result of passing n through `int::to_str_bytes`. + #[inline] + fn write_int(&mut self, n: int) -> IoResult<()> { + write!(self, "{}", n) + } + + /// Write the result of passing n through `uint::to_str_bytes`. + #[inline] + fn write_uint(&mut self, n: uint) -> IoResult<()> { + write!(self, "{}", n) + } + + /// Write a little-endian uint (number of bytes depends on system). + #[inline] + fn write_le_uint(&mut self, n: uint) -> IoResult<()> { + extensions::u64_to_le_bytes(n as u64, uint::BYTES, |v| self.write_all(v)) + } + + /// Write a little-endian int (number of bytes depends on system). + #[inline] + fn write_le_int(&mut self, n: int) -> IoResult<()> { + extensions::u64_to_le_bytes(n as u64, int::BYTES, |v| self.write_all(v)) + } + + /// Write a big-endian uint (number of bytes depends on system). + #[inline] + fn write_be_uint(&mut self, n: uint) -> IoResult<()> { + extensions::u64_to_be_bytes(n as u64, uint::BYTES, |v| self.write_all(v)) + } + + /// Write a big-endian int (number of bytes depends on system). + #[inline] + fn write_be_int(&mut self, n: int) -> IoResult<()> { + extensions::u64_to_be_bytes(n as u64, int::BYTES, |v| self.write_all(v)) + } + + /// Write a big-endian u64 (8 bytes). + #[inline] + fn write_be_u64(&mut self, n: u64) -> IoResult<()> { + extensions::u64_to_be_bytes(n, 8u, |v| self.write_all(v)) + } + + /// Write a big-endian u32 (4 bytes). + #[inline] + fn write_be_u32(&mut self, n: u32) -> IoResult<()> { + extensions::u64_to_be_bytes(n as u64, 4u, |v| self.write_all(v)) + } + + /// Write a big-endian u16 (2 bytes). + #[inline] + fn write_be_u16(&mut self, n: u16) -> IoResult<()> { + extensions::u64_to_be_bytes(n as u64, 2u, |v| self.write_all(v)) + } + + /// Write a big-endian i64 (8 bytes). + #[inline] + fn write_be_i64(&mut self, n: i64) -> IoResult<()> { + extensions::u64_to_be_bytes(n as u64, 8u, |v| self.write_all(v)) + } + + /// Write a big-endian i32 (4 bytes). + #[inline] + fn write_be_i32(&mut self, n: i32) -> IoResult<()> { + extensions::u64_to_be_bytes(n as u64, 4u, |v| self.write_all(v)) + } + + /// Write a big-endian i16 (2 bytes). + #[inline] + fn write_be_i16(&mut self, n: i16) -> IoResult<()> { + extensions::u64_to_be_bytes(n as u64, 2u, |v| self.write_all(v)) + } + + /// Write a big-endian IEEE754 double-precision floating-point (8 bytes). + #[inline] + fn write_be_f64(&mut self, f: f64) -> IoResult<()> { + unsafe { + self.write_be_u64(transmute(f)) + } + } + + /// Write a big-endian IEEE754 single-precision floating-point (4 bytes). + #[inline] + fn write_be_f32(&mut self, f: f32) -> IoResult<()> { + unsafe { + self.write_be_u32(transmute(f)) + } + } + + /// Write a little-endian u64 (8 bytes). + #[inline] + fn write_le_u64(&mut self, n: u64) -> IoResult<()> { + extensions::u64_to_le_bytes(n, 8u, |v| self.write_all(v)) + } + + /// Write a little-endian u32 (4 bytes). + #[inline] + fn write_le_u32(&mut self, n: u32) -> IoResult<()> { + extensions::u64_to_le_bytes(n as u64, 4u, |v| self.write_all(v)) + } + + /// Write a little-endian u16 (2 bytes). + #[inline] + fn write_le_u16(&mut self, n: u16) -> IoResult<()> { + extensions::u64_to_le_bytes(n as u64, 2u, |v| self.write_all(v)) + } + + /// Write a little-endian i64 (8 bytes). + #[inline] + fn write_le_i64(&mut self, n: i64) -> IoResult<()> { + extensions::u64_to_le_bytes(n as u64, 8u, |v| self.write_all(v)) + } + + /// Write a little-endian i32 (4 bytes). + #[inline] + fn write_le_i32(&mut self, n: i32) -> IoResult<()> { + extensions::u64_to_le_bytes(n as u64, 4u, |v| self.write_all(v)) + } + + /// Write a little-endian i16 (2 bytes). + #[inline] + fn write_le_i16(&mut self, n: i16) -> IoResult<()> { + extensions::u64_to_le_bytes(n as u64, 2u, |v| self.write_all(v)) + } + + /// Write a little-endian IEEE754 double-precision floating-point + /// (8 bytes). + #[inline] + fn write_le_f64(&mut self, f: f64) -> IoResult<()> { + unsafe { + self.write_le_u64(transmute(f)) + } + } + + /// Write a little-endian IEEE754 single-precision floating-point + /// (4 bytes). + #[inline] + fn write_le_f32(&mut self, f: f32) -> IoResult<()> { + unsafe { + self.write_le_u32(transmute(f)) + } + } + + /// Write a u8 (1 byte). + #[inline] + fn write_u8(&mut self, n: u8) -> IoResult<()> { + self.write_all(&[n]) + } + + /// Write an i8 (1 byte). + #[inline] + fn write_i8(&mut self, n: i8) -> IoResult<()> { + self.write_all(&[n as u8]) + } +} + +/// A writer which can be converted to a RefWriter. +pub trait ByRefWriter { + /// Creates a wrapper around a mutable reference to the writer. + /// + /// This is useful to allow applying wrappers while still + /// retaining ownership of the original value. + #[inline] + fn by_ref<'a>(&'a mut self) -> RefWriter<'a, Self>; +} + +impl<T: Writer> ByRefWriter for T { + fn by_ref<'a>(&'a mut self) -> RefWriter<'a, T> { + RefWriter { inner: self } + } +} + +impl<'a> Writer for Box<Writer+'a> { + #[inline] + fn write_all(&mut self, buf: &[u8]) -> IoResult<()> { + (&mut **self).write_all(buf) + } + + #[inline] + fn flush(&mut self) -> IoResult<()> { + (&mut **self).flush() + } +} + +impl<'a> Writer for &'a mut (Writer+'a) { + #[inline] + fn write_all(&mut self, buf: &[u8]) -> IoResult<()> { (**self).write_all(buf) } + + #[inline] + fn flush(&mut self) -> IoResult<()> { (**self).flush() } +} + +/// A `RefWriter` is a struct implementing `Writer` which contains a reference +/// to another writer. This is often useful when composing streams. +/// +/// # Example +/// +/// ``` +/// use std::old_io::util::TeeReader; +/// use std::old_io::{stdin, ByRefWriter}; +/// +/// fn process_input<R: Reader>(r: R) {} +/// +/// let mut output = Vec::new(); +/// +/// { +/// // Don't give ownership of 'output' to the 'tee'. Instead we keep a +/// // handle to it in the outer scope +/// let mut tee = TeeReader::new(stdin(), output.by_ref()); +/// process_input(tee); +/// } +/// +/// println!("input processed: {:?}", output); +/// ``` +pub struct RefWriter<'a, W:'a> { + /// The underlying writer which this is referencing + inner: &'a mut W +} + +impl<'a, W: Writer> Writer for RefWriter<'a, W> { + #[inline] + fn write_all(&mut self, buf: &[u8]) -> IoResult<()> { self.inner.write_all(buf) } + + #[inline] + fn flush(&mut self) -> IoResult<()> { self.inner.flush() } +} + + +/// A Stream is a readable and a writable object. Data written is typically +/// received by the object which reads receive data from. +pub trait Stream: Reader + Writer { } + +impl<T: Reader + Writer> Stream for T {} + +/// An iterator that reads a line on each iteration, +/// until `.read_line()` encounters `EndOfFile`. +/// +/// # Notes about the Iteration Protocol +/// +/// The `Lines` 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 Lines<'r, T:'r> { + buffer: &'r mut T, +} + +impl<'r, T: Buffer> Iterator for Lines<'r, T> { + type Item = IoResult<String>; + + fn next(&mut self) -> Option<IoResult<String>> { + match self.buffer.read_line() { + Ok(x) => Some(Ok(x)), + Err(IoError { kind: EndOfFile, ..}) => None, + Err(y) => Some(Err(y)) + } + } +} + +/// An iterator that reads a utf8-encoded character on each iteration, +/// until `.read_char()` encounters `EndOfFile`. +/// +/// # Notes about the Iteration Protocol +/// +/// The `Chars` 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 Chars<'r, T:'r> { + buffer: &'r mut T +} + +impl<'r, T: Buffer> Iterator for Chars<'r, T> { + type Item = IoResult<char>; + + fn next(&mut self) -> Option<IoResult<char>> { + match self.buffer.read_char() { + Ok(x) => Some(Ok(x)), + Err(IoError { kind: EndOfFile, ..}) => None, + Err(y) => Some(Err(y)) + } + } +} + +/// A Buffer is a type of reader which has some form of internal buffering to +/// allow certain kinds of reading operations to be more optimized than others. +/// This type extends the `Reader` trait with a few methods that are not +/// possible to reasonably implement with purely a read interface. +pub trait Buffer: Reader { + /// Fills the internal buffer of this object, returning the buffer contents. + /// Note that none of the contents will be "read" in the sense that later + /// calling `read` may return the same contents. + /// + /// The `consume` function must be called with the number of bytes that are + /// consumed from this buffer returned to ensure that the bytes are never + /// returned twice. + /// + /// # Error + /// + /// This function will return an I/O error if the underlying reader was + /// read, but returned an error. Note that it is not an error to return a + /// 0-length buffer. + fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]>; + + /// Tells this buffer that `amt` bytes have been consumed from the buffer, + /// so they should no longer be returned in calls to `read`. + fn consume(&mut self, amt: uint); + + /// Reads the next line of input, interpreted as a sequence of UTF-8 + /// encoded Unicode codepoints. If a newline is encountered, then the + /// newline is contained in the returned string. + /// + /// # Example + /// + /// ```rust + /// use std::old_io::BufReader; + /// + /// let mut reader = BufReader::new(b"hello\nworld"); + /// assert_eq!("hello\n", &*reader.read_line().unwrap()); + /// ``` + /// + /// # Error + /// + /// This function has the same error semantics as `read_until`: + /// + /// * All non-EOF errors will be returned immediately + /// * If an error is returned previously consumed bytes are lost + /// * EOF is only returned if no bytes have been read + /// * Reach EOF may mean that the delimiter is not present in the return + /// value + /// + /// Additionally, this function can fail if the line of input read is not a + /// valid UTF-8 sequence of bytes. + fn read_line(&mut self) -> IoResult<String> { + self.read_until(b'\n').and_then(|line| + match String::from_utf8(line) { + Ok(s) => Ok(s), + Err(_) => Err(standard_error(InvalidInput)), + } + ) + } + + /// Reads a sequence of bytes leading up to a specified delimiter. Once the + /// specified byte is encountered, reading ceases and the bytes up to and + /// including the delimiter are returned. + /// + /// # Error + /// + /// If any I/O error is encountered other than EOF, the error is immediately + /// returned. Note that this may discard bytes which have already been read, + /// and those bytes will *not* be returned. It is recommended to use other + /// methods if this case is worrying. + /// + /// If EOF is encountered, then this function will return EOF if 0 bytes + /// have been read, otherwise the pending byte buffer is returned. This + /// is the reason that the byte buffer returned may not always contain the + /// delimiter. + fn read_until(&mut self, byte: u8) -> IoResult<Vec<u8>> { + let mut res = Vec::new(); + + loop { + let (done, used) = { + let available = match self.fill_buf() { + Ok(n) => n, + Err(ref e) if res.len() > 0 && e.kind == EndOfFile => { + return Ok(res); + } + Err(e) => return Err(e) + }; + match available.iter().position(|&b| b == byte) { + Some(i) => { + res.push_all(&available[..i + 1]); + (true, i + 1) + } + None => { + res.push_all(available); + (false, available.len()) + } + } + }; + self.consume(used); + if done { + return Ok(res); + } + } + } + + /// Reads the next utf8-encoded character from the underlying stream. + /// + /// # Error + /// + /// If an I/O error occurs, or EOF, then this function will return `Err`. + /// This function will also return error if the stream does not contain a + /// valid utf-8 encoded codepoint as the next few bytes in the stream. + fn read_char(&mut self) -> IoResult<char> { + let first_byte = try!(self.read_byte()); + let width = unicode::str::utf8_char_width(first_byte); + if width == 1 { return Ok(first_byte as char) } + if width == 0 { return Err(standard_error(InvalidInput)) } // not utf8 + let mut buf = [first_byte, 0, 0, 0]; + { + let mut start = 1; + while start < width { + match try!(self.read(&mut buf[start .. width])) { + n if n == width - start => break, + n if n < width - start => { start += n; } + _ => return Err(standard_error(InvalidInput)), + } + } + } + match str::from_utf8(&buf[..width]).ok() { + Some(s) => Ok(s.char_at(0)), + None => Err(standard_error(InvalidInput)) + } + } +} + +/// Extension methods for the Buffer trait which are included in the prelude. +pub trait BufferPrelude { + /// Create an iterator that reads a utf8-encoded character on each iteration + /// until EOF. + /// + /// # 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. + fn chars<'r>(&'r mut self) -> Chars<'r, Self>; + + /// Create an iterator that reads a line on each iteration until EOF. + /// + /// # 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. + fn lines<'r>(&'r mut self) -> Lines<'r, Self>; +} + +impl<T: Buffer> BufferPrelude for T { + fn chars<'r>(&'r mut self) -> Chars<'r, T> { + Chars { buffer: self } + } + + fn lines<'r>(&'r mut self) -> Lines<'r, T> { + Lines { buffer: self } + } +} + +/// When seeking, the resulting cursor is offset from a base by the offset given +/// to the `seek` function. The base used is specified by this enumeration. +#[derive(Copy)] +pub enum SeekStyle { + /// Seek from the beginning of the stream + SeekSet, + /// Seek from the end of the stream + SeekEnd, + /// Seek from the current position + SeekCur, +} + +/// An object implementing `Seek` internally has some form of cursor which can +/// be moved within a stream of bytes. The stream typically has a fixed size, +/// allowing seeking relative to either end. +pub trait Seek { + /// Return position of file cursor in the stream + fn tell(&self) -> IoResult<u64>; + + /// Seek to an offset in a stream + /// + /// A successful seek clears the EOF indicator. Seeking beyond EOF is + /// allowed, but seeking before position 0 is not allowed. + /// + /// # Errors + /// + /// * Seeking to a negative offset is considered an error + /// * Seeking past the end of the stream does not modify the underlying + /// stream, but the next write may cause the previous data to be filled in + /// with a bit pattern. + fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()>; +} + +/// A listener is a value that can consume itself to start listening for +/// connections. +/// +/// Doing so produces some sort of Acceptor. +pub trait Listener<T, A: Acceptor<T>> { + /// Spin up the listener and start queuing incoming connections + /// + /// # Error + /// + /// Returns `Err` if this listener could not be bound to listen for + /// connections. In all cases, this listener is consumed. + fn listen(self) -> IoResult<A>; +} + +/// An acceptor is a value that presents incoming connections +pub trait Acceptor<T> { + /// Wait for and accept an incoming connection + /// + /// # Error + /// + /// Returns `Err` if an I/O error is encountered. + fn accept(&mut self) -> IoResult<T>; + + /// Create an iterator over incoming connection attempts. + /// + /// Note that I/O errors will be yielded by the iterator itself. + fn incoming<'r>(&'r mut self) -> IncomingConnections<'r, Self> { + IncomingConnections { inc: self } + } +} + +/// An infinite iterator over incoming connection attempts. +/// Calling `next` will block the task until a connection is attempted. +/// +/// Since connection attempts can continue forever, this iterator always returns +/// `Some`. The `Some` contains the `IoResult` representing whether the +/// connection attempt was successful. A successful connection will be wrapped +/// in `Ok`. A failed connection is represented as an `Err`. +pub struct IncomingConnections<'a, A: ?Sized +'a> { + inc: &'a mut A, +} + +#[old_impl_check] +impl<'a, T, A: ?Sized + Acceptor<T>> Iterator for IncomingConnections<'a, A> { + type Item = IoResult<T>; + + fn next(&mut self) -> Option<IoResult<T>> { + Some(self.inc.accept()) + } +} + +/// Creates a standard error for a commonly used flavor of error. The `detail` +/// field of the returned error will always be `None`. +/// +/// # Example +/// +/// ``` +/// use std::old_io as io; +/// +/// let eof = io::standard_error(io::EndOfFile); +/// let einval = io::standard_error(io::InvalidInput); +/// ``` +pub fn standard_error(kind: IoErrorKind) -> IoError { + let desc = match kind { + EndOfFile => "end of file", + IoUnavailable => "I/O is unavailable", + InvalidInput => "invalid input", + OtherIoError => "unknown I/O error", + FileNotFound => "file not found", + PermissionDenied => "permission denied", + ConnectionFailed => "connection failed", + Closed => "stream is closed", + ConnectionRefused => "connection refused", + ConnectionReset => "connection reset", + ConnectionAborted => "connection aborted", + NotConnected => "not connected", + BrokenPipe => "broken pipe", + PathAlreadyExists => "file already exists", + PathDoesntExist => "no such file", + MismatchedFileTypeForOperation => "mismatched file type", + ResourceUnavailable => "resource unavailable", + TimedOut => "operation timed out", + ShortWrite(..) => "short write", + NoProgress => "no progress", + }; + IoError { + kind: kind, + desc: desc, + detail: None, + } +} + +/// A mode specifies how a file should be opened or created. These modes are +/// passed to `File::open_mode` and are used to control where the file is +/// positioned when it is initially opened. +#[derive(Copy, Clone, PartialEq, Eq, Show)] +pub enum FileMode { + /// Opens a file positioned at the beginning. + Open, + /// Opens a file positioned at EOF. + Append, + /// Opens a file, truncating it if it already exists. + Truncate, +} + +/// Access permissions with which the file should be opened. `File`s +/// opened with `Read` will return an error if written to. +#[derive(Copy, Clone, PartialEq, Eq, Show)] +pub enum FileAccess { + /// Read-only access, requests to write will result in an error + Read, + /// Write-only access, requests to read will result in an error + Write, + /// Read-write access, no requests are denied by default + ReadWrite, +} + +/// Different kinds of files which can be identified by a call to stat +#[derive(Copy, PartialEq, Show, Hash, Clone)] +pub enum FileType { + /// This is a normal file, corresponding to `S_IFREG` + RegularFile, + + /// This file is a directory, corresponding to `S_IFDIR` + Directory, + + /// This file is a named pipe, corresponding to `S_IFIFO` + NamedPipe, + + /// This file is a block device, corresponding to `S_IFBLK` + BlockSpecial, + + /// This file is a symbolic link to another file, corresponding to `S_IFLNK` + Symlink, + + /// The type of this file is not recognized as one of the other categories + Unknown, +} + +/// A structure used to describe metadata information about a file. This +/// structure is created through the `stat` method on a `Path`. +/// +/// # Examples +/// +/// ```no_run +/// +/// use std::old_io::fs::PathExtensions; +/// +/// let info = match Path::new("foo.txt").stat() { +/// Ok(stat) => stat, +/// Err(e) => panic!("couldn't read foo.txt: {}", e), +/// }; +/// +/// println!("byte size: {}", info.size); +/// ``` +#[derive(Copy, Hash)] +pub struct FileStat { + /// The size of the file, in bytes + pub size: u64, + /// The kind of file this path points to (directory, file, pipe, etc.) + pub kind: FileType, + /// The file permissions currently on the file + pub perm: FilePermission, + + // FIXME(#10301): These time fields are pretty useless without an actual + // time representation, what are the milliseconds relative + // to? + + /// The time that the file was created at, in platform-dependent + /// milliseconds + pub created: u64, + /// The time that this file was last modified, in platform-dependent + /// milliseconds + pub modified: u64, + /// The time that this file was last accessed, in platform-dependent + /// milliseconds + pub accessed: u64, + + /// Information returned by stat() which is not guaranteed to be + /// platform-independent. This information may be useful on some platforms, + /// but it may have different meanings or no meaning at all on other + /// platforms. + /// + /// Usage of this field is discouraged, but if access is desired then the + /// fields are located here. + #[unstable(feature = "io")] + pub unstable: UnstableFileStat, +} + +/// This structure represents all of the possible information which can be +/// returned from a `stat` syscall which is not contained in the `FileStat` +/// structure. This information is not necessarily platform independent, and may +/// have different meanings or no meaning at all on some platforms. +#[unstable(feature = "io")] +#[derive(Copy, Hash)] +pub struct UnstableFileStat { + /// The ID of the device containing the file. + pub device: u64, + /// The file serial number. + pub inode: u64, + /// The device ID. + pub rdev: u64, + /// The number of hard links to this file. + pub nlink: u64, + /// The user ID of the file. + pub uid: u64, + /// The group ID of the file. + pub gid: u64, + /// The optimal block size for I/O. + pub blksize: u64, + /// The blocks allocated for this file. + pub blocks: u64, + /// User-defined flags for the file. + pub flags: u64, + /// The file generation number. + pub gen: u64, +} + + +bitflags! { + /// A set of permissions for a file or directory is represented by a set of + /// flags which are or'd together. + #[derive(Show)] + flags FilePermission: u32 { + const USER_READ = 0o400, + const USER_WRITE = 0o200, + const USER_EXECUTE = 0o100, + const GROUP_READ = 0o040, + const GROUP_WRITE = 0o020, + const GROUP_EXECUTE = 0o010, + const OTHER_READ = 0o004, + const OTHER_WRITE = 0o002, + const OTHER_EXECUTE = 0o001, + + const USER_RWX = USER_READ.bits | USER_WRITE.bits | USER_EXECUTE.bits, + const GROUP_RWX = GROUP_READ.bits | GROUP_WRITE.bits | GROUP_EXECUTE.bits, + const OTHER_RWX = OTHER_READ.bits | OTHER_WRITE.bits | OTHER_EXECUTE.bits, + + /// Permissions for user owned files, equivalent to 0644 on unix-like + /// systems. + const USER_FILE = USER_READ.bits | USER_WRITE.bits | GROUP_READ.bits | OTHER_READ.bits, + + /// Permissions for user owned directories, equivalent to 0755 on + /// unix-like systems. + const USER_DIR = USER_RWX.bits | GROUP_READ.bits | GROUP_EXECUTE.bits | + OTHER_READ.bits | OTHER_EXECUTE.bits, + + /// Permissions for user owned executables, equivalent to 0755 + /// on unix-like systems. + const USER_EXEC = USER_DIR.bits, + + /// All possible permissions enabled. + const ALL_PERMISSIONS = USER_RWX.bits | GROUP_RWX.bits | OTHER_RWX.bits, + } +} + + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for FilePermission { + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + fn default() -> FilePermission { FilePermission::empty() } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for FilePermission { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:04o}", self.bits) + } +} + +#[cfg(test)] +mod tests { + use self::BadReaderBehavior::*; + use super::{IoResult, Reader, MemReader, NoProgress, InvalidInput, Writer}; + use prelude::v1::{Ok, Vec, Buffer, SliceExt}; + use uint; + + #[derive(Clone, PartialEq, Show)] + enum BadReaderBehavior { + GoodBehavior(uint), + BadBehavior(uint) + } + + struct BadReader<T> { + r: T, + behavior: Vec<BadReaderBehavior>, + } + + impl<T: Reader> BadReader<T> { + fn new(r: T, behavior: Vec<BadReaderBehavior>) -> BadReader<T> { + BadReader { behavior: behavior, r: r } + } + } + + impl<T: Reader> Reader for BadReader<T> { + fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { + let BadReader { ref mut behavior, ref mut r } = *self; + loop { + if behavior.is_empty() { + // fall back on good + return r.read(buf); + } + match behavior.as_mut_slice()[0] { + GoodBehavior(0) => (), + GoodBehavior(ref mut x) => { + *x -= 1; + return r.read(buf); + } + BadBehavior(0) => (), + BadBehavior(ref mut x) => { + *x -= 1; + return Ok(0); + } + }; + behavior.remove(0); + } + } + } + + #[test] + fn test_read_at_least() { + let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), + vec![GoodBehavior(uint::MAX)]); + let buf = &mut [0u8; 5]; + assert!(r.read_at_least(1, buf).unwrap() >= 1); + assert!(r.read_exact(5).unwrap().len() == 5); // read_exact uses read_at_least + assert!(r.read_at_least(0, buf).is_ok()); + + let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), + vec![BadBehavior(50), GoodBehavior(uint::MAX)]); + assert!(r.read_at_least(1, buf).unwrap() >= 1); + + let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), + vec![BadBehavior(1), GoodBehavior(1), + BadBehavior(50), GoodBehavior(uint::MAX)]); + assert!(r.read_at_least(1, buf).unwrap() >= 1); + assert!(r.read_at_least(1, buf).unwrap() >= 1); + + let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), + vec![BadBehavior(uint::MAX)]); + assert_eq!(r.read_at_least(1, buf).unwrap_err().kind, NoProgress); + + let mut r = MemReader::new(b"hello, world!".to_vec()); + assert_eq!(r.read_at_least(5, buf).unwrap(), 5); + assert_eq!(r.read_at_least(6, buf).unwrap_err().kind, InvalidInput); + } + + #[test] + fn test_push_at_least() { + let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), + vec![GoodBehavior(uint::MAX)]); + let mut buf = Vec::new(); + assert!(r.push_at_least(1, 5, &mut buf).unwrap() >= 1); + assert!(r.push_at_least(0, 5, &mut buf).is_ok()); + + let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), + vec![BadBehavior(50), GoodBehavior(uint::MAX)]); + assert!(r.push_at_least(1, 5, &mut buf).unwrap() >= 1); + + let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), + vec![BadBehavior(1), GoodBehavior(1), + BadBehavior(50), GoodBehavior(uint::MAX)]); + assert!(r.push_at_least(1, 5, &mut buf).unwrap() >= 1); + assert!(r.push_at_least(1, 5, &mut buf).unwrap() >= 1); + + let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), + vec![BadBehavior(uint::MAX)]); + assert_eq!(r.push_at_least(1, 5, &mut buf).unwrap_err().kind, NoProgress); + + let mut r = MemReader::new(b"hello, world!".to_vec()); + assert_eq!(r.push_at_least(5, 1, &mut buf).unwrap_err().kind, InvalidInput); + } + + #[test] + fn test_show() { + use super::*; + + assert_eq!(format!("{}", USER_READ), "0400"); + assert_eq!(format!("{}", USER_FILE), "0644"); + assert_eq!(format!("{}", USER_EXEC), "0755"); + assert_eq!(format!("{}", USER_RWX), "0700"); + assert_eq!(format!("{}", GROUP_RWX), "0070"); + assert_eq!(format!("{}", OTHER_RWX), "0007"); + assert_eq!(format!("{}", ALL_PERMISSIONS), "0777"); + assert_eq!(format!("{}", USER_READ | USER_WRITE | OTHER_WRITE), "0602"); + } + + fn _ensure_buffer_is_object_safe<T: Buffer>(x: &T) -> &Buffer { + x as &Buffer + } +} diff --git a/src/libstd/old_io/net/addrinfo.rs b/src/libstd/old_io/net/addrinfo.rs new file mode 100644 index 00000000000..9800cc6829e --- /dev/null +++ b/src/libstd/old_io/net/addrinfo.rs @@ -0,0 +1,137 @@ +// 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::IteratorExt; +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, Show)] +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, Show)] +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, Show)] +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, Show)] +pub struct Hint { + pub family: uint, + pub socktype: Option<SocketType>, + pub protocol: Option<Protocol>, + pub flags: uint, +} + +#[derive(Copy, Show)] +pub struct Info { + pub address: SocketAddr, + pub family: uint, + pub socktype: Option<SocketType>, + pub protocol: Option<Protocol>, + pub flags: uint, +} + +/// 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.iter() { + found_local = found_local || addr == local_addr; + } + assert!(found_local); + } + + #[ignore] + #[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. + get_host_addresses("example.com").unwrap(); + } +} diff --git a/src/libstd/old_io/net/ip.rs b/src/libstd/old_io/net/ip.rs new file mode 100644 index 00000000000..e60b455aecd --- /dev/null +++ b/src/libstd/old_io/net/ip.rs @@ -0,0 +1,700 @@ +// 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, IteratorExt}; +use ops::{FnOnce, FnMut}; +use option::Option; +use option::Option::{None, Some}; +use result::Result::{Ok, Err}; +use slice::SliceExt; +use str::{FromStr, StrExt}; +use vec::Vec; + +pub type Port = u16; + +#[derive(Copy, PartialEq, Eq, Clone, Hash, Show)] +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, Show)] +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: uint, +} + +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.iter_mut() { + 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 = 0u32; + 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 = [0u8; 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 = [0u16; 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: uint) -> (uint, 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 = [0u16; 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 = [0u16; 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 = |&mut: p: &mut Parser| p.read_ipv4_addr(); + let ipv6_addr = |&mut: p: &mut Parser| p.read_ipv6_addr(); + self.read_or(&mut [box ipv4_addr, box ipv6_addr]) + } + + fn read_socket_addr(&mut self) -> Option<SocketAddr> { + let ip_addr = |&: p: &mut Parser| { + let ipv4_p = |&mut: p: &mut Parser| p.read_ip_addr(); + let ipv6_p = |&mut: 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 [box ipv4_p, box 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 { + fn from_str(s: &str) -> Option<IpAddr> { + Parser::new(s).read_till_eof(|p| p.read_ip_addr()) + } +} + +impl FromStr for SocketAddr { + fn from_str(s: &str) -> Option<SocketAddr> { + Parser::new(s).read_till_eof(|p| p.read_socket_addr()) + } +} + +/// 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 +/// # #![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), 12345u16)); +/// let tcp_s = TcpStream::connect(("127.0.0.1", 12345u16)); +/// let tcp_s = TcpStream::connect(("localhost", 12345u16)); +/// 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", 23451u16)).unwrap(); +/// udp_s.send_to([7u8, 7u8, 7u8].as_slice(), (Ipv4Addr(127, 0, 0, 1), 23451u16)); +/// } +/// ``` +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!(FromStr::from_str(port_str), "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 FromStr::from_str(host) { + 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 FromStr::from_str(*self) { + 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 FromStr::from_str(*self) { + 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!(Some(Ipv4Addr(127, 0, 0, 1)), FromStr::from_str("127.0.0.1")); + assert_eq!(Some(Ipv4Addr(255, 255, 255, 255)), FromStr::from_str("255.255.255.255")); + assert_eq!(Some(Ipv4Addr(0, 0, 0, 0)), FromStr::from_str("0.0.0.0")); + + // out of range + let none: Option<IpAddr> = FromStr::from_str("256.0.0.1"); + assert_eq!(None, none); + // too short + let none: Option<IpAddr> = FromStr::from_str("255.0.0"); + assert_eq!(None, none); + // too long + let none: Option<IpAddr> = FromStr::from_str("255.0.0.1.2"); + assert_eq!(None, none); + // no number between dots + let none: Option<IpAddr> = FromStr::from_str("255.0..1"); + assert_eq!(None, none); + } + + #[test] + fn test_from_str_ipv6() { + assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 0)), FromStr::from_str("0:0:0:0:0:0:0:0")); + assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1)), FromStr::from_str("0:0:0:0:0:0:0:1")); + + assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1)), FromStr::from_str("::1")); + assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 0)), FromStr::from_str("::")); + + assert_eq!(Some(Ipv6Addr(0x2a02, 0x6b8, 0, 0, 0, 0, 0x11, 0x11)), + FromStr::from_str("2a02:6b8::11:11")); + + // too long group + let none: Option<IpAddr> = FromStr::from_str("::00000"); + assert_eq!(None, none); + // too short + let none: Option<IpAddr> = FromStr::from_str("1:2:3:4:5:6:7"); + assert_eq!(None, none); + // too long + let none: Option<IpAddr> = FromStr::from_str("1:2:3:4:5:6:7:8:9"); + assert_eq!(None, none); + // triple colon + let none: Option<IpAddr> = FromStr::from_str("1:2:::6:7:8"); + assert_eq!(None, none); + // two double colons + let none: Option<IpAddr> = FromStr::from_str("1:2::6::8"); + assert_eq!(None, none); + } + + #[test] + fn test_from_str_ipv4_in_ipv6() { + assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 49152, 545)), + FromStr::from_str("::192.0.2.33")); + assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0xFFFF, 49152, 545)), + FromStr::from_str("::FFFF:192.0.2.33")); + assert_eq!(Some(Ipv6Addr(0x64, 0xff9b, 0, 0, 0, 0, 49152, 545)), + FromStr::from_str("64:ff9b::192.0.2.33")); + assert_eq!(Some(Ipv6Addr(0x2001, 0xdb8, 0x122, 0xc000, 0x2, 0x2100, 49152, 545)), + FromStr::from_str("2001:db8:122:c000:2:2100:192.0.2.33")); + + // colon after v4 + let none: Option<IpAddr> = FromStr::from_str("::127.0.0.1:"); + assert_eq!(None, none); + // not enough groups + let none: Option<IpAddr> = FromStr::from_str("1.2.3.4.5:127.0.0.1"); + assert_eq!(None, none); + // too many groups + let none: Option<IpAddr> = + FromStr::from_str("1.2.3.4.5:6:7:127.0.0.1"); + assert_eq!(None, none); + } + + #[test] + fn test_from_str_socket_addr() { + assert_eq!(Some(SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 80 }), + FromStr::from_str("77.88.21.11:80")); + assert_eq!(Some(SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 }), + FromStr::from_str("[2a02:6b8:0:1::1]:53")); + assert_eq!(Some(SocketAddr { ip: Ipv6Addr(0, 0, 0, 0, 0, 0, 0x7F00, 1), port: 22 }), + FromStr::from_str("[::127.0.0.1]:22")); + + // without port + let none: Option<SocketAddr> = FromStr::from_str("127.0.0.1"); + assert_eq!(None, none); + // without port + let none: Option<SocketAddr> = FromStr::from_str("127.0.0.1:"); + assert_eq!(None, none); + // wrong brackets around v4 + let none: Option<SocketAddr> = FromStr::from_str("[127.0.0.1]:22"); + assert_eq!(None, none); + // port out of range + let none: Option<SocketAddr> = FromStr::from_str("127.0.0.1:123456"); + 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 = 12345u16; + 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", 24352u16).to_socket_addr()); + assert_eq!(Ok(vec![a]), ("77.88.21.11", 24352u16).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", 23924u16).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)); + } +} diff --git a/src/libstd/old_io/net/mod.rs b/src/libstd/old_io/net/mod.rs new file mode 100644 index 00000000000..d8394aa8b6a --- /dev/null +++ b/src/libstd/old_io/net/mod.rs @@ -0,0 +1,46 @@ +// 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 + +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.into_iter() { + match action(addr) { + Ok(r) => return Ok(r), + Err(e) => err = e + } + } + Err(err) +} diff --git a/src/libstd/old_io/net/pipe.rs b/src/libstd/old_io/net/pipe.rs new file mode 100644 index 00000000000..71b77adcd96 --- /dev/null +++ b/src/libstd/old_io/net/pipe.rs @@ -0,0 +1,869 @@ +// 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)] + +use prelude::v1::*; + +use ffi::CString; +use path::BytesContainer; +use old_io::{Listener, Acceptor, IoResult, TimedOut, standard_error}; +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. + /// + /// # Example + /// + /// ```rust + /// # #![allow(unused_must_use)] + /// use std::old_io::net::pipe::UnixStream; + /// + /// 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 = CString::from_slice(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 = CString::from_slice(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<uint> { + 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. + /// + /// # Example + /// + /// ``` + /// # fn foo() { + /// use std::old_io::net::pipe::UnixListener; + /// use std::old_io::{Listener, Acceptor}; + /// + /// let server = Path::new("/path/to/my/socket"); + /// let stream = UnixListener::bind(&server); + /// for mut client in stream.listen().incoming() { + /// client.write(&[1, 2, 3, 4]); + /// } + /// # } + /// ``` + pub fn bind<P: BytesContainer>(path: P) -> IoResult<UnixListener> { + let path = CString::from_slice(path.container_as_bytes()); + UnixListenerImp::bind(&path) + .map(|inner| UnixListener { inner: inner }) + } +} + +impl Listener<UnixStream, 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<UnixStream> for UnixAcceptor { + 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::test::*; + use super::*; + use sync::mpsc::channel; + use thread::Thread; + use time::Duration; + + pub fn smalltest<F,G>(server: F, client: G) + where F : FnOnce(UnixStream), F : Send, + G : FnOnce(UnixStream), G : Send + { + 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 range(0u, times) { + let mut stream = UnixStream::connect(&path2); + match stream.write(&[100]) { + Ok(..) => {} + Err(e) => panic!("failed write: {}", e) + } + } + }); + + for _ in range(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 range(0u, 1001) { + match a.accept() { + Ok(..) => break, + Err(ref e) if e.kind == TimedOut => {} + Err(e) => panic!("error: {}", e), + } + ::thread::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 range(0u, 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 range(0u, 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 range(0u, 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); + } +} diff --git a/src/libstd/old_io/net/tcp.rs b/src/libstd/old_io/net/tcp.rs new file mode 100644 index 00000000000..1e76bb3ab0d --- /dev/null +++ b/src/libstd/old_io/net/tcp.rs @@ -0,0 +1,1477 @@ +// 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. + +//! TCP network connections +//! +//! This module contains the ability to open a TCP stream to a socket address, +//! as well as creating a socket server to accept incoming connections. The +//! destination and binding addresses can either be an IPv4 or IPv6 address. +//! +//! A TCP connection implements the `Reader` and `Writer` traits, while the TCP +//! listener (socket server) implements the `Listener` and `Acceptor` traits. + +use clone::Clone; +use old_io::IoResult; +use result::Result::Err; +use old_io::net::ip::{SocketAddr, ToSocketAddr}; +use old_io::{Reader, Writer, Listener, Acceptor}; +use old_io::{standard_error, TimedOut}; +use option::Option; +use option::Option::{None, Some}; +use time::Duration; + +use sys::tcp::TcpStream as TcpStreamImp; +use sys::tcp::TcpListener as TcpListenerImp; +use sys::tcp::TcpAcceptor as TcpAcceptorImp; + +use sys_common; + +/// A structure which represents a TCP stream between a local socket and a +/// remote socket. +/// +/// The socket will be closed when the value is dropped. +/// +/// # Example +/// +/// ```no_run +/// use std::old_io::TcpStream; +/// +/// { +/// let mut stream = TcpStream::connect("127.0.0.1:34254"); +/// +/// // ignore the Result +/// let _ = stream.write(&[1]); +/// +/// let mut buf = [0]; +/// let _ = stream.read(&mut buf); // ignore here too +/// } // the stream is closed here +/// ``` +pub struct TcpStream { + inner: TcpStreamImp, +} + +impl TcpStream { + fn new(s: TcpStreamImp) -> TcpStream { + TcpStream { inner: s } + } + + /// Open a TCP connection to a remote host. + /// + /// `addr` is an address of the remote host. Anything which implements `ToSocketAddr` + /// trait can be supplied for the address; see this trait documentation for + /// concrete examples. + pub fn connect<A: ToSocketAddr>(addr: A) -> IoResult<TcpStream> { + super::with_addresses(addr, |addr| { + TcpStreamImp::connect(addr, None).map(TcpStream::new) + }) + } + + /// Creates a TCP connection to a remote socket address, timing out after + /// the specified duration. + /// + /// This is the same as the `connect` method, except that if the timeout + /// specified elapses before a connection is made an error will be + /// returned. The error's kind will be `TimedOut`. + /// + /// Same as the `connect` method, `addr` argument type can be anything which + /// implements `ToSocketAddr` trait. + /// + /// 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 may eventually change types")] + pub fn connect_timeout<A: ToSocketAddr>(addr: A, + timeout: Duration) -> IoResult<TcpStream> { + if timeout <= Duration::milliseconds(0) { + return Err(standard_error(TimedOut)); + } + + super::with_addresses(addr, |addr| { + TcpStreamImp::connect(addr, Some(timeout.num_milliseconds() as u64)) + .map(TcpStream::new) + }) + } + + /// Returns the socket address of the remote peer of this TCP connection. + pub fn peer_name(&mut self) -> IoResult<SocketAddr> { + self.inner.peer_name() + } + + /// Returns the socket address of the local half of this TCP connection. + pub fn socket_name(&mut self) -> IoResult<SocketAddr> { + self.inner.socket_name() + } + + /// Sets the nodelay flag on this connection to the boolean specified + #[unstable(feature = "io")] + pub fn set_nodelay(&mut self, nodelay: bool) -> IoResult<()> { + self.inner.set_nodelay(nodelay) + } + + /// Sets the keepalive timeout to the timeout specified. + /// + /// If the value specified is `None`, then the keepalive flag is cleared on + /// this connection. Otherwise, the keepalive timeout will be set to the + /// specified time, in seconds. + #[unstable(feature = "io")] + pub fn set_keepalive(&mut self, delay_in_seconds: Option<uint>) -> IoResult<()> { + self.inner.set_keepalive(delay_in_seconds) + } + + /// 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. + /// + /// # Example + /// + /// ```no_run + /// # #![allow(unused_must_use)] + /// use std::old_io::timer; + /// use std::old_io::TcpStream; + /// use std::time::Duration; + /// use std::thread::Thread; + /// + /// let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap(); + /// let stream2 = stream.clone(); + /// + /// let _t = Thread::spawn(move|| { + /// // close this stream after one second + /// timer::sleep(Duration::seconds(1)); + /// let mut stream = stream2; + /// stream.close_read(); + /// }); + /// + /// // wait for some data, will get canceled after one second + /// let mut buf = [0]; + /// stream.read(&mut buf); + /// ``` + /// + /// 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 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 a timeout, in milliseconds, for blocking operations on this stream. + /// + /// This function will set a timeout for all blocking operations (including + /// reads and writes) on this stream. The timeout specified is a relative + /// time, in milliseconds, into the future after which point operations will + /// time out. This means that the timeout must be reset periodically to keep + /// it from expiring. Specifying a value of `None` will clear the timeout + /// for this stream. + /// + /// The timeout on this stream is local to this stream only. Setting a + /// timeout does not affect any other cloned instances of this stream, nor + /// does the timeout propagated to cloned handles of this stream. Setting + /// this timeout will override any specific read or write timeouts + /// previously set for this stream. + /// + /// For clarification on the semantics of interrupting a read and a write, + /// take a look at `set_read_timeout` and `set_write_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 timeout for read operations on this stream. + /// + /// See documentation in `set_timeout` for the semantics of this read time. + /// This will overwrite any previous read timeout set through either this + /// function or `set_timeout`. + /// + /// # Errors + /// + /// When this timeout expires, if there is no pending read operation, no + /// action is taken. Otherwise, the read operation will be scheduled to + /// promptly return. If a timeout error is returned, then no data was read + /// during the timeout period. + #[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 timeout for write operations on this stream. + /// + /// See documentation in `set_timeout` for the semantics of this write time. + /// This will overwrite any previous write timeout set through either this + /// function or `set_timeout`. + /// + /// # Errors + /// + /// When this timeout expires, if there is no pending write operation, no + /// action is taken. Otherwise, the pending write operation will be + /// scheduled to promptly return. The actual state of the underlying stream + /// is not specified. + /// + /// The write operation may return an error of type `ShortWrite` which + /// indicates that the object is known to have written an exact number of + /// bytes successfully during the timeout period, and the remaining bytes + /// were never written. + /// + /// If the write operation returns `TimedOut`, then it the timeout primitive + /// does not know how many bytes were written as part of the timeout + /// operation. It may be the case that bytes continue to be written in an + /// asynchronous fashion after the call to write returns. + #[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 TcpStream { + /// Creates a new handle to this TCP stream, allowing for simultaneous reads + /// and writes of this connection. + /// + /// The underlying TCP stream will not be closed until all handles to the + /// stream have been deallocated. All handles will also follow the same + /// stream, but 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) -> TcpStream { + TcpStream { inner: self.inner.clone() } + } +} + +impl Reader for TcpStream { + fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { + self.inner.read(buf) + } +} + +impl Writer for TcpStream { + fn write_all(&mut self, buf: &[u8]) -> IoResult<()> { + self.inner.write(buf) + } +} + +impl sys_common::AsInner<TcpStreamImp> for TcpStream { + fn as_inner(&self) -> &TcpStreamImp { + &self.inner + } +} + +/// A structure representing a socket server. This listener is used to create a +/// `TcpAcceptor` which can be used to accept sockets on a local port. +/// +/// # Examples +/// +/// ``` +/// # fn foo() { +/// use std::old_io::{TcpListener, TcpStream}; +/// use std::old_io::{Acceptor, Listener}; +/// use std::thread::Thread; +/// +/// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); +/// +/// // bind the listener to the specified address +/// let mut acceptor = listener.listen().unwrap(); +/// +/// fn handle_client(mut stream: TcpStream) { +/// // ... +/// # &mut stream; // silence unused mutability/variable warning +/// } +/// // accept connections and process them, spawning a new tasks for each one +/// for stream in acceptor.incoming() { +/// match stream { +/// Err(e) => { /* connection failed */ } +/// Ok(stream) => { +/// Thread::spawn(move|| { +/// // connection succeeded +/// handle_client(stream) +/// }); +/// } +/// } +/// } +/// +/// // close the socket server +/// drop(acceptor); +/// # } +/// ``` +pub struct TcpListener { + inner: TcpListenerImp, +} + +impl TcpListener { + /// Creates a new `TcpListener` which will be bound to the specified address. + /// This listener is not ready for accepting connections, `listen` must be called + /// on it before that's possible. + /// + /// Binding with a port number of 0 will request that the OS assigns a port + /// to this listener. The port allocated can be queried via the + /// `socket_name` function. + /// + /// The address type can be any implementer of `ToSocketAddr` trait. See its + /// documentation for concrete examples. + pub fn bind<A: ToSocketAddr>(addr: A) -> IoResult<TcpListener> { + super::with_addresses(addr, |addr| { + TcpListenerImp::bind(addr).map(|inner| TcpListener { inner: inner }) + }) + } + + /// Returns the local socket address of this listener. + pub fn socket_name(&mut self) -> IoResult<SocketAddr> { + self.inner.socket_name() + } +} + +impl Listener<TcpStream, TcpAcceptor> for TcpListener { + fn listen(self) -> IoResult<TcpAcceptor> { + self.inner.listen(128).map(|a| TcpAcceptor { inner: a }) + } +} + +impl sys_common::AsInner<TcpListenerImp> for TcpListener { + fn as_inner(&self) -> &TcpListenerImp { + &self.inner + } +} + +/// The accepting half of a TCP socket server. This structure is created through +/// a `TcpListener`'s `listen` method, and this object can be used to accept new +/// `TcpStream` instances. +pub struct TcpAcceptor { + inner: TcpAcceptorImp, +} + +impl TcpAcceptor { + /// Prevents blocking on all future accepts after `ms` milliseconds have + /// elapsed. + /// + /// This function is used to set a deadline after which this acceptor will + /// time out accepting any connections. The argument is the relative + /// distance, in milliseconds, to a point in the future after which all + /// accepts will fail. + /// + /// If the argument specified is `None`, then any previously registered + /// timeout is cleared. + /// + /// A timeout of `0` can be used to "poll" this acceptor to see if it has + /// any pending connections. All pending connections will be accepted, + /// regardless of whether the timeout has expired or not (the accept will + /// not block in this case). + /// + /// # Example + /// + /// ```no_run + /// use std::old_io::TcpListener; + /// use std::old_io::{Listener, Acceptor, TimedOut}; + /// + /// let mut a = TcpListener::bind("127.0.0.1:8482").listen().unwrap(); + /// + /// // After 100ms have passed, all accepts will fail + /// a.set_timeout(Some(100)); + /// + /// match a.accept() { + /// Ok(..) => println!("accepted a socket"), + /// Err(ref e) if e.kind == TimedOut => { println!("timed out!"); } + /// Err(e) => println!("err: {}", e), + /// } + /// + /// // Reset the timeout and try again + /// a.set_timeout(Some(100)); + /// let socket = a.accept(); + /// + /// // Clear the timeout and block indefinitely waiting for a connection + /// a.set_timeout(None); + /// let socket = a.accept(); + /// ``` + #[unstable(feature = "io", + reason = "the type of the argument and name of this function are \ + subject to change")] + pub fn set_timeout(&mut self, ms: Option<u64>) { self.inner.set_timeout(ms); } + + /// Closes the accepting capabilities of this acceptor. + /// + /// This function is similar to `TcpStream`'s `close_{read,write}` methods + /// in that it will affect *all* cloned handles of this acceptor's original + /// handle. + /// + /// Once this function succeeds, all future calls to `accept` will return + /// immediately with an error, preventing all future calls to accept. The + /// underlying socket will not be relinquished back to the OS until all + /// acceptors have been deallocated. + /// + /// This is useful for waking up a thread in an accept loop to indicate that + /// it should exit. + /// + /// # Example + /// + /// ``` + /// use std::old_io::{TcpListener, Listener, Acceptor, EndOfFile}; + /// use std::thread::Thread; + /// + /// let mut a = TcpListener::bind("127.0.0.1:8482").listen().unwrap(); + /// let a2 = a.clone(); + /// + /// let _t = Thread::spawn(move|| { + /// let mut a2 = a2; + /// for socket in a2.incoming() { + /// match socket { + /// Ok(s) => { /* handle s */ } + /// Err(ref e) if e.kind == EndOfFile => break, // closed + /// Err(e) => panic!("unexpected error: {}", e), + /// } + /// } + /// }); + /// + /// # fn wait_for_sigint() {} + /// // Now that our accept loop is running, wait for the program to be + /// // requested to exit. + /// wait_for_sigint(); + /// + /// // Signal our accept loop to exit + /// assert!(a.close_accept().is_ok()); + /// ``` + #[unstable(feature = "io")] + pub fn close_accept(&mut self) -> IoResult<()> { + self.inner.close_accept() + } +} + +impl Acceptor<TcpStream> for TcpAcceptor { + fn accept(&mut self) -> IoResult<TcpStream> { + self.inner.accept().map(TcpStream::new) + } +} + +impl Clone for TcpAcceptor { + /// Creates a new handle to this TCP acceptor, allowing for simultaneous + /// accepts. + /// + /// The underlying TCP 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) -> TcpAcceptor { + TcpAcceptor { inner: self.inner.clone() } + } +} + +impl sys_common::AsInner<TcpAcceptorImp> for TcpAcceptor { + fn as_inner(&self) -> &TcpAcceptorImp { + &self.inner + } +} + +#[cfg(test)] +mod test { + use prelude::v1::*; + + use sync::mpsc::channel; + use thread::Thread; + use old_io::net::tcp::*; + use old_io::net::ip::*; + use old_io::test::*; + use old_io::{EndOfFile, TimedOut, ShortWrite, IoError}; + use old_io::{ConnectionRefused, BrokenPipe, ConnectionAborted}; + use old_io::{ConnectionReset, NotConnected, PermissionDenied, OtherIoError}; + use old_io::{Acceptor, Listener}; + + // FIXME #11530 this fails on android because tests are run as root + #[cfg_attr(any(windows, target_os = "android"), ignore)] + #[test] + fn bind_error() { + match TcpListener::bind("0.0.0.0:1") { + Ok(..) => panic!(), + Err(e) => assert_eq!(e.kind, PermissionDenied), + } + } + + #[test] + fn connect_error() { + match TcpStream::connect("0.0.0.0:1") { + Ok(..) => panic!(), + Err(e) => assert_eq!(e.kind, ConnectionRefused), + } + } + + #[test] + fn listen_ip4_localhost() { + let socket_addr = next_test_ip4(); + let listener = TcpListener::bind(socket_addr); + let mut acceptor = listener.listen(); + + let _t = Thread::spawn(move|| { + let mut stream = TcpStream::connect(("localhost", socket_addr.port)); + stream.write(&[144]).unwrap(); + }); + + let mut stream = acceptor.accept(); + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert!(buf[0] == 144); + } + + #[test] + fn connect_localhost() { + let addr = next_test_ip4(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let mut stream = TcpStream::connect(("localhost", addr.port)); + stream.write(&[64]).unwrap(); + }); + + let mut stream = acceptor.accept(); + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert!(buf[0] == 64); + } + + #[test] + fn connect_ip4_loopback() { + let addr = next_test_ip4(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let mut stream = TcpStream::connect(("127.0.0.1", addr.port)); + stream.write(&[44]).unwrap(); + }); + + let mut stream = acceptor.accept(); + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert!(buf[0] == 44); + } + + #[test] + fn connect_ip6_loopback() { + let addr = next_test_ip6(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let mut stream = TcpStream::connect(("::1", addr.port)); + stream.write(&[66]).unwrap(); + }); + + let mut stream = acceptor.accept(); + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert!(buf[0] == 66); + } + + #[test] + fn smoke_test_ip4() { + let addr = next_test_ip4(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let mut stream = TcpStream::connect(addr); + stream.write(&[99]).unwrap(); + }); + + let mut stream = acceptor.accept(); + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert!(buf[0] == 99); + } + + #[test] + fn smoke_test_ip6() { + let addr = next_test_ip6(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let mut stream = TcpStream::connect(addr); + stream.write(&[99]).unwrap(); + }); + + let mut stream = acceptor.accept(); + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert!(buf[0] == 99); + } + + #[test] + fn read_eof_ip4() { + let addr = next_test_ip4(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let _stream = TcpStream::connect(addr); + // Close + }); + + let mut stream = acceptor.accept(); + let mut buf = [0]; + let nread = stream.read(&mut buf); + assert!(nread.is_err()); + } + + #[test] + fn read_eof_ip6() { + let addr = next_test_ip6(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let _stream = TcpStream::connect(addr); + // Close + }); + + let mut stream = acceptor.accept(); + let mut buf = [0]; + let nread = stream.read(&mut buf); + assert!(nread.is_err()); + } + + #[test] + fn read_eof_twice_ip4() { + let addr = next_test_ip4(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let _stream = TcpStream::connect(addr); + // Close + }); + + let mut stream = acceptor.accept(); + let mut buf = [0]; + let nread = stream.read(&mut buf); + assert!(nread.is_err()); + + match stream.read(&mut buf) { + Ok(..) => panic!(), + Err(ref e) => { + assert!(e.kind == NotConnected || e.kind == EndOfFile, + "unknown kind: {:?}", e.kind); + } + } + } + + #[test] + fn read_eof_twice_ip6() { + let addr = next_test_ip6(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let _stream = TcpStream::connect(addr); + // Close + }); + + let mut stream = acceptor.accept(); + let mut buf = [0]; + let nread = stream.read(&mut buf); + assert!(nread.is_err()); + + match stream.read(&mut buf) { + Ok(..) => panic!(), + Err(ref e) => { + assert!(e.kind == NotConnected || e.kind == EndOfFile, + "unknown kind: {:?}", e.kind); + } + } + } + + #[test] + fn write_close_ip4() { + let addr = next_test_ip4(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let (tx, rx) = channel(); + let _t = Thread::spawn(move|| { + drop(TcpStream::connect(addr)); + tx.send(()).unwrap(); + }); + + let mut stream = acceptor.accept(); + rx.recv().unwrap(); + let buf = [0]; + match stream.write(&buf) { + Ok(..) => {} + Err(e) => { + assert!(e.kind == ConnectionReset || + e.kind == BrokenPipe || + e.kind == ConnectionAborted, + "unknown error: {}", e); + } + } + } + + #[test] + fn write_close_ip6() { + let addr = next_test_ip6(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let (tx, rx) = channel(); + let _t = Thread::spawn(move|| { + drop(TcpStream::connect(addr)); + tx.send(()).unwrap(); + }); + + let mut stream = acceptor.accept(); + rx.recv().unwrap(); + let buf = [0]; + match stream.write(&buf) { + Ok(..) => {} + Err(e) => { + assert!(e.kind == ConnectionReset || + e.kind == BrokenPipe || + e.kind == ConnectionAborted, + "unknown error: {}", e); + } + } + } + + #[test] + fn multiple_connect_serial_ip4() { + let addr = next_test_ip4(); + let max = 10u; + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + for _ in range(0, max) { + let mut stream = TcpStream::connect(addr); + stream.write(&[99]).unwrap(); + } + }); + + for ref mut stream in acceptor.incoming().take(max) { + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert_eq!(buf[0], 99); + } + } + + #[test] + fn multiple_connect_serial_ip6() { + let addr = next_test_ip6(); + let max = 10u; + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + for _ in range(0, max) { + let mut stream = TcpStream::connect(addr); + stream.write(&[99]).unwrap(); + } + }); + + for ref mut stream in acceptor.incoming().take(max) { + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert_eq!(buf[0], 99); + } + } + + #[test] + fn multiple_connect_interleaved_greedy_schedule_ip4() { + let addr = next_test_ip4(); + static MAX: int = 10; + let acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let mut acceptor = acceptor; + for (i, stream) in acceptor.incoming().enumerate().take(MAX as uint) { + // Start another task to handle the connection + let _t = Thread::spawn(move|| { + let mut stream = stream; + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert!(buf[0] == i as u8); + debug!("read"); + }); + } + }); + + connect(0, addr); + + fn connect(i: int, addr: SocketAddr) { + if i == MAX { return } + + let _t = Thread::spawn(move|| { + debug!("connecting"); + let mut stream = TcpStream::connect(addr); + // Connect again before writing + connect(i + 1, addr); + debug!("writing"); + stream.write(&[i as u8]).unwrap(); + }); + } + } + + #[test] + fn multiple_connect_interleaved_greedy_schedule_ip6() { + let addr = next_test_ip6(); + static MAX: int = 10; + let acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let mut acceptor = acceptor; + for (i, stream) in acceptor.incoming().enumerate().take(MAX as uint) { + // Start another task to handle the connection + let _t = Thread::spawn(move|| { + let mut stream = stream; + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert!(buf[0] == i as u8); + debug!("read"); + }); + } + }); + + connect(0, addr); + + fn connect(i: int, addr: SocketAddr) { + if i == MAX { return } + + let _t = Thread::spawn(move|| { + debug!("connecting"); + let mut stream = TcpStream::connect(addr); + // Connect again before writing + connect(i + 1, addr); + debug!("writing"); + stream.write(&[i as u8]).unwrap(); + }); + } + } + + #[test] + fn multiple_connect_interleaved_lazy_schedule_ip4() { + static MAX: int = 10; + let addr = next_test_ip4(); + let acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let mut acceptor = acceptor; + for stream in acceptor.incoming().take(MAX as uint) { + // Start another task to handle the connection + let _t = Thread::spawn(move|| { + let mut stream = stream; + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert!(buf[0] == 99); + debug!("read"); + }); + } + }); + + connect(0, addr); + + fn connect(i: int, addr: SocketAddr) { + if i == MAX { return } + + let _t = Thread::spawn(move|| { + debug!("connecting"); + let mut stream = TcpStream::connect(addr); + // Connect again before writing + connect(i + 1, addr); + debug!("writing"); + stream.write(&[99]).unwrap(); + }); + } + } + + #[test] + fn multiple_connect_interleaved_lazy_schedule_ip6() { + static MAX: int = 10; + let addr = next_test_ip6(); + let acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let mut acceptor = acceptor; + for stream in acceptor.incoming().take(MAX as uint) { + // Start another task to handle the connection + let _t = Thread::spawn(move|| { + let mut stream = stream; + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert!(buf[0] == 99); + debug!("read"); + }); + } + }); + + connect(0, addr); + + fn connect(i: int, addr: SocketAddr) { + if i == MAX { return } + + let _t = Thread::spawn(move|| { + debug!("connecting"); + let mut stream = TcpStream::connect(addr); + // Connect again before writing + connect(i + 1, addr); + debug!("writing"); + stream.write(&[99]).unwrap(); + }); + } + } + + pub fn socket_name(addr: SocketAddr) { + let mut listener = TcpListener::bind(addr).unwrap(); + + // Make sure socket_name gives + // us the socket we binded to. + let so_name = listener.socket_name(); + assert!(so_name.is_ok()); + assert_eq!(addr, so_name.unwrap()); + } + + pub fn peer_name(addr: SocketAddr) { + let acceptor = TcpListener::bind(addr).listen(); + let _t = Thread::spawn(move|| { + let mut acceptor = acceptor; + acceptor.accept().unwrap(); + }); + + let stream = TcpStream::connect(addr); + + assert!(stream.is_ok()); + let mut stream = stream.unwrap(); + + // Make sure peer_name gives us the + // address/port of the peer we've + // connected to. + let peer_name = stream.peer_name(); + assert!(peer_name.is_ok()); + assert_eq!(addr, peer_name.unwrap()); + } + + #[test] + fn socket_and_peer_name_ip4() { + peer_name(next_test_ip4()); + socket_name(next_test_ip4()); + } + + #[test] + fn socket_and_peer_name_ip6() { + // FIXME: peer name is not consistent + //peer_name(next_test_ip6()); + socket_name(next_test_ip6()); + } + + #[test] + fn partial_read() { + let addr = next_test_ip4(); + let (tx, rx) = channel(); + let _t = Thread::spawn(move|| { + let mut srv = TcpListener::bind(addr).listen().unwrap(); + tx.send(()).unwrap(); + let mut cl = srv.accept().unwrap(); + cl.write(&[10]).unwrap(); + let mut b = [0]; + cl.read(&mut b).unwrap(); + tx.send(()).unwrap(); + }); + + rx.recv().unwrap(); + let mut c = TcpStream::connect(addr).unwrap(); + let mut b = [0; 10]; + assert_eq!(c.read(&mut b), Ok(1)); + c.write(&[1]).unwrap(); + rx.recv().unwrap(); + } + + #[test] + fn double_bind() { + let addr = next_test_ip4(); + let listener = TcpListener::bind(addr).unwrap().listen(); + assert!(listener.is_ok()); + match TcpListener::bind(addr).listen() { + Ok(..) => panic!(), + Err(e) => { + assert!(e.kind == ConnectionRefused || e.kind == OtherIoError, + "unknown error: {} {:?}", e, e.kind); + } + } + } + + #[test] + fn fast_rebind() { + let addr = next_test_ip4(); + let (tx, rx) = channel(); + + let _t = Thread::spawn(move|| { + rx.recv().unwrap(); + let _stream = TcpStream::connect(addr).unwrap(); + // Close + rx.recv().unwrap(); + }); + + { + let mut acceptor = TcpListener::bind(addr).listen(); + tx.send(()).unwrap(); + { + let _stream = acceptor.accept().unwrap(); + // Close client + tx.send(()).unwrap(); + } + // Close listener + } + let _listener = TcpListener::bind(addr); + } + + #[test] + fn tcp_clone_smoke() { + let addr = next_test_ip4(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let mut s = TcpStream::connect(addr); + let mut buf = [0, 0]; + assert_eq!(s.read(&mut buf), Ok(1)); + assert_eq!(buf[0], 1); + s.write(&[2]).unwrap(); + }); + + 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(); + s2.write(&[1]).unwrap(); + tx2.send(()).unwrap(); + }); + tx1.send(()).unwrap(); + let mut buf = [0, 0]; + assert_eq!(s1.read(&mut buf), Ok(1)); + rx2.recv().unwrap(); + } + + #[test] + fn tcp_clone_two_read() { + let addr = next_test_ip6(); + let mut acceptor = TcpListener::bind(addr).listen(); + let (tx1, rx) = channel(); + let tx2 = tx1.clone(); + + let _t = Thread::spawn(move|| { + let mut s = TcpStream::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 tcp_clone_two_write() { + let addr = next_test_ip4(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let mut s = TcpStream::connect(addr); + let mut buf = [0, 1]; + s.read(&mut buf).unwrap(); + s.read(&mut buf).unwrap(); + }); + + let mut s1 = acceptor.accept().unwrap(); + let s2 = s1.clone(); + + let (done, rx) = channel(); + let _t = Thread::spawn(move|| { + let mut s2 = s2; + s2.write(&[1]).unwrap(); + done.send(()).unwrap(); + }); + s1.write(&[2]).unwrap(); + + rx.recv().unwrap(); + } + + #[test] + fn shutdown_smoke() { + let addr = next_test_ip4(); + let a = TcpListener::bind(addr).unwrap().listen(); + let _t = Thread::spawn(move|| { + let mut a = a; + let mut c = a.accept().unwrap(); + assert_eq!(c.read_to_end(), Ok(vec!())); + c.write(&[1]).unwrap(); + }); + + let mut s = TcpStream::connect(addr).unwrap(); + assert!(s.inner.close_write().is_ok()); + assert!(s.write(&[1]).is_err()); + assert_eq!(s.read_to_end(), Ok(vec!(1))); + } + + #[test] + fn accept_timeout() { + let addr = next_test_ip4(); + let mut a = TcpListener::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. + // + // FIXME: freebsd apparently never sees the pending connection, but + // testing manually always works. Need to investigate this + // flakiness. + if !cfg!(target_os = "freebsd") { + let (tx, rx) = channel(); + let _t = Thread::spawn(move|| { + tx.send(TcpStream::connect(addr).unwrap()).unwrap(); + }); + let _l = rx.recv().unwrap(); + for i in range(0i, 1001) { + match a.accept() { + Ok(..) => break, + Err(ref e) if e.kind == TimedOut => {} + Err(e) => panic!("error: {}", e), + } + ::thread::Thread::yield_now(); + if i == 1000 { panic!("should have a pending connection") } + } + } + + // Unset the timeout and make sure that this always blocks. + a.set_timeout(None); + let _t = Thread::spawn(move|| { + drop(TcpStream::connect(addr).unwrap()); + }); + a.accept().unwrap(); + } + + #[test] + fn close_readwrite_smoke() { + let addr = next_test_ip4(); + let a = TcpListener::bind(addr).listen().unwrap(); + let (_tx, rx) = channel::<()>(); + Thread::spawn(move|| { + let mut a = a; + let _s = a.accept().unwrap(); + let _ = rx.recv().unwrap(); + }); + + let mut b = [0]; + let mut s = TcpStream::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_ip4(); + let a = TcpListener::bind(addr).listen().unwrap(); + let (_tx, rx) = channel::<()>(); + Thread::spawn(move|| { + let mut a = a; + let _s = a.accept().unwrap(); + let _ = rx.recv().unwrap(); + }); + + let mut s = TcpStream::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_ip6(); + let mut a = TcpListener::bind(addr).listen().unwrap(); + let (tx, rx) = channel::<()>(); + Thread::spawn(move|| { + let mut s = TcpStream::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 range(0i, 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?!"); } + } + 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_ip6(); + let mut a = TcpListener::bind(addr).listen().unwrap(); + let (tx, rx) = channel::<()>(); + Thread::spawn(move|| { + let mut s = TcpStream::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 range(0i, 100) { + assert!(s.write(&[0;128 * 1024]).is_ok()); + } + } + + #[test] + fn write_timeouts() { + let addr = next_test_ip6(); + let mut a = TcpListener::bind(addr).listen().unwrap(); + let (tx, rx) = channel::<()>(); + Thread::spawn(move|| { + let mut s = TcpStream::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 range(0i, 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?!"); } + } + assert_eq!(s.write(&[0]).err().unwrap().kind, TimedOut); + + tx.send(()).unwrap(); + assert!(s.read(&mut [0]).is_ok()); + } + + #[test] + fn timeout_concurrent_read() { + let addr = next_test_ip6(); + let mut a = TcpListener::bind(addr).listen().unwrap(); + let (tx, rx) = channel::<()>(); + Thread::spawn(move|| { + let mut s = TcpStream::connect(addr).unwrap(); + rx.recv().unwrap(); + assert_eq!(s.write(&[0]), 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_eq!(s2.read(&mut [0]), Ok(1)); + 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(); + } + + #[test] + fn clone_while_reading() { + let addr = next_test_ip6(); + let listen = TcpListener::bind(addr); + let mut accept = listen.listen().unwrap(); + + // Enqueue a task to write to a socket + let (tx, rx) = channel(); + let (txdone, rxdone) = channel(); + let txdone2 = txdone.clone(); + let _t = Thread::spawn(move|| { + let mut tcp = TcpStream::connect(addr).unwrap(); + rx.recv().unwrap(); + tcp.write_u8(0).unwrap(); + txdone2.send(()).unwrap(); + }); + + // Spawn off a reading clone + let tcp = accept.accept().unwrap(); + let tcp2 = tcp.clone(); + let txdone3 = txdone.clone(); + let _t = Thread::spawn(move|| { + let mut tcp2 = tcp2; + tcp2.read_u8().unwrap(); + txdone3.send(()).unwrap(); + }); + + // Try to ensure that the reading clone is indeed reading + for _ in range(0i, 50) { + ::thread::Thread::yield_now(); + } + + // clone the handle again while it's reading, then let it finish the + // read. + let _ = tcp.clone(); + tx.send(()).unwrap(); + rxdone.recv().unwrap(); + rxdone.recv().unwrap(); + } + + #[test] + fn clone_accept_smoke() { + let addr = next_test_ip4(); + let l = TcpListener::bind(addr); + let mut a = l.listen().unwrap(); + let mut a2 = a.clone(); + + let _t = Thread::spawn(move|| { + let _ = TcpStream::connect(addr); + }); + let _t = Thread::spawn(move|| { + let _ = TcpStream::connect(addr); + }); + + assert!(a.accept().is_ok()); + assert!(a2.accept().is_ok()); + } + + #[test] + fn clone_accept_concurrent() { + let addr = next_test_ip4(); + let l = TcpListener::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 _t = Thread::spawn(move|| { + let _ = TcpStream::connect(addr); + }); + let _t = Thread::spawn(move|| { + let _ = TcpStream::connect(addr); + }); + + assert!(rx.recv().unwrap().is_ok()); + assert!(rx.recv().unwrap().is_ok()); + } + + #[test] + fn close_accept_smoke() { + let addr = next_test_ip4(); + let l = TcpListener::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_ip4(); + let l = TcpListener::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); + } +} diff --git a/src/libstd/old_io/net/udp.rs b/src/libstd/old_io/net/udp.rs new file mode 100644 index 00000000000..9055a089eec --- /dev/null +++ b/src/libstd/old_io/net/udp.rs @@ -0,0 +1,459 @@ +// 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. +/// +/// # Example +/// +/// ```rust,no_run +/// # #![allow(unused_must_use)] +/// #![feature(slicing_syntax)] +/// +/// 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 = buf.slice_to_mut(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<(uint, 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: int) -> IoResult<()> { + self.inner.multicast_time_to_live(ttl) + } + + /// Sets this socket's TTL + #[unstable(feature = "io")] + pub fn set_ttl(&mut self, ttl: int) -> 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::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 range(0u, 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), + } + } + } +} diff --git a/src/libstd/old_io/pipe.rs b/src/libstd/old_io/pipe.rs new file mode 100644 index 00000000000..5843b1ba1b1 --- /dev/null +++ b/src/libstd/old_io/pipe.rs @@ -0,0 +1,139 @@ +// 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; +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. + /// + /// # Example + /// + /// ```{rust,no_run} + /// # #![allow(unused_must_use)] + /// extern crate libc; + /// + /// use std::old_io::pipe::PipeStream; + /// + /// 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<uint> { + 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 sync::mpsc::channel; + use thread::Thread; + + #[test] + fn partial_read() { + use os; + use old_io::pipe::PipeStream; + + let os::Pipe { reader, writer } = unsafe { os::pipe().unwrap() }; + let out = PipeStream::open(writer); + let mut input = PipeStream::open(reader); + 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(); + } +} diff --git a/src/libstd/old_io/process.rs b/src/libstd/old_io/process.rs new file mode 100644 index 00000000000..78910882467 --- /dev/null +++ b/src/libstd/old_io/process.rs @@ -0,0 +1,1230 @@ +// 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. + +//! Bindings for executing child processes + +#![allow(non_upper_case_globals)] + +pub use self::StdioContainer::*; +pub use self::ProcessExit::*; + +use prelude::v1::*; + +use collections::HashMap; +use ffi::CString; +use fmt; +use old_io::pipe::{PipeStream, PipePair}; +use old_io::{IoResult, IoError}; +use old_io; +use libc; +use os; +use path::BytesContainer; +use sync::mpsc::{channel, Receiver}; +use sys::fs::FileDesc; +use sys::process::Process as ProcessImp; +use sys; +use thread::Thread; + +#[cfg(windows)] use hash; +#[cfg(windows)] use str; + +/// Signal a process to exit, without forcibly killing it. Corresponds to +/// SIGTERM on unix platforms. +#[cfg(windows)] pub const PleaseExitSignal: int = 15; +/// Signal a process to exit immediately, forcibly killing it. Corresponds to +/// SIGKILL on unix platforms. +#[cfg(windows)] pub const MustDieSignal: int = 9; +/// Signal a process to exit, without forcibly killing it. Corresponds to +/// SIGTERM on unix platforms. +#[cfg(not(windows))] pub const PleaseExitSignal: int = libc::SIGTERM as int; +/// Signal a process to exit immediately, forcibly killing it. Corresponds to +/// SIGKILL on unix platforms. +#[cfg(not(windows))] pub const MustDieSignal: int = libc::SIGKILL as int; + +/// Representation of a running or exited child process. +/// +/// This structure is used to represent and manage child processes. A child +/// process is created via the `Command` struct, which configures the spawning +/// process and can itself be constructed using a builder-style interface. +/// +/// # Example +/// +/// ```should_fail +/// use std::old_io::Command; +/// +/// let mut child = match Command::new("/bin/cat").arg("file.txt").spawn() { +/// Ok(child) => child, +/// Err(e) => panic!("failed to execute child: {}", e), +/// }; +/// +/// let contents = child.stdout.as_mut().unwrap().read_to_end(); +/// assert!(child.wait().unwrap().success()); +/// ``` +pub struct Process { + handle: ProcessImp, + forget: bool, + + /// None until wait() is called. + exit_code: Option<ProcessExit>, + + /// Manually delivered signal + exit_signal: Option<int>, + + /// Deadline after which wait() will return + deadline: u64, + + /// Handle to the child's stdin, if the `stdin` field of this process's + /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`. + pub stdin: Option<PipeStream>, + + /// Handle to the child's stdout, if the `stdout` field of this process's + /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`. + pub stdout: Option<PipeStream>, + + /// Handle to the child's stderr, if the `stderr` field of this process's + /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`. + pub stderr: Option<PipeStream>, +} + +/// A representation of environment variable name +/// It compares case-insensitive on Windows and case-sensitive everywhere else. +#[cfg(not(windows))] +#[derive(Hash, PartialEq, Eq, Clone, Show)] +struct EnvKey(CString); + +#[doc(hidden)] +#[cfg(windows)] +#[derive(Eq, Clone, Show)] +struct EnvKey(CString); + +#[cfg(windows)] +impl<H: hash::Writer + hash::Hasher> hash::Hash<H> for EnvKey { + fn hash(&self, state: &mut H) { + let &EnvKey(ref x) = self; + match str::from_utf8(x.as_bytes()) { + Ok(s) => for ch in s.chars() { + (ch as u8 as char).to_lowercase().hash(state); + }, + Err(..) => x.hash(state) + } + } +} + +#[cfg(windows)] +impl PartialEq for EnvKey { + fn eq(&self, other: &EnvKey) -> bool { + let &EnvKey(ref x) = self; + let &EnvKey(ref y) = other; + match (str::from_utf8(x.as_bytes()), str::from_utf8(y.as_bytes())) { + (Ok(xs), Ok(ys)) => { + if xs.len() != ys.len() { + return false + } else { + for (xch, ych) in xs.chars().zip(ys.chars()) { + if xch.to_lowercase() != ych.to_lowercase() { + return false; + } + } + return true; + } + }, + // If either is not a valid utf8 string, just compare them byte-wise + _ => return x.eq(y) + } + } +} + +impl BytesContainer for EnvKey { + fn container_as_bytes<'a>(&'a self) -> &'a [u8] { + let &EnvKey(ref k) = self; + k.container_as_bytes() + } +} + +/// A HashMap representation of environment variables. +pub type EnvMap = HashMap<EnvKey, CString>; + +/// The `Command` type acts as a process builder, providing fine-grained control +/// over how a new process should be spawned. A default configuration can be +/// generated using `Command::new(program)`, where `program` gives a path to the +/// program to be executed. Additional builder methods allow the configuration +/// to be changed (for example, by adding arguments) prior to spawning: +/// +/// ``` +/// use std::old_io::Command; +/// +/// let mut process = match Command::new("sh").arg("-c").arg("echo hello").spawn() { +/// Ok(p) => p, +/// Err(e) => panic!("failed to execute process: {}", e), +/// }; +/// +/// let output = process.stdout.as_mut().unwrap().read_to_end(); +/// ``` +#[derive(Clone)] +pub struct Command { + // The internal data for the builder. Documented by the builder + // methods below, and serialized into rt::rtio::ProcessConfig. + program: CString, + args: Vec<CString>, + env: Option<EnvMap>, + cwd: Option<CString>, + stdin: StdioContainer, + stdout: StdioContainer, + stderr: StdioContainer, + uid: Option<uint>, + gid: Option<uint>, + detach: bool, +} + +// FIXME (#12938): Until DST lands, we cannot decompose &str into & and str, so +// we cannot usefully take BytesContainer arguments by reference (without forcing an +// additional & around &str). So we are instead temporarily adding an instance +// for &Path, so that we can take BytesContainer as owned. When DST lands, the &Path +// instance should be removed, and arguments bound by BytesContainer should be passed by +// reference. (Here: {new, arg, args, env}.) + +impl Command { + /// Constructs a new `Command` for launching the program at + /// path `program`, with the following default configuration: + /// + /// * No arguments to the program + /// * Inherit the current process's environment + /// * Inherit the current process's working directory + /// * A readable pipe for stdin (file descriptor 0) + /// * A writeable pipe for stdout and stderr (file descriptors 1 and 2) + /// + /// Builder methods are provided to change these defaults and + /// otherwise configure the process. + pub fn new<T: BytesContainer>(program: T) -> Command { + Command { + program: CString::from_slice(program.container_as_bytes()), + args: Vec::new(), + env: None, + cwd: None, + stdin: CreatePipe(true, false), + stdout: CreatePipe(false, true), + stderr: CreatePipe(false, true), + uid: None, + gid: None, + detach: false, + } + } + + /// Add an argument to pass to the program. + pub fn arg<'a, T: BytesContainer>(&'a mut self, arg: T) -> &'a mut Command { + self.args.push(CString::from_slice(arg.container_as_bytes())); + self + } + + /// Add multiple arguments to pass to the program. + pub fn args<'a, T: BytesContainer>(&'a mut self, args: &[T]) -> &'a mut Command { + self.args.extend(args.iter().map(|arg| { + CString::from_slice(arg.container_as_bytes()) + })); + self + } + // Get a mutable borrow of the environment variable map for this `Command`. + fn get_env_map<'a>(&'a mut self) -> &'a mut EnvMap { + match self.env { + Some(ref mut map) => map, + None => { + // if the env is currently just inheriting from the parent's, + // materialize the parent's env into a hashtable. + self.env = Some(os::env_as_bytes().into_iter().map(|(k, v)| { + (EnvKey(CString::from_slice(k.as_slice())), + CString::from_slice(v.as_slice())) + }).collect()); + self.env.as_mut().unwrap() + } + } + } + + /// Inserts or updates an environment variable mapping. + /// + /// Note that environment variable names are case-insensitive (but case-preserving) on Windows, + /// and case-sensitive on all other platforms. + pub fn env<'a, T, U>(&'a mut self, key: T, val: U) + -> &'a mut Command + where T: BytesContainer, U: BytesContainer { + let key = EnvKey(CString::from_slice(key.container_as_bytes())); + let val = CString::from_slice(val.container_as_bytes()); + self.get_env_map().insert(key, val); + self + } + + /// Removes an environment variable mapping. + pub fn env_remove<'a, T>(&'a mut self, key: T) -> &'a mut Command + where T: BytesContainer { + let key = EnvKey(CString::from_slice(key.container_as_bytes())); + self.get_env_map().remove(&key); + self + } + + /// Sets the entire environment map for the child process. + /// + /// If the given slice contains multiple instances of an environment + /// variable, the *rightmost* instance will determine the value. + pub fn env_set_all<'a, T, U>(&'a mut self, env: &[(T,U)]) + -> &'a mut Command + where T: BytesContainer, U: BytesContainer { + self.env = Some(env.iter().map(|&(ref k, ref v)| { + (EnvKey(CString::from_slice(k.container_as_bytes())), + CString::from_slice(v.container_as_bytes())) + }).collect()); + self + } + + /// Set the working directory for the child process. + pub fn cwd<'a>(&'a mut self, dir: &Path) -> &'a mut Command { + self.cwd = Some(CString::from_slice(dir.as_vec())); + self + } + + /// Configuration for the child process's stdin handle (file descriptor 0). + /// Defaults to `CreatePipe(true, false)` so the input can be written to. + pub fn stdin<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command { + self.stdin = cfg; + self + } + + /// Configuration for the child process's stdout handle (file descriptor 1). + /// Defaults to `CreatePipe(false, true)` so the output can be collected. + pub fn stdout<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command { + self.stdout = cfg; + self + } + + /// Configuration for the child process's stderr handle (file descriptor 2). + /// Defaults to `CreatePipe(false, true)` so the output can be collected. + pub fn stderr<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command { + self.stderr = cfg; + self + } + + /// Sets the child process's user id. This translates to a `setuid` call in + /// the child process. Setting this value on windows will cause the spawn to + /// fail. Failure in the `setuid` call on unix will also cause the spawn to + /// fail. + pub fn uid<'a>(&'a mut self, id: uint) -> &'a mut Command { + self.uid = Some(id); + self + } + + /// Similar to `uid`, but sets the group id of the child process. This has + /// the same semantics as the `uid` field. + pub fn gid<'a>(&'a mut self, id: uint) -> &'a mut Command { + self.gid = Some(id); + self + } + + /// Sets the child process to be spawned in a detached state. On unix, this + /// means that the child is the leader of a new process group. + pub fn detached<'a>(&'a mut self) -> &'a mut Command { + self.detach = true; + self + } + + /// Executes the command as a child process, which is returned. + pub fn spawn(&self) -> IoResult<Process> { + let (their_stdin, our_stdin) = try!(setup_io(self.stdin)); + let (their_stdout, our_stdout) = try!(setup_io(self.stdout)); + let (their_stderr, our_stderr) = try!(setup_io(self.stderr)); + + match ProcessImp::spawn(self, their_stdin, their_stdout, their_stderr) { + Err(e) => Err(e), + Ok(handle) => Ok(Process { + handle: handle, + forget: false, + exit_code: None, + exit_signal: None, + deadline: 0, + stdin: our_stdin, + stdout: our_stdout, + stderr: our_stderr, + }) + } + } + + /// Executes the command as a child process, waiting for it to finish and + /// collecting all of its output. + /// + /// # Example + /// + /// ``` + /// use std::old_io::Command; + /// + /// let output = match Command::new("cat").arg("foot.txt").output() { + /// Ok(output) => output, + /// Err(e) => panic!("failed to execute process: {}", e), + /// }; + /// + /// println!("status: {}", output.status); + /// println!("stdout: {}", String::from_utf8_lossy(output.output.as_slice())); + /// println!("stderr: {}", String::from_utf8_lossy(output.error.as_slice())); + /// ``` + pub fn output(&self) -> IoResult<ProcessOutput> { + self.spawn().and_then(|p| p.wait_with_output()) + } + + /// Executes a command as a child process, waiting for it to finish and + /// collecting its exit status. + /// + /// # Example + /// + /// ``` + /// use std::old_io::Command; + /// + /// let status = match Command::new("ls").status() { + /// Ok(status) => status, + /// Err(e) => panic!("failed to execute process: {}", e), + /// }; + /// + /// println!("process exited with: {}", status); + /// ``` + pub fn status(&self) -> IoResult<ProcessExit> { + self.spawn().and_then(|mut p| p.wait()) + } +} + +impl fmt::Debug for Command { + /// Format the program and arguments of a Command for display. Any + /// non-utf8 data is lossily converted using the utf8 replacement + /// character. + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + try!(write!(f, "{}", String::from_utf8_lossy(self.program.as_bytes()))); + for arg in self.args.iter() { + try!(write!(f, " '{}'", String::from_utf8_lossy(arg.as_bytes()))); + } + Ok(()) + } +} + +fn setup_io(io: StdioContainer) -> IoResult<(Option<PipeStream>, Option<PipeStream>)> { + let ours; + let theirs; + match io { + Ignored => { + theirs = None; + ours = None; + } + InheritFd(fd) => { + theirs = Some(PipeStream::from_filedesc(FileDesc::new(fd, false))); + ours = None; + } + CreatePipe(readable, _writable) => { + let PipePair { reader, writer } = try!(PipeStream::pair()); + if readable { + theirs = Some(reader); + ours = Some(writer); + } else { + theirs = Some(writer); + ours = Some(reader); + } + } + } + Ok((theirs, ours)) +} + +// Allow the sys module to get access to the Command state +impl sys::process::ProcessConfig<EnvKey, CString> for Command { + fn program(&self) -> &CString { + &self.program + } + fn args(&self) -> &[CString] { + self.args.as_slice() + } + fn env(&self) -> Option<&EnvMap> { + self.env.as_ref() + } + fn cwd(&self) -> Option<&CString> { + self.cwd.as_ref() + } + fn uid(&self) -> Option<uint> { + self.uid.clone() + } + fn gid(&self) -> Option<uint> { + self.gid.clone() + } + fn detach(&self) -> bool { + self.detach + } + +} + +/// The output of a finished process. +#[derive(PartialEq, Eq, Clone)] +pub struct ProcessOutput { + /// The status (exit code) of the process. + pub status: ProcessExit, + /// The data that the process wrote to stdout. + pub output: Vec<u8>, + /// The data that the process wrote to stderr. + pub error: Vec<u8>, +} + +/// Describes what to do with a standard io stream for a child process. +#[derive(Clone, Copy)] +pub enum StdioContainer { + /// This stream will be ignored. This is the equivalent of attaching the + /// stream to `/dev/null` + Ignored, + + /// The specified file descriptor is inherited for the stream which it is + /// specified for. Ownership of the file descriptor is *not* taken, so the + /// caller must clean it up. + InheritFd(libc::c_int), + + /// Creates a pipe for the specified file descriptor which will be created + /// when the process is spawned. + /// + /// The first boolean argument is whether the pipe is readable, and the + /// second is whether it is writable. These properties are from the view of + /// the *child* process, not the parent process. + CreatePipe(bool /* readable */, bool /* writable */), +} + +/// Describes the result of a process after it has terminated. +/// Note that Windows have no signals, so the result is usually ExitStatus. +#[derive(PartialEq, Eq, Clone, Copy, Show)] +pub enum ProcessExit { + /// Normal termination with an exit status. + ExitStatus(int), + + /// Termination by signal, with the signal number. + ExitSignal(int), +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for ProcessExit { + /// Format a ProcessExit enum, to nicely present the information. + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ExitStatus(code) => write!(f, "exit code: {}", code), + ExitSignal(code) => write!(f, "signal: {}", code), + } + } +} + +impl ProcessExit { + /// Was termination successful? Signal termination not considered a success, + /// and success is defined as a zero exit status. + pub fn success(&self) -> bool { + return self.matches_exit_status(0); + } + + /// Checks whether this ProcessExit matches the given exit status. + /// Termination by signal will never match an exit code. + pub fn matches_exit_status(&self, wanted: int) -> bool { + *self == ExitStatus(wanted) + } +} + +impl Process { + /// Sends `signal` to another process in the system identified by `id`. + /// + /// Note that windows doesn't quite have the same model as unix, so some + /// unix signals are mapped to windows signals. Notably, unix termination + /// signals (SIGTERM/SIGKILL/SIGINT) are translated to `TerminateProcess`. + /// + /// Additionally, a signal number of 0 can check for existence of the target + /// process. Note, though, that on some platforms signals will continue to + /// be successfully delivered if the child has exited, but not yet been + /// reaped. + pub fn kill(id: libc::pid_t, signal: int) -> IoResult<()> { + unsafe { ProcessImp::killpid(id, signal) } + } + + /// Returns the process id of this child process + pub fn id(&self) -> libc::pid_t { self.handle.id() } + + /// Sends the specified signal to the child process, returning whether the + /// signal could be delivered or not. + /// + /// Note that signal 0 is interpreted as a poll to check whether the child + /// process is still alive or not. If an error is returned, then the child + /// process has exited. + /// + /// On some unix platforms signals will continue to be received after a + /// child has exited but not yet been reaped. In order to report the status + /// of signal delivery correctly, unix implementations may invoke + /// `waitpid()` with `WNOHANG` in order to reap the child as necessary. + /// + /// # Errors + /// + /// If the signal delivery fails, the corresponding error is returned. + pub fn signal(&mut self, signal: int) -> IoResult<()> { + #[cfg(unix)] fn collect_status(p: &mut Process) { + // On Linux (and possibly other unices), a process that has exited will + // continue to accept signals because it is "defunct". The delivery of + // signals will only fail once the child has been reaped. For this + // reason, if the process hasn't exited yet, then we attempt to collect + // their status with WNOHANG. + if p.exit_code.is_none() { + match p.handle.try_wait() { + Some(code) => { p.exit_code = Some(code); } + None => {} + } + } + } + #[cfg(windows)] fn collect_status(_p: &mut Process) {} + + collect_status(self); + + // if the process has finished, and therefore had waitpid called, + // and we kill it, then on unix we might ending up killing a + // newer process that happens to have the same (re-used) id + if self.exit_code.is_some() { + return Err(IoError { + kind: old_io::InvalidInput, + desc: "invalid argument: can't kill an exited process", + detail: None, + }) + } + + // A successfully delivered signal that isn't 0 (just a poll for being + // alive) is recorded for windows (see wait()) + match unsafe { self.handle.kill(signal) } { + Ok(()) if signal == 0 => Ok(()), + Ok(()) => { self.exit_signal = Some(signal); Ok(()) } + Err(e) => Err(e), + } + + } + + /// Sends a signal to this child requesting that it exits. This is + /// equivalent to sending a SIGTERM on unix platforms. + pub fn signal_exit(&mut self) -> IoResult<()> { + self.signal(PleaseExitSignal) + } + + /// Sends a signal to this child forcing it to exit. This is equivalent to + /// sending a SIGKILL on unix platforms. + pub fn signal_kill(&mut self) -> IoResult<()> { + self.signal(MustDieSignal) + } + + /// Wait for the child to exit completely, returning the status that it + /// exited with. This function will continue to have the same return value + /// after it has been called at least once. + /// + /// The stdin handle to the child process will be closed before waiting. + /// + /// # Errors + /// + /// This function can fail if a timeout was previously specified via + /// `set_timeout` and the timeout expires before the child exits. + pub fn wait(&mut self) -> IoResult<ProcessExit> { + drop(self.stdin.take()); + match self.exit_code { + Some(code) => Ok(code), + None => { + let code = try!(self.handle.wait(self.deadline)); + // On windows, waitpid will never return a signal. If a signal + // was successfully delivered to the process, however, we can + // consider it as having died via a signal. + let code = match self.exit_signal { + None => code, + Some(signal) if cfg!(windows) => ExitSignal(signal), + Some(..) => code, + }; + self.exit_code = Some(code); + Ok(code) + } + } + } + + /// Sets a timeout, in milliseconds, for future calls to wait(). + /// + /// The argument specified is a relative distance into the future, in + /// milliseconds, after which any call to wait() will return immediately + /// with a timeout error, and all future calls to wait() will not block. + /// + /// A value of `None` will clear any previous timeout, and a value of `Some` + /// will override any previously set timeout. + /// + /// # Example + /// + /// ```no_run + /// use std::old_io::{Command, IoResult}; + /// use std::old_io::process::ProcessExit; + /// + /// fn run_gracefully(prog: &str) -> IoResult<ProcessExit> { + /// let mut p = try!(Command::new("long-running-process").spawn()); + /// + /// // give the process 10 seconds to finish completely + /// p.set_timeout(Some(10_000)); + /// match p.wait() { + /// Ok(status) => return Ok(status), + /// Err(..) => {} + /// } + /// + /// // Attempt to exit gracefully, but don't wait for it too long + /// try!(p.signal_exit()); + /// p.set_timeout(Some(1_000)); + /// match p.wait() { + /// Ok(status) => return Ok(status), + /// Err(..) => {} + /// } + /// + /// // Well, we did our best, forcefully kill the process + /// try!(p.signal_kill()); + /// p.set_timeout(None); + /// p.wait() + /// } + /// ``` + #[unstable(feature = "io", + reason = "the type of the timeout is likely to change")] + pub fn set_timeout(&mut self, timeout_ms: Option<u64>) { + self.deadline = timeout_ms.map(|i| i + sys::timer::now()).unwrap_or(0); + } + + /// Simultaneously wait for the child to exit and collect all remaining + /// output on the stdout/stderr handles, returning a `ProcessOutput` + /// instance. + /// + /// The stdin handle to the child is closed before waiting. + /// + /// # Errors + /// + /// This function can fail for any of the same reasons that `wait()` can + /// fail. + pub fn wait_with_output(mut self) -> IoResult<ProcessOutput> { + drop(self.stdin.take()); + fn read(stream: Option<old_io::PipeStream>) -> Receiver<IoResult<Vec<u8>>> { + let (tx, rx) = channel(); + match stream { + Some(stream) => { + Thread::spawn(move |:| { + let mut stream = stream; + tx.send(stream.read_to_end()).unwrap(); + }); + } + None => tx.send(Ok(Vec::new())).unwrap() + } + rx + } + let stdout = read(self.stdout.take()); + let stderr = read(self.stderr.take()); + + let status = try!(self.wait()); + + Ok(ProcessOutput { + status: status, + output: stdout.recv().unwrap().unwrap_or(Vec::new()), + error: stderr.recv().unwrap().unwrap_or(Vec::new()), + }) + } + + /// Forgets this process, allowing it to outlive the parent + /// + /// This function will forcefully prevent calling `wait()` on the child + /// process in the destructor, allowing the child to outlive the + /// parent. Note that this operation can easily lead to leaking the + /// resources of the child process, so care must be taken when + /// invoking this method. + pub fn forget(mut self) { + self.forget = true; + } +} + +impl Drop for Process { + fn drop(&mut self) { + if self.forget { return } + + // Close all I/O before exiting to ensure that the child doesn't wait + // forever to print some text or something similar. + drop(self.stdin.take()); + drop(self.stdout.take()); + drop(self.stderr.take()); + + self.set_timeout(None); + let _ = self.wait().unwrap(); + } +} + +#[cfg(test)] +mod tests { + use old_io::{Truncate, Write, TimedOut, timer, process, FileNotFound}; + use prelude::v1::{Ok, Err, range, drop, Some, None, Vec}; + use prelude::v1::{Path, String, Reader, Writer, Clone}; + use prelude::v1::{SliceExt, Str, StrExt, AsSlice, ToString, GenericPath}; + use old_io::fs::PathExtensions; + use old_io::timer::*; + use rt::running_on_valgrind; + use str; + use super::{CreatePipe}; + use super::{InheritFd, Process, PleaseExitSignal, Command, ProcessOutput}; + use sync::mpsc::channel; + use thread::Thread; + use time::Duration; + + // FIXME(#10380) these tests should not all be ignored on android. + + #[cfg(not(target_os="android"))] + #[test] + fn smoke() { + let p = Command::new("true").spawn(); + assert!(p.is_ok()); + let mut p = p.unwrap(); + assert!(p.wait().unwrap().success()); + } + + #[cfg(not(target_os="android"))] + #[test] + fn smoke_failure() { + match Command::new("if-this-is-a-binary-then-the-world-has-ended").spawn() { + Ok(..) => panic!(), + Err(..) => {} + } + } + + #[cfg(not(target_os="android"))] + #[test] + fn exit_reported_right() { + let p = Command::new("false").spawn(); + assert!(p.is_ok()); + let mut p = p.unwrap(); + assert!(p.wait().unwrap().matches_exit_status(1)); + drop(p.wait().clone()); + } + + #[cfg(all(unix, not(target_os="android")))] + #[test] + fn signal_reported_right() { + let p = Command::new("/bin/sh").arg("-c").arg("kill -1 $$").spawn(); + assert!(p.is_ok()); + let mut p = p.unwrap(); + match p.wait().unwrap() { + process::ExitSignal(1) => {}, + result => panic!("not terminated by signal 1 (instead, {})", result), + } + } + + pub fn read_all(input: &mut Reader) -> String { + input.read_to_string().unwrap() + } + + pub fn run_output(cmd: Command) -> String { + let p = cmd.spawn(); + assert!(p.is_ok()); + let mut p = p.unwrap(); + assert!(p.stdout.is_some()); + let ret = read_all(p.stdout.as_mut().unwrap() as &mut Reader); + assert!(p.wait().unwrap().success()); + return ret; + } + + #[cfg(not(target_os="android"))] + #[test] + fn stdout_works() { + let mut cmd = Command::new("echo"); + cmd.arg("foobar").stdout(CreatePipe(false, true)); + assert_eq!(run_output(cmd), "foobar\n"); + } + + #[cfg(all(unix, not(target_os="android")))] + #[test] + fn set_cwd_works() { + let mut cmd = Command::new("/bin/sh"); + cmd.arg("-c").arg("pwd") + .cwd(&Path::new("/")) + .stdout(CreatePipe(false, true)); + assert_eq!(run_output(cmd), "/\n"); + } + + #[cfg(all(unix, not(target_os="android")))] + #[test] + fn stdin_works() { + let mut p = Command::new("/bin/sh") + .arg("-c").arg("read line; echo $line") + .stdin(CreatePipe(true, false)) + .stdout(CreatePipe(false, true)) + .spawn().unwrap(); + p.stdin.as_mut().unwrap().write("foobar".as_bytes()).unwrap(); + drop(p.stdin.take()); + let out = read_all(p.stdout.as_mut().unwrap() as &mut Reader); + assert!(p.wait().unwrap().success()); + assert_eq!(out, "foobar\n"); + } + + #[cfg(not(target_os="android"))] + #[test] + fn detach_works() { + let mut p = Command::new("true").detached().spawn().unwrap(); + assert!(p.wait().unwrap().success()); + } + + #[cfg(windows)] + #[test] + fn uid_fails_on_windows() { + assert!(Command::new("test").uid(10).spawn().is_err()); + } + + #[cfg(all(unix, not(target_os="android")))] + #[test] + fn uid_works() { + use libc; + let mut p = Command::new("/bin/sh") + .arg("-c").arg("true") + .uid(unsafe { libc::getuid() as uint }) + .gid(unsafe { libc::getgid() as uint }) + .spawn().unwrap(); + assert!(p.wait().unwrap().success()); + } + + #[cfg(all(unix, not(target_os="android")))] + #[test] + fn uid_to_root_fails() { + use libc; + + // if we're already root, this isn't a valid test. Most of the bots run + // as non-root though (android is an exception). + if unsafe { libc::getuid() == 0 } { return } + assert!(Command::new("/bin/ls").uid(0).gid(0).spawn().is_err()); + } + + #[cfg(not(target_os="android"))] + #[test] + fn test_process_status() { + let mut status = Command::new("false").status().unwrap(); + assert!(status.matches_exit_status(1)); + + status = Command::new("true").status().unwrap(); + assert!(status.success()); + } + + #[test] + fn test_process_output_fail_to_start() { + match Command::new("/no-binary-by-this-name-should-exist").output() { + Err(e) => assert_eq!(e.kind, FileNotFound), + Ok(..) => panic!() + } + } + + #[cfg(not(target_os="android"))] + #[test] + fn test_process_output_output() { + let ProcessOutput {status, output, error} + = Command::new("echo").arg("hello").output().unwrap(); + let output_str = str::from_utf8(output.as_slice()).unwrap(); + + assert!(status.success()); + assert_eq!(output_str.trim().to_string(), "hello"); + // FIXME #7224 + if !running_on_valgrind() { + assert_eq!(error, Vec::new()); + } + } + + #[cfg(not(target_os="android"))] + #[test] + fn test_process_output_error() { + let ProcessOutput {status, output, error} + = Command::new("mkdir").arg(".").output().unwrap(); + + assert!(status.matches_exit_status(1)); + assert_eq!(output, Vec::new()); + assert!(!error.is_empty()); + } + + #[cfg(not(target_os="android"))] + #[test] + fn test_finish_once() { + let mut prog = Command::new("false").spawn().unwrap(); + assert!(prog.wait().unwrap().matches_exit_status(1)); + } + + #[cfg(not(target_os="android"))] + #[test] + fn test_finish_twice() { + let mut prog = Command::new("false").spawn().unwrap(); + assert!(prog.wait().unwrap().matches_exit_status(1)); + assert!(prog.wait().unwrap().matches_exit_status(1)); + } + + #[cfg(not(target_os="android"))] + #[test] + fn test_wait_with_output_once() { + let prog = Command::new("echo").arg("hello").spawn().unwrap(); + let ProcessOutput {status, output, error} = prog.wait_with_output().unwrap(); + let output_str = str::from_utf8(output.as_slice()).unwrap(); + + assert!(status.success()); + assert_eq!(output_str.trim().to_string(), "hello"); + // FIXME #7224 + if !running_on_valgrind() { + assert_eq!(error, Vec::new()); + } + } + + #[cfg(all(unix, not(target_os="android")))] + pub fn pwd_cmd() -> Command { + Command::new("pwd") + } + #[cfg(target_os="android")] + pub fn pwd_cmd() -> Command { + let mut cmd = Command::new("/system/bin/sh"); + cmd.arg("-c").arg("pwd"); + cmd + } + + #[cfg(windows)] + pub fn pwd_cmd() -> Command { + let mut cmd = Command::new("cmd"); + cmd.arg("/c").arg("cd"); + cmd + } + + #[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().output).unwrap(); + let parent_dir = os::getcwd().unwrap(); + let child_dir = 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 = os::getcwd().unwrap().dir_path(); + let prog = pwd_cmd().cwd(&parent_dir).spawn().unwrap(); + + let output = String::from_utf8(prog.wait_with_output().unwrap().output).unwrap(); + let child_dir = 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") + } + #[cfg(target_os="android")] + pub fn env_cmd() -> Command { + let mut cmd = Command::new("/system/bin/sh"); + cmd.arg("-c").arg("set"); + cmd + } + + #[cfg(windows)] + pub fn env_cmd() -> Command { + let mut cmd = Command::new("cmd"); + cmd.arg("/c").arg("set"); + cmd + } + + #[cfg(not(target_os="android"))] + #[test] + fn test_inherit_env() { + use os; + if running_on_valgrind() { return; } + + let prog = env_cmd().spawn().unwrap(); + let output = String::from_utf8(prog.wait_with_output().unwrap().output).unwrap(); + + let r = os::env(); + for &(ref k, ref v) in r.iter() { + // don't check windows magical empty-named variables + assert!(k.is_empty() || + output.contains(format!("{}={}", *k, *v).as_slice()), + "output doesn't contain `{}={}`\n{}", + k, v, output); + } + } + #[cfg(target_os="android")] + #[test] + fn test_inherit_env() { + use os; + if running_on_valgrind() { return; } + + let mut prog = env_cmd().spawn().unwrap(); + let output = String::from_utf8(prog.wait_with_output().unwrap().output).unwrap(); + + let r = os::env(); + for &(ref k, ref v) in r.iter() { + // don't check android RANDOM variables + if *k != "RANDOM".to_string() { + assert!(output.contains(format!("{}={}", + *k, + *v).as_slice()) || + output.contains(format!("{}=\'{}\'", + *k, + *v).as_slice())); + } + } + } + + #[test] + fn test_override_env() { + use os; + + // In some build environments (such as chrooted Nix builds), `env` can + // only be found in the explicitly-provided PATH env variable, not in + // default places such as /bin or /usr/bin. So we need to pass through + // PATH to our sub-process. + let path_val: String; + let mut new_env = vec![("RUN_TEST_NEW_ENV", "123")]; + match os::getenv("PATH") { + None => {} + Some(val) => { + path_val = val; + new_env.push(("PATH", path_val.as_slice())) + } + } + + let prog = env_cmd().env_set_all(new_env.as_slice()).spawn().unwrap(); + let result = prog.wait_with_output().unwrap(); + let output = String::from_utf8_lossy(result.output.as_slice()).to_string(); + + assert!(output.contains("RUN_TEST_NEW_ENV=123"), + "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", output); + } + + #[test] + fn test_add_to_env() { + let prog = env_cmd().env("RUN_TEST_NEW_ENV", "123").spawn().unwrap(); + let result = prog.wait_with_output().unwrap(); + let output = String::from_utf8_lossy(result.output.as_slice()).to_string(); + + assert!(output.contains("RUN_TEST_NEW_ENV=123"), + "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", output); + } + + #[cfg(unix)] + pub fn sleeper() -> Process { + Command::new("sleep").arg("1000").spawn().unwrap() + } + #[cfg(windows)] + pub fn sleeper() -> Process { + // There's a `timeout` command on windows, but it doesn't like having + // its output piped, so instead just ping ourselves a few times with + // gaps in between so we're sure this process is alive for awhile + Command::new("ping").arg("127.0.0.1").arg("-n").arg("1000").spawn().unwrap() + } + + #[test] + fn test_kill() { + let mut p = sleeper(); + Process::kill(p.id(), PleaseExitSignal).unwrap(); + assert!(!p.wait().unwrap().success()); + } + + #[test] + fn test_exists() { + let mut p = sleeper(); + assert!(Process::kill(p.id(), 0).is_ok()); + p.signal_kill().unwrap(); + assert!(!p.wait().unwrap().success()); + } + + #[test] + fn test_zero() { + let mut p = sleeper(); + p.signal_kill().unwrap(); + for _ in range(0i, 20) { + if p.signal(0).is_err() { + assert!(!p.wait().unwrap().success()); + return + } + timer::sleep(Duration::milliseconds(100)); + } + panic!("never saw the child go away"); + } + + #[test] + fn wait_timeout() { + let mut p = sleeper(); + p.set_timeout(Some(10)); + assert_eq!(p.wait().err().unwrap().kind, TimedOut); + assert_eq!(p.wait().err().unwrap().kind, TimedOut); + p.signal_kill().unwrap(); + p.set_timeout(None); + assert!(p.wait().is_ok()); + } + + #[test] + fn wait_timeout2() { + let (tx, rx) = channel(); + let tx2 = tx.clone(); + let _t = Thread::spawn(move|| { + let mut p = sleeper(); + p.set_timeout(Some(10)); + assert_eq!(p.wait().err().unwrap().kind, TimedOut); + p.signal_kill().unwrap(); + tx.send(()).unwrap(); + }); + let _t = Thread::spawn(move|| { + let mut p = sleeper(); + p.set_timeout(Some(10)); + assert_eq!(p.wait().err().unwrap().kind, TimedOut); + p.signal_kill().unwrap(); + tx2.send(()).unwrap(); + }); + rx.recv().unwrap(); + rx.recv().unwrap(); + } + + #[test] + fn forget() { + let p = sleeper(); + let id = p.id(); + p.forget(); + assert!(Process::kill(id, 0).is_ok()); + assert!(Process::kill(id, PleaseExitSignal).is_ok()); + } + + #[test] + fn dont_close_fd_on_command_spawn() { + use sys::fs; + + let path = if cfg!(windows) { + Path::new("NUL") + } else { + Path::new("/dev/null") + }; + + let fdes = match fs::open(&path, Truncate, Write) { + Ok(f) => f, + Err(_) => panic!("failed to open file descriptor"), + }; + + let mut cmd = pwd_cmd(); + let _ = cmd.stdout(InheritFd(fdes.fd())); + assert!(cmd.status().unwrap().success()); + assert!(fdes.write("extra write\n".as_bytes()).is_ok()); + } + + #[test] + #[cfg(windows)] + fn env_map_keys_ci() { + use ffi::CString; + use super::EnvKey; + let mut cmd = Command::new(""); + cmd.env("path", "foo"); + cmd.env("Path", "bar"); + let env = &cmd.env.unwrap(); + let val = env.get(&EnvKey(CString::from_slice(b"PATH"))); + assert!(val.unwrap() == &CString::from_slice(b"bar")); + } +} diff --git a/src/libstd/old_io/result.rs b/src/libstd/old_io/result.rs new file mode 100644 index 00000000000..96b979860ae --- /dev/null +++ b/src/libstd/old_io/result.rs @@ -0,0 +1,129 @@ +// 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<uint> { + 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<T, A: Acceptor<T>, L: Listener<T, A>> Listener<T, A> for IoResult<L> { + fn listen(self) -> IoResult<A> { + match self { + Ok(listener) => listener.listen(), + Err(e) => Err(e), + } + } +} + +impl<T, A: Acceptor<T>> Acceptor<T> for IoResult<A> { + fn accept(&mut self) -> IoResult<T> { + 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; + + #[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(), vec!(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.as_slice(), 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), + } + } +} diff --git a/src/libstd/old_io/stdio.rs b/src/libstd/old_io/stdio.rs new file mode 100644 index 00000000000..70400619bea --- /dev/null +++ b/src/libstd/old_io/stdio.rs @@ -0,0 +1,566 @@ +// 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. +//! +//! # Example +//! +//! ```rust +//! # #![allow(unused_must_use)] +//! use std::old_io; +//! +//! let mut out = old_io::stdout(); +//! out.write_all(b"Hello, world!"); +//! ``` + +use self::StdSource::*; + +use boxed::Box; +use cell::RefCell; +use clone::Clone; +use failure::LOCAL_STDERR; +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 slice::SliceExt; +use str::StrExt; +use string::String; +use sys::{fs, tty}; +use sync::{Arc, Mutex, MutexGuard, Once, ONCE_INIT}; +use uint; +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 + /// + /// ```rust + /// use std::old_io; + /// + /// for line in old_io::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<uint> { + 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: uint, buf: &mut [u8]) -> IoResult<uint> { + self.inner.lock().unwrap().0.read_at_least(min, buf) + } + + fn push_at_least(&mut self, min: uint, len: uint, buf: &mut Vec<u8>) -> IoResult<uint> { + 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: uint) -> IoResult<u64> { + self.inner.lock().unwrap().0.read_le_uint_n(nbytes) + } + + fn read_be_uint_n(&mut self, nbytes: uint) -> 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: *const StdinReader = 0 as *const 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 = mem::transmute(box stdin); + + // Make sure to free it at exit + rt::at_exit(|| { + mem::transmute::<_, Box<StdinReader>>(STDIN); + STDIN = ptr::null(); + }); + }); + + (*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. +pub fn set_stderr(stderr: Box<Writer + Send>) -> Option<Box<Writer + Send>> { + let mut new = Some(stderr); + LOCAL_STDERR.with(|slot| { + mem::replace(&mut *slot.borrow_mut(), new.take()) + }).and_then(|mut s| { + let _ = s.flush(); + Some(s) + }) +} + +// 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 = LOCAL_STDOUT.with(|slot| { + slot.borrow_mut().take() + }).unwrap_or_else(|| { + box stdout() as Box<Writer + Send> + }); + 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. +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. +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<uint> { + 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 uint) + }, + File(ref mut file) => file.read(buf).map(|i| i as uint), + }; + 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<(int, int)> { + 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 {uint::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::Thread; + + #[test] + fn smoke() { + // Just make sure we can acquire handles + stdin(); + stdout(); + stderr(); + } + + #[test] + fn capture_stdout() { + use old_io::{ChanReader, ChanWriter}; + + let (tx, rx) = channel(); + let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx)); + let _t = Thread::spawn(move|| { + set_stdout(box w); + println!("hello!"); + }); + assert_eq!(r.read_to_string().unwrap(), "hello!\n"); + } + + #[test] + fn capture_stderr() { + use old_io::{ChanReader, ChanWriter, Reader}; + + let (tx, rx) = channel(); + let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx)); + let _t = Thread::spawn(move || -> () { + set_stderr(box w); + panic!("my special message"); + }); + let s = r.read_to_string().unwrap(); + assert!(s.contains("my special message")); + } +} diff --git a/src/libstd/old_io/tempfile.rs b/src/libstd/old_io/tempfile.rs new file mode 100644 index 00000000000..029fef7c197 --- /dev/null +++ b/src/libstd/old_io/tempfile.rs @@ -0,0 +1,182 @@ +// 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 + +use old_io::{fs, IoError, IoErrorKind, IoResult}; +use old_io; +use iter::{IteratorExt, range}; +use ops::Drop; +use option::Option; +use option::Option::{None, Some}; +use os; +use path::{Path, GenericPath}; +use rand::{Rng, thread_rng}; +use result::Result::{Ok, Err}; +use str::StrExt; +use string::String; + +/// A wrapper for a path to temporary directory implementing automatic +/// scope-based deletion. +/// +/// # Examples +/// +/// ```no_run +/// use std::old_io::TempDir; +/// +/// { +/// // 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: uint = 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. + pub fn new_in(tmpdir: &Path, prefix: &str) -> IoResult<TempDir> { + if !tmpdir.is_absolute() { + let abs_tmpdir = try!(os::make_absolute(tmpdir)); + return TempDir::new_in(&abs_tmpdir, prefix); + } + + let mut rng = thread_rng(); + for _ in range(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. + pub fn new(prefix: &str) -> IoResult<TempDir> { + TempDir::new_in(&os::tmpdir(), 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 diff --git a/src/libstd/old_io/test.rs b/src/libstd/old_io/test.rs new file mode 100644 index 00000000000..f49e2397d42 --- /dev/null +++ b/src/libstd/old_io/test.rs @@ -0,0 +1,175 @@ +// 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 libc; +use os; +use std::old_io::net::ip::*; +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"))] +pub fn next_test_unix() -> Path { + let string = next_test_unix_socket(); + if cfg!(unix) { + os::tmpdir().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 = 9600u16; + let range = 1000u16; + + 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 = os::getcwd().unwrap(); + let path_s = path.as_str().unwrap(); + + let mut final_base = base; + + for &(dir, base) in bases.iter() { + 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 os::last_os_error; + + // 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 = 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 = 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 = 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() {} +} diff --git a/src/libstd/old_io/timer.rs b/src/libstd/old_io/timer.rs new file mode 100644 index 00000000000..7e15c9ad7fc --- /dev/null +++ b/src/libstd/old_io/timer.rs @@ -0,0 +1,481 @@ +// 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 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 +/// +/// ``` +/// # 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. +/// +/// ``` +/// # 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. + /// + /// # Example + /// + /// ```rust + /// 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 range(0u, 100) { /* do work */ } + /// + /// // blocks until 10 ms after the `oneshot` call + /// ten_milliseconds.recv().unwrap(); + /// ``` + /// + /// ```rust + /// 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 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. + /// + /// # Example + /// + /// ```rust + /// 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 range(0u, 100) { /* do work */ } + /// + /// // blocks until 10 ms after the `periodic` call + /// ten_milliseconds.recv().unwrap(); + /// + /// for _ in range(0u, 100) { /* do work */ } + /// + /// // blocks until 20 ms after the `periodic` call (*not* 10ms after the + /// // previous `recv`) + /// ten_milliseconds.recv().unwrap(); + /// ``` + /// + /// ```rust + /// 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 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::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_fail] + fn oneshot_fail() { + let mut timer = Timer::new().unwrap(); + let _rx = timer.oneshot(Duration::milliseconds(1)); + panic!(); + } + + #[test] + #[should_fail] + fn period_fail() { + let mut timer = Timer::new().unwrap(); + let _rx = timer.periodic(Duration::milliseconds(1)); + panic!(); + } + + #[test] + #[should_fail] + 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(); + } + +} diff --git a/src/libstd/old_io/util.rs b/src/libstd/old_io/util.rs new file mode 100644 index 00000000000..4b6d9b08141 --- /dev/null +++ b/src/libstd/old_io/util.rs @@ -0,0 +1,444 @@ +// 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 + +use prelude::v1::*; +use cmp; +use old_io; +use slice::bytes::MutableByteVector; + +/// Wraps a `Reader`, limiting the number of bytes that can be read from it. +#[derive(Show)] +pub struct LimitReader<R> { + limit: uint, + inner: R +} + +impl<R: Reader> LimitReader<R> { + /// Creates a new `LimitReader` + pub fn new(r: R, limit: uint) -> 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) -> uint { self.limit } +} + +impl<R: Reader> Reader for LimitReader<R> { + fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<uint> { + 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 + } +} + +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: uint) { + // 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, Show)] +pub struct NullWriter; + +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, Show)] +pub struct ZeroReader; + +impl Reader for ZeroReader { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<uint> { + buf.set_memory(0); + Ok(buf.len()) + } +} + +impl Buffer for ZeroReader { + fn fill_buf<'a>(&'a mut self) -> old_io::IoResult<&'a [u8]> { + static DATA: [u8; 64] = [0; 64]; + Ok(DATA.as_slice()) + } + + fn consume(&mut self, _amt: uint) {} +} + +/// A `Reader` which is always at EOF, like /dev/null. +#[derive(Copy, Show)] +pub struct NullReader; + +impl Reader for NullReader { + #[inline] + fn read(&mut self, _buf: &mut [u8]) -> old_io::IoResult<uint> { + Err(old_io::standard_error(old_io::EndOfFile)) + } +} + +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: uint) {} +} + +/// 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(Show)] +pub struct MultiWriter<W> { + writers: Vec<W> +} + +impl<W> MultiWriter<W> where W: Writer { + /// Creates a new `MultiWriter` + pub fn new(writers: Vec<W>) -> MultiWriter<W> { + MultiWriter { writers: writers } + } +} + +impl<W> Writer for MultiWriter<W> where W: Writer { + #[inline] + fn write_all(&mut self, buf: &[u8]) -> old_io::IoResult<()> { + for writer in self.writers.iter_mut() { + try!(writer.write_all(buf)); + } + Ok(()) + } + + #[inline] + fn flush(&mut self) -> old_io::IoResult<()> { + for writer in self.writers.iter_mut() { + try!(writer.flush()); + } + Ok(()) + } +} + +/// A `Reader` which chains input from multiple `Reader`s, reading each to +/// completion before moving onto the next. +#[derive(Clone, Show)] +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` + pub fn new(mut readers: I) -> ChainedReader<I, R> { + let r = readers.next(); + ChainedReader { readers: readers, cur_reader: r } + } +} + +impl<R: Reader, I: Iterator<Item=R>> Reader for ChainedReader<I, R> { + fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<uint> { + 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(Show)] +pub struct TeeReader<R, W> { + reader: R, + writer: W, +} + +impl<R: Reader, W: Writer> TeeReader<R, W> { + /// Creates a new `TeeReader` + 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) + } +} + +impl<R: Reader, W: Writer> Reader for TeeReader<R, W> { + fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<uint> { + 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`. +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, Show)] +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<uint> { + 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}; + 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!(vec!(0, 1, 2), r.read_to_end().unwrap()); + } + } + + #[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!(vec!(0, 1), r.read_to_end().unwrap()); + } + assert_eq!(vec!(2), r.read_to_end().unwrap()); + } + + #[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!(vec!(1, 2), r.read_to_end().unwrap()); + 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!(vec![], r.read_to_end().unwrap()); + } + + #[test] + fn test_null_writer() { + let mut s = NullWriter; + let buf = vec![0, 0, 0]; + s.write_all(buf.as_slice()).unwrap(); + s.flush().unwrap(); + } + + #[test] + fn test_zero_reader() { + let mut s = ZeroReader; + let mut buf = vec![1, 2, 3]; + assert_eq!(s.read(buf.as_mut_slice()), Ok(3)); + assert_eq!(vec![0, 0, 0], buf); + } + + #[test] + fn test_null_reader() { + let mut r = NullReader; + let mut buf = vec![0]; + assert!(r.read(buf.as_mut_slice()).is_err()); + } + + #[test] + fn test_multi_writer() { + static mut writes: uint = 0; + static mut flushes: uint = 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!(vec!(0, 1, 2, 3), r.read_to_end().unwrap()); + } + + #[test] + fn test_tee_reader() { + let mut r = TeeReader::new(MemReader::new(vec!(0, 1, 2)), + Vec::new()); + assert_eq!(vec!(0, 1, 2), r.read_to_end().unwrap()); + let (_, w) = r.into_inner(); + assert_eq!(vec!(0, 1, 2), w); + } + + #[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!(vec!(0, 1, 2, 3, 4), w); + } + + #[test] + fn limit_reader_buffer() { + let r = &mut b"0123456789\n0123456789\n"; + { + 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(range(0u8, 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(range(0u8, 8)); + let mut buf = []; + assert_eq!(Ok(0), r.read(&mut buf)); + } +} |
