Rename to tokio-core, add in futures-io

Renames the futures-mio crate to tokio-core, pulls in the futures-io crate under
an `io` module, and gets everything compiling.
This commit is contained in:
Alex Crichton
2016-08-26 14:39:47 -07:00
parent e71d509fee
commit f107c8d860
29 changed files with 1495 additions and 77 deletions
+82
View File
@@ -0,0 +1,82 @@
use std::io::{self, Read, Write};
use futures::{Future, Poll};
/// A future which will copy all data from a reader into a writer.
///
/// Created by the `copy` function, this future will resolve to the number of
/// bytes copied or an error if one happens.
pub struct Copy<R, W> {
reader: R,
read_done: bool,
writer: W,
pos: usize,
cap: usize,
amt: u64,
buf: Box<[u8]>,
}
/// Creates a future which represents copying all the bytes from one object to
/// another.
///
/// The returned future will copy all the bytes read from `reader` into the
/// `writer` specified. This future will only complete once the `reader` has hit
/// EOF and all bytes have been written to and flushed from the `writer`
/// provided.
///
/// On success the number of bytes is returned and the `reader` and `writer` are
/// consumed. On error the error is returned and the I/O objects are consumed as
/// well.
pub fn copy<R, W>(reader: R, writer: W) -> Copy<R, W>
where R: Read,
W: Write,
{
Copy {
reader: reader,
read_done: false,
writer: writer,
amt: 0,
pos: 0,
cap: 0,
buf: Box::new([0; 2048]),
}
}
impl<R, W> Future for Copy<R, W>
where R: Read,
W: Write,
{
type Item = u64;
type Error = io::Error;
fn poll(&mut self) -> Poll<u64, io::Error> {
loop {
// If our buffer is empty, then we need to read some data to
// continue.
if self.pos == self.cap && !self.read_done {
let n = try_nb!(self.reader.read(&mut self.buf));
if n == 0 {
self.read_done = true;
} else {
self.pos = 0;
self.cap = n;
}
}
// If our buffer has some data, let's write it out!
while self.pos < self.cap {
let i = try_nb!(self.writer.write(&self.buf[self.pos..self.cap]));
self.pos += i;
self.amt += i as u64;
}
// If we've written al the data and we've seen EOF, flush out the
// data and finish the transfer.
// done with the entire transfer.
if self.pos == self.cap && self.read_done {
try_nb!(self.writer.flush());
return Poll::Ok(self.amt)
}
}
}
}
+39
View File
@@ -0,0 +1,39 @@
use std::io::{self, Write};
use futures::{Poll, Future};
/// A future used to fully flush an I/O object.
///
/// Resolves to the underlying I/O object once the flush operation is complete.
///
/// Created by the `flush` function.
pub struct Flush<A> {
a: Option<A>,
}
/// Creates a future which will entirely flush an I/O object and then yield the
/// object itself.
///
/// This function will consume the object provided if an error happens, and
/// otherwise it will repeatedly call `flush` until it sees `Ok(())`, scheduling
/// a retry if `WouldBlock` is seen along the way.
pub fn flush<A>(a: A) -> Flush<A>
where A: Write,
{
Flush {
a: Some(a),
}
}
impl<A> Future for Flush<A>
where A: Write,
{
type Item = A;
type Error = io::Error;
fn poll(&mut self) -> Poll<A, io::Error> {
try_nb!(self.a.as_mut().unwrap().flush());
Poll::Ok(self.a.take().unwrap())
}
}
+47
View File
@@ -0,0 +1,47 @@
//! I/O conveniences when working with primitives in `tokio-core`
//!
//! Contains various combinators to work with I/O objects and type definitions
//! as well.
use std::io;
use futures::BoxFuture;
use futures::stream::BoxStream;
/// A convenience typedef around a `Future` whose error component is `io::Error`
pub type IoFuture<T> = BoxFuture<T, io::Error>;
/// A convenience typedef around a `Stream` whose error component is `io::Error`
pub type IoStream<T> = BoxStream<T, io::Error>;
/// A convenience macro for working with `io::Result<T>` from the `Read` and
/// `Write` traits.
///
/// This macro takes `io::Result<T>` as input, and returns `T` as the output. If
/// the input type is of the `Err` variant, then `Poll::NotReady` is returned if
/// it indicates `WouldBlock` or otherwise `Err` is returned.
#[macro_export]
macro_rules! try_nb {
($e:expr) => (match $e {
Ok(t) => t,
Err(ref e) if e.kind() == ::std::io::ErrorKind::WouldBlock => {
return ::futures::Poll::NotReady
}
Err(e) => return ::futures::Poll::Err(e.into()),
})
}
mod copy;
mod flush;
mod read_exact;
mod read_to_end;
mod task;
mod window;
mod write_all;
pub use self::copy::{copy, Copy};
pub use self::flush::{flush, Flush};
pub use self::read_exact::{read_exact, ReadExact};
pub use self::read_to_end::{read_to_end, ReadToEnd};
pub use self::task::{TaskIo, TaskIoRead, TaskIoWrite};
pub use self::window::Window;
pub use self::write_all::{write_all, WriteAll};
+77
View File
@@ -0,0 +1,77 @@
use std::io::{self, Read};
use std::mem;
use futures::{Poll, Future};
/// A future which can be used to easily read the entire contents of a stream
/// into a vector.
///
/// Created by the `read_exact` function.
pub struct ReadExact<A, T> {
state: State<A, T>,
}
enum State<A, T> {
Reading {
a: A,
buf: T,
pos: usize,
},
Empty,
}
/// Creates a future which will read exactly enough bytes to fill `buf`,
/// returning an error if EOF is hit sooner.
///
/// The returned future will resolve to both the I/O stream as well as the
/// buffer once the read operation is completed.
///
/// In the case of an error the buffer and the object will be discarded, with
/// the error yielded. In the case of success the object will be destroyed and
/// the buffer will be returned, with all data read from the stream appended to
/// the buffer.
pub fn read_exact<A, T>(a: A, buf: T) -> ReadExact<A, T>
where A: Read,
T: AsMut<[u8]>,
{
ReadExact {
state: State::Reading {
a: a,
buf: buf,
pos: 0,
},
}
}
fn eof() -> io::Error {
io::Error::new(io::ErrorKind::UnexpectedEof, "early eof")
}
impl<A, T> Future for ReadExact<A, T>
where A: Read,
T: AsMut<[u8]>,
{
type Item = (A, T);
type Error = io::Error;
fn poll(&mut self) -> Poll<(A, T), io::Error> {
match self.state {
State::Reading { ref mut a, ref mut buf, ref mut pos } => {
let buf = buf.as_mut();
while *pos < buf.len() {
let n = try_nb!(a.read(&mut buf[*pos..]));
*pos += n;
if n == 0 {
return Poll::Err(eof())
}
}
}
State::Empty => panic!("poll a WriteAll after it's done"),
}
match mem::replace(&mut self.state, State::Empty) {
State::Reading { a, buf, .. } => Poll::Ok((a, buf)),
State::Empty => panic!(),
}
}
}
+62
View File
@@ -0,0 +1,62 @@
use std::io::{self, Read};
use std::mem;
use futures::{Poll, Future};
/// A future which can be used to easily read the entire contents of a stream
/// into a vector.
///
/// Created by the `read_to_end` function.
pub struct ReadToEnd<A> {
state: State<A>,
}
enum State<A> {
Reading {
a: A,
buf: Vec<u8>,
},
Empty,
}
/// Creates a future which will read all the bytes associated with the I/O
/// object `A` into the buffer provided.
///
/// In the case of an error the buffer and the object will be discarded, with
/// the error yielded. In the case of success the object will be destroyed and
/// the buffer will be returned, with all data read from the stream appended to
/// the buffer.
pub fn read_to_end<A>(a: A, buf: Vec<u8>) -> ReadToEnd<A>
where A: Read,
{
ReadToEnd {
state: State::Reading {
a: a,
buf: buf,
}
}
}
impl<A> Future for ReadToEnd<A>
where A: Read,
{
type Item = (A, Vec<u8>);
type Error = io::Error;
fn poll(&mut self) -> Poll<(A, Vec<u8>), io::Error> {
match self.state {
State::Reading { ref mut a, ref mut buf } => {
// If we get `Ok`, then we know the stream hit EOF and we're done. If we
// hit "would block" then all the read data so far is in our buffer, and
// otherwise we propagate errors
try_nb!(a.read_to_end(buf));
},
State::Empty => panic!("poll ReadToEnd after it's done"),
}
match mem::replace(&mut self.state, State::Empty) {
State::Reading { a, buf } => Poll::Ok((a, buf)),
State::Empty => unreachable!(),
}
}
}
+102
View File
@@ -0,0 +1,102 @@
use std::cell::RefCell;
use std::io::{self, Read, Write};
use futures::task::TaskData;
/// Abstraction that allows inserting an I/O object into task-local storage,
/// returning a handle that can be split.
///
/// A `TaskIo<T>` handle implements the `ReadTask` and `WriteTask` and will only
/// work with the same task that the associated object was inserted into. The
/// handle may then be optionally `split` into the read/write halves so they can
/// be worked with independently.
///
/// Note that it is important that the future returned from `TaskIo::new`, when
/// polled, will pin the yielded `TaskIo<T>` object to that specific task. Any
/// attempt to read or write the object on other tasks will result in a panic.
pub struct TaskIo<T> {
handle: TaskData<RefCell<T>>,
}
/// The readable half of a `TaskIo<T>` instance returned from `TaskIo::split`.
///
/// This handle implements the `ReadTask` trait and can be used to split up an
/// I/O object into two distinct halves.
pub struct TaskIoRead<T> {
handle: TaskData<RefCell<T>>,
}
/// The writable half of a `TaskIo<T>` instance returned from `TaskIo::split`.
///
/// This handle implements the `WriteTask` trait and can be used to split up an
/// I/O object into two distinct halves.
pub struct TaskIoWrite<T> {
handle: TaskData<RefCell<T>>,
}
impl<T> TaskIo<T> {
/// Returns a new future which represents the insertion of the I/O object
/// `T` into task local storage, returning a `TaskIo<T>` handle to it.
///
/// The returned future will never resolve to an error.
pub fn new(t: T) -> TaskIo<T> {
TaskIo {
handle: TaskData::new(RefCell::new(t)),
}
}
}
impl<T> TaskIo<T>
where T: Read + Write,
{
/// For an I/O object which is both readable and writable, this method can
/// be used to split the handle into two independently owned halves.
///
/// The returned pair implements the `ReadTask` and `WriteTask` traits,
/// respectively, and can be used to pass around the object to different
/// combinators if necessary.
pub fn split(self) -> (TaskIoRead<T>, TaskIoWrite<T>) {
(TaskIoRead { handle: self.handle.clone() },
TaskIoWrite { handle: self.handle })
}
}
impl<T> Read for TaskIo<T>
where T: io::Read,
{
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.handle.with(|t| t.borrow_mut().read(buf))
}
}
impl<T> Write for TaskIo<T>
where T: io::Write,
{
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.handle.with(|t| t.borrow_mut().write(buf))
}
fn flush(&mut self) -> io::Result<()> {
self.handle.with(|t| t.borrow_mut().flush())
}
}
impl<T> Read for TaskIoRead<T>
where T: io::Read,
{
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.handle.with(|t| t.borrow_mut().read(buf))
}
}
impl<T> Write for TaskIoWrite<T>
where T: io::Write,
{
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.handle.with(|t| t.borrow_mut().write(buf))
}
fn flush(&mut self) -> io::Result<()> {
self.handle.with(|t| t.borrow_mut().flush())
}
}
+116
View File
@@ -0,0 +1,116 @@
use std::ops;
/// A owned window around an underlying buffer.
///
/// Normally slices work great for considering sub-portions of a buffer, but
/// unfortunately a slice is a *borrowed* type in Rust which has an associated
/// lifetime. When working with future and async I/O these lifetimes are not
/// always appropriate, and are sometimes difficult to store in tasks. This
/// type strives to fill this gap by providing an "owned slice" around an
/// underlying buffer of bytes.
///
/// A `Window<T>` wraps an underlying buffer, `T`, and has configurable
/// start/end indexes to alter the behavior of the `AsRef<[u8]>` implementation
/// that this type carries.
///
/// This type can be particularly useful when working with the `write_all`
/// combinator in this crate. Data can be sliced via `Window`, consumed by
/// `write_all`, and then earned back once the write operation finishes through
/// the `into_inner` method on this type.
pub struct Window<T> {
inner: T,
range: ops::Range<usize>,
}
impl<T: AsRef<[u8]>> Window<T> {
/// Creates a new window around the buffer `t` defaulting to the entire
/// slice.
///
/// Further methods can be called on the returned `Window<T>` to alter the
/// window into the data provided.
pub fn new(t: T) -> Window<T> {
Window {
range: 0..t.as_ref().len(),
inner: t,
}
}
/// Gets a shared reference to the underlying buffer inside of this
/// `Window`.
pub fn get_ref(&self) -> &T {
&self.inner
}
/// Gets a mutable reference to the underlying buffer inside of this
/// `Window`.
pub fn get_mut(&mut self) -> &mut T {
&mut self.inner
}
/// Consumes this `Window`, returning the underlying buffer.
pub fn into_inner(self) -> T {
self.inner
}
/// Returns the starting index of this window into the underlying buffer
/// `T`.
pub fn start(&self) -> usize {
self.range.start
}
/// Returns the end index of this window into the underlying buffer
/// `T`.
pub fn end(&self) -> usize {
self.range.end
}
/// Changes the starting index of this window to the index specified.
///
/// Returns the windows back to chain multiple calls to this method.
///
/// # Panics
///
/// This method will panic if `start` is out of bounds for the underlying
/// slice or if it comes after the `end` configured in this window.
pub fn set_start(&mut self, start: usize) -> &mut Window<T> {
assert!(start < self.inner.as_ref().len());
assert!(start <= self.range.end);
self.range.start = start;
self
}
/// Changes the end index of this window to the index specified.
///
/// Returns the windows back to chain multiple calls to this method.
///
/// # Panics
///
/// This method will panic if `end` is out of bounds for the underlying
/// slice or if it comes after the `end` configured in this window.
pub fn set_end(&mut self, end: usize) -> &mut Window<T> {
assert!(end < self.inner.as_ref().len());
assert!(self.range.start <= end);
self.range.end = end;
self
}
// TODO: how about a generic set() method along the lines of:
//
// buffer.set(..3)
// .set(0..2)
// .set(4..)
//
// etc.
}
impl<T: AsRef<[u8]>> AsRef<[u8]> for Window<T> {
fn as_ref(&self) -> &[u8] {
&self.inner.as_ref()[self.range.start..self.range.end]
}
}
impl<T: AsMut<[u8]>> AsMut<[u8]> for Window<T> {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.inner.as_mut()[self.range.start..self.range.end]
}
}
+80
View File
@@ -0,0 +1,80 @@
use std::io::{self, Write};
use std::mem;
use futures::{Poll, Future};
/// A future used to write the entire contents of some data to a stream.
///
/// This is created by the `write_all` top-level method.
pub struct WriteAll<A, T> {
state: State<A, T>,
}
enum State<A, T> {
Writing {
a: A,
buf: T,
pos: usize,
},
Empty,
}
/// Creates a future that will write the entire contents of the buffer `buf` to
/// the stream `a` provided.
///
/// The returned future will not return until all the data has been written, and
/// the future will resolve to the stream as well as the buffer (for reuse if
/// needed).
///
/// Any error which happens during writing will cause both the stream and the
/// buffer to get destroyed.
///
/// The `buf` parameter here only requires the `AsRef<[u8]>` trait, which should
/// be broadly applicable to accepting data which can be converted to a slice.
/// The `Window` struct is also available in this crate to provide a different
/// window into a slice if necessary.
pub fn write_all<A, T>(a: A, buf: T) -> WriteAll<A, T>
where A: Write,
T: AsRef<[u8]>,
{
WriteAll {
state: State::Writing {
a: a,
buf: buf,
pos: 0,
},
}
}
fn zero_write() -> io::Error {
io::Error::new(io::ErrorKind::WriteZero, "zero-length write")
}
impl<A, T> Future for WriteAll<A, T>
where A: Write,
T: AsRef<[u8]>,
{
type Item = (A, T);
type Error = io::Error;
fn poll(&mut self) -> Poll<(A, T), io::Error> {
match self.state {
State::Writing { ref mut a, ref buf, ref mut pos } => {
let buf = buf.as_ref();
while *pos < buf.len() {
let n = try_nb!(a.write(&buf[*pos..]));
*pos += n;
if n == 0 {
return Poll::Err(zero_write())
}
}
}
State::Empty => panic!("poll a WriteAll after it's done"),
}
match mem::replace(&mut self.state, State::Empty) {
State::Writing { a, buf, .. } => Poll::Ok((a, buf)),
State::Empty => panic!(),
}
}
}