<commit_before>use nom::{alphanumeric, digit, multispace};
use nom::{IResult, Err, ErrorKind, Needed};
use std::str;
use std::str::FromStr;

use common::{column_identifier_no_alias, field_list, sql_identifier, statement_terminator,
             table_reference, SqlType, TableKey};
use column::Column;
use table::Table;

#[derive(Clone, Debug, Default, Hash, PartialEq)]
pub struct CreateTableStatement {
    pub table: Table,
    pub fields: Vec<Column>,
    pub keys: Option<Vec<TableKey>>,
}

fn len_as_u16(len: &[u8]) -> u16 {
    match str::from_utf8(len) {
        Ok(s) => {
            match u16::from_str(s) {
                Ok(v) => v,
                Err(e) => panic!(e),
            }
        }
        Err(e) => panic!(e),
    }
}

/// A SQL type specifier.
named!(pub type_identifier<&[u8], SqlType>,
    alt_complete!(
          chain!(
              caseless_tag!("mediumtext"),
              || { SqlType::Mediumtext }
          )
        | chain!(
              caseless_tag!("timestamp") ~
              _len: opt!(delimited!(tag!("("), digit, tag!(")"))) ~
              multispace?,
              || { SqlType::Timestamp }
          )
        | chain!(
              caseless_tag!("varbinary") ~
              len: delimited!(tag!("("), digit, tag!(")")) ~
              multispace?,
              || { SqlType::Varbinary(len_as_u16(len)) }
          )
        | chain!(
              caseless_tag!("mediumblob"),
              || { SqlType::Mediumblob }
          )
        | chain!(
              caseless_tag!("longblob"),
              || { SqlType::Longblob }
          )
        | chain!(
              caseless_tag!("tinyblob"),
              || { SqlType::Tinyblob }
          )
        | chain!(
              caseless_tag!("tinytext"),
              || { SqlType::Tinytext }
          )
        | chain!(
              caseless_tag!("varchar") ~
              len: delimited!(tag!("("), digit, tag!(")")) ~
              multispace? ~
              _binary: opt!(caseless_tag!("binary")),
              || { SqlType::Varchar(len_as_u16(len)) }
          )
        | chain!(
              caseless_tag!("tinyint") ~
              len: delimited!(tag!("("), digit, tag!(")")) ~
              multispace? ~
              _signed: opt!(alt_complete!(caseless_tag!("unsigned") | caseless_tag!("signed"))),
              || { SqlType::Tinyint(len_as_u16(len)) }
          )
        | chain!(
              caseless_tag!("bigint") ~
              len: delimited!(tag!("("), digit, tag!(")")) ~
              multispace? ~
              _signed: opt!(alt_complete!(caseless_tag!("unsigned") | caseless_tag!("signed"))),
              || { SqlType::Bigint(len_as_u16(len)) }
          )
        | chain!(
              caseless_tag!("double") ~
              multispace? ~
              _signed: opt!(alt_complete!(caseless_tag!("unsigned") | caseless_tag!("signed"))),
              || { SqlType::Double }
          )
        | chain!(
              caseless_tag!("float") ~
              multispace? ~
              _signed: opt!(alt_complete!(caseless_tag!("unsigned") | caseless_tag!("signed"))),
              || { SqlType::Float }
          )
        | chain!(
              caseless_tag!("blob"),
              || { SqlType::Blob }
          )
        | chain!(
              caseless_tag!("date"),
              || { SqlType::Date }
          )
        | chain!(
              caseless_tag!("real") ~
              multispace? ~
              _signed: opt!(alt_complete!(caseless_tag!("unsigned") | caseless_tag!("signed"))),
              || { SqlType::Real }
          )
        | chain!(
              caseless_tag!("text"),
              || { SqlType::Text }
          )
        | chain!(
              caseless_tag!("char") ~
              len: delimited!(tag!("("), digit, tag!(")")) ~
              multispace? ~
              _binary: opt!(caseless_tag!("binary")),
              || { SqlType::Char(len_as_u16(len)) }
          )
        | chain!(
              caseless_tag!("int") ~
              len: opt!(delimited!(tag!("("), digit, tag!(")"))) ~
              multispace? ~
              _signed: opt!(alt_complete!(caseless_tag!("unsigned") | caseless_tag!("signed"))),
              || { SqlType::Int(match len {
                  Some(len) => len_as_u16(len),
                  None => 32 as u16,
              }) }
          )
    )
);

/// Parse rule for an individual key specification.
named!(pub key_specification<&[u8], TableKey>,
    alt_complete!(
          chain!(
              caseless_tag!("fulltext key") ~
              multispace? ~
              name: opt!(sql_identifier) ~
              multispace? ~
              columns: delimited!(tag!("("), field_list, tag!(")")),
              || {
                  match name {
                      Some(name) => {
                          let n = String::from(str::from_utf8(name).unwrap());
                          TableKey::FulltextKey(Some(n), columns)
                      },
                      None => TableKey::FulltextKey(None, columns),
                  }
              }
          )
        | chain!(
              caseless_tag!("primary key") ~
              multispace? ~
              columns: delimited!(tag!("("), field_list, tag!(")")) ~
              opt!(complete!(chain!(
                          multispace ~
                          caseless_tag!("autoincrement"),
                          || { }
                   ))
              ),
              || { TableKey::PrimaryKey(columns) }
          )
        | chain!(
              caseless_tag!("unique key") ~
              multispace? ~
              name: opt!(sql_identifier) ~
              multispace? ~
              columns: delimited!(tag!("("), field_list, tag!(")")),
              || {
                  match name {
                      Some(name) => {
                          let n = String::from(str::from_utf8(name).unwrap());
                          TableKey::UniqueKey(Some(n), columns)
                      },
                      None => TableKey::UniqueKey(None, columns),
                  }
              }
          )
        | chain!(
              caseless_tag!("key") ~
              multispace? ~
              name: sql_identifier ~
              multispace? ~
              columns: delimited!(tag!("("), field_list, tag!(")")),
              || {
                  let n = String::from(str::from_utf8(name).unwrap());
                  TableKey::Key(n, columns)
              }
          )
    )
);

/// Parse rule for a comma-separated list.
named!(pub key_specification_list<&[u8], Vec<TableKey>>,
       many1!(
           complete!(chain!(
               key: key_specification ~
               opt!(
                   complete!(chain!(
                       multispace? ~
                       tag!(",") ~
                       multispace?,
                       || {}
                   ))
               ),
               || { key }
           ))
       )
);

/// Parse rule for a comma-separated list.
named!(pub field_specification_list<&[u8], Vec<Column> >,
       many1!(
           complete!(chain!(
               fieldname: column_identifier_no_alias ~
               _fieldtype: opt!(complete!(chain!(multispace ~
                                      type_identifier ~
                                      multispace?,
                                      || {}
                               ))
               ) ~
               // XXX(malte): some of these are mutually exclusive...
               opt!(complete!(chain!(multispace? ~
                           caseless_tag!("not null") ~
                           multispace?,
                           || {}
                    ))
               ) ~
               opt!(complete!(chain!(multispace? ~
                           caseless_tag!("auto_increment") ~
                           multispace?,
                           || {}
                    ))
               ) ~
               opt!(complete!(
                       chain!(
                           multispace? ~
                           caseless_tag!("default") ~
                           multispace ~
                           alt_complete!(
                                 delimited!(tag!("'"), take_until!("'"), tag!("'"))
                               | digit
                               | tag!("''")
                               | caseless_tag!("null")
                               | caseless_tag!("current_timestamp")
                           ) ~
                           multispace?,
                           || {}
                       ))
               ) ~
               opt!(
                   complete!(chain!(
                       multispace? ~
                       tag!(",") ~
                       multispace?,
                       || {}
                   ))
               ),
               || { fieldname }
           ))
       )
);

/// Parse rule for a SQL CREATE TABLE query.
/// TODO(malte): support types, TEMPORARY tables, IF NOT EXISTS, AS stmt
named!(pub creation<&[u8], CreateTableStatement>,
    complete!(chain!(
        caseless_tag!("create") ~
        multispace ~
        caseless_tag!("table") ~
        multispace ~
        table: table_reference ~
        multispace ~
        tag!("(") ~
        multispace? ~
        fields: field_specification_list ~
        multispace? ~
        keys: opt!(key_specification_list) ~
        multispace? ~
        tag!(")") ~
        multispace? ~
        // XXX(malte): wrap the two below in a permutation! rule that permits arbitrary ordering
        opt!(
            complete!(
                chain!(
                    caseless_tag!("type") ~
                    multispace? ~
                    tag!("=") ~
                    multispace? ~
                    alphanumeric,
                    || {}
                )
            )
        ) ~
        multispace? ~
        opt!(
            complete!(
                chain!(
                    caseless_tag!("pack_keys") ~
                    multispace? ~
                    tag!("=") ~
                    multispace? ~
                    alt_complete!(tag!("0") | tag!("1")),
                    || {}
                )
            )
        ) ~
        multispace? ~
        opt!(
            complete!(
                chain!(
                    caseless_tag!("engine") ~
                    multispace? ~
                    tag!("=") ~
                    multispace? ~
                    alphanumeric,
                    || {}
                )
            )
        ) ~
        multispace? ~
        opt!(
            complete!(
                chain!(
                    caseless_tag!("default charset") ~
                    multispace? ~
                    tag!("=") ~
                    multispace? ~
                    alt_complete!(tag!("utf8")),
                    || {}
                )
            )
        ) ~
        statement_terminator,
        || {
            // "table AS alias" isn't legal in CREATE statements
            assert!(table.alias.is_none());
            CreateTableStatement {
                table: table,
                fields: fields,
                keys: keys,
            }
        }
    ))
);

#[cfg(test)]
mod tests {
    use super::*;
    use column::Column;
    use table::Table;

    #[test]
    fn sql_types() {
        let type0 = "bigint(20) unsigned";
        let type1 = "varchar(255) binary";

        let res = type_identifier(type0.as_bytes());
        assert_eq!(res.unwrap().1, SqlType::Bigint(20));
        let res = type_identifier(type1.as_bytes());
        assert_eq!(res.unwrap().1, SqlType::Varchar(255));
    }

    #[test]
    fn field_spec() {
        // N.B. trailing comma here because field_specification_list! doesn't handle the eof case
        // because it is never validly the end of a query
        let qstring = "id bigint(20), name varchar(255),";

        let res = field_specification_list(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   vec![Column::from("id"), Column::from("name")]);
    }

    #[test]
    fn simple_create() {
        let qstring = "CREATE TABLE users (id bigint(20), name varchar(255), email varchar(255));";

        let res = creation(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   CreateTableStatement {
                       table: Table::from("users"),
                       fields: vec![Column::from("id"),
                                    Column::from("name"),
                                    Column::from("email")],
                       ..Default::default()
                   });
    }

    #[test]
    fn mediawiki_create() {
        let qstring = "CREATE TABLE user_newtalk (  user_id int(5) NOT NULL default '0',  user_ip \
                       varchar(40) NOT NULL default '') TYPE=MyISAM;";
        let res = creation(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   CreateTableStatement {
                       table: Table::from("user_newtalk"),
                       fields: vec![Column::from("user_id"), Column::from("user_ip")],
                       ..Default::default()
                   });
    }

    #[test]
    fn keys() {
        // simple primary key
        let qstring = "CREATE TABLE users (id bigint(20), name varchar(255), email varchar(255), \
                       PRIMARY KEY (id));";

        let res = creation(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   CreateTableStatement {
                       table: Table::from("users"),
                       fields: vec![Column::from("id"),
                                    Column::from("name"),
                                    Column::from("email")],
                       keys: Some(vec![TableKey::PrimaryKey(vec![Column::from("id")])]),
                       ..Default::default()
                   });

        // named unique key
        let qstring = "CREATE TABLE users (id bigint(20), name varchar(255), email varchar(255), \
                       UNIQUE KEY id_k (id));";

        let res = creation(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   CreateTableStatement {
                       table: Table::from("users"),
                       fields: vec![Column::from("id"),
                                    Column::from("name"),
                                    Column::from("email")],
                       keys: Some(vec![TableKey::UniqueKey(Some(String::from("id_k")),
                                                           vec![Column::from("id")])]),
                       ..Default::default()
                   });
    }
}
<commit_msg>Return column types with `CreateTableStatement`.<commit_after>use nom::{alphanumeric, digit, multispace};
use nom::{IResult, Err, ErrorKind, Needed};
use std::str;
use std::str::FromStr;

use common::{column_identifier_no_alias, field_list, sql_identifier, statement_terminator,
             table_reference, SqlType, TableKey};
use column::Column;
use table::Table;

#[derive(Clone, Debug, Default, Hash, PartialEq)]
pub struct CreateTableStatement {
    pub table: Table,
    pub fields: Vec<(Column, SqlType)>,
    pub keys: Option<Vec<TableKey>>,
}

fn len_as_u16(len: &[u8]) -> u16 {
    match str::from_utf8(len) {
        Ok(s) => {
            match u16::from_str(s) {
                Ok(v) => v,
                Err(e) => panic!(e),
            }
        }
        Err(e) => panic!(e),
    }
}

/// A SQL type specifier.
named!(pub type_identifier<&[u8], SqlType>,
    alt_complete!(
          chain!(
              caseless_tag!("mediumtext"),
              || { SqlType::Mediumtext }
          )
        | chain!(
              caseless_tag!("timestamp") ~
              _len: opt!(delimited!(tag!("("), digit, tag!(")"))) ~
              multispace?,
              || { SqlType::Timestamp }
          )
        | chain!(
              caseless_tag!("varbinary") ~
              len: delimited!(tag!("("), digit, tag!(")")) ~
              multispace?,
              || { SqlType::Varbinary(len_as_u16(len)) }
          )
        | chain!(
              caseless_tag!("mediumblob"),
              || { SqlType::Mediumblob }
          )
        | chain!(
              caseless_tag!("longblob"),
              || { SqlType::Longblob }
          )
        | chain!(
              caseless_tag!("tinyblob"),
              || { SqlType::Tinyblob }
          )
        | chain!(
              caseless_tag!("tinytext"),
              || { SqlType::Tinytext }
          )
        | chain!(
              caseless_tag!("varchar") ~
              len: delimited!(tag!("("), digit, tag!(")")) ~
              multispace? ~
              _binary: opt!(caseless_tag!("binary")),
              || { SqlType::Varchar(len_as_u16(len)) }
          )
        | chain!(
              caseless_tag!("tinyint") ~
              len: delimited!(tag!("("), digit, tag!(")")) ~
              multispace? ~
              _signed: opt!(alt_complete!(caseless_tag!("unsigned") | caseless_tag!("signed"))),
              || { SqlType::Tinyint(len_as_u16(len)) }
          )
        | chain!(
              caseless_tag!("bigint") ~
              len: delimited!(tag!("("), digit, tag!(")")) ~
              multispace? ~
              _signed: opt!(alt_complete!(caseless_tag!("unsigned") | caseless_tag!("signed"))),
              || { SqlType::Bigint(len_as_u16(len)) }
          )
        | chain!(
              caseless_tag!("double") ~
              multispace? ~
              _signed: opt!(alt_complete!(caseless_tag!("unsigned") | caseless_tag!("signed"))),
              || { SqlType::Double }
          )
        | chain!(
              caseless_tag!("float") ~
              multispace? ~
              _signed: opt!(alt_complete!(caseless_tag!("unsigned") | caseless_tag!("signed"))),
              || { SqlType::Float }
          )
        | chain!(
              caseless_tag!("blob"),
              || { SqlType::Blob }
          )
        | chain!(
              caseless_tag!("date"),
              || { SqlType::Date }
          )
        | chain!(
              caseless_tag!("real") ~
              multispace? ~
              _signed: opt!(alt_complete!(caseless_tag!("unsigned") | caseless_tag!("signed"))),
              || { SqlType::Real }
          )
        | chain!(
              caseless_tag!("text"),
              || { SqlType::Text }
          )
        | chain!(
              caseless_tag!("char") ~
              len: delimited!(tag!("("), digit, tag!(")")) ~
              multispace? ~
              _binary: opt!(caseless_tag!("binary")),
              || { SqlType::Char(len_as_u16(len)) }
          )
        | chain!(
              caseless_tag!("int") ~
              len: opt!(delimited!(tag!("("), digit, tag!(")"))) ~
              multispace? ~
              _signed: opt!(alt_complete!(caseless_tag!("unsigned") | caseless_tag!("signed"))),
              || { SqlType::Int(match len {
                  Some(len) => len_as_u16(len),
                  None => 32 as u16,
              }) }
          )
    )
);

/// Parse rule for an individual key specification.
named!(pub key_specification<&[u8], TableKey>,
    alt_complete!(
          chain!(
              caseless_tag!("fulltext key") ~
              multispace? ~
              name: opt!(sql_identifier) ~
              multispace? ~
              columns: delimited!(tag!("("), field_list, tag!(")")),
              || {
                  match name {
                      Some(name) => {
                          let n = String::from(str::from_utf8(name).unwrap());
                          TableKey::FulltextKey(Some(n), columns)
                      },
                      None => TableKey::FulltextKey(None, columns),
                  }
              }
          )
        | chain!(
              caseless_tag!("primary key") ~
              multispace? ~
              columns: delimited!(tag!("("), field_list, tag!(")")) ~
              opt!(complete!(chain!(
                          multispace ~
                          caseless_tag!("autoincrement"),
                          || { }
                   ))
              ),
              || { TableKey::PrimaryKey(columns) }
          )
        | chain!(
              caseless_tag!("unique key") ~
              multispace? ~
              name: opt!(sql_identifier) ~
              multispace? ~
              columns: delimited!(tag!("("), field_list, tag!(")")),
              || {
                  match name {
                      Some(name) => {
                          let n = String::from(str::from_utf8(name).unwrap());
                          TableKey::UniqueKey(Some(n), columns)
                      },
                      None => TableKey::UniqueKey(None, columns),
                  }
              }
          )
        | chain!(
              caseless_tag!("key") ~
              multispace? ~
              name: sql_identifier ~
              multispace? ~
              columns: delimited!(tag!("("), field_list, tag!(")")),
              || {
                  let n = String::from(str::from_utf8(name).unwrap());
                  TableKey::Key(n, columns)
              }
          )
    )
);

/// Parse rule for a comma-separated list.
named!(pub key_specification_list<&[u8], Vec<TableKey>>,
       many1!(
           complete!(chain!(
               key: key_specification ~
               opt!(
                   complete!(chain!(
                       multispace? ~
                       tag!(",") ~
                       multispace?,
                       || {}
                   ))
               ),
               || { key }
           ))
       )
);

/// Parse rule for a comma-separated list.
named!(pub field_specification_list<&[u8], Vec<(Column, SqlType)> >,
       many1!(
           complete!(chain!(
               fieldname: column_identifier_no_alias ~
               fieldtype: opt!(complete!(chain!(multispace ~
                                      ti: type_identifier ~
                                      multispace?,
                                      || { ti }
                               ))
               ) ~
               // XXX(malte): some of these are mutually exclusive...
               opt!(complete!(chain!(multispace? ~
                           caseless_tag!("not null") ~
                           multispace?,
                           || {}
                    ))
               ) ~
               opt!(complete!(chain!(multispace? ~
                           caseless_tag!("auto_increment") ~
                           multispace?,
                           || {}
                    ))
               ) ~
               opt!(complete!(
                       chain!(
                           multispace? ~
                           caseless_tag!("default") ~
                           multispace ~
                           alt_complete!(
                                 delimited!(tag!("'"), take_until!("'"), tag!("'"))
                               | digit
                               | tag!("''")
                               | caseless_tag!("null")
                               | caseless_tag!("current_timestamp")
                           ) ~
                           multispace?,
                           || {}
                       ))
               ) ~
               opt!(
                   complete!(chain!(
                       multispace? ~
                       tag!(",") ~
                       multispace?,
                       || {}
                   ))
               ),
               || {
                   let t = match fieldtype {
                       None => SqlType::Text,
                       Some(ref t) => t.clone(),
                   };
                   (fieldname, t)
               }
           ))
       )
);

/// Parse rule for a SQL CREATE TABLE query.
/// TODO(malte): support types, TEMPORARY tables, IF NOT EXISTS, AS stmt
named!(pub creation<&[u8], CreateTableStatement>,
    complete!(chain!(
        caseless_tag!("create") ~
        multispace ~
        caseless_tag!("table") ~
        multispace ~
        table: table_reference ~
        multispace ~
        tag!("(") ~
        multispace? ~
        fields: field_specification_list ~
        multispace? ~
        keys: opt!(key_specification_list) ~
        multispace? ~
        tag!(")") ~
        multispace? ~
        // XXX(malte): wrap the two below in a permutation! rule that permits arbitrary ordering
        opt!(
            complete!(
                chain!(
                    caseless_tag!("type") ~
                    multispace? ~
                    tag!("=") ~
                    multispace? ~
                    alphanumeric,
                    || {}
                )
            )
        ) ~
        multispace? ~
        opt!(
            complete!(
                chain!(
                    caseless_tag!("pack_keys") ~
                    multispace? ~
                    tag!("=") ~
                    multispace? ~
                    alt_complete!(tag!("0") | tag!("1")),
                    || {}
                )
            )
        ) ~
        multispace? ~
        opt!(
            complete!(
                chain!(
                    caseless_tag!("engine") ~
                    multispace? ~
                    tag!("=") ~
                    multispace? ~
                    alphanumeric,
                    || {}
                )
            )
        ) ~
        multispace? ~
        opt!(
            complete!(
                chain!(
                    caseless_tag!("default charset") ~
                    multispace? ~
                    tag!("=") ~
                    multispace? ~
                    alt_complete!(tag!("utf8")),
                    || {}
                )
            )
        ) ~
        statement_terminator,
        || {
            // "table AS alias" isn't legal in CREATE statements
            assert!(table.alias.is_none());

            CreateTableStatement {
                table: table,
                fields: fields,
                keys: keys,
            }
        }
    ))
);

#[cfg(test)]
mod tests {
    use super::*;
    use column::Column;
    use table::Table;

    #[test]
    fn sql_types() {
        let type0 = "bigint(20) unsigned";
        let type1 = "varchar(255) binary";

        let res = type_identifier(type0.as_bytes());
        assert_eq!(res.unwrap().1, SqlType::Bigint(20));
        let res = type_identifier(type1.as_bytes());
        assert_eq!(res.unwrap().1, SqlType::Varchar(255));
    }

    #[test]
    fn field_spec() {
        // N.B. trailing comma here because field_specification_list! doesn't handle the eof case
        // because it is never validly the end of a query
        let qstring = "id bigint(20), name varchar(255),";

        let res = field_specification_list(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   vec![(Column::from("id"), SqlType::Bigint(20)),
                        (Column::from("name"), SqlType::Varchar(255))]);
    }

    #[test]
    fn simple_create() {
        let qstring = "CREATE TABLE users (id bigint(20), name varchar(255), email varchar(255));";

        let res = creation(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   CreateTableStatement {
                       table: Table::from("users"),
                       fields: vec![(Column::from("id"), SqlType::Bigint(20)),
                                    (Column::from("name"), SqlType::Varchar(255)),
                                    (Column::from("email"), SqlType::Varchar(255))],
                       ..Default::default()
                   });
    }

    #[test]
    fn mediawiki_create() {
        let qstring = "CREATE TABLE user_newtalk (  user_id int(5) NOT NULL default '0',  user_ip \
                       varchar(40) NOT NULL default '') TYPE=MyISAM;";
        let res = creation(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   CreateTableStatement {
                       table: Table::from("user_newtalk"),
                       fields: vec![(Column::from("user_id"), SqlType::Int(5)),
                                    (Column::from("user_ip"), SqlType::Varchar(40))],
                       ..Default::default()
                   });
    }

    #[test]
    fn keys() {
        // simple primary key
        let qstring = "CREATE TABLE users (id bigint(20), name varchar(255), email varchar(255), \
                       PRIMARY KEY (id));";

        let res = creation(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   CreateTableStatement {
                       table: Table::from("users"),
                       fields: vec![(Column::from("id"), SqlType::Bigint(20)),
                                    (Column::from("name"), SqlType::Varchar(255)),
                                    (Column::from("email"), SqlType::Varchar(255))],
                       keys: Some(vec![TableKey::PrimaryKey(vec![Column::from("id")])]),
                       ..Default::default()
                   });

        // named unique key
        let qstring = "CREATE TABLE users (id bigint(20), name varchar(255), email varchar(255), \
                       UNIQUE KEY id_k (id));";

        let res = creation(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   CreateTableStatement {
                       table: Table::from("users"),
                       fields: vec![(Column::from("id"), SqlType::Bigint(20)),
                                    (Column::from("name"), SqlType::Varchar(255)),
                                    (Column::from("email"), SqlType::Varchar(255))],
                       keys: Some(vec![TableKey::UniqueKey(Some(String::from("id_k")),
                                                           vec![Column::from("id")])]),
                       ..Default::default()
                   });
    }
}
<|endoftext|>


<commit_before>use std::f32::{NEG_INFINITY, INFINITY};
use std::cmp::Ordering;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread;

use time;

use identity::{Team, JobDescription, Agent};
use life::{Commit, WorldState};
use motion::CENTER_OF_THE_WORLD;
use space::Pinfield;

pub fn orientation(team: Team) -> f32 {
    match team {
        Team::Orange => 1.0,
        Team::Blue => -1.0,
    }
}

pub fn figurine_valuation(agent: Agent) -> f32 {
    let value = match agent.job_description {
        // en.wikipedia.org/wiki/
        // Chess_piece_relative_value#Hans_Berliner.27s_system
        JobDescription::Servant => 1.0,
        JobDescription::Pony => 3.2,
        JobDescription::Scholar => 3.3,
        JobDescription::Cop => 5.1,
        JobDescription::Princess => 8.8,
        JobDescription::Figurehead => 20000.0,
    };
    orientation(agent.team) * value
}

pub fn score(world: WorldState) -> f32 {
    let mut valuation = 0.0;

    for team in Team::league().into_iter() {
        for agent in Agent::dramatis_personæ(team).into_iter() {
            valuation += world.agent_to_pinfield_ref(
                agent).pincount() as f32 * figurine_valuation(agent);
        }
        // breadth of scholarship bonus
        if world.agent_to_pinfield_ref(Agent {
                    team: team,
                    job_description: JobDescription::Scholar,
                })
                .to_locales()
                .len() >= 2 {
            valuation += orientation(team) * 0.5
        }
    }

    // ponies and servants want to be in the center of the world's
    // action, maybe??
    let center = Pinfield(CENTER_OF_THE_WORLD);
    // cast to signed to avoid overflow
    let orange_centerism: i8 = world.orange_servants.union(
        world.orange_ponies).intersection(center).pincount() as i8;
    let blue_centerism: i8 = world.blue_servants.union(
        world.blue_ponies).intersection(center).pincount() as i8;
    valuation += (orange_centerism - blue_centerism) as f32 * 0.1;

    valuation
}

fn mmv_lva_heuristic(commit: &Commit) -> f32 {
    // https://chessprogramming.wikispaces.com/MVV-LVA
    match commit.hospitalization {
        Some(patient) => (
            figurine_valuation(patient) - figurine_valuation(commit.patch.star)),
        None => 0.0,
    }
}

fn order_moves(commits: &mut Vec<Commit>) {
    commits.sort_by(|a, b| {
        mmv_lva_heuristic(&b)
            .partial_cmp(&mmv_lva_heuristic(&a))
            .unwrap_or(Ordering::Equal)
    });
}

pub fn α_β_negamax_search(
    world: WorldState, depth: u8, mut α: f32, β: f32,
    memory_bank: Arc<Mutex<HashMap<WorldState, f32>>>) -> (Option<Commit>, f32) {
    let team = world.to_move;
    let potential_score = orientation(team) * score(world);
    let mut premonitions = world.reckless_lookahead();
    order_moves(&mut premonitions);
    if depth == 0 || premonitions.is_empty() {
        return (None, potential_score)
    };
    let mut optimum = NEG_INFINITY;
    let mut optimand = None;
    for premonition in premonitions.into_iter() {
        let mut value = NEG_INFINITY;  // can't hurt to be pessimistic
        let cached: bool;
        {
            let open_vault = memory_bank.lock().unwrap();
            let cached_value_maybe = open_vault.get(&premonition.tree);
            match cached_value_maybe {
                Some(&cached_value) => {
                    cached = true;
                    value = cached_value;
                }
                None => { cached = false; }
            };
        }

        if !cached {
            let (_, acquired_value) = α_β_negamax_search(
                premonition.tree, depth - 1,
                -β, -α, memory_bank.clone()
            );
            value = -acquired_value;
            memory_bank.lock().unwrap().insert(premonition.tree, value);
        }

        if value > optimum {
            optimum = value;
            optimand = Some(premonition);
        }
        if value > α {
            α = value;
        }
        if α >= β {
            break;
        }
    }

    (optimand, optimum)
}

pub fn potentially_timebound_kickoff(world: &WorldState, depth: u8,
                                     nihilistically: bool,
                                     deadline_maybe: Option<time::Timespec>)
                                     -> Option<Vec<(Commit, f32)>> {
    let déjà_vu_table: HashMap<WorldState, f32> = HashMap::new();
    let memory_bank = Arc::new(Mutex::new(déjà_vu_table));
    let mut premonitions;
    if nihilistically {
        premonitions = world.reckless_lookahead();
    } else {
        premonitions = world.lookahead();
    }
    order_moves(&mut premonitions);
    let mut forecasts = Vec::new();
    for &premonition in &premonitions {
        if let Some(deadline) = deadline_maybe {
            if time::get_time() > deadline {
                return None;
            }
        }
        let travel_bank = memory_bank.clone();
        let thread_handle = thread::spawn(move || {
            α_β_negamax_search(
                premonition.tree, depth - 1,
                NEG_INFINITY, INFINITY,
                travel_bank
            )
        });
        let (_grandchild, mut value) = thread_handle.join().ok().unwrap();
        value = -value;
        forecasts.push((premonition, value));
    }
    forecasts.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(Ordering::Equal));
    Some(forecasts)
}

pub fn kickoff(world: &WorldState, depth: u8,
               nihilistically: bool) -> Vec<(Commit, f32)> {
    potentially_timebound_kickoff(world, depth, nihilistically, None).unwrap()
}

pub fn iterative_deepening_kickoff(world: &WorldState, timeout: time::Duration,
                                   nihilistically: bool)
                                   -> (Vec<(Commit, f32)>, u8) {
    let deadline = time::get_time() + timeout;
    let mut depth = 1;
    let mut forecasts = potentially_timebound_kickoff(
        world, depth, nihilistically, None).unwrap();
    while let Some(prophecy) = potentially_timebound_kickoff(
            world, depth, nihilistically, Some(deadline)) {
        forecasts = prophecy;
        depth += 1;
    }
    (forecasts, depth)
}

#[cfg(test)]
mod tests {
    extern crate test;
    use self::test::Bencher;

    use time;
    use super::{kickoff, score};
    use space::Locale;
    use life::WorldState;
    use identity::Team;

    impl WorldState {
        fn no_castling_at_all(&mut self) {
            self.orange_east_service_eligibility = false;
            self.orange_west_service_eligibility = false;
            self.blue_east_service_eligibility = false;
            self.blue_west_service_eligibility = false;
        }
    }

    #[bench]
    fn benchmark_scoring(b: &mut Bencher) {
        b.iter(|| score(WorldState::new()));
    }

    #[bench]
    fn benchmark_kickoff_depth_1(b: &mut Bencher) {
        let ws = WorldState::new();
        b.iter(|| kickoff(&ws, 1, true));
    }

    #[bench]
    fn benchmark_kickoff_depth_2_arbys(b: &mut Bencher) {
        let ws = WorldState::new();
        b.iter(|| kickoff(&ws, 2, true));
    }

    #[bench]
    fn benchmark_kickoff_depth_2_carefully(b: &mut Bencher) {
        let ws = WorldState::new();
        b.iter(|| kickoff(&ws, 2, false));
    }

    #[ignore]
    #[bench]
    fn benchmark_kickoff_depth_3(b: &mut Bencher) {
        let ws = WorldState::new();
        b.iter(|| kickoff(&ws, 3, true));
    }

    #[test]
    #[ignore]  // more research is needed
    fn concerning_short_circuiting_upon_finding_critical_endangerment() {
        let ws = WorldState::reconstruct("7K/r7/1r6/8/8/8/8/7k b -".to_owned());
        let start = time::get_time();
        kickoff(&ws, 30, true);
        let duration = time::get_time() - start;
        assert!(duration.num_seconds() < 20);
    }

    #[test]
    #[allow(float_cmp)]
    fn concerning_fairness_of_the_initial_position() {
        // It's okay to assume this is really 0.0. Floats may be imprecise,
        // but they do have well-defined behavior.
        assert_eq!(0.0, score(WorldState::new()));
    }

    #[test]
    fn concerning_servant_ascension_choices() {
        let ws = WorldState::reconstruct("8/q1P1k/8/8/8/8/6PP/7K w -".to_owned());
        // looking ahead 3 moves allows the Leafline AI to catch the
        // split, whereby transforming into a pony (rather than
        // transitioning into a princess, as would usually be
        // expected) endangers both the blue princess and figurehead
        let (ref best_move, score) = kickoff(&ws, 3, true)[0];
        println!("{:?}", best_move);
        assert!(score > 0.0);
        assert_eq!(best_move.tree.preserve(), "2N5/q3k3/8/8/8/8/6PP/7K b -");
    }

    #[test]
    fn experimentally_about_kickoff() {
        let mut world = WorldState::new_except_empty();
        // SCENARIO: let's imagine Orange (to move) has separate attacks against
        // Blue's pony and servant, against which Blue has no defense but
        // to run away. We predict that Orange will take the pony, and
        // then Blue will move the servant out of the way.

        // scholar endangers pony
        world.blue_ponies = world.blue_ponies.alight(
            Locale { rank: 0, file: 0 });
        world.orange_scholars = world.orange_scholars.alight(
            Locale { rank: 2, file: 2 });

        // pony endangers servant
        world.blue_servants = world.blue_servants.alight(
            Locale { rank: 7, file: 1 });
        world.orange_ponies = world.orange_ponies.alight(
            Locale { rank: 5, file: 2 });

        // Blue has another servant sitting nowhere interesting
        world.blue_servants = world.blue_servants.alight(
            Locale { rank: 3, file: 6 });
        world.no_castling_at_all();

        let depth = 2;
        let advisory = kickoff(&world, depth, true);

        // taking the pony is the right thing to do
        assert_eq!(Locale { rank: 0, file: 0 }, advisory[0].0.patch.whither);

        // And, furthermore, the answer should be the same if we face the
        // same situation with the colors reversed
        //
        // XXX this would be tidier and less copy-pastey if I had more
        // general figurine-placing functions that were three rather than
        // two levels of abstraction above twiddling bits on an unsigned
        // int ... oh, well
        let mut negaworld = WorldState::new_except_empty();
        negaworld.to_move = Team::Blue;

        // scholar endangers pony
        negaworld.orange_ponies = negaworld.orange_ponies.alight(
            Locale { rank: 0, file: 0 });
        negaworld.blue_scholars = negaworld.blue_scholars.alight(
            Locale { rank: 2, file: 2 });

        // pony endangers servant
        negaworld.orange_servants = negaworld.orange_servants.alight(
            Locale { rank: 7, file: 1 });
        negaworld.blue_ponies = negaworld.blue_ponies.alight(
            Locale { rank: 5, file: 2 });

        // Orange has another servant sitting nowhere interesting
        negaworld.orange_servants = negaworld.orange_servants.alight(
            Locale { rank: 3, file: 6 });
        negaworld.to_move = Team::Blue;

        negaworld.no_castling_at_all();

        let negadvisory = kickoff(&negaworld, depth, true);

        // taking the pony is still the right thing to do, even in the
        // negaworld
        assert_eq!(Locale { rank: 0, file: 0 },
                   negadvisory[0].0.patch.whither);
    }

}
<commit_msg>poll channel to sharpen iterative deepening timeout; depth display fix<commit_after>use std::f32::{NEG_INFINITY, INFINITY};
use std::cmp::Ordering;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::sync::mpsc;
use std::thread;

use time;

use identity::{Team, JobDescription, Agent};
use life::{Commit, WorldState};
use motion::CENTER_OF_THE_WORLD;
use space::Pinfield;

pub fn orientation(team: Team) -> f32 {
    match team {
        Team::Orange => 1.0,
        Team::Blue => -1.0,
    }
}

pub fn figurine_valuation(agent: Agent) -> f32 {
    let value = match agent.job_description {
        // en.wikipedia.org/wiki/
        // Chess_piece_relative_value#Hans_Berliner.27s_system
        JobDescription::Servant => 1.0,
        JobDescription::Pony => 3.2,
        JobDescription::Scholar => 3.3,
        JobDescription::Cop => 5.1,
        JobDescription::Princess => 8.8,
        JobDescription::Figurehead => 20000.0,
    };
    orientation(agent.team) * value
}

pub fn score(world: WorldState) -> f32 {
    let mut valuation = 0.0;

    for team in Team::league().into_iter() {
        for agent in Agent::dramatis_personæ(team).into_iter() {
            valuation += world.agent_to_pinfield_ref(
                agent).pincount() as f32 * figurine_valuation(agent);
        }
        // breadth of scholarship bonus
        if world.agent_to_pinfield_ref(Agent {
                    team: team,
                    job_description: JobDescription::Scholar,
                })
                .to_locales()
                .len() >= 2 {
            valuation += orientation(team) * 0.5
        }
    }

    // ponies and servants want to be in the center of the world's
    // action, maybe??
    let center = Pinfield(CENTER_OF_THE_WORLD);
    // cast to signed to avoid overflow
    let orange_centerism: i8 = world.orange_servants.union(
        world.orange_ponies).intersection(center).pincount() as i8;
    let blue_centerism: i8 = world.blue_servants.union(
        world.blue_ponies).intersection(center).pincount() as i8;
    valuation += (orange_centerism - blue_centerism) as f32 * 0.1;

    valuation
}

fn mmv_lva_heuristic(commit: &Commit) -> f32 {
    // https://chessprogramming.wikispaces.com/MVV-LVA
    match commit.hospitalization {
        Some(patient) => (
            figurine_valuation(patient) - figurine_valuation(commit.patch.star)),
        None => 0.0,
    }
}

fn order_moves(commits: &mut Vec<Commit>) {
    commits.sort_by(|a, b| {
        mmv_lva_heuristic(&b)
            .partial_cmp(&mmv_lva_heuristic(&a))
            .unwrap_or(Ordering::Equal)
    });
}

pub fn α_β_negamax_search(
    world: WorldState, depth: u8, mut α: f32, β: f32,
    memory_bank: Arc<Mutex<HashMap<WorldState, f32>>>) -> (Option<Commit>, f32) {
    let team = world.to_move;
    let potential_score = orientation(team) * score(world);
    let mut premonitions = world.reckless_lookahead();
    order_moves(&mut premonitions);
    if depth == 0 || premonitions.is_empty() {
        return (None, potential_score)
    };
    let mut optimum = NEG_INFINITY;
    let mut optimand = None;
    for premonition in premonitions.into_iter() {
        let mut value = NEG_INFINITY;  // can't hurt to be pessimistic
        let cached: bool;
        {
            let open_vault = memory_bank.lock().unwrap();
            let cached_value_maybe = open_vault.get(&premonition.tree);
            match cached_value_maybe {
                Some(&cached_value) => {
                    cached = true;
                    value = cached_value;
                }
                None => { cached = false; }
            };
        }

        if !cached {
            let (_, acquired_value) = α_β_negamax_search(
                premonition.tree, depth - 1,
                -β, -α, memory_bank.clone()
            );
            value = -acquired_value;
            memory_bank.lock().unwrap().insert(premonition.tree, value);
        }

        if value > optimum {
            optimum = value;
            optimand = Some(premonition);
        }
        if value > α {
            α = value;
        }
        if α >= β {
            break;
        }
    }

    (optimand, optimum)
}

pub fn potentially_timebound_kickoff(world: &WorldState, depth: u8,
                                     nihilistically: bool,
                                     deadline_maybe: Option<time::Timespec>)
                                     -> Option<Vec<(Commit, f32)>> {
    let déjà_vu_table: HashMap<WorldState, f32> = HashMap::new();
    let memory_bank = Arc::new(Mutex::new(déjà_vu_table));
    let mut premonitions;
    if nihilistically {
        premonitions = world.reckless_lookahead();
    } else {
        premonitions = world.lookahead();
    }
    order_moves(&mut premonitions);
    let mut forecasts = Vec::new();
    let (tx, rx) = mpsc::channel();
    for &premonition in &premonitions {
        let travel_bank = memory_bank.clone();
        let explorer_radio = tx.clone();
        thread::spawn(move || {
            let search_hit = α_β_negamax_search(
                premonition.tree, depth - 1,
                NEG_INFINITY, INFINITY,
                travel_bank
            );
            explorer_radio.send(search_hit).ok();
        });
        loop {  // polling for results
            if let Some(deadline) = deadline_maybe {
                if time::get_time() > deadline {
                    return None;
                }
            }
            let search_hit_maybe = rx.try_recv().ok();
            match search_hit_maybe {
                Some(search_hit) => {
                    let (_grandchild, mut value) = search_hit;
                    value = -value;
                    forecasts.push((premonition, value));
                    break;
                },
                None => { thread::sleep_ms(2); }
            }
        }
    }
    forecasts.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(Ordering::Equal));
    Some(forecasts)
}

pub fn kickoff(world: &WorldState, depth: u8,
               nihilistically: bool) -> Vec<(Commit, f32)> {
    potentially_timebound_kickoff(world, depth, nihilistically, None).unwrap()
}

pub fn iterative_deepening_kickoff(world: &WorldState, timeout: time::Duration,
                                   nihilistically: bool)
                                   -> (Vec<(Commit, f32)>, u8) {
    let deadline = time::get_time() + timeout;
    let mut depth = 1;
    let mut forecasts = potentially_timebound_kickoff(
        world, depth, nihilistically, None).unwrap();
    while let Some(prophecy) = potentially_timebound_kickoff(
            world, depth, nihilistically, Some(deadline)) {
        forecasts = prophecy;
        depth += 1;
    }
    (forecasts, depth-1)
}

#[cfg(test)]
mod tests {
    extern crate test;
    use self::test::Bencher;

    use time;
    use super::{kickoff, score};
    use space::Locale;
    use life::WorldState;
    use identity::Team;

    impl WorldState {
        fn no_castling_at_all(&mut self) {
            self.orange_east_service_eligibility = false;
            self.orange_west_service_eligibility = false;
            self.blue_east_service_eligibility = false;
            self.blue_west_service_eligibility = false;
        }
    }

    #[bench]
    fn benchmark_scoring(b: &mut Bencher) {
        b.iter(|| score(WorldState::new()));
    }

    #[bench]
    fn benchmark_kickoff_depth_1(b: &mut Bencher) {
        let ws = WorldState::new();
        b.iter(|| kickoff(&ws, 1, true));
    }

    #[bench]
    fn benchmark_kickoff_depth_2_arbys(b: &mut Bencher) {
        let ws = WorldState::new();
        b.iter(|| kickoff(&ws, 2, true));
    }

    #[bench]
    fn benchmark_kickoff_depth_2_carefully(b: &mut Bencher) {
        let ws = WorldState::new();
        b.iter(|| kickoff(&ws, 2, false));
    }

    #[ignore]
    #[bench]
    fn benchmark_kickoff_depth_3(b: &mut Bencher) {
        let ws = WorldState::new();
        b.iter(|| kickoff(&ws, 3, true));
    }

    #[test]
    #[ignore]  // more research is needed
    fn concerning_short_circuiting_upon_finding_critical_endangerment() {
        let ws = WorldState::reconstruct("7K/r7/1r6/8/8/8/8/7k b -".to_owned());
        let start = time::get_time();
        kickoff(&ws, 30, true);
        let duration = time::get_time() - start;
        assert!(duration.num_seconds() < 20);
    }

    #[test]
    #[allow(float_cmp)]
    fn concerning_fairness_of_the_initial_position() {
        // It's okay to assume this is really 0.0. Floats may be imprecise,
        // but they do have well-defined behavior.
        assert_eq!(0.0, score(WorldState::new()));
    }

    #[test]
    fn concerning_servant_ascension_choices() {
        let ws = WorldState::reconstruct("8/q1P1k/8/8/8/8/6PP/7K w -".to_owned());
        // looking ahead 3 moves allows the Leafline AI to catch the
        // split, whereby transforming into a pony (rather than
        // transitioning into a princess, as would usually be
        // expected) endangers both the blue princess and figurehead
        let (ref best_move, score) = kickoff(&ws, 3, true)[0];
        println!("{:?}", best_move);
        assert!(score > 0.0);
        assert_eq!(best_move.tree.preserve(), "2N5/q3k3/8/8/8/8/6PP/7K b -");
    }

    #[test]
    fn experimentally_about_kickoff() {
        let mut world = WorldState::new_except_empty();
        // SCENARIO: let's imagine Orange (to move) has separate attacks against
        // Blue's pony and servant, against which Blue has no defense but
        // to run away. We predict that Orange will take the pony, and
        // then Blue will move the servant out of the way.

        // scholar endangers pony
        world.blue_ponies = world.blue_ponies.alight(
            Locale { rank: 0, file: 0 });
        world.orange_scholars = world.orange_scholars.alight(
            Locale { rank: 2, file: 2 });

        // pony endangers servant
        world.blue_servants = world.blue_servants.alight(
            Locale { rank: 7, file: 1 });
        world.orange_ponies = world.orange_ponies.alight(
            Locale { rank: 5, file: 2 });

        // Blue has another servant sitting nowhere interesting
        world.blue_servants = world.blue_servants.alight(
            Locale { rank: 3, file: 6 });
        world.no_castling_at_all();

        let depth = 2;
        let advisory = kickoff(&world, depth, true);

        // taking the pony is the right thing to do
        assert_eq!(Locale { rank: 0, file: 0 }, advisory[0].0.patch.whither);

        // And, furthermore, the answer should be the same if we face the
        // same situation with the colors reversed
        //
        // XXX this would be tidier and less copy-pastey if I had more
        // general figurine-placing functions that were three rather than
        // two levels of abstraction above twiddling bits on an unsigned
        // int ... oh, well
        let mut negaworld = WorldState::new_except_empty();
        negaworld.to_move = Team::Blue;

        // scholar endangers pony
        negaworld.orange_ponies = negaworld.orange_ponies.alight(
            Locale { rank: 0, file: 0 });
        negaworld.blue_scholars = negaworld.blue_scholars.alight(
            Locale { rank: 2, file: 2 });

        // pony endangers servant
        negaworld.orange_servants = negaworld.orange_servants.alight(
            Locale { rank: 7, file: 1 });
        negaworld.blue_ponies = negaworld.blue_ponies.alight(
            Locale { rank: 5, file: 2 });

        // Orange has another servant sitting nowhere interesting
        negaworld.orange_servants = negaworld.orange_servants.alight(
            Locale { rank: 3, file: 6 });
        negaworld.to_move = Team::Blue;

        negaworld.no_castling_at_all();

        let negadvisory = kickoff(&negaworld, depth, true);

        // taking the pony is still the right thing to do, even in the
        // negaworld
        assert_eq!(Locale { rank: 0, file: 0 },
                   negadvisory[0].0.patch.whither);
    }

}
<|endoftext|>


<commit_before>//! A module of helper traits and iterators that are not intended to be used
//! directly.

use std::ops::{
    FullRange,
    Range,
    RangeTo,
    RangeFrom
};

/// A helper trait for (x,y,z) ++ w => (x,y,z,w),
/// used for implementing `iproduct!` and `izip!`
pub trait AppendTuple<X> {
    type Result;
    fn append(self, x: X) -> Self::Result;
}

macro_rules! impl_append_tuple(
    () => (
        impl<T> AppendTuple<T> for () {
            type Result = (T, );
            fn append(self, x: T) -> (T, ) {
                (x, )
            }
        }
    );

    ($A:ident, $($B:ident,)*) => (
        impl_append_tuple!($($B,)*);
        #[allow(non_snake_case)]
        impl<$A, $($B,)* T> AppendTuple<T> for ($A, $($B),*) {
            type Result = ($A, $($B, )* T);
            fn append(self, x: T) -> ($A, $($B,)* T) {
                let ($A, $($B),*) = self;
                ($A, $($B,)* x)
            }
        }
    );
);

impl_append_tuple!(A, B, C, D, E, F, G, H, I, J, K, L,);

/// A helper iterator that maps an iterator of tuples like
/// `((A, B), C)` to an iterator of `(A, B, C)`.
///
/// Used by the `iproduct!()` macro.
#[derive(Clone)]
pub struct FlatTuples<I> {
    iter: I,
}

impl<I> FlatTuples<I>
{
    /// Create a new **FlatTuples**.
    pub fn new(iter: I) -> Self
    {
        FlatTuples{iter: iter}
    }
}

impl<X, T, I>
Iterator for FlatTuples<I>
    where
        I: Iterator<Item=(T, X)>,
        T: AppendTuple<X>,
{
    type Item = <T as AppendTuple<X>>::Result;
    #[inline]
    fn next(&mut self) -> Option< <Self as Iterator>::Item>
    {
        self.iter.next().map(|(t, x)| t.append(x))
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.iter.size_hint()
    }
}

impl<X, T, I> DoubleEndedIterator for FlatTuples<I>
    where
        I: DoubleEndedIterator<Item=(T, X)>,
        T: AppendTuple<X>,
{
    #[inline]
    fn next_back(&mut self) -> Option< <Self as Iterator>::Item>
    {
        self.iter.next_back().map(|(t, x)| t.append(x))
    }
}

/// **GenericRange** is implemented by Rust's built-in range types, produced
/// by range syntax like `a..`, `..b` or `c..d`.
pub trait GenericRange {
    /// Start index (inclusive)
    fn start(&self) -> Option<usize> { None }
    /// End index (exclusive)
    fn end(&self) -> Option<usize> { None }
}

impl GenericRange for FullRange {}

impl GenericRange for RangeFrom<usize> {
    fn start(&self) -> Option<usize> { Some(self.start) }
}

impl GenericRange for RangeTo<usize> {
    fn end(&self) -> Option<usize> { Some(self.end) }
}

impl GenericRange for Range<usize> {
    fn start(&self) -> Option<usize> { Some(self.start) }
    fn end(&self) -> Option<usize> { Some(self.end) }
}

<commit_msg>Update for latest rustc.<commit_after>//! A module of helper traits and iterators that are not intended to be used
//! directly.

use std::ops::{
    RangeFull,
    Range,
    RangeTo,
    RangeFrom
};

/// A helper trait for (x,y,z) ++ w => (x,y,z,w),
/// used for implementing `iproduct!` and `izip!`
pub trait AppendTuple<X> {
    type Result;
    fn append(self, x: X) -> Self::Result;
}

macro_rules! impl_append_tuple(
    () => (
        impl<T> AppendTuple<T> for () {
            type Result = (T, );
            fn append(self, x: T) -> (T, ) {
                (x, )
            }
        }
    );

    ($A:ident, $($B:ident,)*) => (
        impl_append_tuple!($($B,)*);
        #[allow(non_snake_case)]
        impl<$A, $($B,)* T> AppendTuple<T> for ($A, $($B),*) {
            type Result = ($A, $($B, )* T);
            fn append(self, x: T) -> ($A, $($B,)* T) {
                let ($A, $($B),*) = self;
                ($A, $($B,)* x)
            }
        }
    );
);

impl_append_tuple!(A, B, C, D, E, F, G, H, I, J, K, L,);

/// A helper iterator that maps an iterator of tuples like
/// `((A, B), C)` to an iterator of `(A, B, C)`.
///
/// Used by the `iproduct!()` macro.
#[derive(Clone)]
pub struct FlatTuples<I> {
    iter: I,
}

impl<I> FlatTuples<I>
{
    /// Create a new **FlatTuples**.
    pub fn new(iter: I) -> Self
    {
        FlatTuples{iter: iter}
    }
}

impl<X, T, I>
Iterator for FlatTuples<I>
    where
        I: Iterator<Item=(T, X)>,
        T: AppendTuple<X>,
{
    type Item = <T as AppendTuple<X>>::Result;
    #[inline]
    fn next(&mut self) -> Option< <Self as Iterator>::Item>
    {
        self.iter.next().map(|(t, x)| t.append(x))
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.iter.size_hint()
    }
}

impl<X, T, I> DoubleEndedIterator for FlatTuples<I>
    where
        I: DoubleEndedIterator<Item=(T, X)>,
        T: AppendTuple<X>,
{
    #[inline]
    fn next_back(&mut self) -> Option< <Self as Iterator>::Item>
    {
        self.iter.next_back().map(|(t, x)| t.append(x))
    }
}

/// **GenericRange** is implemented by Rust's built-in range types, produced
/// by range syntax like `a..`, `..b` or `c..d`.
pub trait GenericRange {
    /// Start index (inclusive)
    fn start(&self) -> Option<usize> { None }
    /// End index (exclusive)
    fn end(&self) -> Option<usize> { None }
}

impl GenericRange for RangeFull {}

impl GenericRange for RangeFrom<usize> {
    fn start(&self) -> Option<usize> { Some(self.start) }
}

impl GenericRange for RangeTo<usize> {
    fn end(&self) -> Option<usize> { Some(self.end) }
}

impl GenericRange for Range<usize> {
    fn start(&self) -> Option<usize> { Some(self.start) }
    fn end(&self) -> Option<usize> { Some(self.end) }
}

<|endoftext|>


<commit_before>use cocoa::base::{id, nil};
use cocoa::foundation::{NSUInteger, NSString};
use objc::runtime::{BOOL, NO, YES};
use sys::{MTLCreateSystemDefaultDevice, MTLCopyAllDevices, MTLDevice};
use std::borrow::Cow;
use std::error::Error;
use std::fmt::{self, Display, Formatter};
use std::ffi::CStr;
use {Size};

pub trait ReadOnlyDevice {
    fn is_depth24_stencil8_pixel_format_supported(&self) -> bool;
    fn is_headless(&self) -> bool;
    fn is_low_power(&self) -> bool;
    fn max_threads_per_group(&self) -> bool;
    fn get_name(&self) -> Cow<str>;
    fn supports_texture_sample_count(&self, sample_count: usize) -> bool;
}

pub struct Device(id);

impl Device {
    pub fn system_default_device() -> Result<Self, DeviceError> {
        let device = unsafe { MTLCreateSystemDefaultDevice() };
        if device != nil {
            Ok(Device(device))
        } else {
            Err(DeviceError::ConstructionFailed)
        }
    }

    #[allow(unused_mut, unused_variables)]
    pub fn enumerate_all_system_devices() -> Vec<Result<Self, DeviceError>> {
        let all_devices = unsafe { MTLCopyAllDevices() };
        let mut devices_vec = vec![];

        devices_vec
    }

    pub unsafe fn into_raw(self) -> id {
        self.0
    }

    pub unsafe fn from_raw_unchecked(device_ptr: id) -> Self {
        Device(device_ptr)
    }

    pub fn from_raw(device_ptr: id) -> Result<Self, DeviceError> {
        #[link(name = "Foundation", kind = "framework")]
        extern {
            fn NSProtocolFromString(namestr: id) -> id;
        }

        let conforms_to_protocol: BOOL = unsafe {
            let mtl_device_protocol_str = NSString::alloc(nil).init_str("MTLDevice");
            let mtl_device_protocol = NSProtocolFromString(mtl_device_protocol_str);
            msg_send![device_ptr, conformsToProtocol:mtl_device_protocol]
        };

        if device_ptr == nil {
            Err(DeviceError::ConstructedFromNil)
        } else if conforms_to_protocol == NO {
            Err(DeviceError::ConstructedFromWrongPointerType)
        } else {
            Ok(Device(device_ptr))
        }
    }

    pub fn is_depth24_stencil8_pixel_format_supported(&self) -> bool {
        unsafe { self.0.depth24Stencil8PixelFormatSupported() == YES }
    }

    pub fn is_headless(&self) -> bool {
        unsafe { self.0.headless() == YES }
    }

    pub fn is_low_power(&self) -> bool {
        unsafe { self.0.lowPower() == YES }
    }

    pub fn max_threads_per_group(&self) -> Size {
        unsafe { self.0.maxThreadsPerGroup().into() }
    }

    pub fn get_name(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.name().UTF8String()).to_string_lossy() }
    }

    pub fn supports_texture_sample_count(&self, sample_count: usize) -> bool {
        unsafe { self.0.supportsTextureSampleCount(sample_count as NSUInteger) == YES }
    }
}

/// Internal utility function to get a Device's id without consuming it.
/// Not exported publicly from this crate.
#[doc(hidden)]
pub unsafe fn _device_get_raw(device: &Device) -> id {
    device.0
}

// TODO(George): Should this have a lifetime associated with it?
pub struct DeviceRef(id);

impl ReadOnlyDevice for DeviceRef {
    pub fn is_depth24_stencil8_pixel_format_supported(&self) -> bool {
        unsafe { self.0.depth24Stencil8PixelFormatSupported() == YES }
    }

    pub fn is_headless(&self) -> bool {
        unsafe { self.0.headless() == YES }
    }

    pub fn is_low_power(&self) -> bool {
        unsafe { self.0.lowPower() == YES }
    }

    pub fn max_threads_per_group(&self) -> Size {
        unsafe { self.0.maxThreadsPerGroup().into() }
    }

    pub fn get_name(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.name().UTF8String()).to_string_lossy() }
    }

    pub fn supports_texture_sample_count(&self, sample_count: usize) -> bool {
        unsafe { self.0.supportsTextureSampleCount(sample_count as NSUInteger) == YES }
    }
}

/// Internal utility function to create a DeviceRef.
/// Not exported publicly from this crate.
#[doc(hidden)]
pub unsafe fn _make_device_ref(device: id) -> DeviceRef {
    DeviceRef(device)
}

#[derive(Clone, Debug)]
pub enum DeviceError {
    ConstructionFailed,
    ConstructedFromNil,
    ConstructedFromWrongPointerType,
    OtherRuntimeError
}

impl Display for DeviceError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let descr = match *self {
            DeviceError::ConstructionFailed => "DeviceError::ConstructionFailed",
            DeviceError::ConstructedFromNil => "DeviceError::ConstructedFromNil",
            DeviceError::ConstructedFromWrongPointerType => "DeviceError::ConstructedFromWrongPointerType",
            DeviceError::OtherRuntimeError => "DeviceError::OtherRuntimeError"
        };
        write!(f, "{}", descr)
    }
}

impl Error for DeviceError {
    fn description(&self) -> &str {
        match *self {
            DeviceError::ConstructionFailed =>
                "Could not create a default device. Please ensure that you are using at least OSX 10.11 or iOS 8.0",
            DeviceError::ConstructedFromNil => "Attempted to create a device from a nil pointer",
            DeviceError::ConstructedFromWrongPointerType =>
                "Attempted to create a device from a pointer which does not implement MTLDevice",
            DeviceError::OtherRuntimeError => "An error occured in the Objective-C runtime"
        }
    }
}
<commit_msg>`ReadOnlyDevice::max_threads_per_group` should return a Size, not a bool<commit_after>use cocoa::base::{id, nil};
use cocoa::foundation::{NSUInteger, NSString};
use objc::runtime::{BOOL, NO, YES};
use sys::{MTLCreateSystemDefaultDevice, MTLCopyAllDevices, MTLDevice};
use std::borrow::Cow;
use std::error::Error;
use std::fmt::{self, Display, Formatter};
use std::ffi::CStr;
use {Size};

pub trait ReadOnlyDevice {
    fn is_depth24_stencil8_pixel_format_supported(&self) -> bool;
    fn is_headless(&self) -> bool;
    fn is_low_power(&self) -> bool;
    fn max_threads_per_group(&self) -> Size;
    fn get_name(&self) -> Cow<str>;
    fn supports_texture_sample_count(&self, sample_count: usize) -> bool;
}

pub struct Device(id);

impl Device {
    pub fn system_default_device() -> Result<Self, DeviceError> {
        let device = unsafe { MTLCreateSystemDefaultDevice() };
        if device != nil {
            Ok(Device(device))
        } else {
            Err(DeviceError::ConstructionFailed)
        }
    }

    #[allow(unused_mut, unused_variables)]
    pub fn enumerate_all_system_devices() -> Vec<Result<Self, DeviceError>> {
        let all_devices = unsafe { MTLCopyAllDevices() };
        let mut devices_vec = vec![];

        devices_vec
    }

    pub unsafe fn into_raw(self) -> id {
        self.0
    }

    pub unsafe fn from_raw_unchecked(device_ptr: id) -> Self {
        Device(device_ptr)
    }

    pub fn from_raw(device_ptr: id) -> Result<Self, DeviceError> {
        #[link(name = "Foundation", kind = "framework")]
        extern {
            fn NSProtocolFromString(namestr: id) -> id;
        }

        let conforms_to_protocol: BOOL = unsafe {
            let mtl_device_protocol_str = NSString::alloc(nil).init_str("MTLDevice");
            let mtl_device_protocol = NSProtocolFromString(mtl_device_protocol_str);
            msg_send![device_ptr, conformsToProtocol:mtl_device_protocol]
        };

        if device_ptr == nil {
            Err(DeviceError::ConstructedFromNil)
        } else if conforms_to_protocol == NO {
            Err(DeviceError::ConstructedFromWrongPointerType)
        } else {
            Ok(Device(device_ptr))
        }
    }

    pub fn is_depth24_stencil8_pixel_format_supported(&self) -> bool {
        unsafe { self.0.depth24Stencil8PixelFormatSupported() == YES }
    }

    pub fn is_headless(&self) -> bool {
        unsafe { self.0.headless() == YES }
    }

    pub fn is_low_power(&self) -> bool {
        unsafe { self.0.lowPower() == YES }
    }

    pub fn max_threads_per_group(&self) -> Size {
        unsafe { self.0.maxThreadsPerGroup().into() }
    }

    pub fn get_name(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.name().UTF8String()).to_string_lossy() }
    }

    pub fn supports_texture_sample_count(&self, sample_count: usize) -> bool {
        unsafe { self.0.supportsTextureSampleCount(sample_count as NSUInteger) == YES }
    }
}

/// Internal utility function to get a Device's id without consuming it.
/// Not exported publicly from this crate.
#[doc(hidden)]
pub unsafe fn _device_get_raw(device: &Device) -> id {
    device.0
}

// TODO(George): Should this have a lifetime associated with it?
pub struct DeviceRef(id);

impl ReadOnlyDevice for DeviceRef {
    pub fn is_depth24_stencil8_pixel_format_supported(&self) -> bool {
        unsafe { self.0.depth24Stencil8PixelFormatSupported() == YES }
    }

    pub fn is_headless(&self) -> bool {
        unsafe { self.0.headless() == YES }
    }

    pub fn is_low_power(&self) -> bool {
        unsafe { self.0.lowPower() == YES }
    }

    pub fn max_threads_per_group(&self) -> Size {
        unsafe { self.0.maxThreadsPerGroup().into() }
    }

    pub fn get_name(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.name().UTF8String()).to_string_lossy() }
    }

    pub fn supports_texture_sample_count(&self, sample_count: usize) -> bool {
        unsafe { self.0.supportsTextureSampleCount(sample_count as NSUInteger) == YES }
    }
}

/// Internal utility function to create a DeviceRef.
/// Not exported publicly from this crate.
#[doc(hidden)]
pub unsafe fn _make_device_ref(device: id) -> DeviceRef {
    DeviceRef(device)
}

#[derive(Clone, Debug)]
pub enum DeviceError {
    ConstructionFailed,
    ConstructedFromNil,
    ConstructedFromWrongPointerType,
    OtherRuntimeError
}

impl Display for DeviceError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let descr = match *self {
            DeviceError::ConstructionFailed => "DeviceError::ConstructionFailed",
            DeviceError::ConstructedFromNil => "DeviceError::ConstructedFromNil",
            DeviceError::ConstructedFromWrongPointerType => "DeviceError::ConstructedFromWrongPointerType",
            DeviceError::OtherRuntimeError => "DeviceError::OtherRuntimeError"
        };
        write!(f, "{}", descr)
    }
}

impl Error for DeviceError {
    fn description(&self) -> &str {
        match *self {
            DeviceError::ConstructionFailed =>
                "Could not create a default device. Please ensure that you are using at least OSX 10.11 or iOS 8.0",
            DeviceError::ConstructedFromNil => "Attempted to create a device from a nil pointer",
            DeviceError::ConstructedFromWrongPointerType =>
                "Attempted to create a device from a pointer which does not implement MTLDevice",
            DeviceError::OtherRuntimeError => "An error occured in the Objective-C runtime"
        }
    }
}
<|endoftext|>


<commit_before>//
// Author: Patrick Walton
//

use mem::Mem;

pub struct Disassembler<'a, M: Mem + 'a> {
    pub pc: u16,
    pub mem: &'a mut M
}

impl<'a, M: Mem> Disassembler<'a, M> {
    //
    // Loads and byte-to-string conversion
    //

    fn loadb_bump_pc(&mut self) -> u8 {
        let val = (&mut *self.mem).loadb(self.pc);
        self.pc += 1;
        val
    }
    fn loadw_bump_pc(&mut self) -> u16 {
        let bottom = self.loadb_bump_pc() as u16;
        let top = (self.loadb_bump_pc() as u16) << 8;
        bottom | top
    }

    fn disb_bump_pc(&mut self) -> String {
        format!("${:02X}", self.loadb_bump_pc() as usize)
    }
    fn disw_bump_pc(&mut self) -> String {
        format!("${:04X}", self.loadw_bump_pc() as usize)
    }

    //
    // Mnemonics
    //

    // TODO: When we get method macros some of this ugly duplication can go away.

    // Loads
    fn lda(&mut self, am: String) -> String { format!("LDA {}", am) }
    fn ldx(&mut self, am: String) -> String { format!("LDX {}", am) }
    fn ldy(&mut self, am: String) -> String { format!("LDY {}", am) }

    // Stores
    fn sta(&mut self, am: String) -> String { format!("STA {}", am) }
    fn stx(&mut self, am: String) -> String { format!("STX {}", am) }
    fn sty(&mut self, am: String) -> String { format!("STY {}", am) }

    // Arithmetic
    fn adc(&mut self, am: String) -> String { format!("ADC {}", am) }
    fn sbc(&mut self, am: String) -> String { format!("SBC {}", am) }

    // Comparisons
    fn cmp(&mut self, am: String) -> String { format!("CMP {}", am) }
    fn cpx(&mut self, am: String) -> String { format!("CPX {}", am) }
    fn cpy(&mut self, am: String) -> String { format!("CPY {}", am) }

    // Bitwise operations
    fn and(&mut self, am: String) -> String { format!("AND {}", am) }
    fn ora(&mut self, am: String) -> String { format!("ORA {}", am) }
    fn eor(&mut self, am: String) -> String { format!("EOR {}", am) }
    fn bit(&mut self, am: String) -> String { format!("BIT {}", am) }

    // Shifts and rotates
    fn rol(&mut self, am: String) -> String { format!("ROL {}", am) }
    fn ror(&mut self, am: String) -> String { format!("ROR {}", am) }
    fn asl(&mut self, am: String) -> String { format!("ASL {}", am) }
    fn lsr(&mut self, am: String) -> String { format!("LSR {}", am) }

    // Increments and decrements
    fn inc(&mut self, am: String) -> String { format!("INC {}", am) }
    fn dec(&mut self, am: String) -> String { format!("DEC {}", am) }
    fn inx(&mut self) -> String           { "INX".to_owned()       }
    fn dex(&mut self) -> String           { "DEX".to_owned()       }
    fn iny(&mut self) -> String           { "INY".to_owned()       }
    fn dey(&mut self) -> String           { "DEY".to_owned()       }

    // Register moves
    fn tax(&mut self) -> String           { "TAX".to_owned()       }
    fn tay(&mut self) -> String           { "TAY".to_owned()       }
    fn txa(&mut self) -> String           { "TXA".to_owned()       }
    fn tya(&mut self) -> String           { "TYA".to_owned()       }
    fn txs(&mut self) -> String           { "TXS".to_owned()       }
    fn tsx(&mut self) -> String           { "TSX".to_owned()       }

    // Flag operations
    fn clc(&mut self) -> String           { "CLC".to_owned()       }
    fn sec(&mut self) -> String           { "SEC".to_owned()       }
    fn cli(&mut self) -> String           { "CLI".to_owned()       }
    fn sei(&mut self) -> String           { "SEI".to_owned()       }
    fn clv(&mut self) -> String           { "CLV".to_owned()       }
    fn cld(&mut self) -> String           { "CLD".to_owned()       }
    fn sed(&mut self) -> String           { "SED".to_owned()       }

    // Branches
    // FIXME: Should disassemble the displacement!
    fn bpl(&mut self) -> String           { "BPL xx".to_owned()    }
    fn bmi(&mut self) -> String           { "BMI xx".to_owned()    }
    fn bvc(&mut self) -> String           { "BVC xx".to_owned()    }
    fn bvs(&mut self) -> String           { "BVS xx".to_owned()    }
    fn bcc(&mut self) -> String           { "BCC xx".to_owned()    }
    fn bcs(&mut self) -> String           { "BCS xx".to_owned()    }
    fn bne(&mut self) -> String           { "BNE xx".to_owned()    }
    fn beq(&mut self) -> String           { "BEQ xx".to_owned()    }

    // Jumps
    // FIXME: Should disassemble the address!
    fn jmp(&mut self) -> String           { format!("JMP {}", self.disw_bump_pc()) }
    fn jmpi(&mut self) -> String          { format!("JMP ({})", self.disw_bump_pc())  }

    // Procedure calls
    // FIXME: Should disassemble the address!
    fn jsr(&mut self) -> String           { "JSR xx".to_owned()    }
    fn rts(&mut self) -> String           { "RTS".to_owned()       }
    fn brk(&mut self) -> String           { "BRK".to_owned()       }
    fn rti(&mut self) -> String           { "RTI".to_owned()       }

    // Stack operations
    fn pha(&mut self) -> String           { "PHA".to_owned()       }
    fn pla(&mut self) -> String           { "PLA".to_owned()       }
    fn php(&mut self) -> String           { "PHP".to_owned()       }
    fn plp(&mut self) -> String           { "PLP".to_owned()       }

    // No operation
    fn nop(&mut self) -> String           { "NOP".to_owned()       }

    // Addressing modes
    fn immediate(&mut self) -> String {
        format!("{}{}", "#", self.disb_bump_pc())
    }
    fn accumulator(&mut self) -> String {
        String::new()
    }
    fn zero_page(&mut self) -> String {
        self.disb_bump_pc()
    }
    fn zero_page_x(&mut self) -> String {
        let mut buf = self.disb_bump_pc();
        buf.push_str(",X");
        buf
    }
    fn zero_page_y(&mut self) -> String {
        let mut buf = self.disb_bump_pc();
        buf.push_str(",Y");
        buf
    }
    fn absolute(&mut self) -> String           { self.disw_bump_pc()                       }
    fn absolute_x(&mut self) -> String {
        let mut buf = self.disw_bump_pc();
        buf.push_str(",X");
        buf
    }
    fn absolute_y(&mut self) -> String {
        let mut buf = self.disw_bump_pc();
        buf.push_str(",Y");
        buf
    }
    fn indexed_indirect_x(&mut self) -> String {
        format!("({},X)", self.disb_bump_pc())
    }
    fn indirect_indexed_y(&mut self) -> String {
        format!("({}),Y", self.disb_bump_pc())
    }

    // The main disassembly routine.
    #[inline(never)]
    pub fn disassemble(&mut self) -> String {
        let op = self.loadb_bump_pc();
        decode_op!(op, self)
    }
}
<commit_msg>Disassemble branch displacement<commit_after>//
// Author: Patrick Walton
//

use mem::Mem;

pub struct Disassembler<'a, M: Mem + 'a> {
    pub pc: u16,
    pub mem: &'a mut M
}

impl<'a, M: Mem> Disassembler<'a, M> {
    //
    // Loads and byte-to-string conversion
    //

    fn loadb_bump_pc(&mut self) -> u8 {
        let val = (&mut *self.mem).loadb(self.pc);
        self.pc += 1;
        val
    }
    fn loadw_bump_pc(&mut self) -> u16 {
        let bottom = self.loadb_bump_pc() as u16;
        let top = (self.loadb_bump_pc() as u16) << 8;
        bottom | top
    }

    fn disb_bump_pc(&mut self) -> String {
        format!("${:02X}", self.loadb_bump_pc() as usize)
    }
    fn disw_bump_pc(&mut self) -> String {
        format!("${:04X}", self.loadw_bump_pc() as usize)
    }

    //
    // Mnemonics
    //

    // TODO: When we get method macros some of this ugly duplication can go away.

    // Loads
    fn lda(&mut self, am: String) -> String { format!("LDA {}", am) }
    fn ldx(&mut self, am: String) -> String { format!("LDX {}", am) }
    fn ldy(&mut self, am: String) -> String { format!("LDY {}", am) }

    // Stores
    fn sta(&mut self, am: String) -> String { format!("STA {}", am) }
    fn stx(&mut self, am: String) -> String { format!("STX {}", am) }
    fn sty(&mut self, am: String) -> String { format!("STY {}", am) }

    // Arithmetic
    fn adc(&mut self, am: String) -> String { format!("ADC {}", am) }
    fn sbc(&mut self, am: String) -> String { format!("SBC {}", am) }

    // Comparisons
    fn cmp(&mut self, am: String) -> String { format!("CMP {}", am) }
    fn cpx(&mut self, am: String) -> String { format!("CPX {}", am) }
    fn cpy(&mut self, am: String) -> String { format!("CPY {}", am) }

    // Bitwise operations
    fn and(&mut self, am: String) -> String { format!("AND {}", am) }
    fn ora(&mut self, am: String) -> String { format!("ORA {}", am) }
    fn eor(&mut self, am: String) -> String { format!("EOR {}", am) }
    fn bit(&mut self, am: String) -> String { format!("BIT {}", am) }

    // Shifts and rotates
    fn rol(&mut self, am: String) -> String { format!("ROL {}", am) }
    fn ror(&mut self, am: String) -> String { format!("ROR {}", am) }
    fn asl(&mut self, am: String) -> String { format!("ASL {}", am) }
    fn lsr(&mut self, am: String) -> String { format!("LSR {}", am) }

    // Increments and decrements
    fn inc(&mut self, am: String) -> String { format!("INC {}", am) }
    fn dec(&mut self, am: String) -> String { format!("DEC {}", am) }
    fn inx(&mut self) -> String           { "INX".to_owned()       }
    fn dex(&mut self) -> String           { "DEX".to_owned()       }
    fn iny(&mut self) -> String           { "INY".to_owned()       }
    fn dey(&mut self) -> String           { "DEY".to_owned()       }

    // Register moves
    fn tax(&mut self) -> String           { "TAX".to_owned()       }
    fn tay(&mut self) -> String           { "TAY".to_owned()       }
    fn txa(&mut self) -> String           { "TXA".to_owned()       }
    fn tya(&mut self) -> String           { "TYA".to_owned()       }
    fn txs(&mut self) -> String           { "TXS".to_owned()       }
    fn tsx(&mut self) -> String           { "TSX".to_owned()       }

    // Flag operations
    fn clc(&mut self) -> String           { "CLC".to_owned()       }
    fn sec(&mut self) -> String           { "SEC".to_owned()       }
    fn cli(&mut self) -> String           { "CLI".to_owned()       }
    fn sei(&mut self) -> String           { "SEI".to_owned()       }
    fn clv(&mut self) -> String           { "CLV".to_owned()       }
    fn cld(&mut self) -> String           { "CLD".to_owned()       }
    fn sed(&mut self) -> String           { "SED".to_owned()       }

    // Branches
    fn bpl(&mut self) -> String           { format!("BPL {}", self.relative()) }
    fn bmi(&mut self) -> String           { format!("BMI {}", self.relative()) }
    fn bvc(&mut self) -> String           { format!("BVC {}", self.relative()) }
    fn bvs(&mut self) -> String           { format!("BVS {}", self.relative()) }
    fn bcc(&mut self) -> String           { format!("BCC {}", self.relative()) }
    fn bcs(&mut self) -> String           { format!("BCS {}", self.relative()) }
    fn bne(&mut self) -> String           { format!("BNE {}", self.relative()) }
    fn beq(&mut self) -> String           { format!("BEQ {}", self.relative()) }

    // Jumps
    fn jmp(&mut self) -> String           { format!("JMP {}", self.disw_bump_pc()) }
    fn jmpi(&mut self) -> String          { format!("JMP ({})", self.disw_bump_pc())  }

    // Procedure calls
    fn jsr(&mut self) -> String           { format!("JSR {}", self.disw_bump_pc()) }
    fn rts(&mut self) -> String           { "RTS".to_owned()       }
    fn brk(&mut self) -> String           { "BRK".to_owned()       }
    fn rti(&mut self) -> String           { "RTI".to_owned()       }

    // Stack operations
    fn pha(&mut self) -> String           { "PHA".to_owned()       }
    fn pla(&mut self) -> String           { "PLA".to_owned()       }
    fn php(&mut self) -> String           { "PHP".to_owned()       }
    fn plp(&mut self) -> String           { "PLP".to_owned()       }

    // No operation
    fn nop(&mut self) -> String           { "NOP".to_owned()       }

    // Addressing modes
    fn immediate(&mut self) -> String {
        format!("#{}", self.disb_bump_pc())
    }
    fn accumulator(&mut self) -> String {
        String::new()
    }
    fn zero_page(&mut self) -> String {
        self.disb_bump_pc()
    }
    fn zero_page_x(&mut self) -> String {
        let mut buf = self.disb_bump_pc();
        buf.push_str(",X");
        buf
    }
    fn zero_page_y(&mut self) -> String {
        let mut buf = self.disb_bump_pc();
        buf.push_str(",Y");
        buf
    }
    fn absolute(&mut self) -> String      { self.disw_bump_pc() }
    fn absolute_x(&mut self) -> String {
        let mut buf = self.disw_bump_pc();
        buf.push_str(",X");
        buf
    }
    fn absolute_y(&mut self) -> String {
        let mut buf = self.disw_bump_pc();
        buf.push_str(",Y");
        buf
    }
    fn indexed_indirect_x(&mut self) -> String {
        format!("({},X)", self.disb_bump_pc())
    }
    fn indirect_indexed_y(&mut self) -> String {
        format!("({}),Y", self.disb_bump_pc())
    }
    // Special mode only used in comparison opcodes
    fn relative(&mut self) -> String {
        format!("{:+}", self.loadb_bump_pc() as i8)
    }

    // The main disassembly routine.
    #[inline(never)]
    pub fn disassemble(&mut self) -> String {
        let op = self.loadb_bump_pc();
        decode_op!(op, self)
    }
}
<|endoftext|>


<commit_before>use std::thread::Thread;
use std::{cmp, iter, mem, raw};

// Proxy struct to send raw pointers across task boundaries
struct RawPtr<T>(*const T);

impl<T> Copy for RawPtr<T> {}

unsafe impl<T> Send for RawPtr<T> where T: Send {}

/// Parallelizes an `operation` over a mutable slice
///
/// The `data` will be divided in chunks of `granularity` size. A new thread will be spawned to
/// "operate" over each chunk.
///
/// `operation` will receive two arguments:
///
/// - The mutable chunk of data, and
/// - The offset of this chunk from the start of `data`
///
/// # Panics
///
/// Panics if any of the underlying threads panics
///
/// # Example
///
/// Parallel map
///
/// ```
/// use std::num::FloatMath;
/// use std::rand::{Rng, XorShiftRng, mod};
///
/// let ref mut rng: XorShiftRng = rand::task_rng().gen();
/// let mut v = Vec::from_fn(1_000, |_| rng.gen::<f32>());
/// # let w = v.iter().map(|x| x.sin()).collect::<Vec<_>>();
/// parallel::divide(v.as_mut_slice(), 100, |data, _| {
///     for x in data.iter_mut() {
///         *x = x.sin();
///     }
/// });
/// # assert_eq!(v, w);
/// ```
pub fn divide<T, F>(data: &mut [T], granularity: uint, operation: F) where
    T: Send,
    F: Fn(&mut [T], uint) + Sync,
{
    assert!(granularity > 0);

    let raw::Slice { data, len } = unsafe { mem::transmute::<_, raw::Slice<T>>(data) };
    let data = RawPtr(data);
    let op = RawPtr(&operation as *const _ as *const ());

    let threads = iter::range_step(0, len, granularity).map(|offset| {
        Thread::spawn(move || {
            // NB Is safe to send the slice/closure because the thread won't outlive this function
            let slice = raw::Slice {
                data: unsafe { data.0.offset(offset as int) },
                len: cmp::min(granularity, len - offset)
            };
            let data = unsafe { mem::transmute::<_, &mut [T]>(slice) };
            let operation = unsafe { mem::transmute::<_, &F>(op.0) };

            (*operation)(data, offset);
        })
    }).collect::<Vec<_>>();

    for thread in threads.into_iter() {
        if thread.join().is_err() {
            panic!();
        }
    }
}

#[cfg(test)]
mod test {
    use quickcheck::TestResult;
    use std::rand::{Rng, XorShiftRng, mod};

    #[quickcheck]
    fn clone(size: uint, granularity: uint) -> TestResult {
        if granularity == 0 {
            return TestResult::discard();
        }

        let mut rng: XorShiftRng = rand::task_rng().gen();
        let original = Vec::from_fn(size, |_| rng.gen::<f64>());
        let mut clone = Vec::from_elem(size, 0f64);

        let original_slice = original[];
        super::divide(&mut *clone, granularity, |data, offset| {
            for (i, x) in data.iter_mut().enumerate() {
                *x = original_slice[offset + i]
            }
        });

        TestResult::from_bool(original == clone)
    }

    #[quickcheck]
    fn new(size: uint, granularity: uint) -> TestResult {
        if granularity == 0 {
            return TestResult::discard();
        }

        let mut v = Vec::from_elem(size, None::<f64>);

        super::divide(&mut *v, granularity, |data, _| {
            let mut rng: XorShiftRng = rand::task_rng().gen();

            for x in data.iter_mut() {
                *x = Some(rng.gen())
            }
        });

        TestResult::from_bool(v.iter().all(|&x| match x {
            None => false,
            Some(x) => x > 0. && x < 1.
        }))
    }
}
<commit_msg>fix(tests): `Vec::from_elem/from_fn` has been deprecated<commit_after>use std::thread::Thread;
use std::{cmp, iter, mem, raw};

// Proxy struct to send raw pointers across task boundaries
struct RawPtr<T>(*const T);

impl<T> Copy for RawPtr<T> {}

unsafe impl<T> Send for RawPtr<T> where T: Send {}

/// Parallelizes an `operation` over a mutable slice
///
/// The `data` will be divided in chunks of `granularity` size. A new thread will be spawned to
/// "operate" over each chunk.
///
/// `operation` will receive two arguments:
///
/// - The mutable chunk of data, and
/// - The offset of this chunk from the start of `data`
///
/// # Panics
///
/// Panics if any of the underlying threads panics
///
/// # Example
///
/// Parallel map
///
/// ```
/// use std::num::FloatMath;
/// use std::rand::{Rng, XorShiftRng, mod};
///
/// let ref mut rng: XorShiftRng = rand::thread_rng().gen();
/// let mut v = range(0, 1_000u).map(|_| rng.gen::<f32>()).collect::<Vec<_>>();
/// # let w = v.iter().map(|x| x.sin()).collect::<Vec<_>>();
/// parallel::divide(v.as_mut_slice(), 100, |data, _| {
///     for x in data.iter_mut() {
///         *x = x.sin();
///     }
/// });
/// # assert_eq!(v, w);
/// ```
pub fn divide<T, F>(data: &mut [T], granularity: uint, operation: F) where
    T: Send,
    F: Fn(&mut [T], uint) + Sync,
{
    assert!(granularity > 0);

    let raw::Slice { data, len } = unsafe { mem::transmute::<_, raw::Slice<T>>(data) };
    let data = RawPtr(data);
    let op = RawPtr(&operation as *const _ as *const ());

    let threads = iter::range_step(0, len, granularity).map(|offset| {
        Thread::spawn(move || {
            // NB Is safe to send the slice/closure because the thread won't outlive this function
            let slice = raw::Slice {
                data: unsafe { data.0.offset(offset as int) },
                len: cmp::min(granularity, len - offset)
            };
            let data = unsafe { mem::transmute::<_, &mut [T]>(slice) };
            let operation = unsafe { mem::transmute::<_, &F>(op.0) };

            (*operation)(data, offset);
        })
    }).collect::<Vec<_>>();

    for thread in threads.into_iter() {
        if thread.join().is_err() {
            panic!();
        }
    }
}

#[cfg(test)]
mod test {
    use quickcheck::TestResult;
    use std::iter;
    use std::rand::{Rng, XorShiftRng, mod};

    #[quickcheck]
    fn clone(size: uint, granularity: uint) -> TestResult {
        if granularity == 0 {
            return TestResult::discard();
        }

        let mut rng: XorShiftRng = rand::thread_rng().gen();
        let original = range(0, size).map(|_| rng.gen::<f64>()).collect::<Vec<_>>();
        let mut clone = iter::repeat(0f64).take(size).collect::<Vec<_>>();

        let original_slice = &*original;
        super::divide(&mut *clone, granularity, |data, offset| {
            for (i, x) in data.iter_mut().enumerate() {
                *x = original_slice[offset + i]
            }
        });

        TestResult::from_bool(original == clone)
    }

    #[quickcheck]
    fn new(size: uint, granularity: uint) -> TestResult {
        if granularity == 0 {
            return TestResult::discard();
        }

        let mut v = iter::repeat(None::<f64>).take(size).collect::<Vec<_>>();

        super::divide(&mut *v, granularity, |data, _| {
            let mut rng: XorShiftRng = rand::thread_rng().gen();

            for x in data.iter_mut() {
                *x = Some(rng.gen())
            }
        });

        TestResult::from_bool(v.iter().all(|&x| match x {
            None => false,
            Some(x) => x > 0. && x < 1.
        }))
    }
}
<|endoftext|>