Skip to content

Instantly share code, notes, and snippets.

@eddyb
Last active March 26, 2020 18:37
Show Gist options
  • Save eddyb/822c658190ccf18058db to your computer and use it in GitHub Desktop.
Save eddyb/822c658190ccf18058db to your computer and use it in GitHub Desktop.
use async::fs::File;
use async::io::Async;
fn copy(src: &Path, dst: &Path) -> impl Async<()> {
let src = try!(File::open(src));
let dst = try!(File::create(dst));
// Copy a kiB at a time.
let mut buffer = Vec::with_capacity(1024);
loop {
buffer = await!(src.read(buffer));
if buffer.is_empty() { break; }
buffer = await!(dst.write(buffer));
buffer.clear();
let s_meta = await!(src.metadata());
let d_meta = await!(dst.metadata());
println!("{}B copied out of {}B", d_meta.size, s_meta.size);
}
Ok(())
}
fn copy(src: &Path, dst: &Path) -> impl Async<()> {
let src = try!(File::open(src));
let dst = try!(File::create(dst));
// Copy a kiB at a time.
let mut buffer = Vec::with_capacity(1024);
loop {
buffer = try!(yield+ src.read(buffer));
if buffer.is_empty() { break; }
buffer = try!(yield+ dst.write(buffer));
buffer.clear();
let s_meta = try!(yield+ src.metadata());
let d_meta = try!(yield+ dst.metadata());
println!("{}B copied out of {}B", d_meta.size, s_meta.size);
}
Ok(())
}
fn copy(src: &Path, dst: &Path) -> impl Async<()> {
let src = try!(File::open(src));
let dst = try!(File::create(dst));
// Copy a kiB at a time.
let mut buffer = Vec::with_capacity(1024);
loop {
buffer = try!(match src.read(buffer).generate() {
Yield::Value((mut action, mut gen)) => {
loop {
// NOTE not the case here, but this yield could need
// to do polymorphic pass-through to `gen`.
match gen.next(yield action) {
Yield::Value(next) => action = next,
// NOTE cheating via break with value.
Yield::Return(res) => break res
}
}
}
Yield::Return(res) => res
});
if buffer.is_empty() { break; }
buffer = try!(match dst.write(buffer).generate() {
Yield::Value((mut action, mut gen)) => {
loop {
match gen.next(yield action) {
Yield::Value(next) => action = next,
Yield::Return(res) => break res
}
}
}
Yield::Return(res) => res
});
buffer.clear();
let s_meta = try!(match src.metadata().generate() {
Yield::Value((mut action, mut gen)) => {
loop {
match gen.next(yield action) {
Yield::Value(next) => action = next,
Yield::Return(res) => break res
}
}
}
Yield::Return(res) => res
});
let d_meta = try!(match dst.metadata().generate() {
Yield::Value((mut action, mut gen)) => {
loop {
match gen.next(yield action) {
Yield::Value(next) => action = next,
Yield::Return(res) => break res
}
}
}
Yield::Return(res) => res
});
println!("{}B copied out of {}B", d_meta.size, s_meta.size);
}
Ok(())
}
fn copy(src: &Path, dst: &Path) -> impl Async<()> {
let src = match File::open(src) {
Ok(file) => file,
Err(e) => return Err(e)
};
let dst = match File::create(dst) {
Ok(file) => file,
Err(e) => return Err(e)
};
// Copy a kiB at a time.
let mut buffer = Vec::with_capacity(1024);
loop {
let res = match src.read(buffer).generate() {
Yield::Value((mut action, mut gen)) => {
loop {
// NOTE not the case here, but this yield could need
// to do polymorphic pass-through to `gen`.
match gen.next(yield action) {
Yield::Value(next) => action = next,
// NOTE cheating via break with value.
Yield::Return(res) => break res
}
}
}
Yield::Return(res) => res
};
match res {
Ok(b) => buffer = b,
Err(e) => return Err(e)
}
if buffer.is_empty() { break; }
let res = match dst.write(buffer).generate() {
Yield::Value((mut action, mut gen)) => {
loop {
match gen.next(yield action) {
Yield::Value(next) => action = next,
Yield::Return(res) => break res
}
}
}
Yield::Return(res) => res
};
match res {
Ok(b) => buffer = b,
Err(e) => return Err(e)
}
buffer.clear();
let res = match src.metadata().generate() {
Yield::Value((mut action, mut gen)) => {
loop {
match gen.next(yield action) {
Yield::Value(next) => action = next,
Yield::Return(res) => break res
}
}
}
Yield::Return(res) => res
};
let s_meta = match res {
Ok(x) => x,
Err(e) => return Err(e)
};
let res = match dst.metadata().generate() {
Yield::Value((mut action, mut gen)) => {
loop {
match gen.next(yield action) {
Yield::Value(next) => action = next,
Yield::Return(res) => break res
}
}
}
Yield::Return(res) => res
};
let d_meta = match res {
Ok(x) => x,
Err(e) => return Err(e)
};
println!("{}B copied out of {}B", d_meta.size, s_meta.size);
}
Ok(())
}
fn copy(src: &Path, dst: &Path) -> impl Async<()> {
let src = match File::open(src) {
Ok(file) => file,
Err(e) => return Yield::Return(Err(e))
};
let dst = match File::create(dst) {
Ok(file) => file,
Err(e) => return Yield::Return(Err(e))
};
// Copy a kiB at a time.
let mut buffer = Vec::with_capacity(1024);
loop {
let res = match src.read(buffer).generate() {
Yield::Value((mut action, mut gen)) => {
loop {
return Yield::Value(action);
// NOTE input into the generator is denoted with in(T).
match gen.next(in(io::Result<Vec<u8>>)) {
Yield::Value(next) => action = next,
// NOTE cheating via break with value.
Yield::Return(res) => break res
}
}
}
Yield::Return(res) => res
};
match res {
Ok(b) => buffer = b,
Err(e) => return Yield::Return(Err(e))
}
if buffer.is_empty() { return Yield::Return(Ok(())); }
let res = match dst.write(buffer).generate() {
Yield::Value((mut action, mut gen)) => {
loop {
return Yield::Value(action);
match gen.next(in(io::Result<Vec<u8>>)) {
Yield::Value(next) => action = next,
Yield::Return(res) => break res
}
}
}
Yield::Return(res) => res
};
match res {
Ok(b) => buffer = b,
Err(e) => return Yield::Return(Err(e))
}
buffer.clear();
let res = match src.metadata().generate() {
Yield::Value((mut action, mut gen)) => {
loop {
return Yield::Value(action);
match gen.next(in(io::Result<fs::Metadata>)) {
Yield::Value(next) => action = next,
Yield::Return(res) => break res
}
}
}
Yield::Return(res) => res
};
let s_meta = match res {
Ok(x) => x,
Err(e) => return Yield::Return(Err(e))
};
let res = match dst.metadata().generate() {
Yield::Value((mut action, mut gen)) => {
loop {
return Yield::Value(action);
match gen.next(in(io::Result<fs::Metadata>)) {
Yield::Value(next) => action = next,
Yield::Return(res) => break res
}
}
}
Yield::Return(res) => res
};
let d_meta = match res {
Ok(x) => x,
Err(e) => return Yield::Return(Err(e))
};
println!("{}B copied out of {}B", d_meta.size, s_meta.size);
}
}
use gen::{Generate, Generator, GeneratorBase, Yield, Mirror};
use fs::File;
use io::Async;
use std::mem;
use std::path::Path;
struct Copy<'a, 'b> { src: &'a Path, dst: &'b Path }
fn copy<'a, 'b>(src: &'a Path, dst: &'b Path) -> Copy<'a, 'b> {
Copy { src: src, dst: dst }
}
impl<'a, 'b> Generate for Copy<'a, 'b> {
type G = S;
// NOTE due to how generators can return immediatelly,
// it's theoretically possible to have `copy` finish
// without yielding any io::Action (e.g. a sync mock-up).
// However, inlining the src.read(buffer).generate() call
// from S::advance should kill the rest of that loop here.
fn generate(self) -> Yield<(io::Action, S), io::Result<()>> {
let Copy { src, dst } = self;
let src = match File::open(src) {
Ok(file) => file,
Err(e) => return Yield::Return(Err(e))
};
let dst = match File::create(dst) {
Ok(file) => file,
Err(e) => return Yield::Return(Err(e))
};
// Copy a kiB at a time.
let buffer = Vec::with_capacity(1024);
let mut gen = S::Read { src: src, dst: dst, buffer: buffer };
match gen.advance() {
Yield::Value(x) => Yield::Value((x, gen)),
Yield::Return(r) => Yield::Return(r)
}
}
}
enum S {
Read { src: File, dst: File, buffer: Vec<u8> },
ReadY { src: File, dst: File, gen: Mirror<io::Action, io::Result<Vec<u8>>> },
Write { src: File, dst: File, buffer: Vec<u8> },
WriteY{ src: File, dst: File, gen: Mirror<io::Action, io::Result<Vec<u8>>> },
SMeta { src: File, dst: File, buffer: Vec<u8> },
SMetaY{ src: File, dst: File, buffer: Vec<u8>, gen: Mirror<io::Action, io::Result<fs::Metadata>> },
DMeta { src: File, dst: File, buffer: Vec<u8>, s_meta: fs::Metadata },
DMetaY{ src: File, dst: File, buffer: Vec<u8>, s_meta: fs::Metadata, gen: Mirror<io::Action, io::Result<fs::Metadata>> },
Print { src: File, dst: File, buffer: Vec<u8>, s_meta: fs::Metadata, d_meta: fs::Metadata },
Completed
}
impl GeneratorBase for S {
type Value = io::Action;
type Return = io::Result<()>;
}
impl S {
// Continue execution until the next yield or return.
fn advance(&mut self) -> Yield<io::Action, io::Result<()>> {
loop {
match mem::replace(self, S::Completed) {
S::Read { src, dst, buffer } => {
match src.read(buffer).generate() {
Yield::Value((action, gen)) => {
*self = S::ReadY { src: src, dst: dst, gen: gen };
return Yield::Value(action);
}
Yield::Return(Err(e)) => return Yield::Return(Err(e)),
Yield::Return(Ok(res)) => {
*self = S::Write { src: src, dst: dst, buffer: res };
}
}
}
S::Write { src, dst, buffer } => {
if buffer.is_empty() { return Yield::Return(Ok(())); }
match dst.write(buffer).generate() {
Yield::Value((action, gen)) => {
*self = S::WriteY { src: src, dst: dst, gen: gen };
return Yield::Value(action);
}
Yield::Return(Err(e)) => return Yield::Return(Err(e)),
Yield::Return(Ok(res)) => {
*self = S::SMeta { src: src, dst: dst, buffer: res };
}
}
}
S::SMeta { src, dst, mut buffer } => {
buffer.clear();
match src.metadata().generate() {
Yield::Value((action, gen)) => {
*self = S::SMetaY { src: src, dst: dst, buffer: buffer, gen: gen };
return Yield::Value(action);
}
Yield::Return(Err(e)) => return Yield::Return(Err(e)),
Yield::Return(Ok(res)) => {
*self = S::DMeta { src: src, dst: dst, buffer: buffer, s_meta: res };
}
}
}
S::DMeta { src, dst, buffer, s_meta } => {
match dst.metadata().generate() {
Yield::Value((action, gen)) => {
*self = S::DMetaY { src: src, dst: dst, buffer: buffer, s_meta: s_meta, gen: gen };
return Yield::Value(action);
}
Yield::Return(Err(e)) => return Yield::Return(Err(e)),
Yield::Return(Ok(res)) => {
*self = S::Print { src: src, dst: dst, buffer: buffer, s_meta: s_meta, d_meta: res };
}
}
}
S::Print { src, dst, buffer, s_meta, d_meta } => {
println!("{}B copied out of {}B", d_meta.len(), s_meta.len());
*self = S::Read { src: src, dst: dst, buffer: buffer };
}
S::ReadY {..} | S::WriteY {..} | S::SMetaY {..} | S::DMetaY {..} => {
unreachable!("generator needs input at yield point")
}
S::Completed => unreachable!("generator has already completed")
}
}
}
}
impl Generator<io::Result<Vec<u8>>> for S {
fn next(&mut self, input: io::Result<Vec<u8>>) -> Yield<io::Action, io::Result<()>> {
match mem::replace(self, S::Completed) {
S::ReadY { src, dst, mut gen } => {
match gen.next(input) {
Yield::Value(next) => {
*self = S::ReadY { src: src, dst: dst, gen: gen };
return Yield::Value(next);
}
Yield::Return(Err(e)) => return Yield::Return(Err(e)),
Yield::Return(Ok(res)) => {
*self = S::Write { src: src, dst: dst, buffer: res };
}
}
}
S::WriteY { src, dst, mut gen } => {
match gen.next(input) {
Yield::Value(next) => {
*self = S::WriteY { src: src, dst: dst, gen: gen };
return Yield::Value(next);
}
Yield::Return(Err(e)) => return Yield::Return(Err(e)),
Yield::Return(Ok(res)) => {
*self = S::SMeta { src: src, dst: dst, buffer: res };
}
}
}
_ => unreachable!("generator state does not accept Vec<u8> as input")
}
self.advance()
}
}
impl Generator<io::Result<fs::Metadata>> for S {
fn next(&mut self, input: io::Result<fs::Metadata>) -> Yield<io::Action, io::Result<()>> {
match mem::replace(self, S::Completed) {
S::SMetaY { src, dst, buffer, mut gen } => {
match gen.next(input) {
Yield::Value(next) => {
*self = S::SMetaY { src: src, dst: dst, buffer: buffer, gen: gen };
return Yield::Value(next);
}
Yield::Return(Err(e)) => return Yield::Return(Err(e)),
Yield::Return(Ok(res)) => {
*self = S::DMeta { src: src, dst: dst, buffer: buffer, s_meta: res };
}
}
}
S::DMetaY { src, dst, buffer, s_meta, mut gen } => {
match gen.next(input) {
Yield::Value(next) => {
*self = S::DMetaY { src: src, dst: dst, buffer: buffer, s_meta: s_meta, gen: gen };
return Yield::Value(next);
}
Yield::Return(Err(e)) => return Yield::Return(Err(e)),
Yield::Return(Ok(res)) => {
*self = S::Print { src: src, dst: dst, buffer: buffer, s_meta: s_meta, d_meta: res };
}
}
}
_ => unreachable!("generator state does not accept fs::Metadata as input")
}
self.advance()
}
}
pub mod gen {
use std::marker::PhantomData;
#[cfg_attr(std, lang = "yield")]
pub enum Yield<T, R> {
Value(T),
Return(R)
}
#[cfg_attr(std, lang = "generator_base")]
pub trait GeneratorBase {
type Value;
type Return;
}
fn type_name<T: ?Sized>() -> &'static str {
"<unknown>"
/*unsafe {
::std::intrinsics::type_name::<T>()
}*/
}
#[cfg_attr(std, lang = "generator")]
pub trait Generator<Input>: GeneratorBase {
fn next(&mut self, _: Input) -> Yield<Self::Value, Self::Return> {
panic!("generator `{}` does not take `{}` as input",
type_name::<Self>(), type_name::<Input>())
}
}
#[cfg(std)]
impl<G: Generator<(), Return=()>> Iterator for G {
type Item = G::Value;
fn next(&mut self) -> Option<G::Value> {
match self.next(()) {
Yield::Value(x) => Some(x),
Yield::Return(_) => None
}
}
}
pub trait Generate {
type G: GeneratorBase;
/// Run up to the first yield, return that and the generator.
fn generate(self) -> Yield<(<Self::G as GeneratorBase>::Value, Self::G),
<Self::G as GeneratorBase>::Return>;
}
pub struct YieldAnd<V, G>(pub V, pub G);
impl<V, G: GeneratorBase<Value = V>> Generate for YieldAnd<V, G> {
type G = G;
fn generate(self) -> Yield<(V, G), G::Return> {
Yield::Value((self.0, self.1))
}
}
pub struct Mirror<V, R> {
_data: PhantomData<(V, R)>
}
impl<V, R> Mirror<V, R> {
pub fn new() -> Mirror<V, R> {
Mirror {
_data: PhantomData
}
}
}
impl<V, R> GeneratorBase for Mirror<V, R> {
type Value = V;
type Return = R;
}
impl<V, R> Generator<R> for Mirror<V, R> {
fn next(&mut self, value: R) -> Yield<V, R> {
Yield::Return(value)
}
}
pub type YieldAndMirror<V, R> = YieldAnd<V, Mirror<V, R>>;
}
pub mod io {
use gen::{Generate, GeneratorBase, Yield};
use fs;
pub use std::io::{Error, Result};
pub enum Action {
File(fs::File, fs::Action)
}
pub trait Async<R>: Generate
where Self::G: GeneratorBase<Value=Action, Return=Result<R>>
+ fs::Async {
fn run_sync(self) -> Result<R> where Self: Sized {
match self.generate() {
Yield::Value((mut action, mut gen)) => {
loop {
match Self::do_io(&mut gen, action) {
Yield::Value(next) => action = next,
Yield::Return(res) => return res
};
}
}
Yield::Return(res) => res
}
}
fn do_io(gen: &mut Self::G, action: Action) -> Yield<Action, Result<R>> {
match action {
Action::File(file, action) => fs::Async::do_file(gen, file, action)
}
}
}
impl<T: Generate, R> Async<R> for T
where T::G: GeneratorBase<Value=Action, Return=Result<R>>
+ fs::Async {}
macro_rules! await {
($x:expr) => (try!(yield+ $x))
}
}
pub mod fs {
use gen::{Generator, Mirror, Yield, YieldAnd, YieldAndMirror};
use io;
use std::fs as sync;
use std::io::prelude::*;
use std::os::unix::prelude::*;
use std::path::Path;
pub struct File { sync: sync::File }
pub use std::fs::Metadata;
impl File {
pub fn open<P: AsRef<Path>>(path: P) -> io::Result<File> {
sync::File::open(path).map(|file| File { sync: file })
}
pub fn create<P: AsRef<Path>>(path: P) -> io::Result<File> {
sync::File::create(path).map(|file| File { sync: file })
}
}
impl Clone for File {
fn clone(&self) -> File {
extern "C" {
fn dup(fd: i32) -> i32;
}
File {
sync: unsafe {
sync::File::from_raw_fd(dup(self.sync.as_raw_fd()))
}
}
}
}
pub enum Action {
Read(Vec<u8>),
Write(Vec<u8>),
GetMetadata
}
pub trait Async: Generator<io::Result<Vec<u8>>>
+ Generator<io::Result<sync::Metadata>> {
fn do_file(&mut self, mut file: File, action: Action)
-> Yield<Self::Value, Self::Return> {
match action {
Action::Read(mut buffer) => {
// Nasty zero-fill because read takes &mut [u8].
let extra = buffer.capacity() - buffer.len();
buffer.extend((0..extra).map(|_| 0));
self.next(file.sync.read(&mut buffer).map(|bytes| {
buffer.truncate(bytes);
buffer
}))
}
Action::Write(buffer) => {
self.next(file.sync.write(&buffer).map(|_| buffer))
}
Action::GetMetadata => {
self.next(file.sync.metadata())
}
}
}
}
impl<G> Async for G
where G: Generator<io::Result<Vec<u8>>>
+ Generator<io::Result<Metadata>> {}
macro_rules! action {
($fun:ident => $act:ident -> $ret:ty) => {
pub fn $fun(&self) -> YieldAndMirror<io::Action, io::Result<$ret>> {
YieldAnd(io::Action::File(self.clone(), Action::$act), Mirror::new())
}
};
($fun:ident => $act:ident($($arg:ident: $ty:ty),+) -> $ret:ty) => {
pub fn $fun(&self, $($arg: $ty),+) -> YieldAndMirror<io::Action, io::Result<$ret>> {
YieldAnd(io::Action::File(self.clone(), Action::$act($($arg),+)), Mirror::new())
}
}
}
impl File {
action!(read => Read(buffer: Vec<u8>) -> Vec<u8>);
action!(write => Write(buffer: Vec<u8>) -> Vec<u8>);
action!(metadata => GetMetadata -> Metadata);
}
}
@eddyb
Copy link
Author

eddyb commented Jul 26, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment