<commit_before>#[allow(default_methods)];  // For the benefit of Server. Hopefully it'll work.

extern mod extra;

use std::comm::SharedChan;
use std::task::spawn_with;
use std::rt::io::Listener;
use std::rt::io::net::ip::IpAddr;
use std::rt::io::io_error;
use extra::time::precise_time_ns;

use std::rt::io::net::tcp::TcpListener;

use super::request::{RequestBuffer, Request};
use super::response::ResponseWriter;

// TODO: when mozilla/rust#7661 is resolved, assuming also that specifying inheritance of kinds for
// the trait works:
// - Scrap ServerUtil (including any using it into the local scope)
// - Change "trait Server" to "trait Server: Send"
// - Shift the serve_forever method into Server
pub trait Server {
	pub fn handle_request(&self, request: &Request, response: &mut ResponseWriter) -> ();

	// XXX: this could also be implemented on the serve methods
	pub fn get_config(&self) -> Config;
}

/// A temporary trait to fix current deficiencies in Rust's default methods on traits.
/// You'll need to import `ServerUtil` to be able to call `serve_forever` on a Server.
pub trait ServerUtil {
    pub fn serve_forever(self);
}

impl<T: Send + Clone + Server> ServerUtil for T {
	/**
	 * Attempt to bind to the address and port and start serving forever.
	 *
	 * This will only return if the initial connection fails or something else blows up.
	 */
    pub fn serve_forever(self) {
        let config = self.get_config();
        debug!("About to bind to %?", config.bind_address);
        let mut optlistener = TcpListener::bind(config.bind_address);
        debug!("Bind attempt completed");
        let (perf_po, perf_ch) = stream();
        let perf_ch = SharedChan::new(perf_ch);
        spawn_with(perf_po, perf_dumper);
        match optlistener {
            None => {
                debug!("listen failed :-(");
                return;
            }
            Some(ref mut listener) => {
                debug!("listening");
                loop {
                    // OK, we're sort of shadowing an IoError here. Perhaps this should be done in a
                    // separate task so that it can safely fail...
                    let mut error = None;
                    let optstream = io_error::cond.trap(|e| {
                        error = Some(e);
                    }).in(|| {
                        listener.accept()
                    });

                    let time_start = precise_time_ns();
                    if optstream.is_none() {
                        debug!("accept failed: %?", error);
                        // Question: is this the correct thing to do? We should probably be more
                        // intelligent, for there are some accept failures that are likely to be
                        // permanent, such that continuing would be a very bad idea, such as
                        // ENOBUFS/ENOMEM; and some where it should just be ignored, e.g.
                        // ECONNABORTED. TODO.
                        loop;
                    }
                    let stream = ::std::cell::Cell::new(optstream.unwrap());
                    let child_perf_ch = perf_ch.clone();
                    let child_self = self.clone();
                    do spawn {
                        let mut stream = ~stream.take();
                        debug!("accepted connection, got %?", stream);
                        //RequestBuffer::new(stream);
                        let time_spawned = precise_time_ns();
                        let (request, err_status) = Request::get(~RequestBuffer::new(stream));
                        let time_request_made = precise_time_ns();
                        let mut response = ~ResponseWriter::new(stream, request);
                        let time_response_made = precise_time_ns();
                        match err_status {
                            Ok(()) => {
                                child_self.handle_request(request, response);
                                // Ensure that we actually do send a response:
                                response.try_write_headers();
                            },
                            Err(status) => {
                                // Uh oh, it's a response that I as a server cannot cope with.
                                // No good user-agent should have caused this, so for the moment at
                                // least I am content to send no body in the response.
                                response.status = status;
                                response.headers.insert(~"Content-Length", ~"0");
                                response.write_headers();
                            },
                        }
                        let time_finished = precise_time_ns();
                        child_perf_ch.send((time_start, time_spawned, time_request_made, time_response_made, time_finished));
                    }
                }
            }
        }
    }
}

/// The necessary configuration for an HTTP server.
///
/// At present, only the IP address and port to bind to are needed, but it's possible that other
/// options may turn up later.
pub struct Config {
	bind_address: IpAddr,
}

/* Sorry, but Rust isn't ready for this yet; SimpleServer can't be made Clone just yet. (For
 * starters, ~fn() isn't cloneable.)

/// A simple `Server`-implementing class, allowing code to be written in an imperative style.
pub struct SimpleServer {
    config: Config,
    handler: ~fn(&Request, &mut ResponseWriter),
}

impl SimpleServer {
    /// Create a new `SimpleServer` instance with the provided members.
    pub fn new(config: Config, handler: ~fn(&Request, &mut ResponseWriter)) -> SimpleServer {
        SimpleServer {
            config: config,
            handler: handler,
        }
    }
}

impl Server for SimpleServer {
    /// Handles a request by passing it on to the structure's handler function.
    #[inline]
	pub fn handle_request(&self, request: &Request, response: &mut ResponseWriter) {
        (self.handler)(request, response);
    }

    /// Returns the structure's known config.
    #[inline]
	pub fn get_config(&self) -> Config {
        self.config
    }
}

/// Create a simple server and immediately start serving forever.
///
/// This is equivalent to
///
/// ~~~ {.rust}
/// SimpleServer::new(Config { bind_address: ip_addr }, handler).serve_forever();
/// ~~~
///
/// But it's nicer this way with `do` blocks and closures:
///
/// ~~~ {.rust}
/// do serve_forever(ip_addr) |r, mut w| {
///     // Now you can handle the request here and write the wresponse.
/// }
/// ~~~
///
/// The signature of this method is liable to change if `Config` gets more members.
// Please, pretty please, don't correct the word "wresponse".
#[inline]
pub fn serve_forever(ip_addr: IpAddr, handler: ~fn(&Request, &mut ResponseWriter)) {
    SimpleServer::new(Config { bind_address: ip_addr }, handler).serve_forever();
}

/// 0.0.0.0, port 80: publicly bound to the standard HTTP port.
/// Not recommended at present as this server is not hardened against the sort of traffic you may
/// encounter on the Internet and is vulnerable to various DoS attacks. Sit it behind a gateway.
static PUBLIC: IpAddr = Ipv4(0, 0, 0, 0, 80);
*/

static PERF_DUMP_FREQUENCY : u64 = 10_000;

/// Simple function to dump out perf stats every `PERF_DUMP_FREQUENCY` requests
fn perf_dumper(perf_po: Port<(u64, u64, u64, u64, u64)>) {
    // Total durations
    let mut td_spawn = 0u64;
    let mut td_request = 0u64;
    let mut td_response = 0u64;
    let mut td_handle = 0u64;
    let mut td_total = 0u64;
    let mut i = 0u64;
    loop {
        let data = perf_po.recv();
        let (start, spawned, request_made, response_made, finished) = data;
        td_spawn += spawned - start;
        td_request += request_made - spawned;
        td_response += response_made - request_made;
        td_handle += finished - response_made;
        td_total += finished - start;
        i += 1;
        if i % PERF_DUMP_FREQUENCY == 0 {
            println("");
            println(fmt!("%? requests made thus far. Current means:", i));
            println(fmt!("- Total:               100%%, %12?", td_total / i));
            println(fmt!("- Spawn:               %3?%%, %12?", 100 * td_spawn / td_total, td_spawn / i));
            println(fmt!("- Load request:        %3?%%, %12?", 100 * td_request / td_total, td_request / i));
            println(fmt!("- Initialise response: %3?%%, %12?", 100 * td_response / td_total, td_response / i));
            println(fmt!("- Handle:              %3?%%, %12?", 100 * td_handle / td_total, td_handle / i));
        }
    }
}
<commit_msg>Spawn tasks as supervised for request handlers.<commit_after>#[allow(default_methods)];  // For the benefit of Server. Hopefully it'll work.

extern mod extra;

use std::comm::SharedChan;
use std::task::{spawn_with, spawn_supervised};
use std::rt::io::Listener;
use std::rt::io::net::ip::IpAddr;
use std::rt::io::io_error;
use extra::time::precise_time_ns;

use std::rt::io::net::tcp::TcpListener;

use super::request::{RequestBuffer, Request};
use super::response::ResponseWriter;

// TODO: when mozilla/rust#7661 is resolved, assuming also that specifying inheritance of kinds for
// the trait works:
// - Scrap ServerUtil (including any using it into the local scope)
// - Change "trait Server" to "trait Server: Send"
// - Shift the serve_forever method into Server
pub trait Server {
	pub fn handle_request(&self, request: &Request, response: &mut ResponseWriter) -> ();

	// XXX: this could also be implemented on the serve methods
	pub fn get_config(&self) -> Config;
}

/// A temporary trait to fix current deficiencies in Rust's default methods on traits.
/// You'll need to import `ServerUtil` to be able to call `serve_forever` on a Server.
pub trait ServerUtil {
    pub fn serve_forever(self);
}

impl<T: Send + Clone + Server> ServerUtil for T {
	/**
	 * Attempt to bind to the address and port and start serving forever.
	 *
	 * This will only return if the initial connection fails or something else blows up.
	 */
    pub fn serve_forever(self) {
        let config = self.get_config();
        debug!("About to bind to %?", config.bind_address);
        let mut optlistener = TcpListener::bind(config.bind_address);
        debug!("Bind attempt completed");
        let (perf_po, perf_ch) = stream();
        let perf_ch = SharedChan::new(perf_ch);
        spawn_with(perf_po, perf_dumper);
        match optlistener {
            None => {
                debug!("listen failed :-(");
                return;
            }
            Some(ref mut listener) => {
                debug!("listening");
                loop {
                    // OK, we're sort of shadowing an IoError here. Perhaps this should be done in a
                    // separate task so that it can safely fail...
                    let mut error = None;
                    let optstream = io_error::cond.trap(|e| {
                        error = Some(e);
                    }).in(|| {
                        listener.accept()
                    });

                    let time_start = precise_time_ns();
                    if optstream.is_none() {
                        debug!("accept failed: %?", error);
                        // Question: is this the correct thing to do? We should probably be more
                        // intelligent, for there are some accept failures that are likely to be
                        // permanent, such that continuing would be a very bad idea, such as
                        // ENOBUFS/ENOMEM; and some where it should just be ignored, e.g.
                        // ECONNABORTED. TODO.
                        loop;
                    }
                    let stream = ::std::cell::Cell::new(optstream.unwrap());
                    let child_perf_ch = perf_ch.clone();
                    let child_self = self.clone();
                    do spawn_supervised {
                        let mut stream = ~stream.take();
                        debug!("accepted connection, got %?", stream);
                        //RequestBuffer::new(stream);
                        let time_spawned = precise_time_ns();
                        let (request, err_status) = Request::get(~RequestBuffer::new(stream));
                        let time_request_made = precise_time_ns();
                        let mut response = ~ResponseWriter::new(stream, request);
                        let time_response_made = precise_time_ns();
                        match err_status {
                            Ok(()) => {
                                child_self.handle_request(request, response);
                                // Ensure that we actually do send a response:
                                response.try_write_headers();
                            },
                            Err(status) => {
                                // Uh oh, it's a response that I as a server cannot cope with.
                                // No good user-agent should have caused this, so for the moment at
                                // least I am content to send no body in the response.
                                response.status = status;
                                response.headers.insert(~"Content-Length", ~"0");
                                response.write_headers();
                            },
                        }
                        let time_finished = precise_time_ns();
                        child_perf_ch.send((time_start, time_spawned, time_request_made, time_response_made, time_finished));
                    }
                }
            }
        }
    }
}

/// The necessary configuration for an HTTP server.
///
/// At present, only the IP address and port to bind to are needed, but it's possible that other
/// options may turn up later.
pub struct Config {
	bind_address: IpAddr,
}

/* Sorry, but Rust isn't ready for this yet; SimpleServer can't be made Clone just yet. (For
 * starters, ~fn() isn't cloneable.)

/// A simple `Server`-implementing class, allowing code to be written in an imperative style.
pub struct SimpleServer {
    config: Config,
    handler: ~fn(&Request, &mut ResponseWriter),
}

impl SimpleServer {
    /// Create a new `SimpleServer` instance with the provided members.
    pub fn new(config: Config, handler: ~fn(&Request, &mut ResponseWriter)) -> SimpleServer {
        SimpleServer {
            config: config,
            handler: handler,
        }
    }
}

impl Server for SimpleServer {
    /// Handles a request by passing it on to the structure's handler function.
    #[inline]
	pub fn handle_request(&self, request: &Request, response: &mut ResponseWriter) {
        (self.handler)(request, response);
    }

    /// Returns the structure's known config.
    #[inline]
	pub fn get_config(&self) -> Config {
        self.config
    }
}

/// Create a simple server and immediately start serving forever.
///
/// This is equivalent to
///
/// ~~~ {.rust}
/// SimpleServer::new(Config { bind_address: ip_addr }, handler).serve_forever();
/// ~~~
///
/// But it's nicer this way with `do` blocks and closures:
///
/// ~~~ {.rust}
/// do serve_forever(ip_addr) |r, mut w| {
///     // Now you can handle the request here and write the wresponse.
/// }
/// ~~~
///
/// The signature of this method is liable to change if `Config` gets more members.
// Please, pretty please, don't correct the word "wresponse".
#[inline]
pub fn serve_forever(ip_addr: IpAddr, handler: ~fn(&Request, &mut ResponseWriter)) {
    SimpleServer::new(Config { bind_address: ip_addr }, handler).serve_forever();
}

/// 0.0.0.0, port 80: publicly bound to the standard HTTP port.
/// Not recommended at present as this server is not hardened against the sort of traffic you may
/// encounter on the Internet and is vulnerable to various DoS attacks. Sit it behind a gateway.
static PUBLIC: IpAddr = Ipv4(0, 0, 0, 0, 80);
*/

static PERF_DUMP_FREQUENCY : u64 = 10_000;

/// Simple function to dump out perf stats every `PERF_DUMP_FREQUENCY` requests
fn perf_dumper(perf_po: Port<(u64, u64, u64, u64, u64)>) {
    // Total durations
    let mut td_spawn = 0u64;
    let mut td_request = 0u64;
    let mut td_response = 0u64;
    let mut td_handle = 0u64;
    let mut td_total = 0u64;
    let mut i = 0u64;
    loop {
        let data = perf_po.recv();
        let (start, spawned, request_made, response_made, finished) = data;
        td_spawn += spawned - start;
        td_request += request_made - spawned;
        td_response += response_made - request_made;
        td_handle += finished - response_made;
        td_total += finished - start;
        i += 1;
        if i % PERF_DUMP_FREQUENCY == 0 {
            println("");
            println(fmt!("%? requests made thus far. Current means:", i));
            println(fmt!("- Total:               100%%, %12?", td_total / i));
            println(fmt!("- Spawn:               %3?%%, %12?", 100 * td_spawn / td_total, td_spawn / i));
            println(fmt!("- Load request:        %3?%%, %12?", 100 * td_request / td_total, td_request / i));
            println(fmt!("- Initialise response: %3?%%, %12?", 100 * td_response / td_total, td_response / i));
            println(fmt!("- Handle:              %3?%%, %12?", 100 * td_handle / td_total, td_handle / i));
        }
    }
}
<|endoftext|>


<commit_before>// 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.

//! Blocking posix-based file I/O

use prelude::*;
use super::super::*;
use libc::{c_int, FILE};

#[allow(non_camel_case_types)]
pub type fd_t = c_int;

pub struct FileDesc {
    priv fd: fd_t
}

impl FileDesc {
    /// Create a `FileDesc` from an open C file descriptor.
    ///
    /// The `FileDesc` takes ownership of the file descriptor
    /// and will close it upon destruction.
    pub fn new(_fd: fd_t) -> FileDesc { fail2!() }
}

impl Reader for FileDesc {
    fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail2!() }

    fn eof(&mut self) -> bool { fail2!() }
}

impl Writer for FileDesc {
    fn write(&mut self, _buf: &[u8]) { fail2!() }

    fn flush(&mut self) { fail2!() }
}

impl Seek for FileDesc {
    fn tell(&self) -> u64 { fail2!() }

    fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail2!() }
}

pub struct CFile {
    priv file: *FILE
}

impl CFile {
    /// Create a `CFile` from an open `FILE` pointer.
    ///
    /// The `CFile` takes ownership of the file descriptor
    /// and will close it upon destruction.
    pub fn new(_file: *FILE) -> CFile { fail2!() }
}

impl Reader for CFile {
    fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail2!() }

    fn eof(&mut self) -> bool { fail2!() }
}

impl Writer for CFile {
    fn write(&mut self, _buf: &[u8]) { fail2!() }

    fn flush(&mut self) { fail2!() }
}

impl Seek for CFile {
    fn tell(&self) -> u64 { fail2!() }
    fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail2!() }
}
<commit_msg>Implement io::native::file<commit_after>// 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.

//! Blocking posix-based file I/O

#[allow(non_camel_case_types)];

use libc;
use os;
use prelude::*;
use super::super::*;

fn raise_error() {
    // XXX: this should probably be a bit more descriptive...
    let (kind, desc) = match os::errno() as i32 {
        libc::EOF => (EndOfFile, "end of file"),
        _ => (OtherIoError, "unknown error"),
    };

    io_error::cond.raise(IoError {
        kind: kind,
        desc: desc,
        detail: Some(os::last_os_error())
    });
}

fn keep_going(data: &[u8], f: &fn(*u8, uint) -> i64) -> i64 {
    #[cfg(windows)] static eintr: int = 0; // doesn't matter
    #[cfg(not(windows))] static eintr: int = libc::EINTR as int;

    let (data, origamt) = do data.as_imm_buf |data, amt| { (data, amt) };
    let mut data = data;
    let mut amt = origamt;
    while amt > 0 {
        let mut ret;
        loop {
            ret = f(data, amt);
            if cfg!(not(windows)) { break } // windows has no eintr
            // if we get an eintr, then try again
            if ret != -1 || os::errno() as int != eintr { break }
        }
        if ret == 0 {
            break
        } else if ret != -1 {
            amt -= ret as uint;
            data = unsafe { data.offset(ret as int) };
        } else {
            return ret;
        }
    }
    return (origamt - amt) as i64;
}

pub type fd_t = libc::c_int;

pub struct FileDesc {
    priv fd: fd_t,
}

impl FileDesc {
    /// Create a `FileDesc` from an open C file descriptor.
    ///
    /// The `FileDesc` will take ownership of the specified file descriptor and
    /// close it upon destruction.
    ///
    /// Note that all I/O operations done on this object will be *blocking*, but
    /// they do not require the runtime to be active.
    pub fn new(fd: fd_t) -> FileDesc {
        FileDesc { fd: fd }
    }
}

impl Reader for FileDesc {
    #[fixed_stack_segment] #[inline(never)]
    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
        #[cfg(windows)] type rlen = libc::c_uint;
        #[cfg(not(windows))] type rlen = libc::size_t;
        let ret = do keep_going(buf) |buf, len| {
            unsafe {
                libc::read(self.fd, buf as *mut libc::c_void, len as rlen) as i64
            }
        };
        if ret == 0 {
            None
        } else if ret < 0 {
            raise_error();
            None
        } else {
            Some(ret as uint)
        }
    }

    fn eof(&mut self) -> bool { false }
}

impl Writer for FileDesc {
    #[fixed_stack_segment] #[inline(never)]
    fn write(&mut self, buf: &[u8]) {
        #[cfg(windows)] type wlen = libc::c_uint;
        #[cfg(not(windows))] type wlen = libc::size_t;
        let ret = do keep_going(buf) |buf, len| {
            unsafe {
                libc::write(self.fd, buf as *libc::c_void, len as wlen) as i64
            }
        };
        if ret < 0 {
            raise_error();
        }
    }

    fn flush(&mut self) {}
}

impl Drop for FileDesc {
    #[fixed_stack_segment] #[inline(never)]
    fn drop(&mut self) {
        unsafe { libc::close(self.fd); }
    }
}

pub struct CFile {
    priv file: *libc::FILE
}

impl CFile {
    /// Create a `CFile` from an open `FILE` pointer.
    ///
    /// The `CFile` takes ownership of the `FILE` pointer and will close it upon
    /// destruction.
    pub fn new(file: *libc::FILE) -> CFile { CFile { file: file } }
}

impl Reader for CFile {
    #[fixed_stack_segment] #[inline(never)]
    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
        let ret = do keep_going(buf) |buf, len| {
            unsafe {
                libc::fread(buf as *mut libc::c_void, 1, len as libc::size_t,
                            self.file) as i64
            }
        };
        if ret == 0 {
            None
        } else if ret < 0 {
            raise_error();
            None
        } else {
            Some(ret as uint)
        }
    }

    #[fixed_stack_segment] #[inline(never)]
    fn eof(&mut self) -> bool {
        unsafe { libc::feof(self.file) != 0 }
    }
}

impl Writer for CFile {
    #[fixed_stack_segment] #[inline(never)]
    fn write(&mut self, buf: &[u8]) {
        let ret = do keep_going(buf) |buf, len| {
            unsafe {
                libc::fwrite(buf as *libc::c_void, 1, len as libc::size_t,
                            self.file) as i64
            }
        };
        if ret < 0 {
            raise_error();
        }
    }

    #[fixed_stack_segment] #[inline(never)]
    fn flush(&mut self) {
        if unsafe { libc::fflush(self.file) } < 0 {
            raise_error();
        }
    }
}

impl Seek for CFile {
    #[fixed_stack_segment] #[inline(never)]
    fn tell(&self) -> u64 {
        let ret = unsafe { libc::ftell(self.file) };
        if ret < 0 {
            raise_error();
        }
        return ret as u64;
    }

    #[fixed_stack_segment] #[inline(never)]
    fn seek(&mut self, pos: i64, style: SeekStyle) {
        let whence = match style {
            SeekSet => libc::SEEK_SET,
            SeekEnd => libc::SEEK_END,
            SeekCur => libc::SEEK_CUR,
        };
        if unsafe { libc::fseek(self.file, pos as libc::c_long, whence) } < 0 {
            raise_error();
        }
    }
}

impl Drop for CFile {
    #[fixed_stack_segment] #[inline(never)]
    fn drop(&mut self) {
        unsafe { libc::fclose(self.file); }
    }
}

#[cfg(test)]
mod tests {
    use libc;
    use os;
    use prelude::*;
    use rt::io::{io_error, SeekSet};
    use super::*;

    #[test] #[fixed_stack_segment]
    fn test_file_desc() {
        // Run this test with some pipes so we don't have to mess around with
        // opening or closing files.
        unsafe {
            let os::Pipe { input, out } = os::pipe();
            let mut reader = FileDesc::new(input);
            let mut writer = FileDesc::new(out);

            writer.write(bytes!("test"));
            let mut buf = [0u8, ..4];
            match reader.read(buf) {
                Some(4) => {
                    assert_eq!(buf[0], 't' as u8);
                    assert_eq!(buf[1], 'e' as u8);
                    assert_eq!(buf[2], 's' as u8);
                    assert_eq!(buf[3], 't' as u8);
                }
                r => fail2!("invalid read: {:?}", r)
            }

            let mut raised = false;
            do io_error::cond.trap(|_| { raised = true; }).inside {
                writer.read(buf);
            }
            assert!(raised);

            raised = false;
            do io_error::cond.trap(|_| { raised = true; }).inside {
                reader.write(buf);
            }
            assert!(raised);
        }
    }

    #[test] #[fixed_stack_segment]
    #[ignore(windows)] // apparently windows doesn't like tmpfile
    fn test_cfile() {
        unsafe {
            let f = libc::tmpfile();
            assert!(!f.is_null());
            let mut file = CFile::new(f);

            file.write(bytes!("test"));
            let mut buf = [0u8, ..4];
            file.seek(0, SeekSet);
            match file.read(buf) {
                Some(4) => {
                    assert_eq!(buf[0], 't' as u8);
                    assert_eq!(buf[1], 'e' as u8);
                    assert_eq!(buf[2], 's' as u8);
                    assert_eq!(buf[3], 't' as u8);
                }
                r => fail2!("invalid read: {:?}", r)
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Anatoly Ikorsky
//
// 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. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use BoxFuture;
use errors::*;
use std::fmt;
use std::sync::Arc;
use tokio_io::AsyncRead;

pub mod builtin;

/// Trait used to handle local infile requests.
///
/// Simple handler example:
///
/// ```rust
/// # extern crate futures;
/// # extern crate mysql_async as my;
/// # extern crate tokio_core as tokio;
/// # extern crate tokio_io;
///
/// # use futures::Future;
/// # use my::prelude::*;
/// # use tokio::reactor::Core;
/// # use tokio_io::AsyncRead;
/// # use std::env;
/// # fn main() {
/// struct ExampleHandler(&'static [u8]);
///
/// impl LocalInfileHandler for ExampleHandler {
///     fn handle(&self, _: &[u8]) -> Box<Future<Item=Box<AsyncRead>, Error=my::errors::Error>> {
///         Box::new(futures::future::ok(Box::new(self.0) as Box<AsyncRead>))
///     }
/// }
///
/// # let database_url: String = if let Ok(url) = env::var("DATABASE_URL") {
/// #     let opts = my::Opts::from_url(&url).expect("DATABASE_URL invalid");
/// #     if opts.get_db_name().expect("a database name is required").is_empty() {
/// #         panic!("database name is empty");
/// #     }
/// #     url
/// # } else {
/// #     "mysql://root:password@127.0.0.1:3307/mysql".into()
/// # };
///
/// let mut lp = Core::new().unwrap();
///
/// let mut opts = my::OptsBuilder::from_opts(&*database_url);
/// opts.local_infile_handler(Some(ExampleHandler(b"foobar")));
///
/// let pool = my::Pool::new(opts, &lp.handle());
///
/// let future = pool.get_conn()
///     .and_then(|conn| conn.drop_query("CREATE TEMPORARY TABLE tmp (a TEXT);"))
///     .and_then(|conn| conn.drop_query("LOAD DATA LOCAL INFILE 'baz' INTO TABLE tmp;"))
///     .and_then(|conn| conn.prep_exec("SELECT * FROM tmp;", ()))
///     .and_then(|result| result.map_and_drop(|row| my::from_row::<(String,)>(row).0))
///     .map(|(_ /* conn */, result)| {
///         assert_eq!(result.len(), 1);
///         assert_eq!(result[0], "foobar");
///     })
///     .and_then(|_| pool.disconnect());
///
/// lp.run(future).unwrap();
/// # }
/// ```
pub trait LocalInfileHandler: Sync + Send {
    /// `file_name` is the file name in `LOAD DATA LOCAL INFILE '<file name>' INTO TABLE ...;`
    /// query.
    fn handle(&self, file_name: &[u8]) -> BoxFuture<Box<AsyncRead>>;
}

/// Object used to wrap `T: LocalInfileHandler` inside of Opts.
#[derive(Clone)]
pub struct LocalInfileHandlerObject(Arc<LocalInfileHandler>);

impl LocalInfileHandlerObject {
    pub fn new<T: LocalInfileHandler + 'static>(handler: T) -> Self {
        LocalInfileHandlerObject(Arc::new(handler))
    }

    pub fn clone_inner(&self) -> Arc<LocalInfileHandler> {
        self.0.clone()
    }
}

impl PartialEq for LocalInfileHandlerObject {
    fn eq(&self, other: &LocalInfileHandlerObject) -> bool {
        self.0.as_ref() as *const LocalInfileHandler ==
            other.0.as_ref() as *const LocalInfileHandler
    }
}

impl Eq for LocalInfileHandlerObject {}

impl fmt::Debug for LocalInfileHandlerObject {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Local infile handler object")
    }
}
<commit_msg>Remove unused import<commit_after>// Copyright (c) 2017 Anatoly Ikorsky
//
// 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. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use BoxFuture;
use std::fmt;
use std::sync::Arc;
use tokio_io::AsyncRead;

pub mod builtin;

/// Trait used to handle local infile requests.
///
/// Simple handler example:
///
/// ```rust
/// # extern crate futures;
/// # extern crate mysql_async as my;
/// # extern crate tokio_core as tokio;
/// # extern crate tokio_io;
///
/// # use futures::Future;
/// # use my::prelude::*;
/// # use tokio::reactor::Core;
/// # use tokio_io::AsyncRead;
/// # use std::env;
/// # fn main() {
/// struct ExampleHandler(&'static [u8]);
///
/// impl LocalInfileHandler for ExampleHandler {
///     fn handle(&self, _: &[u8]) -> Box<Future<Item=Box<AsyncRead>, Error=my::errors::Error>> {
///         Box::new(futures::future::ok(Box::new(self.0) as Box<AsyncRead>))
///     }
/// }
///
/// # let database_url: String = if let Ok(url) = env::var("DATABASE_URL") {
/// #     let opts = my::Opts::from_url(&url).expect("DATABASE_URL invalid");
/// #     if opts.get_db_name().expect("a database name is required").is_empty() {
/// #         panic!("database name is empty");
/// #     }
/// #     url
/// # } else {
/// #     "mysql://root:password@127.0.0.1:3307/mysql".into()
/// # };
///
/// let mut lp = Core::new().unwrap();
///
/// let mut opts = my::OptsBuilder::from_opts(&*database_url);
/// opts.local_infile_handler(Some(ExampleHandler(b"foobar")));
///
/// let pool = my::Pool::new(opts, &lp.handle());
///
/// let future = pool.get_conn()
///     .and_then(|conn| conn.drop_query("CREATE TEMPORARY TABLE tmp (a TEXT);"))
///     .and_then(|conn| conn.drop_query("LOAD DATA LOCAL INFILE 'baz' INTO TABLE tmp;"))
///     .and_then(|conn| conn.prep_exec("SELECT * FROM tmp;", ()))
///     .and_then(|result| result.map_and_drop(|row| my::from_row::<(String,)>(row).0))
///     .map(|(_ /* conn */, result)| {
///         assert_eq!(result.len(), 1);
///         assert_eq!(result[0], "foobar");
///     })
///     .and_then(|_| pool.disconnect());
///
/// lp.run(future).unwrap();
/// # }
/// ```
pub trait LocalInfileHandler: Sync + Send {
    /// `file_name` is the file name in `LOAD DATA LOCAL INFILE '<file name>' INTO TABLE ...;`
    /// query.
    fn handle(&self, file_name: &[u8]) -> BoxFuture<Box<AsyncRead>>;
}

/// Object used to wrap `T: LocalInfileHandler` inside of Opts.
#[derive(Clone)]
pub struct LocalInfileHandlerObject(Arc<LocalInfileHandler>);

impl LocalInfileHandlerObject {
    pub fn new<T: LocalInfileHandler + 'static>(handler: T) -> Self {
        LocalInfileHandlerObject(Arc::new(handler))
    }

    pub fn clone_inner(&self) -> Arc<LocalInfileHandler> {
        self.0.clone()
    }
}

impl PartialEq for LocalInfileHandlerObject {
    fn eq(&self, other: &LocalInfileHandlerObject) -> bool {
        self.0.as_ref() as *const LocalInfileHandler ==
            other.0.as_ref() as *const LocalInfileHandler
    }
}

impl Eq for LocalInfileHandlerObject {}

impl fmt::Debug for LocalInfileHandlerObject {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Local infile handler object")
    }
}
<|endoftext|>


<commit_before>use crate::NewlineStyle;

/// Apply this newline style to the formatted text. When the style is set
/// to `Auto`, the `raw_input_text` is used to detect the existing line
/// endings.
///
/// If the style is set to `Auto` and `raw_input_text` contains no
/// newlines, the `Native` style will be used.
pub(crate) fn apply_newline_style(
    newline_style: NewlineStyle,
    formatted_text: &mut String,
    raw_input_text: &str,
) {
    const WINDOWS_NEWLINE: &str = "\r\n";

    match effective_newline_style(newline_style, raw_input_text) {
        EffectiveNewlineStyle::Windows => {
            let mut transformed = String::with_capacity(2 * formatted_text.capacity());
            for c in formatted_text.chars() {
                match c {
                    LINE_FEED => transformed.push_str(WINDOWS_NEWLINE),
                    CARRIAGE_RETURN => continue,
                    c => transformed.push(c),
                }
            }
            *formatted_text = transformed;
        }
        EffectiveNewlineStyle::Unix => {}
    }
}

const LINE_FEED: char = '\n';
const CARRIAGE_RETURN: char = '\r';

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum EffectiveNewlineStyle {
    Windows,
    Unix,
}

fn effective_newline_style(
    newline_style: NewlineStyle,
    raw_input_text: &str,
) -> EffectiveNewlineStyle {
    match newline_style {
        NewlineStyle::Auto => auto_detect_newline_style(raw_input_text),
        NewlineStyle::Native => native_newline_style(),
        NewlineStyle::Windows => EffectiveNewlineStyle::Windows,
        NewlineStyle::Unix => EffectiveNewlineStyle::Unix,
    }
}

fn auto_detect_newline_style(raw_input_text: &str) -> EffectiveNewlineStyle {
    if let Some(pos) = raw_input_text.find(LINE_FEED) {
        let pos = pos.saturating_sub(1);
        if let Some(CARRIAGE_RETURN) = raw_input_text.chars().nth(pos) {
            EffectiveNewlineStyle::Windows
        } else {
            EffectiveNewlineStyle::Unix
        }
    } else {
        native_newline_style()
    }
}

fn native_newline_style() -> EffectiveNewlineStyle {
    if cfg!(windows) {
        EffectiveNewlineStyle::Windows
    } else {
        EffectiveNewlineStyle::Unix
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_newline_style_auto_detect() {
        let lf = "One\nTwo\nThree";
        let crlf = "One\r\nTwo\r\nThree";
        let none = "One Two Three";

        assert_eq!(EffectiveNewlineStyle::Unix, auto_detect_newline_style(lf));
        assert_eq!(
            EffectiveNewlineStyle::Windows,
            auto_detect_newline_style(crlf)
        );

        if cfg!(windows) {
            assert_eq!(
                EffectiveNewlineStyle::Windows,
                auto_detect_newline_style(none)
            );
        } else {
            assert_eq!(EffectiveNewlineStyle::Unix, auto_detect_newline_style(none));
        }
    }

    #[test]
    fn test_newline_style_auto_apply() {
        let auto = NewlineStyle::Auto;

        let formatted_text = "One\nTwo\nThree";
        let raw_input_text = "One\nTwo\nThree";

        let mut out = String::from(formatted_text);
        apply_newline_style(auto, &mut out, raw_input_text);
        assert_eq!("One\nTwo\nThree", &out, "auto should detect 'lf'");

        let formatted_text = "One\nTwo\nThree";
        let raw_input_text = "One\r\nTwo\r\nThree";

        let mut out = String::from(formatted_text);
        apply_newline_style(auto, &mut out, raw_input_text);
        assert_eq!("One\r\nTwo\r\nThree", &out, "auto should detect 'crlf'");

        #[cfg(not(windows))]
        {
            let formatted_text = "One\nTwo\nThree";
            let raw_input_text = "One Two Three";

            let mut out = String::from(formatted_text);
            apply_newline_style(auto, &mut out, raw_input_text);
            assert_eq!(
                "One\nTwo\nThree", &out,
                "auto-native-unix should detect 'lf'"
            );
        }

        #[cfg(windows)]
        {
            let formatted_text = "One\nTwo\nThree";
            let raw_input_text = "One Two Three";

            let mut out = String::from(formatted_text);
            apply_newline_style(auto, &mut out, raw_input_text);
            assert_eq!(
                "One\r\nTwo\r\nThree", &out,
                "auto-native-windows should detect 'crlf'"
            );
        }
    }
}
<commit_msg>Extract conversion to windows newlines into fn<commit_after>use crate::NewlineStyle;

/// Apply this newline style to the formatted text. When the style is set
/// to `Auto`, the `raw_input_text` is used to detect the existing line
/// endings.
///
/// If the style is set to `Auto` and `raw_input_text` contains no
/// newlines, the `Native` style will be used.
pub(crate) fn apply_newline_style(
    newline_style: NewlineStyle,
    formatted_text: &mut String,
    raw_input_text: &str,
) {
    match effective_newline_style(newline_style, raw_input_text) {
        EffectiveNewlineStyle::Windows => {
            *formatted_text = convert_to_windows_newlines(formatted_text);
        }
        EffectiveNewlineStyle::Unix => {}
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum EffectiveNewlineStyle {
    Windows,
    Unix,
}

fn effective_newline_style(
    newline_style: NewlineStyle,
    raw_input_text: &str,
) -> EffectiveNewlineStyle {
    match newline_style {
        NewlineStyle::Auto => auto_detect_newline_style(raw_input_text),
        NewlineStyle::Native => native_newline_style(),
        NewlineStyle::Windows => EffectiveNewlineStyle::Windows,
        NewlineStyle::Unix => EffectiveNewlineStyle::Unix,
    }
}

const LINE_FEED: char = '\n';
const CARRIAGE_RETURN: char = '\r';

fn auto_detect_newline_style(raw_input_text: &str) -> EffectiveNewlineStyle {
    if let Some(pos) = raw_input_text.find(LINE_FEED) {
        let pos = pos.saturating_sub(1);
        if let Some(CARRIAGE_RETURN) = raw_input_text.chars().nth(pos) {
            EffectiveNewlineStyle::Windows
        } else {
            EffectiveNewlineStyle::Unix
        }
    } else {
        native_newline_style()
    }
}

fn native_newline_style() -> EffectiveNewlineStyle {
    if cfg!(windows) {
        EffectiveNewlineStyle::Windows
    } else {
        EffectiveNewlineStyle::Unix
    }
}

fn convert_to_windows_newlines(formatted_text: &String) -> String {
    let mut transformed = String::with_capacity(2 * formatted_text.capacity());
    for c in formatted_text.chars() {
        const WINDOWS_NEWLINE: &str = "\r\n";
        match c {
            LINE_FEED => transformed.push_str(WINDOWS_NEWLINE),
            CARRIAGE_RETURN => continue,
            c => transformed.push(c),
        }
    }
    transformed
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_newline_style_auto_detect() {
        let lf = "One\nTwo\nThree";
        let crlf = "One\r\nTwo\r\nThree";
        let none = "One Two Three";

        assert_eq!(EffectiveNewlineStyle::Unix, auto_detect_newline_style(lf));
        assert_eq!(
            EffectiveNewlineStyle::Windows,
            auto_detect_newline_style(crlf)
        );

        if cfg!(windows) {
            assert_eq!(
                EffectiveNewlineStyle::Windows,
                auto_detect_newline_style(none)
            );
        } else {
            assert_eq!(EffectiveNewlineStyle::Unix, auto_detect_newline_style(none));
        }
    }

    #[test]
    fn test_newline_style_auto_apply() {
        let auto = NewlineStyle::Auto;

        let formatted_text = "One\nTwo\nThree";
        let raw_input_text = "One\nTwo\nThree";

        let mut out = String::from(formatted_text);
        apply_newline_style(auto, &mut out, raw_input_text);
        assert_eq!("One\nTwo\nThree", &out, "auto should detect 'lf'");

        let formatted_text = "One\nTwo\nThree";
        let raw_input_text = "One\r\nTwo\r\nThree";

        let mut out = String::from(formatted_text);
        apply_newline_style(auto, &mut out, raw_input_text);
        assert_eq!("One\r\nTwo\r\nThree", &out, "auto should detect 'crlf'");

        #[cfg(not(windows))]
        {
            let formatted_text = "One\nTwo\nThree";
            let raw_input_text = "One Two Three";

            let mut out = String::from(formatted_text);
            apply_newline_style(auto, &mut out, raw_input_text);
            assert_eq!(
                "One\nTwo\nThree", &out,
                "auto-native-unix should detect 'lf'"
            );
        }

        #[cfg(windows)]
        {
            let formatted_text = "One\nTwo\nThree";
            let raw_input_text = "One Two Three";

            let mut out = String::from(formatted_text);
            apply_newline_style(auto, &mut out, raw_input_text);
            assert_eq!(
                "One\r\nTwo\r\nThree", &out,
                "auto-native-windows should detect 'crlf'"
            );
        }
    }
}
<|endoftext|>


<commit_before>use std::io::{BufReader, BufRead};
use std::str::FromStr;
use std::fs::File;
use std::cmp;
use tar::Archive;
use zigzag::Zigzag;
use dfa::{Dfa, DfaState};

struct Move {
    x: u32,
    y: u32,
    p: f64 // probability
}

struct Pattern {
    p: f64, // priority (probability = p / sum(p))
    moves: Vec<Move>
}

struct Patterns {
    dfa: Dfa,
    patterns: Vec<Pattern>
}

impl Patterns {
    fn read_sizes<T: BufRead>(input: &mut T, s: &mut String) -> (u32, u32, u32) {
        s.clear();
        input.read_line(s).ok();
        let mut split = s.split(' ').fuse();
        let width = u32::from_str(split.next().expect("Invalid pattern format: expected width.")).expect("Invalid pattern format: width must be u32.");
        let height = u32::from_str(split.next().expect("Invalid pattern format: expected height.")).expect("Invalid pattern format: height must be u32.");
        let moves_count = u32::from_str(split.next().expect("Invalid pattern format: expected moves count.")).expect("Invalid pattern format: moves count must be u32.");
        (width, height, moves_count)
    }

    fn read_pattern<T: BufRead>(input: &mut T, s: &mut String, width: u32, height: u32) {
        for y in 0 .. height {
            input.read_line(s).ok();
        }
    }

    fn covering_zigzag_length(side_of_square: u32) -> u32 {
        let x = side_of_square / 2;
        (8 * x - 13) * x + 6
    }

    fn build_dfa(width: u32, height: u32, pattern: u32, s: &str) -> Dfa {
        let center_x = width / 2;
        let center_y = height / 2;
        let zigzag_length = Patterns::covering_zigzag_length(cmp::max(width, height)) as usize;
        let mut states = Vec::with_capacity(zigzag_length + 1);
        let mut i = 0;
        for (shift_x, shift_y) in Zigzag::new().into_iter().take(zigzag_length) {
            i += 1;
            let x = center_x as i32 + shift_x;
            let y = center_y as i32 + shift_y;
            let state = if x >= 0 && x < width as i32 && y >= 0 && y < height as i32 {
                let pos = y as u32 * width + x as u32;
                match s.char_at(pos as usize) {
                    '.' => DfaState::new(i, -1, -1, -1, -1),
                    '?' => DfaState::new(i, i, i, i, -1),
                    'R' => DfaState::new(-1, i, -1, -1, -1),
                    'B' => DfaState::new(-1, -1, i, -1, -1),
                    'r' => DfaState::new(i, i, -1, -1, -1),
                    'b' => DfaState::new(i, -1, i, -1, -1),
                    '*' => DfaState::new(-1, -1, -1, i, -1),
                    c   => panic!("Invalid character in pattern: {}", c)
                }

            } else {
                DfaState::new(-1, -1, -1, -1, -1)
            };
            states.push(state);
        }
        states.push(DfaState::new(-1, -1, -1, -1, pattern as i32));
        Dfa::new(states)
    }

    pub fn load(file: File) -> Patterns {
        let archive = Archive::new(file);
        let mut s = String::new();
        for file in archive.files().expect("Reading of tar archive is failed.").into_iter().map(|file| file.expect("Reading of file in tar archive is failed.")) {
            let mut input = BufReader::new(file);
            let (width, height, moves_count) = Patterns::read_sizes(&mut input, &mut s);
        }
        unimplemented!()
    }
}
<commit_msg>Some more patterns implementation.<commit_after>use std::io::{BufReader, BufRead};
use std::str::FromStr;
use std::fs::File;
use std::cmp;
use tar::Archive;
use zigzag::Zigzag;
use dfa::{Dfa, DfaState};

struct Move {
    x: u32,
    y: u32,
    p: f64 // probability
}

struct Pattern {
    p: f64, // priority (probability = p / sum(p))
    moves: Vec<Move>
}

struct Patterns {
    dfa: Dfa,
    patterns: Vec<Pattern>
}

impl Patterns {
    fn read_header<T: BufRead>(input: &mut T, s: &mut String) -> (u32, u32, u32, f64) {
        s.clear();
        input.read_line(s).ok();
        let mut split = s.split(' ').fuse();
        let width = u32::from_str(split.next().expect("Invalid pattern format: expected width.")).expect("Invalid pattern format: width must be u32."); //TODO: validate all this.
        let height = u32::from_str(split.next().expect("Invalid pattern format: expected height.")).expect("Invalid pattern format: height must be u32.");
        let moves_count = u32::from_str(split.next().expect("Invalid pattern format: expected moves count.")).expect("Invalid pattern format: moves count must be u32.");
        let priority = f64::from_str(split.next().expect("Invalid pattern format: expected priority.")).expect("Invalid pattern format: priority must be f64.");
        (width, height, moves_count, priority)
    }

    fn read_pattern<T: BufRead>(input: &mut T, s: &mut String, width: u32, height: u32) {
        s.clear();
        for y in 0 .. height {
            input.read_line(s).ok(); //TODO: check sizes.
        }
    }

    fn read_moves<T: BufRead>(input: &mut T, s: &mut String, moves_count: u32) -> Vec<Move> {
        unimplemented!()
    }

    fn covering_zigzag_length(side_of_square: u32) -> u32 {
        let x = side_of_square / 2;
        (8 * x - 13) * x + 6
    }

    fn build_dfa(width: u32, height: u32, pattern: u32, s: &str) -> Dfa {
        let center_x = width / 2;
        let center_y = height / 2;
        let zigzag_length = Patterns::covering_zigzag_length(cmp::max(width, height)) as usize;
        let mut states = Vec::with_capacity(zigzag_length + 1);
        let mut i = 0;
        for (shift_x, shift_y) in Zigzag::new().into_iter().take(zigzag_length) {
            i += 1;
            let x = center_x as i32 + shift_x;
            let y = center_y as i32 + shift_y;
            let state = if x >= 0 && x < width as i32 && y >= 0 && y < height as i32 {
                let pos = y as u32 * width + x as u32;
                match s.char_at(pos as usize) {
                    '.' => DfaState::new(i, -1, -1, -1, -1),
                    '?' => DfaState::new(i, i, i, i, -1),
                    'R' => DfaState::new(-1, i, -1, -1, -1),
                    'B' => DfaState::new(-1, -1, i, -1, -1),
                    'r' => DfaState::new(i, i, -1, -1, -1),
                    'b' => DfaState::new(i, -1, i, -1, -1),
                    '*' => DfaState::new(-1, -1, -1, i, -1),
                    c   => panic!("Invalid character in pattern: {}", c)
                }

            } else {
                DfaState::new(-1, -1, -1, -1, -1)
            };
            states.push(state);
        }
        states.push(DfaState::new(-1, -1, -1, -1, pattern as i32));
        Dfa::new(states)
    }

    pub fn load(file: File) -> Patterns {
        let archive = Archive::new(file);
        let mut s = String::new();
        let mut patterns = Vec::new();
        let mut iter = archive.files().expect("Reading of tar archive is failed.").into_iter().map(|file| file.expect("Reading of file in tar archive is failed."));
        let mut dfa = {
            let file = iter.next().expect("Archive should contain at least one pattern.");
            let mut input = BufReader::new(file);
            let (width, height, moves_count, priority) = Patterns::read_header(&mut input, &mut s);
            Patterns::read_pattern(&mut input, &mut s, width, height);
            let cur_dfa = Patterns::build_dfa(width, height, 0, &s);
            let moves = Patterns::read_moves(&mut input, &mut s, moves_count);
            patterns.push(Pattern {
                p: priority,
                moves: moves
            });
            cur_dfa
        };
        for file in iter {
            let mut input = BufReader::new(file);
            let (width, height, moves_count, priority) = Patterns::read_header(&mut input, &mut s);
            Patterns::read_pattern(&mut input, &mut s, width, height);
            let cur_dfa = Patterns::build_dfa(width, height, patterns.len() as u32, &s);
            dfa = dfa.product(&cur_dfa);
            dfa.delete_non_reachable();
            let moves = Patterns::read_moves(&mut input, &mut s, moves_count);
            patterns.push(Pattern {
                p: priority,
                moves: moves
            });
        }
        dfa.minimize();
        Patterns {
            dfa: dfa,
            patterns: patterns
        }
    }
}
<|endoftext|>


<commit_before>/*!
This module is for platform-specific stuff.
*/

pub use self::inner::{current_time, file_last_modified, get_cache_dir_for};

#[cfg(unix)]
mod inner {
    use std::path::{Path, PathBuf};
    use error::{MainError, Blame};
    use std::env;

    pub fn get_cache_dir_for<P>(product: P) -> Result<PathBuf, MainError>
    where P: AsRef<Path> {
        let home = match env::var_os("HOME") {
            Some(val) => val,
            None => return Err((Blame::Human, "$HOME is not defined").into())
        };

        match product.as_ref().to_str() {
            Some(s) => {
                let folder = format!(".{}", s.to_lowercase());
                Ok(Path::new(&home).join(folder))
            },
            None => Err("product for `get_cache_dir_for` was not utf8".into())
        }
    }
}

#[cfg(windows)]
pub mod inner {
    #![allow(non_snake_case)]

    extern crate ole32;
    extern crate shell32;
    extern crate winapi;
    extern crate uuid;

    use std::ffi::OsString;
    use std::fmt;
    use std::fs;
    use std::path::{Path, PathBuf};
    use std::mem;
    use std::os::windows::ffi::OsStringExt;
    use self::uuid::FOLDERID_LocalAppData;
    use error::MainError;

    /**
    Gets the last-modified time of a file, in milliseconds since the UNIX epoch.
    */
    pub fn file_last_modified(file: &fs::File) -> u64 {
        use ::std::os::windows::fs::MetadataExt;

        const MS_BETWEEN_1601_1970: u64 = 11_644_473_600_000;

        let mtime_100ns_1601_utc = file.metadata()
            .map(|md| md.last_write_time())
            .unwrap_or(0);
        let mtime_ms_1601_utc = mtime_100ns_1601_utc / (1000*10);

        // This can obviously underflow... but since files created prior to 1970 are going to be *somewhat rare*, I'm just going to saturate to zero.
        let mtime_ms_1970_utc = mtime_ms_1601_utc.saturating_sub(MS_BETWEEN_1601_1970);
        mtime_ms_1970_utc
    }

    /**
    Gets the current system time, in milliseconds since the UNIX epoch.
    */
    pub fn current_time() -> u64 {
        extern crate time;

        /*
        This is kinda dicey, since *ideally* both this function and `file_last_modified` would be using the same underlying APIs.  They are not, insofar as I know.
        */
        let mut now_1970_utc = time::now_utc().to_timespec();
        if now_1970_utc.sec < 0 || now_1970_utc.nsec < 0 {
            // Fuck it.
            now_1970_utc = time::Timespec::new(0, 0);
        }
        let now_ms_1970_utc = (now_1970_utc.sec as u64 * 1000)
            + (now_1970_utc.nsec as u64 / 1_000_000);
        now_ms_1970_utc
    }

    /**
    Get a directory suitable for storing user- and machine-specific data which may or may not be persisted across sessions.

    On Windows, LocalAppData is where user- and machine- specific data should go, but it *might* be more appropriate to use whatever the official name for "Program Data" is, though.
    */
    pub fn get_cache_dir_for<P>(product: P) -> Result<PathBuf, MainError>
    where P: AsRef<Path> {
        let dir = try!(SHGetKnownFolderPath(&FOLDERID_LocalAppData, 0, ::std::ptr::null_mut())
            .map_err(|e| e.to_string()));
        Ok(Path::new(&dir).to_path_buf().join(product))
    }

    type WinResult<T> = Result<T, WinError>;

    struct WinError(winapi::HRESULT);

    impl fmt::Display for WinError {
        fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
            write!(fmt, "HRESULT({})", self.0)
        }
    }

    fn SHGetKnownFolderPath(rfid: &winapi::KNOWNFOLDERID, dwFlags: winapi::DWORD, hToken: winapi::HANDLE) -> WinResult<OsString> {
        use self::winapi::PWSTR;
        let mut psz_path: PWSTR = unsafe { mem::uninitialized() };
        let hresult = unsafe {
            shell32::SHGetKnownFolderPath(
                rfid,
                dwFlags,
                hToken,
                mem::transmute(&mut psz_path as &mut PWSTR as *mut PWSTR)
            )
        };

        if hresult == winapi::S_OK {
            let r = unsafe { pwstr_to_os_string(psz_path) };
            unsafe { ole32::CoTaskMemFree(psz_path as *mut _) };
            Ok(r)
        } else {
            Err(WinError(hresult))
        }
    }

    unsafe fn pwstr_to_os_string(ptr: winapi::PWSTR) -> OsString {
        OsStringExt::from_wide(::std::slice::from_raw_parts(ptr, pwstr_len(ptr)))
    }

    unsafe fn pwstr_len(mut ptr: winapi::PWSTR) -> usize {
        let mut len = 0;
        while *ptr != 0 {
            len += 1;
            ptr = ptr.offset(1);
        }
        len
    }
}
<commit_msg>Implement file_last_modified and current_time for unix platforms<commit_after>/*!
This module is for platform-specific stuff.
*/

pub use self::inner::{current_time, file_last_modified, get_cache_dir_for};

#[cfg(unix)]
mod inner {
    extern crate time;

    use std::path::{Path, PathBuf};
    use std::{cmp, env, fs};
    use std::os::unix::fs::MetadataExt;
    use error::{MainError, Blame};

    /**
    Gets the last-modified time of a file, in milliseconds since the UNIX epoch.
    */
    pub fn file_last_modified(file: &fs::File) -> u64 {
        let mtime_s_1970_utc = file.metadata()
            .map(|md| md.as_raw().mtime())
            .unwrap_or(0);

        let mtime_s_1970_utc = cmp::max(0, mtime_s_1970_utc);
        mtime_s_1970_utc as u64 * 1000
    }

    /**
    Gets the current system time, in milliseconds since the UNIX epoch.
    */
    pub fn current_time() -> u64 {
        /*
        This is kinda dicey, since *ideally* both this function and `file_last_modified` would be using the same underlying APIs.  They are not, insofar as I know.
        */
        let now_1970_utc = time::now_utc().to_timespec();
        if now_1970_utc.sec < 0 || now_1970_utc.nsec < 0 {
            // Fuck it.
            return 0
        }
        let now_ms_1970_utc = (now_1970_utc.sec as u64 * 1000)
            + (now_1970_utc.nsec as u64 / 1_000_000);
        now_ms_1970_utc
    }

    pub fn get_cache_dir_for<P>(product: P) -> Result<PathBuf, MainError>
    where P: AsRef<Path> {
        let home = match env::var_os("HOME") {
            Some(val) => val,
            None => return Err((Blame::Human, "$HOME is not defined").into())
        };

        match product.as_ref().to_str() {
            Some(s) => {
                let folder = format!(".{}", s.to_lowercase());
                Ok(Path::new(&home).join(folder))
            },
            None => Err("product for `get_cache_dir_for` was not utf8".into())
        }
    }
}

#[cfg(windows)]
pub mod inner {
    #![allow(non_snake_case)]

    extern crate ole32;
    extern crate shell32;
    extern crate winapi;
    extern crate uuid;

    use std::ffi::OsString;
    use std::fmt;
    use std::fs;
    use std::path::{Path, PathBuf};
    use std::mem;
    use std::os::windows::ffi::OsStringExt;
    use self::uuid::FOLDERID_LocalAppData;
    use error::MainError;

    /**
    Gets the last-modified time of a file, in milliseconds since the UNIX epoch.
    */
    pub fn file_last_modified(file: &fs::File) -> u64 {
        use ::std::os::windows::fs::MetadataExt;

        const MS_BETWEEN_1601_1970: u64 = 11_644_473_600_000;

        let mtime_100ns_1601_utc = file.metadata()
            .map(|md| md.last_write_time())
            .unwrap_or(0);
        let mtime_ms_1601_utc = mtime_100ns_1601_utc / (1000*10);

        // This can obviously underflow... but since files created prior to 1970 are going to be *somewhat rare*, I'm just going to saturate to zero.
        let mtime_ms_1970_utc = mtime_ms_1601_utc.saturating_sub(MS_BETWEEN_1601_1970);
        mtime_ms_1970_utc
    }

    /**
    Gets the current system time, in milliseconds since the UNIX epoch.
    */
    pub fn current_time() -> u64 {
        extern crate time;

        /*
        This is kinda dicey, since *ideally* both this function and `file_last_modified` would be using the same underlying APIs.  They are not, insofar as I know.
        */
        let mut now_1970_utc = time::now_utc().to_timespec();
        if now_1970_utc.sec < 0 || now_1970_utc.nsec < 0 {
            // Fuck it.
            now_1970_utc = time::Timespec::new(0, 0);
        }
        let now_ms_1970_utc = (now_1970_utc.sec as u64 * 1000)
            + (now_1970_utc.nsec as u64 / 1_000_000);
        now_ms_1970_utc
    }

    /**
    Get a directory suitable for storing user- and machine-specific data which may or may not be persisted across sessions.

    On Windows, LocalAppData is where user- and machine- specific data should go, but it *might* be more appropriate to use whatever the official name for "Program Data" is, though.
    */
    pub fn get_cache_dir_for<P>(product: P) -> Result<PathBuf, MainError>
    where P: AsRef<Path> {
        let dir = try!(SHGetKnownFolderPath(&FOLDERID_LocalAppData, 0, ::std::ptr::null_mut())
            .map_err(|e| e.to_string()));
        Ok(Path::new(&dir).to_path_buf().join(product))
    }

    type WinResult<T> = Result<T, WinError>;

    struct WinError(winapi::HRESULT);

    impl fmt::Display for WinError {
        fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
            write!(fmt, "HRESULT({})", self.0)
        }
    }

    fn SHGetKnownFolderPath(rfid: &winapi::KNOWNFOLDERID, dwFlags: winapi::DWORD, hToken: winapi::HANDLE) -> WinResult<OsString> {
        use self::winapi::PWSTR;
        let mut psz_path: PWSTR = unsafe { mem::uninitialized() };
        let hresult = unsafe {
            shell32::SHGetKnownFolderPath(
                rfid,
                dwFlags,
                hToken,
                mem::transmute(&mut psz_path as &mut PWSTR as *mut PWSTR)
            )
        };

        if hresult == winapi::S_OK {
            let r = unsafe { pwstr_to_os_string(psz_path) };
            unsafe { ole32::CoTaskMemFree(psz_path as *mut _) };
            Ok(r)
        } else {
            Err(WinError(hresult))
        }
    }

    unsafe fn pwstr_to_os_string(ptr: winapi::PWSTR) -> OsString {
        OsStringExt::from_wide(::std::slice::from_raw_parts(ptr, pwstr_len(ptr)))
    }

    unsafe fn pwstr_len(mut ptr: winapi::PWSTR) -> usize {
        let mut len = 0;
        while *ptr != 0 {
            len += 1;
            ptr = ptr.offset(1);
        }
        len
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code, unused_variables)]

use std::io::{Reader, IoResult};

#[repr(u8)]
#[deriving(Show, PartialEq, Eq, FromPrimitive)]
pub enum KnownDirEntity {
    File = b'0',
    Dir  = b'1',
    CsoPhoneBook = b'2',
    Error = b'3',
    MacBinHex = b'4',
    DosBin = b'5',
    Uuenc = b'6',
    IndexSearch = b'7',
    Telnet = b'8',
    Binary = b'9',
    RedundantServer = b'+',
    Tn3270 = b'T',
    Gif = b'g',
    Image = b'I'
}

#[deriving(Show, PartialEq, Eq)]
pub enum DirEntityKind {
    Known(KnownDirEntity),
    Unknown(u8)
}

impl DirEntityKind {
    fn from_byte(byte: u8) -> DirEntityKind {
        match FromPrimitive::from_u8(byte) {
            None => DirEntityKind::Unknown(byte),
            Some(item) => DirEntityKind::Known(item)
        }
    }
}

#[deriving(Show, PartialEq, Eq)]
pub struct DirEntity {
    kind: DirEntityKind,
    // FIXME: RFC 1436 allows (but does not recommend) Latin1 for this field, so
    // this should support that
    display: String,
    // Might be 0-length.
    selector: Vec<u8>,
    host: String,
    port: u16
}

/// Parses the Gopher protocol.
pub struct Parser<'a> {
    reader: Box<Reader + 'a>,
    lookahead: Option<u8>,
    byte: u8
}

// utility function to convert ASCII bytes to a String
fn bytes_to_string(bytes: &[u8]) -> String {
    // FIXME: don't panic
    use std::ascii::{AsciiCast, AsciiStr};
    let ascii = bytes.to_ascii();
    ascii.as_str_ascii().into_string()
}

impl<'a> Parser<'a> {
    pub fn new<R: Reader + 'a>(reader: R) -> IoResult<Parser<'a>> {
        let mut parser = Parser {
            reader: box reader as Box<Reader>,
            lookahead: None,
            byte: 0
        };
        try!(parser.bump());
        Ok(parser)
    }

    fn parse_direntity(&mut self) -> IoResult<DirEntity> {
        use std::str::FromStr;

        let kind = self.byte;
        try!(self.bump());
        let display = bytes_to_string(try!(self.parse_field()).as_slice());
        let selector = try!(self.parse_field());
        let host = bytes_to_string(try!(self.parse_field()).as_slice());
        let port = bytes_to_string(try!(self.parse_field()).as_slice());

        // Skip over any other remaining tab-delimited fields
        loop {
            if self.byte == b'\r' && try!(self.peek()) == b'\n' {
                try!(self.bump()); try!(self.bump());
                break;
            }
            try!(self.bump());
        }
        Ok(DirEntity {
            kind: DirEntityKind::from_byte(kind),
            display: display.into_string(),
            selector: selector,
            host: host.into_string(),
            port: FromStr::from_str(port.as_slice()).unwrap() // FIXME
        })
    }

    /// Parses a Gopher 'menu' listing into `DirEntity`s.
    pub fn parse_menu(&mut self) -> IoResult<Vec<DirEntity>> {
        let mut out = Vec::with_capacity(16);
        loop {
            if self.byte == b'.' {
                break;
            }
            out.push(try!(self.parse_direntity()));
        }
        Ok(out)
    }

    // Parses one tab-delimeted field
    fn parse_field(&mut self) -> IoResult<Vec<u8>> {
        let mut out = Vec::with_capacity(32);
        loop {
            let byte = self.byte;
            if byte == b'\t' {
                try!(self.bump());
                break;
            } else if byte == b'\r' {
                if try!(self.peek()) == b'\n' {
                    break;
                } else {
                    out.push(byte);
                }
            } else {
                out.push(byte);
            }
            try!(self.bump());
        }
        out.shrink_to_fit();
        Ok(out)
    }

    fn peek(&mut self) -> IoResult<u8> {
        match self.lookahead {
            Some(byte) => Ok(byte),
            None => {
                let byte = try!(self.reader.read_byte());
                self.lookahead = Some(byte);
                Ok(byte)
            }
        }
    }

    fn bump(&mut self) -> IoResult<()> {
        self.byte = match self.lookahead.take() {
            Some(byte) => byte,
            None => try!(self.reader.read_byte())
        };
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::{DirEntity, DirEntityKind, KnownDirEntity, Parser};
    use std::io::BufReader;

    fn fixture(bytes: &[u8]) -> Parser {
        Parser::new(BufReader::new(bytes)).unwrap()
    }

    const DATA: &'static [u8] = b"\
    0About internet Gopher\tStuff:About us\trawBits.micro.umn.edu\t70\r\n\
    1Around University of Minnesota\tZ,5692,AUM\tunderdog.micro.umn.edu\t70\r\n\
    1Microcomputer News & Prices\tPrices/\tpserver.bookstore.umn.edu\t70\r\n\
    1Courses, Schedules, Calendars\t\tevents.ais.umn.edu\t9120\r\n\
    1Student-Staff Directories\t\tuinfo.ais.umn.edu\t70\r\n\
    1Departmental Publications\tStuff:DP:\trawBits.micro.umn.edu\t70\r\n\
    .";

    #[test]
    fn test_field() {
        let line = b"1Courses, Schedules, Calendars\t\tevents.ais.umn.edu\t9120\r\n";
        let mut parser = fixture(line);

        assert_eq!(parser.parse_field(), Ok(b"1Courses, Schedules, Calendars".to_vec()));
        assert_eq!(parser.parse_field(), Ok(b"".to_vec()));
        assert_eq!(parser.parse_field(), Ok(b"events.ais.umn.edu".to_vec()));
        assert_eq!(parser.parse_field(), Ok(b"9120".to_vec()));
    }

    #[test]
    fn test_direntity() {
        let mut parser = fixture(DATA);

        let item = DirEntity {
            kind: DirEntityKind::Known(KnownDirEntity::File),
            display: "About internet Gopher".into_string(),
            selector: b"Stuff:About us".to_vec(),
            host: "rawBits.micro.umn.edu".into_string(),
            port: 70
        };
        assert_eq!(parser.parse_direntity(), Ok(item));

        let item = DirEntity {
            kind: DirEntityKind::Known(KnownDirEntity::Dir),
            display: "Around University of Minnesota".into_string(),
            selector: b"Z,5692,AUM".to_vec(),
            host: "underdog.micro.umn.edu".into_string(),
            port: 70
        };
        assert_eq!(parser.parse_direntity(), Ok(item));
    }

    #[test]
    fn test_menu() {
        let mut parser = fixture(DATA);

        let items = parser.parse_menu().unwrap();
        assert_eq!(items.len(), 6);

        let item = DirEntity {
            kind: DirEntityKind::Known(KnownDirEntity::File),
            display: "About internet Gopher".into_string(),
            selector: b"Stuff:About us".to_vec(),
            host: "rawBits.micro.umn.edu".into_string(),
            port: 70
        };
        assert_eq!(items[0], item);

        let item = DirEntity {
            kind: DirEntityKind::Known(KnownDirEntity::Dir),
            display: "Around University of Minnesota".into_string(),
            selector: b"Z,5692,AUM".to_vec(),
            host: "underdog.micro.umn.edu".into_string(),
            port: 70
        };
        assert_eq!(items[1], item);
    }
}

<commit_msg>Beef up display and so forth<commit_after>#![allow(dead_code, unused_variables)]

use std::fmt;
use std::io::{Reader, IoResult};

#[repr(u8)]
#[deriving(PartialEq, Eq, FromPrimitive)]
pub enum KnownDirEntity {
    File = b'0',
    Dir  = b'1',
    CsoQuery = b'2',
    Error = b'3',
    MacBinHex = b'4',
    DosBin = b'5',
    Uuenc = b'6',
    SearchQuery = b'7',
    Telnet = b'8',
    Binary = b'9',
    RedundantServer = b'+',
    Tn3270 = b'T',
    Gif = b'g',
    Html = b'h',
    Info = b'i',
    Image = b'I'
}

impl fmt::Show for KnownDirEntity {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad(match *self {
            KnownDirEntity::File => "file",
            KnownDirEntity::Dir  => "dir",
            KnownDirEntity::CsoQuery => "cso",
            KnownDirEntity::Error => "err",
            KnownDirEntity::MacBinHex => "binhex",
            KnownDirEntity::DosBin => "dosbin",
            KnownDirEntity::Uuenc => "uuenc",
            KnownDirEntity::SearchQuery => "search",
            KnownDirEntity::Telnet => "tel",
            KnownDirEntity::Binary => "bin",
            KnownDirEntity::RedundantServer => "server",
            KnownDirEntity::Tn3270 => "tn3270",
            KnownDirEntity::Gif => "gif",
            KnownDirEntity::Html => "html",
            KnownDirEntity::Info => "info",
            KnownDirEntity::Image => "img",
        })
    }
}

#[deriving(PartialEq, Eq)]
pub enum DirEntityKind {
    Known(KnownDirEntity),
    Unknown(u8)
}

impl DirEntityKind {
    fn from_byte(byte: u8) -> DirEntityKind {
        match FromPrimitive::from_u8(byte) {
            None => DirEntityKind::Unknown(byte),
            Some(item) => DirEntityKind::Known(item)
        }
    }
}

impl fmt::Show for DirEntityKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            DirEntityKind::Known(k) => k.fmt(f),
            DirEntityKind::Unknown(b) => {
                try!(f.pad("? "));
                b.fmt(f)
            }
        }
    }
}

#[deriving(PartialEq, Eq)]
pub struct DirEntity {
    kind: DirEntityKind,
    // FIXME: RFC 1436 allows (but does not recommend) Latin1 for this field, so
    // this should support that
    display: String,
    // Might be 0-length.
    selector: Vec<u8>,
    host: String,
    port: u16
}

impl fmt::Show for DirEntity {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[{:>6}] {}", self.kind, self.display)
    }
}

/// Parses the Gopher protocol.
pub struct Parser<'a> {
    reader: Box<Reader + 'a>,
    lookahead: Option<u8>,
    byte: u8,
}

// utility function to convert ASCII bytes to a String
fn bytes_to_string(bytes: &[u8]) -> String {
    // FIXME: don't panic
    use std::ascii::{AsciiCast, AsciiStr};
    let ascii = bytes.to_ascii();
    ascii.as_str_ascii().into_string()
}

impl<'a> Parser<'a> {
    pub fn new<R: Reader + 'a>(reader: R) -> IoResult<Parser<'a>> {
        let mut parser = Parser {
            reader: box reader as Box<Reader>,
            lookahead: None,
            byte: 0,
        };
        try!(parser.bump());
        Ok(parser)
    }

    fn parse_direntity(&mut self) -> IoResult<DirEntity> {
        use std::str::FromStr;

        let kind = self.byte;
        try!(self.bump());
        let display = bytes_to_string(try!(self.parse_field()).as_slice());
        let selector = try!(self.parse_field());
        let host = bytes_to_string(try!(self.parse_field()).as_slice());
        let port = bytes_to_string(try!(self.parse_field()).as_slice());

        // Skip over any other remaining tab-delimited fields
        loop {
            if self.byte == b'\r' && try!(self.peek()) == b'\n' {
                try!(self.bump()); try!(self.bump());
                break;
            }
            try!(self.bump());
        }
        Ok(DirEntity {
            kind: DirEntityKind::from_byte(kind),
            display: display.into_string(),
            selector: selector,
            host: host.into_string(),
            port: FromStr::from_str(port.as_slice()).unwrap() // FIXME
        })
    }

    /// Parses a Gopher 'menu' listing into `DirEntity`s.
    pub fn parse_menu(&mut self) -> IoResult<Vec<DirEntity>> {
        let mut out = Vec::with_capacity(16);
        loop {
            if self.byte == b'.' {
                break;
            }
            out.push(try!(self.parse_direntity()));
        }
        Ok(out)
    }

    // Parses one tab-delimeted field
    fn parse_field(&mut self) -> IoResult<Vec<u8>> {
        let mut out = Vec::with_capacity(32);
        loop {
            let byte = self.byte;
            if byte == b'\t' {
                try!(self.bump());
                break;
            } else if byte == b'\r' {
                if try!(self.peek()) == b'\n' {
                    break;
                } else {
                    out.push(byte);
                }
            } else {
                out.push(byte);
            }
            try!(self.bump());
        }
        out.shrink_to_fit();
        Ok(out)
    }

    fn peek(&mut self) -> IoResult<u8> {
        match self.lookahead {
            Some(byte) => Ok(byte),
            None => {
                let byte = try!(self.reader.read_byte());
                self.lookahead = Some(byte);
                Ok(byte)
            }
        }
    }

    fn bump(&mut self) -> IoResult<()> {
        self.byte = match self.lookahead.take() {
            Some(byte) => byte,
            None => try!(self.reader.read_byte())
        };
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::{DirEntity, DirEntityKind, KnownDirEntity, Parser};
    use std::io::BufReader;

    fn fixture(bytes: &[u8]) -> Parser {
        Parser::new(BufReader::new(bytes)).unwrap()
    }

    const DATA: &'static [u8] = b"\
    0About internet Gopher\tStuff:About us\trawBits.micro.umn.edu\t70\r\n\
    1Around University of Minnesota\tZ,5692,AUM\tunderdog.micro.umn.edu\t70\r\n\
    1Microcomputer News & Prices\tPrices/\tpserver.bookstore.umn.edu\t70\r\n\
    1Courses, Schedules, Calendars\t\tevents.ais.umn.edu\t9120\r\n\
    1Student-Staff Directories\t\tuinfo.ais.umn.edu\t70\r\n\
    1Departmental Publications\tStuff:DP:\trawBits.micro.umn.edu\t70\r\n\
    .";

    #[test]
    fn test_field() {
        let line = b"1Courses, Schedules, Calendars\t\tevents.ais.umn.edu\t9120\r\n";
        let mut parser = fixture(line);

        assert_eq!(parser.parse_field(), Ok(b"1Courses, Schedules, Calendars".to_vec()));
        assert_eq!(parser.parse_field(), Ok(b"".to_vec()));
        assert_eq!(parser.parse_field(), Ok(b"events.ais.umn.edu".to_vec()));
        assert_eq!(parser.parse_field(), Ok(b"9120".to_vec()));
    }

    #[test]
    fn test_direntity() {
        let mut parser = fixture(DATA);

        let item = DirEntity {
            kind: DirEntityKind::Known(KnownDirEntity::File),
            display: "About internet Gopher".into_string(),
            selector: b"Stuff:About us".to_vec(),
            host: "rawBits.micro.umn.edu".into_string(),
            port: 70
        };
        assert_eq!(parser.parse_direntity(), Ok(item));

        let item = DirEntity {
            kind: DirEntityKind::Known(KnownDirEntity::Dir),
            display: "Around University of Minnesota".into_string(),
            selector: b"Z,5692,AUM".to_vec(),
            host: "underdog.micro.umn.edu".into_string(),
            port: 70
        };
        assert_eq!(parser.parse_direntity(), Ok(item));
    }

    #[test]
    fn test_menu() {
        let mut parser = fixture(DATA);

        let items = parser.parse_menu().unwrap();
        assert_eq!(items.len(), 6);

        let item = DirEntity {
            kind: DirEntityKind::Known(KnownDirEntity::File),
            display: "About internet Gopher".into_string(),
            selector: b"Stuff:About us".to_vec(),
            host: "rawBits.micro.umn.edu".into_string(),
            port: 70
        };
        assert_eq!(items[0], item);

        let item = DirEntity {
            kind: DirEntityKind::Known(KnownDirEntity::Dir),
            display: "Around University of Minnesota".into_string(),
            selector: b"Z,5692,AUM".to_vec(),
            host: "underdog.micro.umn.edu".into_string(),
            port: 70
        };
        assert_eq!(items[1], item);
    }
}

<|endoftext|>


<commit_before>use std::io::prelude::*;
use std::fs::File;
use std::process::{Command, Stdio};

extern crate clap;
use clap::{Arg, App};

extern crate isbfc;
use isbfc::Token::*;
use isbfc::parse;
use isbfc::optimize;

fn main() {
    let matches = App::new("isbfc")
        .version("0.0.1")
        .author("Ian D. Scott <ian@iandouglasscott.com>")
        .about("Brainfuck compiler")
        .arg(Arg::with_name("output_asm")
             .short("S")
             .help("Assemble but do not link"))
        .arg(Arg::with_name("debugging_symbols")
             .short("g")
             .help("Generate debugging information"))
        .arg(Arg::with_name("out_name")
             .short("o")
             .help("Output file name")
             .takes_value(true))
        .arg(Arg::with_name("tape_size")
             .long("tape-size")
             .help("Size of tape; defaults to 8192")
             .takes_value(true)
             .empty_values(false))
        .arg(Arg::with_name("FILENAME")
             .help("Source file to compile")
             .required(true)
             .index(1))
        .get_matches();

    let mut tape_size = 8192;
    if let Some(tape_size_str) = matches.value_of("tape_size") {
        tape_size = tape_size_str.parse::<i32>().unwrap();
    }

    let path = matches.value_of("FILENAME").unwrap();
    let name = path.rsplitn(2, '.').last().unwrap();
    let out_name = matches.value_of("out_name").unwrap_or(name);
    let mut file = File::open(&path).unwrap();
    let mut code = String::new();
    file.read_to_string(&mut code).unwrap();

    println!("Compiling...");
    let output = compile(&code, tape_size);
    if matches.is_present("output_asm") {
        let mut asmfile = File::create(format!("{}.s", name)).unwrap();
        asmfile.write_all(&output.into_bytes()).unwrap();
    } else {
        let debug = matches.is_present("debugging_symbols");
        asm_and_link(&output, &name, &out_name, debug);
    }
}

fn compile(code: &str, tape_size: i32) -> String {
    let tokens = parse(code);
    let tokens = optimize(tokens);

    let mut output = String::new();
    let mut loops: Vec<i32> = Vec::new();
    let mut ifs: Vec<i32> = Vec::new();
    let mut loopnum = 0;
    let mut ifnum = 0;
    let mut outbuffpos = 0;
    let mut outbuffsize = 0;
    for token in tokens.iter() {
        match *token {
            Add(offset, value) => {
                let dest = if offset == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (offset*8))
                };
                if value == 1 && dest == "%r12" {
                    output.push_str("    inc %r12\n");
                } else if value >= 1 {
                    output.push_str(&format!("    addq ${}, {}\n", value, dest));
                } else if value == -1 && dest == "%r12" {
                    output.push_str("    dec %r12\n");
                } else if value <= -1 {
                    output.push_str(&format!("    subq ${}, {}\n", -value, dest));
                }
            },
            MulCopy(src_idx, dest_idx, mul) => {
                let mut src = if src_idx == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (src_idx*8))
                };
                let dest = if dest_idx == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (dest_idx*8))
                };

                if mul != -1 && mul != 1 {
                    output.push_str(&format!(concat!(
                                "    movq {}, %rax\n",
                                "    movq ${}, %rdx\n",
                                "    mulq %rdx\n"), src, mul.abs()));
                    src = "%rax".to_string();
                } else if src != "%r12" && dest != "%r12" {
                    // x86 cannot move memory to memory
                    output.push_str(&format!("    movq {}, %rax\n", src));
                    src = "%rax".to_string();
                }

                if mul > 0 {
                    output.push_str(&format!("    addq {}, {}\n", src, dest));
                } else {
                    output.push_str(&format!("    subq {}, {}\n", src, dest));
                }
            },
            Set(offset, value) => {
                if offset == 0 && value == 0 {
                    output.push_str("    xor %r12, %r12\n");
                } else if offset == 0 {
                    output.push_str(&format!("    movq ${}, %r12\n", value));
                } else {
                    output.push_str(&format!("    movq ${}, {}(%rbx)\n", value, offset*8));
                }
            },
            Move(offset) => {
                if offset != 0 {
                    output.push_str("    movq %r12, (%rbx)\n");
                    if offset > 0 {
                        output.push_str(&format!("    addq ${}, %rbx\n", offset*8));
                    } else {
                        output.push_str(&format!("    subq ${}, %rbx\n", -offset*8));
                    }
                    output.push_str("    movq (%rbx), %r12\n");
                }
            },
            Loop => {
                loopnum += 1;
                loops.push(loopnum);
                output.push_str(&format!(concat!(
                        "    jmp endloop{}\n",
                        "    loop{}:\n"),
                        loopnum, loopnum));
            },
            EndLoop => {
                let curloop = loops.pop().unwrap();
                output.push_str(&format!(concat!(
                            "    endloop{}:\n",
                            "    test %r12, %r12\n",
                            "    jnz loop{}\n"),
                            curloop, curloop))
            },
            If(offset) => {
                ifnum += 1;
                ifs.push(ifnum);
                if offset == 0 {
                    output.push_str("    test %r12, %r12\n");
                } else {
                    output.push_str(&format!("    cmpq $0, {}(%rbx)\n", offset*8));
                }
                output.push_str(&format!("    jz endif{}\n", ifnum));
            },
            EndIf =>
                output.push_str(&format!("    endif{}:\n", ifs.pop().unwrap())),
            Scan(offset) => {
                // Slighly more optimal than normal loop and move
                loopnum += 1;
                output.push_str(&format!(concat!(
                            "    movq %r12, (%rbx)\n",
                            "    jmp endloop{}\n",
                            "    loop{}:\n"),
                            loopnum, loopnum));
                if offset > 0 {
                    output.push_str(&format!("    addq ${}, %rbx\n", offset*8));
                } else {
                    output.push_str(&format!("    subq ${}, %rbx\n", -offset*8));
                }
                output.push_str(&format!(concat!(
                            "    endloop{}:\n",
                            "    cmp $0, (%rbx)\n",
                            "    jnz loop{}\n",
                            "    movq (%rbx), %r12\n"),
                            loopnum, loopnum));
            },
            Input =>
                output.push_str(concat!("\n    xor %rax, %rax\n",
                                        "    xor %rdi, %rdi\n",
                                        "    movq %rbx, %rsi\n",
                                        "    movq $1, %rdx\n",
                                        "    syscall\n",
                                        "    movq (%rbx), %r12\n\n")),
            LoadOut(offset, add) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                if offset == 0 {
                    output.push_str(&format!("    movq %r12, {}\n", outaddr));
                } else {
                    output.push_str(&format!("    movq {}(%rbx), %rax\n", offset*8));
                    output.push_str(&format!("    movq %rax, {}\n", outaddr));
                }
                if add > 0 {
                    output.push_str(&format!("    addb ${}, {}\n", add, outaddr));
                } else if add < 0 {
                    output.push_str(&format!("    subb ${}, {}\n", -add, outaddr));
                }
                outbuffpos += 1;
            },
            LoadOutSet(value) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                output.push_str(&format!("    movq ${}, {}\n", value, outaddr));
                outbuffpos += 1;
            },
            Output => {
                output.push_str(&format!(concat!(
                            "    movq $1, %rax\n",
                            "    movq $1, %rdi\n",
                            "    movq $strbuff, %rsi\n",
                            "    movq ${}, %rdx\n",
                            "    syscall\n\n"),
                            outbuffpos));

                if outbuffsize < outbuffpos + 8 {
                    outbuffsize = outbuffpos + 8;
                }
                outbuffpos = 0;
            }
        }
    }

    // Exit syscall
    output.push_str(concat!("\n    movq $60, %rax\n",
                            "    movq $0, %rdi\n",
                            "    syscall\n"));

    format!(concat!(
            ".section .bss\n",
            "    .lcomm strbuff, {outbuffsize}\n",
            "    .lcomm mem, {}\n",
            "    .set startidx, mem + {}\n",
            ".section .text\n",
            ".global _start\n",
            "_start:\n",
            "    xor %r12, %r12\n",
            "    movq $startidx, %rbx\n\n{}"),
            tape_size, tape_size/2, output, outbuffsize=outbuffsize)
}

fn asm_and_link(code: &str, name: &str, out_name: &str, debug: bool) {
    println!("Assembling...");

    let mut command = Command::new("as");
    if debug {
        command.arg("-g");
    }
    let mut child = command
        .arg("-") // Standard input
        .arg("-o")
        .arg(format!("{}.o", name))
        .stdin(Stdio::piped())
        .spawn().unwrap();

    child.stdin.take().unwrap().write_all(code.as_bytes()).unwrap();

    let status = child.wait().unwrap();
    if status.code() == Some(0) {
        println!("Linking...");
        Command::new("ld")
            .arg(format!("{}.o", name))
            .arg("-o")
            .arg(out_name)
            .spawn().unwrap();
    }
}
<commit_msg>Specify value_name for the help menu<commit_after>use std::io::prelude::*;
use std::fs::File;
use std::process::{Command, Stdio};

extern crate clap;
use clap::{Arg, App};

extern crate isbfc;
use isbfc::Token::*;
use isbfc::parse;
use isbfc::optimize;

fn main() {
    let matches = App::new("isbfc")
        .version("0.0.1")
        .author("Ian D. Scott <ian@iandouglasscott.com>")
        .about("Brainfuck compiler")
        .arg(Arg::with_name("output_asm")
             .short("S")
             .help("Assemble but do not link"))
        .arg(Arg::with_name("debugging_symbols")
             .short("g")
             .help("Generate debugging information"))
        .arg(Arg::with_name("out_name")
             .short("o")
             .help("Output file name")
             .takes_value(true)
             .empty_values(false)
             .value_name("file"))
        .arg(Arg::with_name("tape_size")
             .long("tape-size")
             .help("Size of tape; defaults to 8192")
             .takes_value(true)
             .empty_values(false)
             .value_name("bytes"))
        .arg(Arg::with_name("FILENAME")
             .help("Source file to compile")
             .required(true)
             .index(1))
        .get_matches();

    let mut tape_size = 8192;
    if let Some(tape_size_str) = matches.value_of("tape_size") {
        tape_size = tape_size_str.parse::<i32>().unwrap();
    }

    let path = matches.value_of("FILENAME").unwrap();
    let name = path.rsplitn(2, '.').last().unwrap();
    let out_name = matches.value_of("out_name").unwrap_or(name);
    let mut file = File::open(&path).unwrap();
    let mut code = String::new();
    file.read_to_string(&mut code).unwrap();

    println!("Compiling...");
    let output = compile(&code, tape_size);
    if matches.is_present("output_asm") {
        let mut asmfile = File::create(format!("{}.s", name)).unwrap();
        asmfile.write_all(&output.into_bytes()).unwrap();
    } else {
        let debug = matches.is_present("debugging_symbols");
        asm_and_link(&output, &name, &out_name, debug);
    }
}

fn compile(code: &str, tape_size: i32) -> String {
    let tokens = parse(code);
    let tokens = optimize(tokens);

    let mut output = String::new();
    let mut loops: Vec<i32> = Vec::new();
    let mut ifs: Vec<i32> = Vec::new();
    let mut loopnum = 0;
    let mut ifnum = 0;
    let mut outbuffpos = 0;
    let mut outbuffsize = 0;
    for token in tokens.iter() {
        match *token {
            Add(offset, value) => {
                let dest = if offset == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (offset*8))
                };
                if value == 1 && dest == "%r12" {
                    output.push_str("    inc %r12\n");
                } else if value >= 1 {
                    output.push_str(&format!("    addq ${}, {}\n", value, dest));
                } else if value == -1 && dest == "%r12" {
                    output.push_str("    dec %r12\n");
                } else if value <= -1 {
                    output.push_str(&format!("    subq ${}, {}\n", -value, dest));
                }
            },
            MulCopy(src_idx, dest_idx, mul) => {
                let mut src = if src_idx == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (src_idx*8))
                };
                let dest = if dest_idx == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (dest_idx*8))
                };

                if mul != -1 && mul != 1 {
                    output.push_str(&format!(concat!(
                                "    movq {}, %rax\n",
                                "    movq ${}, %rdx\n",
                                "    mulq %rdx\n"), src, mul.abs()));
                    src = "%rax".to_string();
                } else if src != "%r12" && dest != "%r12" {
                    // x86 cannot move memory to memory
                    output.push_str(&format!("    movq {}, %rax\n", src));
                    src = "%rax".to_string();
                }

                if mul > 0 {
                    output.push_str(&format!("    addq {}, {}\n", src, dest));
                } else {
                    output.push_str(&format!("    subq {}, {}\n", src, dest));
                }
            },
            Set(offset, value) => {
                if offset == 0 && value == 0 {
                    output.push_str("    xor %r12, %r12\n");
                } else if offset == 0 {
                    output.push_str(&format!("    movq ${}, %r12\n", value));
                } else {
                    output.push_str(&format!("    movq ${}, {}(%rbx)\n", value, offset*8));
                }
            },
            Move(offset) => {
                if offset != 0 {
                    output.push_str("    movq %r12, (%rbx)\n");
                    if offset > 0 {
                        output.push_str(&format!("    addq ${}, %rbx\n", offset*8));
                    } else {
                        output.push_str(&format!("    subq ${}, %rbx\n", -offset*8));
                    }
                    output.push_str("    movq (%rbx), %r12\n");
                }
            },
            Loop => {
                loopnum += 1;
                loops.push(loopnum);
                output.push_str(&format!(concat!(
                        "    jmp endloop{}\n",
                        "    loop{}:\n"),
                        loopnum, loopnum));
            },
            EndLoop => {
                let curloop = loops.pop().unwrap();
                output.push_str(&format!(concat!(
                            "    endloop{}:\n",
                            "    test %r12, %r12\n",
                            "    jnz loop{}\n"),
                            curloop, curloop))
            },
            If(offset) => {
                ifnum += 1;
                ifs.push(ifnum);
                if offset == 0 {
                    output.push_str("    test %r12, %r12\n");
                } else {
                    output.push_str(&format!("    cmpq $0, {}(%rbx)\n", offset*8));
                }
                output.push_str(&format!("    jz endif{}\n", ifnum));
            },
            EndIf =>
                output.push_str(&format!("    endif{}:\n", ifs.pop().unwrap())),
            Scan(offset) => {
                // Slighly more optimal than normal loop and move
                loopnum += 1;
                output.push_str(&format!(concat!(
                            "    movq %r12, (%rbx)\n",
                            "    jmp endloop{}\n",
                            "    loop{}:\n"),
                            loopnum, loopnum));
                if offset > 0 {
                    output.push_str(&format!("    addq ${}, %rbx\n", offset*8));
                } else {
                    output.push_str(&format!("    subq ${}, %rbx\n", -offset*8));
                }
                output.push_str(&format!(concat!(
                            "    endloop{}:\n",
                            "    cmp $0, (%rbx)\n",
                            "    jnz loop{}\n",
                            "    movq (%rbx), %r12\n"),
                            loopnum, loopnum));
            },
            Input =>
                output.push_str(concat!("\n    xor %rax, %rax\n",
                                        "    xor %rdi, %rdi\n",
                                        "    movq %rbx, %rsi\n",
                                        "    movq $1, %rdx\n",
                                        "    syscall\n",
                                        "    movq (%rbx), %r12\n\n")),
            LoadOut(offset, add) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                if offset == 0 {
                    output.push_str(&format!("    movq %r12, {}\n", outaddr));
                } else {
                    output.push_str(&format!("    movq {}(%rbx), %rax\n", offset*8));
                    output.push_str(&format!("    movq %rax, {}\n", outaddr));
                }
                if add > 0 {
                    output.push_str(&format!("    addb ${}, {}\n", add, outaddr));
                } else if add < 0 {
                    output.push_str(&format!("    subb ${}, {}\n", -add, outaddr));
                }
                outbuffpos += 1;
            },
            LoadOutSet(value) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                output.push_str(&format!("    movq ${}, {}\n", value, outaddr));
                outbuffpos += 1;
            },
            Output => {
                output.push_str(&format!(concat!(
                            "    movq $1, %rax\n",
                            "    movq $1, %rdi\n",
                            "    movq $strbuff, %rsi\n",
                            "    movq ${}, %rdx\n",
                            "    syscall\n\n"),
                            outbuffpos));

                if outbuffsize < outbuffpos + 8 {
                    outbuffsize = outbuffpos + 8;
                }
                outbuffpos = 0;
            }
        }
    }

    // Exit syscall
    output.push_str(concat!("\n    movq $60, %rax\n",
                            "    movq $0, %rdi\n",
                            "    syscall\n"));

    format!(concat!(
            ".section .bss\n",
            "    .lcomm strbuff, {outbuffsize}\n",
            "    .lcomm mem, {}\n",
            "    .set startidx, mem + {}\n",
            ".section .text\n",
            ".global _start\n",
            "_start:\n",
            "    xor %r12, %r12\n",
            "    movq $startidx, %rbx\n\n{}"),
            tape_size, tape_size/2, output, outbuffsize=outbuffsize)
}

fn asm_and_link(code: &str, name: &str, out_name: &str, debug: bool) {
    println!("Assembling...");

    let mut command = Command::new("as");
    if debug {
        command.arg("-g");
    }
    let mut child = command
        .arg("-") // Standard input
        .arg("-o")
        .arg(format!("{}.o", name))
        .stdin(Stdio::piped())
        .spawn().unwrap();

    child.stdin.take().unwrap().write_all(code.as_bytes()).unwrap();

    let status = child.wait().unwrap();
    if status.code() == Some(0) {
        println!("Linking...");
        Command::new("ld")
            .arg(format!("{}.o", name))
            .arg("-o")
            .arg(out_name)
            .spawn().unwrap();
    }
}
<|endoftext|>