<commit_before>#![feature(macro_rules)]
extern crate string_telephone;

use string_telephone::{ConnectionConfig, ClientConnectionConfig, Client, Packet, PollEmpty};

use std::io::net::ip::{Ipv4Addr, SocketAddr};
use std::io::net::udp::UdpSocket;
use std::time::duration::Duration;

fn deserializer(message: &Vec<u8>) -> Option<Vec<u8>> {
    Some(message.clone())
}

fn serializer(packet: &Vec<u8>) -> Vec<u8> {
    packet.clone()
}

fn generate_settings(port: u16, protocol_id: u32) -> (SocketAddr, SocketAddr, ConnectionConfig<Vec<u8>>, ClientConnectionConfig) {
    let my_addr = SocketAddr{ ip: Ipv4Addr(0, 0, 0, 0), port: 0 };
    let target_addr = SocketAddr{ ip: Ipv4Addr(127, 0, 0, 1), port: port };
    let settings = ConnectionConfig::new(protocol_id, 10, deserializer, serializer);
    let client_settings = ClientConnectionConfig::new(1, Duration::seconds(1));
    (my_addr, target_addr, settings, client_settings)
}

fn get_message(socket: &mut UdpSocket) -> (Vec<u8>, SocketAddr) {
    let mut buf = [0, ..255];
    match socket.recv_from(buf) {
        Ok((amt, src)) => (buf.slice_to(amt).to_owned(), src),
        Err(e) => fail!("Socket didn't get a message")
    }
}

macro_rules! with_bound_socket(
    ($socket:ident, ($variable:ident)$code:block) => (
        spawn(proc() {
            match UdpSocket::bind($socket) {
                Ok(mut $variable) => $code,
                Err(e) => fail!(e)
            }
        });
    )
)

/**
 * Test when there isn't a backend to connect to
 * Connect should return an IOResult stating we can't get there
 */
#[test]
fn connection_ignored() {
    let port = 65000;
    let (my_addr, target_addr, settings, client_settings) = generate_settings(port, 121);

    match Client::connect(my_addr, target_addr, settings, client_settings) {
        Ok(_) => fail!("Reported connected when there is no server!"),
        Err(e) => {
            assert!(e.desc == "Failed to connect")
        }
    };
}

/**
 * Test a normal connection where the backend replies with an accept
 */
#[test]
fn standard_connection() {

    let port = 65001;
    let (my_addr, target_addr, settings, client_settings) = generate_settings(port, 121);

    with_bound_socket!(target_addr, (socket) {
        socket.set_timeout(Some(1000));
        let (_, src) = get_message(&mut socket);
        socket.send_to(Packet::accept(121).serialize().unwrap()[], src);
    });

    match Client::connect(my_addr, target_addr, settings, client_settings) {
        Ok(client) => {
            //Success!
        },
        Err(e) => fail!(e)
    };
}

/**
 * If the server gives us back a different protocol id, we shouldn't accept it
 */
#[test]
fn connection_different_protocol_id() {
    let port = 65002;
    let (my_addr, target_addr, settings, client_settings) = generate_settings(port, 121);

    with_bound_socket!(target_addr, (socket) {
        socket.set_timeout(Some(1000));
        let (_, src) = get_message(&mut socket);
        socket.send_to(Packet::accept(122).serialize().unwrap()[], src);
    });

    match Client::connect(my_addr, target_addr, settings, client_settings) {
        Ok(_) => fail!("Connected to a server with a different protocol ID!"),
        Err(e) => {
            assert!(e.desc == "Failed to connect")
        }
    };
}

/**
 * If the server specifically rejects us, we should give up
 */
#[test]
fn connection_rejected() {
    let port = 65003;
    let (my_addr, target_addr, settings, client_settings) = generate_settings(port, 121);

    with_bound_socket!(target_addr, (socket) {
        socket.set_timeout(Some(1000));
        let (_, src) = get_message(&mut socket);
        socket.send_to(Packet::reject(121).serialize().unwrap()[], src);
    });

    match Client::connect(my_addr, target_addr, settings, client_settings) {
        Ok(_) => fail!("Connected to a server that rejected us!"),
        Err(e) => {
            assert!(e.desc == "Failed to connect")
        }
    };
}

/**
 * We should be able to specify how many retries we want
 */
#[test]
fn different_retry_count() {
    let port = 65004;
    let (my_addr, target_addr, settings, mut client_settings) = generate_settings(port, 121);
    client_settings.max_connect_retries = 3;

    let (tx, rx) = channel();
    with_bound_socket!(target_addr, (socket) {
        socket.set_timeout(Some(10000));
        let mut attempts = 0u8;
        while attempts < 3 {
            get_message(&mut socket);
            attempts += 1;
        }

        tx.send(attempts);
    });

    match Client::connect(my_addr, target_addr, settings, client_settings) {
        Ok(_) => (),
        Err(e) => ()
    };

    assert!(rx.recv() == 3);
}

//TODO: Find a sensible way of testing timeout lengths

/**
 * We should instantly return when polling and there's no data available
 */
#[test]
fn empty_polling() {
    let port = 65005;
    let (my_addr, target_addr, settings, mut client_settings) = generate_settings(port, 121);

    with_bound_socket!(target_addr, (socket) {
        socket.set_timeout(Some(1000));
        let (_, src) = get_message(&mut socket);
        socket.send_to(Packet::accept(121).serialize().unwrap()[], src);
    });

    match Client::connect(my_addr, target_addr, settings, client_settings) {
        Ok(mut client) => {
            assert!(match(client.poll()) { Err(PollEmpty) => true, _ => false});
        },
        Err(e) => fail!(e)
    };
}

#[test]
fn single_item_polling() {
    unimplemented!();
}

#[test]
fn multiple_item_polling() {
    unimplemented!();
}

#[test]
fn disconnection() {
    unimplemented!();
}

#[test]
fn timeout() {
    unimplemented!();
}

#[test]
fn send_correct_handshake() {
    unimplemented!();
}

#[test]
fn send_data() {
    unimplemented!();
}
<commit_msg>Trying to get single item polling test working<commit_after>#![feature(macro_rules)]
extern crate string_telephone;

use string_telephone::{ConnectionConfig, ClientConnectionConfig, Client, Packet, PollEmpty};

use std::io::net::ip::{Ipv4Addr, SocketAddr};
use std::io::net::udp::UdpSocket;
use std::time::duration::Duration;

fn deserializer(message: &Vec<u8>) -> Option<Vec<u8>> {
    Some(message.clone())
}

fn serializer(packet: &Vec<u8>) -> Vec<u8> {
    packet.clone()
}

fn generate_settings(port: u16, protocol_id: u32) -> (SocketAddr, SocketAddr, ConnectionConfig<Vec<u8>>, ClientConnectionConfig) {
    let my_addr = SocketAddr{ ip: Ipv4Addr(0, 0, 0, 0), port: 0 };
    let target_addr = SocketAddr{ ip: Ipv4Addr(127, 0, 0, 1), port: port };
    let settings = ConnectionConfig::new(protocol_id, 10, deserializer, serializer);
    let client_settings = ClientConnectionConfig::new(1, Duration::seconds(1));
    (my_addr, target_addr, settings, client_settings)
}

fn get_message(socket: &mut UdpSocket) -> (Vec<u8>, SocketAddr) {
    let mut buf = [0, ..255];
    match socket.recv_from(buf) {
        Ok((amt, src)) => (buf.slice_to(amt).to_owned(), src),
        Err(e) => fail!("Socket didn't get a message")
    }
}

macro_rules! with_bound_socket(
    ($socket:ident, ($variable:ident)$code:block) => (
        spawn(proc() {
            match UdpSocket::bind($socket) {
                Ok(mut $variable) => $code,
                Err(e) => fail!(e)
            }
        });
    )
)

/**
 * Test when there isn't a backend to connect to
 * Connect should return an IOResult stating we can't get there
 */
#[test]
fn connection_ignored() {
    let port = 65000;
    let (my_addr, target_addr, settings, client_settings) = generate_settings(port, 121);

    match Client::connect(my_addr, target_addr, settings, client_settings) {
        Ok(_) => fail!("Reported connected when there is no server!"),
        Err(e) => {
            assert!(e.desc == "Failed to connect")
        }
    };
}

/**
 * Test a normal connection where the backend replies with an accept
 */
#[test]
fn standard_connection() {

    let port = 65001;
    let (my_addr, target_addr, settings, client_settings) = generate_settings(port, 121);

    with_bound_socket!(target_addr, (socket) {
        socket.set_timeout(Some(1000));
        let (_, src) = get_message(&mut socket);
        socket.send_to(Packet::accept(121).serialize().unwrap()[], src);
    });

    match Client::connect(my_addr, target_addr, settings, client_settings) {
        Ok(client) => {
            //Success!
        },
        Err(e) => fail!(e)
    };
}

/**
 * If the server gives us back a different protocol id, we shouldn't accept it
 */
#[test]
fn connection_different_protocol_id() {
    let port = 65002;
    let (my_addr, target_addr, settings, client_settings) = generate_settings(port, 121);

    with_bound_socket!(target_addr, (socket) {
        socket.set_timeout(Some(1000));
        let (_, src) = get_message(&mut socket);
        socket.send_to(Packet::accept(122).serialize().unwrap()[], src);
    });

    match Client::connect(my_addr, target_addr, settings, client_settings) {
        Ok(_) => fail!("Connected to a server with a different protocol ID!"),
        Err(e) => {
            assert!(e.desc == "Failed to connect")
        }
    };
}

/**
 * If the server specifically rejects us, we should give up
 */
#[test]
fn connection_rejected() {
    let port = 65003;
    let (my_addr, target_addr, settings, client_settings) = generate_settings(port, 121);

    with_bound_socket!(target_addr, (socket) {
        socket.set_timeout(Some(1000));
        let (_, src) = get_message(&mut socket);
        socket.send_to(Packet::reject(121).serialize().unwrap()[], src);
    });

    match Client::connect(my_addr, target_addr, settings, client_settings) {
        Ok(_) => fail!("Connected to a server that rejected us!"),
        Err(e) => {
            assert!(e.desc == "Failed to connect")
        }
    };
}

/**
 * We should be able to specify how many retries we want
 */
#[test]
fn different_retry_count() {
    let port = 65004;
    let (my_addr, target_addr, settings, mut client_settings) = generate_settings(port, 121);
    client_settings.max_connect_retries = 3;

    let (tx, rx) = channel();
    with_bound_socket!(target_addr, (socket) {
        socket.set_timeout(Some(10000));
        let mut attempts = 0u8;
        while attempts < 3 {
            get_message(&mut socket);
            attempts += 1;
        }

        tx.send(attempts);
    });

    match Client::connect(my_addr, target_addr, settings, client_settings) {
        Ok(_) => (),
        Err(e) => ()
    };

    assert!(rx.recv() == 3);
}

//TODO: Find a sensible way of testing timeout lengths

/**
 * We should instantly return when polling and there's no data available
 */
#[test]
fn empty_polling() {
    let port = 65005;
    let (my_addr, target_addr, settings, mut client_settings) = generate_settings(port, 121);

    with_bound_socket!(target_addr, (socket) {
        socket.set_timeout(Some(1000));
        let (_, src) = get_message(&mut socket);
        socket.send_to(Packet::accept(121).serialize().unwrap()[], src);
    });

    match Client::connect(my_addr, target_addr, settings, client_settings) {
        Ok(mut client) => {
            assert!(match(client.poll()) { Err(PollEmpty) => true, _ => false});
        },
        Err(e) => fail!(e)
    };
}

/**
 * We should return an item if there's one there
 */
#[test]
fn single_item_polling() {
    let port = 65006;
    let (my_addr, target_addr, settings, mut client_settings) = generate_settings(port, 121);

    with_bound_socket!(target_addr, (socket) {
        socket.set_timeout(Some(10000));
        let (_, src) = get_message(&mut socket);
        socket.send_to(Packet::accept(121).serialize().unwrap()[], src).ok().expect("Failed to send accept packet");
        socket.send_to(Packet::message(121, vec![1]).serialize().unwrap()[], src).ok().expect("Failed to send message packet");
    });

    match Client::connect(my_addr, target_addr, settings, client_settings) {
        Ok(mut client) => {
            match(client.poll()) {
                Ok(packet) => {
                    assert!(packet == vec![1]);
                },
                Err(e) => fail!("Couldn't match a polled message! - {}", e)
            };
        },
        Err(e) => fail!(e)
    };
}

#[test]
fn multiple_item_polling() {
    unimplemented!();
}

#[test]
fn ignore_bad_queue_items_polling() {
    unimplemented!();
}

#[test]
fn disconnection() {
    unimplemented!();
}

#[test]
fn timeout() {
    unimplemented!();
}

#[test]
fn send_correct_handshake() {
    unimplemented!();
}

#[test]
fn send_data() {
    unimplemented!();
}
<|endoftext|>


<commit_before>#![feature(test)]
extern crate test;

extern crate num;
extern crate byteorder;
extern crate libc;
extern crate argparse;

use test::Bencher;
use std::env;
use std::fs::File;
use std::io::{Read, Write};
use std::io::Cursor;
use std::mem;
use std::iter::FromIterator;
use byteorder::{ByteOrder, LittleEndian, BigEndian, NativeEndian};
use std::time::Duration;
use argparse::{ArgumentParser, StoreTrue, Store};
use std::process::exit;

use libc::{c_int, size_t};
use std::vec;

type c32 = num::Complex<f32>;

macro_rules! timer_start {
    () => {
        {
            let timer = std::time::Instant::now();
            timer
        }
    }
}

macro_rules! timer_stop {
    ($timer:expr, $name:expr) => {
        let d = ($timer).elapsed();
        println!("elapsed time for {}: {:.7}s.", $name, d.as_secs() as f64 + d.subsec_nanos() as f64 / 1.0e9f64);
    }
}

fn kernel1(ai: &mut Vec<f32>, ef: &Vec<c32>) {
    let size = ai.len();
    for i in 0..size {
        ai[i] += ef[i].re.powi(2) + ef[i].im.powi(2);
    }
}

fn kernel2(ai: &mut Vec<f32>, ef_re: &Vec<f32>, ef_im: &Vec<f32>) {
    let size = ai.len();
    for i in 0..size {
        ai[i] += ef_re[i].powi(2) + ef_im[i].powi(2);
    }
}

fn kernel3(ai: &mut Vec<f32>, ef: &Vec<f32>) {
    let size = ai.len();
    for i in 0..size {
        ai[i] += ef[2*i].powi(2) + ef[2*i+1].powi(2);
    }
}

// now play with kernels written in C/C++
#[link(name = "ckernels")]
extern "C" {
    fn kernel4(L: c_int, ai: *mut f32, ef: *const f32);
    fn kernel5(L: c_int, ai: *mut f32, ef: *const f32);
}

struct Options {
    verbose: bool,
    name: String,
    rep_count: usize,
    kernel_num: isize,
}

fn cook_input_data() -> (Options, Vec<f32>, Vec<c32>) {
    let mut options = Options {
        verbose:   false,
        name:      "./data/add_sum_sq/sum_ef_20077.bin".to_string(),
        rep_count: 10000,
        kernel_num: -1,
    };

    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Time elapsing for add_sum_sq kernels.");
        ap.refer(&mut options.verbose)
            .add_option(&["-v", "--verbose"], StoreTrue,
            "set verbose");
        ap.refer(&mut options.name)
            .add_option(&["-i", "--input"], Store,
            "set name of input file");
        ap.refer(&mut options.rep_count)
            .add_option(&["-n", "--num_iters"], Store,
            "set number of iterations");
        ap.refer(&mut options.kernel_num)
            .add_option(&["-k", "--num_kernel"], Store,
            "set number of kernel");
        match ap.parse_args() {
            Ok(()) => {}
            Err(x) => {
                exit(x);
            }
        }
    }

    if options.verbose {
        println!("input file name : {} number of iterations : {} kernel number : {}",
                 options.name, options.rep_count, options.kernel_num);
    }

    let mut file = File::open(&options.name).ok().expect(&format!("The file {:?} does not exist", &options.name));
    let file_info = file.metadata().ok().expect("Cannot get file metadata");
    if !file_info.is_file() {
        panic!("{:?} is not a file", &options.name);
    }
    println!("the file {:?} has length {} bytes", &options.name, file_info.len());

    let mut buf = [0u8; 4];
    file.read(&mut buf[..]);
    let L = LittleEndian::read_u32(&buf[..]);
    println!("L : {}", L);
    let size = L as usize;
    let mut grid_buf = Vec::new();
    let sz = file.read_to_end(&mut grid_buf).unwrap();
    let ai = Vec::from_iter((0..size).map(|i| {
                let start = i * mem::size_of::<f32>();
                let val = LittleEndian::read_f32(&grid_buf[start .. ]);
                //println!("val : {}",val);
                val
            }));
    let ef = Vec::from_iter((0..size).map(|i| {
                let offset = size * mem::size_of::<f32>();
                let start = offset + i * mem::size_of::<c32>();
                let val_real = LittleEndian::read_f32(&grid_buf[start                         .. ]);
                let val_imag = LittleEndian::read_f32(&grid_buf[start + mem::size_of::<f32>() .. ]);
                //println!("val_real : {}  val_imag : {}",val_real,val_imag);
                c32::new(val_real, val_imag)
            }));

    (options, ai, ef)
}

fn ef_views(ef: &Vec<c32>) -> (Vec<f32>, Vec<f32>, Vec<f32>) {
    let size = ef.len();
    let ef_re = Vec::from_iter((0..size).map(|i| ef[i].re));
    let ef_im = Vec::from_iter((0..size).map(|i| ef[i].im));
    let ef_as_f32 = Vec::from_iter((0..2*size).map(|i| {
                let j = i/2;
                if i%2 == 0 {ef[j].re} else {ef[j].im}
            }));
    (ef_re, ef_im, ef_as_f32)
}

fn main() {

    let (options, mut ai, ef) = cook_input_data();
    let size = ai.len();

    let (ef_re, ef_im, ef_as_f32) = ef_views(&ef);

    let rep_count = options.rep_count;

    if options.kernel_num == 1 || options.kernel_num == -1 {
        let timer = timer_start!();

        for _ in 0..rep_count {
            kernel1(&mut ai, &ef);
        }

        timer_stop!(timer, "kernel1");
    }

    if options.kernel_num == 2 || options.kernel_num == -1 {
        let timer = timer_start!();

        for _ in 0..rep_count {
            kernel2(&mut ai, &ef_re, &ef_im);
        }

        timer_stop!(timer, "kernel2");
    }

    if options.kernel_num == 3 || options.kernel_num == -1 {
        let timer = timer_start!();

        for _ in 0..rep_count {
            kernel3(&mut ai, &ef_as_f32);
        }

        timer_stop!(timer, "kernel3");
    }

    if options.kernel_num == 4 || options.kernel_num == -1 {
        let len = size as c_int;
        let pai = ai.as_mut_ptr();
        let pef = ef_as_f32.as_ptr();
        let timer = timer_start!();

        for _ in 0..rep_count {
            unsafe {
                kernel4(len, pai, pef);
            }
        }

        timer_stop!(timer, "kernel4");
    }

    if options.kernel_num == 5 || options.kernel_num == -1 {
        let len = size as c_int;
        let pai = ai.as_mut_ptr();
        let pef = ef_as_f32.as_ptr();
        let timer = timer_start!();

        for _ in 0..rep_count {
            unsafe {
                kernel5(len, pai, pef);
            }
        }

        timer_stop!(timer, "kernel5");
    }

    println!("fini");
}

#[bench]
fn setup_kernel4(b: &mut Bencher) {
    let (options, mut ai, ef) = cook_input_data();
    let size = ai.len();

    let (ef_re, ef_im, ef_as_f32) = ef_views(&ef);

    let len = size as c_int;
    let pai = ai.as_mut_ptr();
    let pef = ef_as_f32.as_ptr();

    b.iter(|| {
        unsafe {
            kernel4(len, pai, pef);
        }
    } )
}

<commit_msg>fix #[bench] target.<commit_after>#![feature(test)]
extern crate test;

extern crate num;
extern crate byteorder;
extern crate libc;
extern crate argparse;

use test::Bencher;
use std::env;
use std::fs::File;
use std::io::{Read, Write};
use std::io::Cursor;
use std::mem;
use std::iter::FromIterator;
use byteorder::{ByteOrder, LittleEndian, BigEndian, NativeEndian};
use std::time::Duration;
use argparse::{ArgumentParser, StoreTrue, Store};
use std::process::exit;

use libc::{c_int, size_t};
use std::vec;

type c32 = num::Complex<f32>;

macro_rules! timer_start {
    () => {
        {
            let timer = std::time::Instant::now();
            timer
        }
    }
}

macro_rules! timer_stop {
    ($timer:expr, $name:expr) => {
        let d = ($timer).elapsed();
        println!("elapsed time for {}: {:.7}s.", $name, d.as_secs() as f64 + d.subsec_nanos() as f64 / 1.0e9f64);
    }
}

fn kernel1(ai: &mut Vec<f32>, ef: &Vec<c32>) {
    let size = ai.len();
    for i in 0..size {
        ai[i] += ef[i].re.powi(2) + ef[i].im.powi(2);
    }
}

fn kernel2(ai: &mut Vec<f32>, ef_re: &Vec<f32>, ef_im: &Vec<f32>) {
    let size = ai.len();
    for i in 0..size {
        ai[i] += ef_re[i].powi(2) + ef_im[i].powi(2);
    }
}

fn kernel3(ai: &mut Vec<f32>, ef: &Vec<f32>) {
    let size = ai.len();
    for i in 0..size {
        ai[i] += ef[2*i].powi(2) + ef[2*i+1].powi(2);
    }
}

// now play with kernels written in C/C++
#[link(name = "ckernels")]
extern "C" {
    fn kernel4(L: c_int, ai: *mut f32, ef: *const f32);
    fn kernel5(L: c_int, ai: *mut f32, ef: *const f32);
}

struct Options {
    verbose: bool,
    name: String,
    rep_count: usize,
    kernel_num: isize,
}

fn cook_input_data() -> (Options, Vec<f32>, Vec<c32>) {
    let mut options = Options {
        verbose:   false,
        name:      "./data/add_sum_sq/sum_ef_20077.bin".to_string(),
        rep_count: 10000,
        kernel_num: -1,
    };

    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Time elapsing for add_sum_sq kernels.");
        ap.refer(&mut options.verbose)
            .add_option(&["-v", "--verbose"], StoreTrue,
            "set verbose");
        ap.refer(&mut options.name)
            .add_option(&["-i", "--input"], Store,
            "set name of input file");
        ap.refer(&mut options.rep_count)
            .add_option(&["-n", "--num_iters"], Store,
            "set number of iterations");
        ap.refer(&mut options.kernel_num)
            .add_option(&["-k", "--num_kernel"], Store,
            "set number of kernel");
        match ap.parse_args() {
            Ok(()) => {}
            Err(x) => {
                //exit(x);
                //println!("unlnown option: {}",x);
            }
        }
    }

    if options.verbose {
        println!("input file name : {} number of iterations : {} kernel number : {}",
                 options.name, options.rep_count, options.kernel_num);
    }

    let mut file = File::open(&options.name).ok().expect(&format!("The file {:?} does not exist", &options.name));
    let file_info = file.metadata().ok().expect("Cannot get file metadata");
    if !file_info.is_file() {
        panic!("{:?} is not a file", &options.name);
    }
    println!("the file {:?} has length {} bytes", &options.name, file_info.len());

    let mut buf = [0u8; 4];
    file.read(&mut buf[..]);
    let L = LittleEndian::read_u32(&buf[..]);
    println!("L : {}", L);
    let size = L as usize;
    let mut grid_buf = Vec::new();
    let sz = file.read_to_end(&mut grid_buf).unwrap();
    let ai = Vec::from_iter((0..size).map(|i| {
                let start = i * mem::size_of::<f32>();
                let val = LittleEndian::read_f32(&grid_buf[start .. ]);
                //println!("val : {}",val);
                val
            }));
    let ef = Vec::from_iter((0..size).map(|i| {
                let offset = size * mem::size_of::<f32>();
                let start = offset + i * mem::size_of::<c32>();
                let val_real = LittleEndian::read_f32(&grid_buf[start                         .. ]);
                let val_imag = LittleEndian::read_f32(&grid_buf[start + mem::size_of::<f32>() .. ]);
                //println!("val_real : {}  val_imag : {}",val_real,val_imag);
                c32::new(val_real, val_imag)
            }));

    (options, ai, ef)
}

fn ef_views(ef: &Vec<c32>) -> (Vec<f32>, Vec<f32>, Vec<f32>) {
    let size = ef.len();
    let ef_re = Vec::from_iter((0..size).map(|i| ef[i].re));
    let ef_im = Vec::from_iter((0..size).map(|i| ef[i].im));
    let ef_as_f32 = Vec::from_iter((0..2*size).map(|i| {
                let j = i/2;
                if i%2 == 0 {ef[j].re} else {ef[j].im}
            }));
    (ef_re, ef_im, ef_as_f32)
}

fn main() {

    let (options, mut ai, ef) = cook_input_data();
    let size = ai.len();

    let (ef_re, ef_im, ef_as_f32) = ef_views(&ef);

    let rep_count = options.rep_count;

    if options.kernel_num == 1 || options.kernel_num == -1 {
        let timer = timer_start!();

        for _ in 0..rep_count {
            kernel1(&mut ai, &ef);
        }

        timer_stop!(timer, "kernel1");
    }

    if options.kernel_num == 2 || options.kernel_num == -1 {
        let timer = timer_start!();

        for _ in 0..rep_count {
            kernel2(&mut ai, &ef_re, &ef_im);
        }

        timer_stop!(timer, "kernel2");
    }

    if options.kernel_num == 3 || options.kernel_num == -1 {
        let timer = timer_start!();

        for _ in 0..rep_count {
            kernel3(&mut ai, &ef_as_f32);
        }

        timer_stop!(timer, "kernel3");
    }

    if options.kernel_num == 4 || options.kernel_num == -1 {
        let len = size as c_int;
        let pai = ai.as_mut_ptr();
        let pef = ef_as_f32.as_ptr();
        let timer = timer_start!();

        for _ in 0..rep_count {
            unsafe {
                kernel4(len, pai, pef);
            }
        }

        timer_stop!(timer, "kernel4");
    }

    if options.kernel_num == 5 || options.kernel_num == -1 {
        let len = size as c_int;
        let pai = ai.as_mut_ptr();
        let pef = ef_as_f32.as_ptr();
        let timer = timer_start!();

        for _ in 0..rep_count {
            unsafe {
                kernel5(len, pai, pef);
            }
        }

        timer_stop!(timer, "kernel5");
    }

    println!("fini");
}

#[bench]
fn setup_kernel4(b: &mut Bencher) {
    let (_, mut ai, ef) = cook_input_data();
    let size = ai.len();

    let (ef_re, ef_im, ef_as_f32) = ef_views(&ef);

    let len = size as c_int;
    let pai = ai.as_mut_ptr();
    let pef = ef_as_f32.as_ptr();

    b.iter(|| {
        unsafe {
            kernel4(len, pai, pef);
        }
    } )
}

<|endoftext|>


<commit_before>use authentication::Authentication;
use config::Config;
use http_headers::*;
use hyper::client::Response as HyperResponse;
use hyper::Client as HyperClient;
use hyper::header::{Accept, ContentType, qitem};
use hyper::method::Method;
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::client::IntoUrl;
use std::io::Error as IoError;
use std::io::Read;
use serde_json;
use serde::ser::Serialize;
use serde::de::Deserialize;

#[doc(no_inline)]
pub use serde_json::error::Error as SerdeError;

#[doc(no_inline)]
pub use hyper::error::Error as HyperError;

pub use url::ParseError as UrlError;

#[derive(Debug,Clone)]
pub struct ApiClient {
    pub config: Config,
}

#[derive(Debug)]
pub enum Error {
    UnsuccessfulResponse(Response),
    Json(SerdeError),
    Hyper(HyperError),
    Url(UrlError),
}

impl From<HyperError> for Error {
    fn from(h: HyperError) -> Error {
        Error::Hyper(h)
    }
}

impl From<IoError> for Error {
    fn from(i: IoError) -> Error {
        Error::Hyper(HyperError::Io(i))
    }
}

impl From<UrlError> for Error {
    fn from(u: UrlError) -> Error {
        Error::Url(u)
    }
}

#[derive(Debug)]
pub struct Response {
    pub hyper_response: HyperResponse,
    pub body: String,
}

impl Response {
    fn from_hyper_response(mut hyper_response: HyperResponse) -> Result<Response, IoError> {
        let mut body = String::new();
        hyper_response.read_to_string(&mut body).map(|_| {
            Response {
                hyper_response: hyper_response,
                body: body,
            }
        })
    }

    pub fn from_json<T: Deserialize>(&self) -> Result<T, Error> {
        serde_json::from_str(&*self.body).map_err(|e| Error::Json(e))
    }
}

impl ApiClient {

    pub fn new(config: Config) -> ApiClient {
        ApiClient { config: config }
    }

    pub fn from_json_config(pth: &str) -> ApiClient {
        let cfg = Config::from_json(pth);
        ApiClient::new(cfg)
    }

    pub fn config(mut self, config: Config) -> ApiClient {
        self.config = config;
        self
    }

    pub fn get(&self, path: &str) -> Result<Response, Error> {
        self.send_with_body(path, "", "get")
    }

    pub fn put<B>(&self, path: &str, body: B) -> Result<Response, Error>
        where B: Serialize
    {
        let body = try!(serde_json::to_string(&body).map_err(|e| Error::Json(e)));
        self.send_with_body(path, body.as_ref(), "put")
    }

    fn send_with_body(&self, path: &str, body: &str, method: &str) -> Result<Response, Error> {
        let userid = &self.config.user.clone().unwrap();

        let auth = Authentication::new();
        let auth = auth.path(path);
        let auth = auth.key(self.config.keypath.clone().unwrap().as_ref());
        let auth = auth.method(method);
        let auth = auth.userid(userid.as_ref());

        let url = try!(format!("{}{}", &self.config.url_base(), path).into_url());

        let auth = auth.body(body.as_ref());

        let mth = match method {
            "put" => Method::Put,
            "post" => Method::Post,
            "delete" => Method::Delete,
            "head" => Method::Head,
            _ => Method::Get,
        };

        let client = HyperClient::new();
        let client = client.request(mth, url);
        let client = client.body(body.as_bytes());

        let headers = auth.as_headers();
        let mut headers = headers.clone();

        let json = Mime(TopLevel::Application, SubLevel::Json, vec![]);
        headers.set(Accept(vec![qitem(json.clone())]));
        headers.set(ContentType(json));
        headers.set(OpsApiInfo(1));
        headers.set(ChefVersion(String::from("12.5.1")));

        let client = client.headers(headers);

        let resp = try!(client.send());
        let resp = try!(Response::from_hyper_response(resp));

        if resp.hyper_response.status.is_success() {
            Ok(resp)
        } else {
            Err(Error::UnsuccessfulResponse(resp))
        }
    }

}
<commit_msg>add a generic error<commit_after>use authentication::Authentication;
use config::Config;
use http_headers::*;
use hyper::client::Response as HyperResponse;
use hyper::Client as HyperClient;
use hyper::header::{Accept, ContentType, qitem};
use hyper::method::Method;
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::client::IntoUrl;
use std::io::Error as IoError;
use std::io::Read;
use serde_json;
use serde::ser::Serialize;
use serde::de::Deserialize;

#[doc(no_inline)]
pub use serde_json::error::Error as SerdeError;

#[doc(no_inline)]
pub use hyper::error::Error as HyperError;

pub use url::ParseError as UrlError;

#[derive(Debug,Clone)]
pub struct ApiClient {
    pub config: Config,
}

#[derive(Debug)]
pub enum Error {
    UnsuccessfulResponse(Response),
    Json(SerdeError),
    Hyper(HyperError),
    Url(UrlError),
    Generic,
}

impl From<HyperError> for Error {
    fn from(h: HyperError) -> Error {
        Error::Hyper(h)
    }
}

impl From<IoError> for Error {
    fn from(i: IoError) -> Error {
        Error::Hyper(HyperError::Io(i))
    }
}

impl From<UrlError> for Error {
    fn from(u: UrlError) -> Error {
        Error::Url(u)
    }
}

#[derive(Debug)]
pub struct Response {
    pub hyper_response: HyperResponse,
    pub body: String,
}

impl Response {
    fn from_hyper_response(mut hyper_response: HyperResponse) -> Result<Response, IoError> {
        let mut body = String::new();
        hyper_response.read_to_string(&mut body).map(|_| {
            Response {
                hyper_response: hyper_response,
                body: body,
            }
        })
    }

    pub fn from_json<T: Deserialize>(&self) -> Result<T, Error> {
        serde_json::from_str(&*self.body).map_err(|e| Error::Json(e))
    }
}

impl ApiClient {

    pub fn new(config: Config) -> ApiClient {
        ApiClient { config: config }
    }

    pub fn from_json_config(pth: &str) -> ApiClient {
        let cfg = Config::from_json(pth);
        ApiClient::new(cfg)
    }

    pub fn config(mut self, config: Config) -> ApiClient {
        self.config = config;
        self
    }

    pub fn get(&self, path: &str) -> Result<Response, Error> {
        self.send_with_body(path, "", "get")
    }

    pub fn put<B>(&self, path: &str, body: B) -> Result<Response, Error>
        where B: Serialize
    {
        let body = try!(serde_json::to_string(&body).map_err(|e| Error::Json(e)));
        self.send_with_body(path, body.as_ref(), "put")
    }

    fn send_with_body(&self, path: &str, body: &str, method: &str) -> Result<Response, Error> {
        let userid = &self.config.user.clone().unwrap();

        let auth = Authentication::new();
        let auth = auth.path(path);
        let auth = auth.key(self.config.keypath.clone().unwrap().as_ref());
        let auth = auth.method(method);
        let auth = auth.userid(userid.as_ref());

        let url = try!(format!("{}{}", &self.config.url_base(), path).into_url());

        let auth = auth.body(body.as_ref());

        let mth = match method {
            "put" => Method::Put,
            "post" => Method::Post,
            "delete" => Method::Delete,
            "head" => Method::Head,
            _ => Method::Get,
        };

        let client = HyperClient::new();
        let client = client.request(mth, url);
        let client = client.body(body.as_bytes());

        let headers = auth.as_headers();
        let mut headers = headers.clone();

        let json = Mime(TopLevel::Application, SubLevel::Json, vec![]);
        headers.set(Accept(vec![qitem(json.clone())]));
        headers.set(ContentType(json));
        headers.set(OpsApiInfo(1));
        headers.set(ChefVersion(String::from("12.5.1")));

        let client = client.headers(headers);

        let resp = try!(client.send());
        let resp = try!(Response::from_hyper_response(resp));

        if resp.hyper_response.status.is_success() {
            Ok(resp)
        } else {
            Err(Error::UnsuccessfulResponse(resp))
        }
    }

}
<|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("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 {
        asm_and_link(&output, &name, &out_name);
    }
}

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) {;
    println!("Assembling...");

    let mut child = Command::new("as")
        .arg("-g")
        .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>Only build with debugging symbols if -g is specified<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))
        .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();
    }
}
<|endoftext|>


<commit_before>use crate::board::Board;
use crate::error::Error;
use crate::file::File;
use crate::movegen::MoveGen;
use crate::piece::Piece;
use crate::rank::Rank;
use crate::square::Square;

use std::cmp::Ordering;
use std::fmt;
use std::str::FromStr;

/// Represent a ChessMove in memory
#[derive(Clone, Copy, Eq, PartialOrd, PartialEq, Default, Debug, Hash)]
pub struct ChessMove {
    source: Square,
    dest: Square,
    promotion: Option<Piece>,
}

impl ChessMove {
    /// Create a new chess move, given a source `Square`, a destination `Square`, and an optional
    /// promotion `Piece`
    #[inline]
    pub fn new(source: Square, dest: Square, promotion: Option<Piece>) -> ChessMove {
        ChessMove {
            source: source,
            dest: dest,
            promotion: promotion,
        }
    }

    /// Get the source square (square the piece is currently on).
    #[inline]
    pub fn get_source(&self) -> Square {
        self.source
    }

    /// Get the destination square (square the piece is going to).
    #[inline]
    pub fn get_dest(&self) -> Square {
        self.dest
    }

    /// Get the promotion piece (maybe).
    #[inline]
    pub fn get_promotion(&self) -> Option<Piece> {
        self.promotion
    }

    pub fn from_san(board: &Board, move_text: &str) -> Result<ChessMove, Error> {
        // Castles first...
        if move_text == "O-O" || move_text == "O-O-O" {
            let rank = board.side_to_move().to_my_backrank();
            let source_file = File::E;
            let dest_file = if move_text == "O-O" { File::G } else { File::C };

            let m = ChessMove::new(
                Square::make_square(rank, source_file),
                Square::make_square(rank, dest_file),
                None,
            );
            if MoveGen::new_legal(&board).any(|l| l == m) {
                return Ok(m);
            } else {
                return Err(Error::InvalidSanMove);
            }
        }

        // forms of SAN moves
        // a4 (Pawn moves to a4)
        // exd4 (Pawn on e file takes on d4)
        // xd4 (Illegal, source file must be specified)
        // 1xd4 (Illegal, source file (not rank) must be specified)
        // Nc3 (Knight (or any piece) on *some square* to c3
        // Nb1c3 (Knight (or any piece) on b1 to c3
        // Nbc3 (Knight on b file to c3)
        // N1c3 (Knight on first rank to c3)
        // Nb1xc3 (Knight on b1 takes on c3)
        // Nbxc3 (Knight on b file takes on c3)
        // N1xc3 (Knight on first rank takes on c3)
        // Nc3+ (Knight moves to c3 with check)
        // Nc3# (Knight moves to c3 with checkmate)

        // Because I'm dumb, I'm wondering if a hash table of all possible moves would be stupid.
        // There are only 186624 possible moves in SAN notation.
        //
        // Would this even be faster?  Somehow I doubt it because caching, but maybe, I dunno...
        // This could take the form of a:
        // struct CheckOrCheckmate {
        //      Neither,
        //      Check,
        //      CheckMate,
        // }
        // struct FromSan {
        //      piece: Piece,
        //      source: Vec<Square>, // possible source squares
        //      // OR
        //      source_rank: Option<Rank>,
        //      source_file: Option<File>,
        //      dest: Square,
        //      takes: bool,
        //      check: CheckOrCheckmate
        // }
        //
        // This could be kept internally as well, and never tell the user about such an abomination
        //
        // I estimate this table would take around 2 MiB, but I had to approximate some things.  It
        // may be less

        // This can be described with the following format
        // [Optional Piece Specifier] ("" | "N" | "B" | "R" | "Q" | "K")
        // [Optional Source Specifier] ( "" | "a-h" | "1-8" | ("a-h" + "1-8"))
        // [Optional Takes Specifier] ("" | "x")
        // [Full Destination Square] ("a-h" + "0-8")
        // [Optional Promotion Specifier] ("" | "N" | "B" | "R" | "Q")
        // [Optional Check(mate) Specifier] ("" | "+" | "#")
        // [Optional En Passant Specifier] ("" | " e.p.")

        let error = Error::InvalidSanMove;
        let mut cur_index: usize = 0;
        let moving_piece = match move_text
            .get(cur_index..(cur_index + 1))
            .ok_or(error.clone())?
        {
            "N" => {
                cur_index += 1;
                Piece::Knight
            }
            "B" => {
                cur_index += 1;
                Piece::Bishop
            }
            "Q" => {
                cur_index += 1;
                Piece::Queen
            }
            "R" => {
                cur_index += 1;
                Piece::Rook
            }
            "K" => {
                cur_index += 1;
                Piece::King
            }
            _ => Piece::Pawn,
        };

        println!("Piece: {}", moving_piece);

        let mut source_file = match move_text
            .get(cur_index..(cur_index + 1))
            .ok_or(error.clone())?
        {
            "a" => {
                cur_index += 1;
                Some(File::A)
            }
            "b" => {
                cur_index += 1;
                Some(File::B)
            }
            "c" => {
                cur_index += 1;
                Some(File::C)
            }
            "d" => {
                cur_index += 1;
                Some(File::D)
            }
            "e" => {
                cur_index += 1;
                Some(File::E)
            }
            "f" => {
                cur_index += 1;
                Some(File::F)
            }
            "g" => {
                cur_index += 1;
                Some(File::G)
            }
            "h" => {
                cur_index += 1;
                Some(File::H)
            }
            _ => None,
        };

        let mut source_rank = match move_text
            .get(cur_index..(cur_index + 1))
            .ok_or(error.clone())?
        {
            "1" => {
                cur_index += 1;
                Some(Rank::First)
            }
            "2" => {
                cur_index += 1;
                Some(Rank::Second)
            }
            "3" => {
                cur_index += 1;
                Some(Rank::Third)
            }
            "4" => {
                cur_index += 1;
                Some(Rank::Fourth)
            }
            "5" => {
                cur_index += 1;
                Some(Rank::Fifth)
            }
            "6" => {
                cur_index += 1;
                Some(Rank::Sixth)
            }
            "7" => {
                cur_index += 1;
                Some(Rank::Seventh)
            }
            "8" => {
                cur_index += 1;
                Some(Rank::Eighth)
            }
            _ => None,
        };

        let takes = if let Some(s) = move_text.get(cur_index..(cur_index + 1)) {
            match s {
                "x" => {
                    cur_index += 1;
                    true
                }
                _ => false,
            }
        } else {
            false
        };

        let dest = if let Some(s) = move_text.get(cur_index..(cur_index + 2)) {
            if let Ok(q) = Square::from_str(s) {
                cur_index += 2;
                q
            } else {
                let sq = Square::make_square(
                    source_rank.ok_or(error.clone())?,
                    source_file.ok_or(error.clone())?,
                );
                source_rank = None;
                source_file = None;
                sq
            }
        } else {
            let sq = Square::make_square(
                source_rank.ok_or(error.clone())?,
                source_file.ok_or(error.clone())?,
            );
            source_rank = None;
            source_file = None;
            sq
        };

        println!("Destination: {}", dest);

        let promotion = if let Some(s) = move_text.get(cur_index..(cur_index + 1)) {
            match s {
                "N" => {
                    cur_index += 1;
                    Some(Piece::Knight)
                }
                "B" => {
                    cur_index += 1;
                    Some(Piece::Bishop)
                }
                "R" => {
                    cur_index += 1;
                    Some(Piece::Rook)
                }
                "Q" => {
                    cur_index += 1;
                    Some(Piece::Queen)
                }
                _ => None,
            }
        } else {
            None
        };

        println!("Promotion: {:?}", promotion);

        if let Some(s) = move_text.get(cur_index..(cur_index + 1)) {
            let _maybe_check_or_mate = match s {
                "+" => {
                    cur_index += 1;
                    Some(false)
                }
                "#" => {
                    cur_index += 1;
                    Some(true)
                }
                _ => None,
            };
        }

        let ep = if let Some(s) = move_text.get(cur_index..) {
            s == " e.p."
        } else {
            false
        };

        println!("EP: {}", ep);

        if ep {
            cur_index += 5;
        }

        // Ok, now we have all the data from the SAN move, in the following structures
        // moveing_piece, source_rank, source_file, taks, dest, promotion, maybe_check_or_mate, and
        // ep

        let mut found_move: Option<ChessMove> = None;
        for m in &mut MoveGen::new_legal(board) {
            // check that the move has the properties specified
            if board.piece_on(m.get_source()) != Some(moving_piece) {
                continue;
            }

            if let Some(rank) = source_rank {
                if m.get_source().get_rank() != rank {
                    continue;
                }
            }

            if let Some(file) = source_file {
                if m.get_source().get_file() != file {
                    continue;
                }
            }

            if m.get_dest() != dest {
                continue;
            }

            if m.get_promotion() != promotion {
                continue;
            }

            if found_move.is_some() {
                return Err(error);
            }

            // takes is complicated, because of e.p.
            if !takes {
                if board.piece_on(m.get_dest()).is_some() {
                    continue;
                }
            }

            if !ep && takes {
                if board.piece_on(m.get_dest()).is_none() {
                    continue;
                }
            }

            found_move = Some(m);
        }

        println!("Must have not found the move...");
        found_move.ok_or(error.clone())
    }
}

impl fmt::Display for ChessMove {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.promotion {
            None => write!(f, "{}{}", self.source, self.dest),
            Some(x) => write!(f, "{}{}{}", self.source, self.dest, x),
        }
    }
}

impl Ord for ChessMove {
    fn cmp(&self, other: &ChessMove) -> Ordering {
        if self.source != other.source {
            self.source.cmp(&other.source)
        } else if self.dest != other.dest {
            self.dest.cmp(&other.dest)
        } else if self.promotion != other.promotion {
            match self.promotion {
                None => Ordering::Less,
                Some(x) => match other.promotion {
                    None => Ordering::Greater,
                    Some(y) => x.cmp(&y),
                },
            }
        } else {
            Ordering::Equal
        }
    }
}

/// Convert a UCI `String` to a move. If invalid, return `None`
/// ```
/// use chess::{ChessMove, Square, Piece};
/// use std::str::FromStr;
///
/// let mv = ChessMove::new(Square::E7, Square::E8, Some(Piece::Queen));
///
/// assert_eq!(ChessMove::from_str("e7e8q").expect("Valid Move"), mv);
/// ```
impl FromStr for ChessMove {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let source = Square::from_str(s.get(0..2).ok_or(Error::InvalidUciMove)?)?;
        let dest = Square::from_str(s.get(2..4).ok_or(Error::InvalidUciMove)?)?;

        let mut promo = None;
        if s.len() == 5 {
            promo = Some(match s.chars().last().ok_or(Error::InvalidUciMove)? {
                'q' => Piece::Queen,
                'r' => Piece::Rook,
                'n' => Piece::Knight,
                'b' => Piece::Bishop,
                _ => return Err(Error::InvalidUciMove),
            });
        }

        Ok(ChessMove::new(source, dest, promo))
    }
}

#[test]
fn test_basic_moves() {
    let board = Board::default();
    assert_eq!(
        ChessMove::from_san(&board, "e4").expect("e4 is valid in the initial position"),
        ChessMove::new(Square::E2, Square::E4, None)
    );
}
<commit_msg>Removed debugging printlns<commit_after>use crate::board::Board;
use crate::error::Error;
use crate::file::File;
use crate::movegen::MoveGen;
use crate::piece::Piece;
use crate::rank::Rank;
use crate::square::Square;

use std::cmp::Ordering;
use std::fmt;
use std::str::FromStr;

/// Represent a ChessMove in memory
#[derive(Clone, Copy, Eq, PartialOrd, PartialEq, Default, Debug, Hash)]
pub struct ChessMove {
    source: Square,
    dest: Square,
    promotion: Option<Piece>,
}

impl ChessMove {
    /// Create a new chess move, given a source `Square`, a destination `Square`, and an optional
    /// promotion `Piece`
    #[inline]
    pub fn new(source: Square, dest: Square, promotion: Option<Piece>) -> ChessMove {
        ChessMove {
            source: source,
            dest: dest,
            promotion: promotion,
        }
    }

    /// Get the source square (square the piece is currently on).
    #[inline]
    pub fn get_source(&self) -> Square {
        self.source
    }

    /// Get the destination square (square the piece is going to).
    #[inline]
    pub fn get_dest(&self) -> Square {
        self.dest
    }

    /// Get the promotion piece (maybe).
    #[inline]
    pub fn get_promotion(&self) -> Option<Piece> {
        self.promotion
    }

    pub fn from_san(board: &Board, move_text: &str) -> Result<ChessMove, Error> {
        // Castles first...
        if move_text == "O-O" || move_text == "O-O-O" {
            let rank = board.side_to_move().to_my_backrank();
            let source_file = File::E;
            let dest_file = if move_text == "O-O" { File::G } else { File::C };

            let m = ChessMove::new(
                Square::make_square(rank, source_file),
                Square::make_square(rank, dest_file),
                None,
            );
            if MoveGen::new_legal(&board).any(|l| l == m) {
                return Ok(m);
            } else {
                return Err(Error::InvalidSanMove);
            }
        }

        // forms of SAN moves
        // a4 (Pawn moves to a4)
        // exd4 (Pawn on e file takes on d4)
        // xd4 (Illegal, source file must be specified)
        // 1xd4 (Illegal, source file (not rank) must be specified)
        // Nc3 (Knight (or any piece) on *some square* to c3
        // Nb1c3 (Knight (or any piece) on b1 to c3
        // Nbc3 (Knight on b file to c3)
        // N1c3 (Knight on first rank to c3)
        // Nb1xc3 (Knight on b1 takes on c3)
        // Nbxc3 (Knight on b file takes on c3)
        // N1xc3 (Knight on first rank takes on c3)
        // Nc3+ (Knight moves to c3 with check)
        // Nc3# (Knight moves to c3 with checkmate)

        // Because I'm dumb, I'm wondering if a hash table of all possible moves would be stupid.
        // There are only 186624 possible moves in SAN notation.
        //
        // Would this even be faster?  Somehow I doubt it because caching, but maybe, I dunno...
        // This could take the form of a:
        // struct CheckOrCheckmate {
        //      Neither,
        //      Check,
        //      CheckMate,
        // }
        // struct FromSan {
        //      piece: Piece,
        //      source: Vec<Square>, // possible source squares
        //      // OR
        //      source_rank: Option<Rank>,
        //      source_file: Option<File>,
        //      dest: Square,
        //      takes: bool,
        //      check: CheckOrCheckmate
        // }
        //
        // This could be kept internally as well, and never tell the user about such an abomination
        //
        // I estimate this table would take around 2 MiB, but I had to approximate some things.  It
        // may be less

        // This can be described with the following format
        // [Optional Piece Specifier] ("" | "N" | "B" | "R" | "Q" | "K")
        // [Optional Source Specifier] ( "" | "a-h" | "1-8" | ("a-h" + "1-8"))
        // [Optional Takes Specifier] ("" | "x")
        // [Full Destination Square] ("a-h" + "0-8")
        // [Optional Promotion Specifier] ("" | "N" | "B" | "R" | "Q")
        // [Optional Check(mate) Specifier] ("" | "+" | "#")
        // [Optional En Passant Specifier] ("" | " e.p.")

        let error = Error::InvalidSanMove;
        let mut cur_index: usize = 0;
        let moving_piece = match move_text
            .get(cur_index..(cur_index + 1))
            .ok_or(error.clone())?
        {
            "N" => {
                cur_index += 1;
                Piece::Knight
            }
            "B" => {
                cur_index += 1;
                Piece::Bishop
            }
            "Q" => {
                cur_index += 1;
                Piece::Queen
            }
            "R" => {
                cur_index += 1;
                Piece::Rook
            }
            "K" => {
                cur_index += 1;
                Piece::King
            }
            _ => Piece::Pawn,
        };

        let mut source_file = match move_text
            .get(cur_index..(cur_index + 1))
            .ok_or(error.clone())?
        {
            "a" => {
                cur_index += 1;
                Some(File::A)
            }
            "b" => {
                cur_index += 1;
                Some(File::B)
            }
            "c" => {
                cur_index += 1;
                Some(File::C)
            }
            "d" => {
                cur_index += 1;
                Some(File::D)
            }
            "e" => {
                cur_index += 1;
                Some(File::E)
            }
            "f" => {
                cur_index += 1;
                Some(File::F)
            }
            "g" => {
                cur_index += 1;
                Some(File::G)
            }
            "h" => {
                cur_index += 1;
                Some(File::H)
            }
            _ => None,
        };

        let mut source_rank = match move_text
            .get(cur_index..(cur_index + 1))
            .ok_or(error.clone())?
        {
            "1" => {
                cur_index += 1;
                Some(Rank::First)
            }
            "2" => {
                cur_index += 1;
                Some(Rank::Second)
            }
            "3" => {
                cur_index += 1;
                Some(Rank::Third)
            }
            "4" => {
                cur_index += 1;
                Some(Rank::Fourth)
            }
            "5" => {
                cur_index += 1;
                Some(Rank::Fifth)
            }
            "6" => {
                cur_index += 1;
                Some(Rank::Sixth)
            }
            "7" => {
                cur_index += 1;
                Some(Rank::Seventh)
            }
            "8" => {
                cur_index += 1;
                Some(Rank::Eighth)
            }
            _ => None,
        };

        let takes = if let Some(s) = move_text.get(cur_index..(cur_index + 1)) {
            match s {
                "x" => {
                    cur_index += 1;
                    true
                }
                _ => false,
            }
        } else {
            false
        };

        let dest = if let Some(s) = move_text.get(cur_index..(cur_index + 2)) {
            if let Ok(q) = Square::from_str(s) {
                cur_index += 2;
                q
            } else {
                let sq = Square::make_square(
                    source_rank.ok_or(error.clone())?,
                    source_file.ok_or(error.clone())?,
                );
                source_rank = None;
                source_file = None;
                sq
            }
        } else {
            let sq = Square::make_square(
                source_rank.ok_or(error.clone())?,
                source_file.ok_or(error.clone())?,
            );
            source_rank = None;
            source_file = None;
            sq
        };

        let promotion = if let Some(s) = move_text.get(cur_index..(cur_index + 1)) {
            match s {
                "N" => {
                    cur_index += 1;
                    Some(Piece::Knight)
                }
                "B" => {
                    cur_index += 1;
                    Some(Piece::Bishop)
                }
                "R" => {
                    cur_index += 1;
                    Some(Piece::Rook)
                }
                "Q" => {
                    cur_index += 1;
                    Some(Piece::Queen)
                }
                _ => None,
            }
        } else {
            None
        };

        if let Some(s) = move_text.get(cur_index..(cur_index + 1)) {
            let _maybe_check_or_mate = match s {
                "+" => {
                    cur_index += 1;
                    Some(false)
                }
                "#" => {
                    cur_index += 1;
                    Some(true)
                }
                _ => None,
            };
        }

        let ep = if let Some(s) = move_text.get(cur_index..) {
            s == " e.p."
        } else {
            false
        };

        if ep {
            cur_index += 5;
        }

        // Ok, now we have all the data from the SAN move, in the following structures
        // moveing_piece, source_rank, source_file, taks, dest, promotion, maybe_check_or_mate, and
        // ep

        let mut found_move: Option<ChessMove> = None;
        for m in &mut MoveGen::new_legal(board) {
            // check that the move has the properties specified
            if board.piece_on(m.get_source()) != Some(moving_piece) {
                continue;
            }

            if let Some(rank) = source_rank {
                if m.get_source().get_rank() != rank {
                    continue;
                }
            }

            if let Some(file) = source_file {
                if m.get_source().get_file() != file {
                    continue;
                }
            }

            if m.get_dest() != dest {
                continue;
            }

            if m.get_promotion() != promotion {
                continue;
            }

            if found_move.is_some() {
                return Err(error);
            }

            // takes is complicated, because of e.p.
            if !takes {
                if board.piece_on(m.get_dest()).is_some() {
                    continue;
                }
            }

            if !ep && takes {
                if board.piece_on(m.get_dest()).is_none() {
                    continue;
                }
            }

            found_move = Some(m);
        }

        found_move.ok_or(error.clone())
    }
}

impl fmt::Display for ChessMove {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.promotion {
            None => write!(f, "{}{}", self.source, self.dest),
            Some(x) => write!(f, "{}{}{}", self.source, self.dest, x),
        }
    }
}

impl Ord for ChessMove {
    fn cmp(&self, other: &ChessMove) -> Ordering {
        if self.source != other.source {
            self.source.cmp(&other.source)
        } else if self.dest != other.dest {
            self.dest.cmp(&other.dest)
        } else if self.promotion != other.promotion {
            match self.promotion {
                None => Ordering::Less,
                Some(x) => match other.promotion {
                    None => Ordering::Greater,
                    Some(y) => x.cmp(&y),
                },
            }
        } else {
            Ordering::Equal
        }
    }
}

/// Convert a UCI `String` to a move. If invalid, return `None`
/// ```
/// use chess::{ChessMove, Square, Piece};
/// use std::str::FromStr;
///
/// let mv = ChessMove::new(Square::E7, Square::E8, Some(Piece::Queen));
///
/// assert_eq!(ChessMove::from_str("e7e8q").expect("Valid Move"), mv);
/// ```
impl FromStr for ChessMove {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let source = Square::from_str(s.get(0..2).ok_or(Error::InvalidUciMove)?)?;
        let dest = Square::from_str(s.get(2..4).ok_or(Error::InvalidUciMove)?)?;

        let mut promo = None;
        if s.len() == 5 {
            promo = Some(match s.chars().last().ok_or(Error::InvalidUciMove)? {
                'q' => Piece::Queen,
                'r' => Piece::Rook,
                'n' => Piece::Knight,
                'b' => Piece::Bishop,
                _ => return Err(Error::InvalidUciMove),
            });
        }

        Ok(ChessMove::new(source, dest, promo))
    }
}

#[test]
fn test_basic_moves() {
    let board = Board::default();
    assert_eq!(
        ChessMove::from_san(&board, "e4").expect("e4 is valid in the initial position"),
        ChessMove::new(Square::E2, Square::E4, None)
    );
}
<|endoftext|>


<commit_before>use futures::{Async, Future, Poll, Sink};
use futures::stream::Stream;
use futures::sink::Send;
use futures_state_stream::{StateStream, StreamEvent};

use native_tls::TlsConnector;

use std::io;
use std::net::ToSocketAddrs;

use tokio::net::{ConnectFuture, TcpStream};
use tokio_io::AsyncRead;
use tokio_tls::{ConnectAsync, TlsConnectorExt};

use imap_proto::{Request, RequestId, State};
use imap_proto::builders::command::Command;
use proto::{ImapCodec, ImapTransport, ResponseData};

pub mod builder {
    pub use imap_proto::builders::command::{CommandBuilder, FetchBuilderAttributes,
                                            FetchBuilderMessages, FetchBuilderModifiers,
                                            FetchCommand, FetchCommandAttributes,
                                            FetchCommandMessages};
}

pub struct Client {
    transport: ImapTransport,
    state: ClientState,
}

impl Client {
    pub fn connect(server: &str) -> io::Result<ImapConnectFuture> {
        let addr = (server, 993).to_socket_addrs()?.next().ok_or_else(|| {
            io::Error::new(io::ErrorKind::Other, format!("no IP addresses found for {}", server))
        })?;
        Ok(ImapConnectFuture::TcpConnecting(TcpStream::connect(&addr), server.to_string()))
    }

    pub fn call(self, cmd: Command) -> ResponseStream {
        let Self { transport, mut state } = self;
        let request_id = state.request_ids.next().unwrap();
        let (cmd_bytes, next_state) = cmd.into_parts();
        let future = transport.send(Request(request_id.clone(), cmd_bytes));
        ResponseStream::new(future, state, request_id, next_state)
    }
}

pub struct ResponseStream {
    future: Option<Send<ImapTransport>>,
    transport: Option<ImapTransport>,
    state: Option<ClientState>,
    request_id: RequestId,
    next_state: Option<State>,
    done: bool,
}

impl ResponseStream {
    pub fn new(future: Send<ImapTransport>, state: ClientState,
               request_id: RequestId, next_state: Option<State>) -> Self {
        Self {
            future: Some(future),
            transport: None,
            state: Some(state),
            request_id: request_id,
            next_state: next_state,
            done: false,
        }
    }
}

impl StateStream for ResponseStream {
    type Item = ResponseData;
    type State = Client;
    type Error = io::Error;
    fn poll(&mut self) -> Poll<StreamEvent<Self::Item, Self::State>, Self::Error> {
        if self.future.is_some() {
            let mut future = self.future.take().unwrap();
            match future.poll() {
                Ok(Async::Ready(transport)) => {
                    self.transport = Some(transport);
                },
                Ok(Async::NotReady) => {
                    self.future = Some(future);
                    return Ok(Async::NotReady);
                },
                Err(e) => {
                    return Err(e);
                },
            }
        }
        if !self.transport.is_some() {
            return Ok(Async::NotReady);
        }
        let mut transport = self.transport.take().unwrap();
        if self.done {
            let mut state = self.state.take().unwrap();
            if self.next_state.is_some() {
                state.state = self.next_state.take().unwrap();
            }
            let client = Client { transport, state };
            return Ok(Async::Ready(StreamEvent::Done(client)));
        }
        match transport.poll() {
            Ok(Async::Ready(Some(rsp))) => {
                if let Some(req_id) = rsp.request_id() {
                    self.done = *req_id == self.request_id;
                };
                self.transport = Some(transport);
                return Ok(Async::Ready(StreamEvent::Next(rsp)));
            },
            Err(e) => {
                return Err(e);
            },
            _ => (),
        }
        self.transport = Some(transport);
        Ok(Async::NotReady)
    }
}

pub enum ImapConnectFuture {
    #[doc(hidden)] TcpConnecting(ConnectFuture, String),
    #[doc(hidden)] TlsHandshake(ConnectAsync<TcpStream>),
    #[doc(hidden)] ServerGreeting(Option<ImapTransport>),
}

impl Future for ImapConnectFuture {
    type Item = (Client, ResponseData);
    type Error = io::Error;
    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let mut new = None;
        if let ImapConnectFuture::TcpConnecting(ref mut future, ref domain) = *self {
            let stream = try_ready!(future.poll());
            let ctx = TlsConnector::builder().unwrap().build().unwrap();
            let future = ctx.connect_async(domain, stream);
            new = Some(ImapConnectFuture::TlsHandshake(future));
        }
        if new.is_some() {
            *self = new.take().unwrap();
        }
        if let ImapConnectFuture::TlsHandshake(ref mut future) = *self {
            let transport = try_ready!(future.map_err(|e| {
                io::Error::new(io::ErrorKind::Other, e)
            }).poll()).framed(ImapCodec::default());
            new = Some(ImapConnectFuture::ServerGreeting(Some(transport)));
        }
        if new.is_some() {
            *self = new.take().unwrap();
        }
        if let ImapConnectFuture::ServerGreeting(ref mut wrapped) = *self {
            let msg = try_ready!(wrapped.as_mut().unwrap().poll()).unwrap();
            return Ok(Async::Ready((Client {
                transport: wrapped.take().unwrap(),
                state: ClientState::new(),
            }, msg)));
        }
        Ok(Async::NotReady)
    }
}

pub struct ClientState {
    state: State,
    request_ids: IdGenerator,
}

impl ClientState {
    pub fn new() -> Self {
        Self {
            state: State::NotAuthenticated,
            request_ids: IdGenerator::new(),
        }
    }
}

impl Default for ClientState {
    fn default() -> Self {
        Self::new()
    }
}

pub struct IdGenerator {
    next: u64,
}

impl IdGenerator {
    pub fn new() -> Self {
        Self { next: 0 }
    }
}

impl Default for IdGenerator {
    fn default() -> Self {
        Self::new()
    }
}

impl Iterator for IdGenerator {
    type Item = RequestId;
    fn next(&mut self) -> Option<Self::Item> {
        self.next += 1;
        Some(RequestId(format!("A{:04}", self.next % 10_000)))
    }
}
<commit_msg>Annotate unwrap() call with comment to explain safety<commit_after>use futures::{Async, Future, Poll, Sink};
use futures::stream::Stream;
use futures::sink::Send;
use futures_state_stream::{StateStream, StreamEvent};

use native_tls::TlsConnector;

use std::io;
use std::net::ToSocketAddrs;

use tokio::net::{ConnectFuture, TcpStream};
use tokio_io::AsyncRead;
use tokio_tls::{ConnectAsync, TlsConnectorExt};

use imap_proto::{Request, RequestId, State};
use imap_proto::builders::command::Command;
use proto::{ImapCodec, ImapTransport, ResponseData};

pub mod builder {
    pub use imap_proto::builders::command::{CommandBuilder, FetchBuilderAttributes,
                                            FetchBuilderMessages, FetchBuilderModifiers,
                                            FetchCommand, FetchCommandAttributes,
                                            FetchCommandMessages};
}

pub struct Client {
    transport: ImapTransport,
    state: ClientState,
}

impl Client {
    pub fn connect(server: &str) -> io::Result<ImapConnectFuture> {
        let addr = (server, 993).to_socket_addrs()?.next().ok_or_else(|| {
            io::Error::new(io::ErrorKind::Other, format!("no IP addresses found for {}", server))
        })?;
        Ok(ImapConnectFuture::TcpConnecting(TcpStream::connect(&addr), server.to_string()))
    }

    pub fn call(self, cmd: Command) -> ResponseStream {
        let Self { transport, mut state } = self;
        let request_id = state.request_ids.next().unwrap(); // safe: never returns Err
        let (cmd_bytes, next_state) = cmd.into_parts();
        let future = transport.send(Request(request_id.clone(), cmd_bytes));
        ResponseStream::new(future, state, request_id, next_state)
    }
}

pub struct ResponseStream {
    future: Option<Send<ImapTransport>>,
    transport: Option<ImapTransport>,
    state: Option<ClientState>,
    request_id: RequestId,
    next_state: Option<State>,
    done: bool,
}

impl ResponseStream {
    pub fn new(future: Send<ImapTransport>, state: ClientState,
               request_id: RequestId, next_state: Option<State>) -> Self {
        Self {
            future: Some(future),
            transport: None,
            state: Some(state),
            request_id: request_id,
            next_state: next_state,
            done: false,
        }
    }
}

impl StateStream for ResponseStream {
    type Item = ResponseData;
    type State = Client;
    type Error = io::Error;
    fn poll(&mut self) -> Poll<StreamEvent<Self::Item, Self::State>, Self::Error> {
        if self.future.is_some() {
            let mut future = self.future.take().unwrap();
            match future.poll() {
                Ok(Async::Ready(transport)) => {
                    self.transport = Some(transport);
                },
                Ok(Async::NotReady) => {
                    self.future = Some(future);
                    return Ok(Async::NotReady);
                },
                Err(e) => {
                    return Err(e);
                },
            }
        }
        if !self.transport.is_some() {
            return Ok(Async::NotReady);
        }
        let mut transport = self.transport.take().unwrap();
        if self.done {
            let mut state = self.state.take().unwrap();
            if self.next_state.is_some() {
                state.state = self.next_state.take().unwrap();
            }
            let client = Client { transport, state };
            return Ok(Async::Ready(StreamEvent::Done(client)));
        }
        match transport.poll() {
            Ok(Async::Ready(Some(rsp))) => {
                if let Some(req_id) = rsp.request_id() {
                    self.done = *req_id == self.request_id;
                };
                self.transport = Some(transport);
                return Ok(Async::Ready(StreamEvent::Next(rsp)));
            },
            Err(e) => {
                return Err(e);
            },
            _ => (),
        }
        self.transport = Some(transport);
        Ok(Async::NotReady)
    }
}

pub enum ImapConnectFuture {
    #[doc(hidden)] TcpConnecting(ConnectFuture, String),
    #[doc(hidden)] TlsHandshake(ConnectAsync<TcpStream>),
    #[doc(hidden)] ServerGreeting(Option<ImapTransport>),
}

impl Future for ImapConnectFuture {
    type Item = (Client, ResponseData);
    type Error = io::Error;
    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let mut new = None;
        if let ImapConnectFuture::TcpConnecting(ref mut future, ref domain) = *self {
            let stream = try_ready!(future.poll());
            let ctx = TlsConnector::builder().unwrap().build().unwrap();
            let future = ctx.connect_async(domain, stream);
            new = Some(ImapConnectFuture::TlsHandshake(future));
        }
        if new.is_some() {
            *self = new.take().unwrap();
        }
        if let ImapConnectFuture::TlsHandshake(ref mut future) = *self {
            let transport = try_ready!(future.map_err(|e| {
                io::Error::new(io::ErrorKind::Other, e)
            }).poll()).framed(ImapCodec::default());
            new = Some(ImapConnectFuture::ServerGreeting(Some(transport)));
        }
        if new.is_some() {
            *self = new.take().unwrap();
        }
        if let ImapConnectFuture::ServerGreeting(ref mut wrapped) = *self {
            let msg = try_ready!(wrapped.as_mut().unwrap().poll()).unwrap();
            return Ok(Async::Ready((Client {
                transport: wrapped.take().unwrap(),
                state: ClientState::new(),
            }, msg)));
        }
        Ok(Async::NotReady)
    }
}

pub struct ClientState {
    state: State,
    request_ids: IdGenerator,
}

impl ClientState {
    pub fn new() -> Self {
        Self {
            state: State::NotAuthenticated,
            request_ids: IdGenerator::new(),
        }
    }
}

impl Default for ClientState {
    fn default() -> Self {
        Self::new()
    }
}

pub struct IdGenerator {
    next: u64,
}

impl IdGenerator {
    pub fn new() -> Self {
        Self { next: 0 }
    }
}

impl Default for IdGenerator {
    fn default() -> Self {
        Self::new()
    }
}

impl Iterator for IdGenerator {
    type Item = RequestId;
    fn next(&mut self) -> Option<Self::Item> {
        self.next += 1;
        Some(RequestId(format!("A{:04}", self.next % 10_000)))
    }
}
<|endoftext|>