<commit_before>use regex::Regex;
use chrono::NaiveDate;
use reqwest;
use std::io::Read;
use select::document::Document;
use select::predicate::{Name, Attr};
use rustc_serialize::json::Json;
use errors::*;

// Some formats borrowed from https://github.com/amir/article-date-extractor
static FMTS: &'static [&str] = &["%A, %B %e, %Y", "%Y-%m-%dT%H:%M:%S%:z", "/%Y/%m/%d/", "/%Y/%d/%m/", "%Y-%m-%d", "%B %e, %Y", "%Y-%m-%d %H:%M:%S", "%Y-%m-%dT%H:%M:%SZ", "%B %k, %Y, %H:%M %p", "%Y-%m-%d %H:%M:%S.000000"];

// Use lazy_static to ensure we only compile the regex once
lazy_static! {
    // Regex by Newspaper3k  - https://github.com/codelucas/newspaper/blob/master/newspaper/urls.py
    static ref RE: Regex = Regex::new(r"([\./\-_]{0,1}(19|20)\d{2})[\./\-_]{0,1}(([0-3]{0,1}[0-9][\./\-_])|(\w{3,5}[\./\-_]))([0-3]{0,1}[0-9][\./\-]{0,1})").unwrap();
}

// Parse the date, trying out each format
fn parse_date(input: &str) -> Result<NaiveDate> {
    let mut result: Result<NaiveDate> = Err("None of the formats matched the date".into());

    'outer: for fmt in FMTS {
        if let Ok(v) = NaiveDate::parse_from_str(input, fmt) {
            { result = Ok(v);
              break 'outer; }
        }
    }

    result
}

// Extract date from a URL
fn extract_from_url(url: &str) -> Option<String> {
    let mut date: Option<String> = None;

    if let Some(val) = RE.find(url) {
        date = Some(val.as_str().to_string());
    }

    date
}

// Extract date from JSON-LD
fn extract_from_ldjson<'a>(html: &'a Document) -> Option<String> {
    let mut json_date: Option<String> = None;
    let mut ldjson = String::new();
    if let Some(ldj) = html.find(Attr("type", "application/ld+json")).next() {
        ldjson = ldj.text();
    }

    let decoded_ldjson = Json::from_str(ldjson.as_str()).unwrap();

    if let Some(date_published) = decoded_ldjson.search("datePublished") {
        if let Some(date) = date_published.as_string() {
            json_date = Some(date.to_string())
        }
    } else if let Some(date_created) = decoded_ldjson.search("dateCreated") {
        if let Some(date) = date_created.as_string() {
            json_date = Some(date.to_string())
        }
    }

    json_date
}

// Extract date from meta tags
fn extract_from_meta<'a>(html: &'a Document) -> Option<String> {
    let mut meta_date: Option<String> = None;

    'outer: for meta in html.find(Name("meta")) {
        let meta_name     = meta.attr("name").unwrap_or("").to_lowercase();
        let item_prop     = meta.attr("itemprop").unwrap_or("").to_lowercase();
        let http_equiv    = meta.attr("http-equiv").unwrap_or("").to_lowercase();
        let meta_property = meta.attr("property").unwrap_or("").to_lowercase();

        match meta_name.as_ref() {
            "pubdate"               | "publishdate"                  | "timestamp"       |
            "dc.date.issued"        | "date"                         | "sailthru.date"   |
            "article.published"     | "published-date"               | "article.created" |
            "article_date_original" | "cxenseparse:recs:publishtime" | "date_published"  => { if let Some(ct) = meta.attr("content") {
                                                                                meta_date = Some(ct.trim().to_string())
                                                                                }
                                                                                break 'outer; },
            _ => {},
        }

        match item_prop.as_ref() {
            "datepublished" | "datecreated" => { if let Some(ct) = meta.attr("content") {
                                                   meta_date = Some(ct.trim().to_string())
                                                 }
                                                 break 'outer; },
            _ => {},
        }

        match http_equiv.as_ref() {
            "date" =>  { if let Some(ct) = meta.attr("content") {
                           meta_date = Some(ct.trim().to_string())
                         }
                         break 'outer; },
            _ => {},
        }

        match meta_property.as_ref() {
            "article:published_time" | "bt:pubdate" => { if let Some(ct) = meta.attr("content") {
                                                           meta_date = Some(ct.trim().to_string())
                                                         }
                                                         break 'outer; },
            "og:image"                              => { if let Some(url) = meta.attr("content") {
                                                           meta_date = extract_from_url(url.trim())
                                                         }
                                                         break 'outer; },

            _ => {},
        }

    }

    meta_date
}

// Extract from html tags
fn extract_from_html_tag<'a>(html: &'a Document) -> Option<String> {
    let mut date: Option<String> = None;

    for time in html.find(Name("time")) {
        if let Some(dt) = time.attr("datetime") {
            date = Some(dt.to_string())
        } else if let Some("timestamp") = time.attr("class") {
            date = Some(time.text())
        }
    }

    if date.is_none() {
        for tag in html.find(Name("meta")) {
            if let Some("datePublished") = tag.attr("itemprop") {
                if let Some(v) = tag.attr("content") {
                    date = Some(v.to_string())
                } else {
                    date = Some(tag.text())
                }
            }
        }
    }

    date
}

// Try to extract the date by using each function one by one
pub fn extract_article_published_date(link: &str, html: Option<String>) -> Result<NaiveDate> {
    let mut body: String = String::new();
    let mut parsed_body: Option<Document> = None;

    if let Some(v) = extract_from_url(link) {
        return parse_date(v.as_str())
    }

    if html.is_none() {
        if let Ok(mut response) = reqwest::get(link) {
            response.read_to_string(&mut body).unwrap();
            let doc = Document::from(body.as_str());
            parsed_body = Some(doc);
        } else {
            return Err("Couldn't open the link".into())
        }
    } else {
        parsed_body = Some(Document::from(html.unwrap().as_str()))
    }

    if let Some(v) = extract_from_url(link) {
        return parse_date(v.as_str())
    } else if let Some(v) = extract_from_ldjson(parsed_body.as_ref().unwrap()) {
        return parse_date(v.as_str())
    } else if let Some(v) = extract_from_meta(parsed_body.as_ref().unwrap()) {
        return parse_date(v.as_str())
    } else if let Some(v) = extract_from_html_tag(parsed_body.as_ref().unwrap()) {
        return parse_date(v.as_str())
    } else {
        return Err("Couldn't find the date to parse".into())
    }
}

// Unit tests
#[cfg(test)]
mod test {
    use super::extract_from_url;
    use super::parse_date;
    use super::extract_from_meta;
    use super::extract_from_ldjson;
    use super::extract_from_html_tag;
    use super::extract_article_published_date;
    use chrono::NaiveDate;
    use reqwest;
    use std::string::String;
    use std::io::Read;
    use select::document::Document;

    #[test]
    fn parsing_date() {
        assert_eq!(NaiveDate::from_ymd(2015,11,30), parse_date("/2015/11/30/").unwrap());
        assert_eq!(NaiveDate::from_ymd(2015,11,30), parse_date("/2015/30/11/").unwrap());

        assert!(parse_date("bad_format").is_err());
    }

    #[test]
    fn extracting_from_url() {
        let link = "http://edition.cnn.com/2015/11/28/opinions/sutter-cop21-paris-preview-two-degrees/index.html";
        assert_eq!(Some("/2015/11/28/".to_string()), extract_from_url(link));

        let link = "";
        assert_eq!(None, extract_from_url(link));
    }

    #[test]
    fn extracting_from_meta() {
        let mut response = reqwest::get("https://techcrunch.com/2015/11/30/atlassian-share-price/").unwrap();
        let mut body = String::new();
        response.read_to_string(&mut body).unwrap();
        let document = Document::from(body.as_str());

        assert_eq!(Some(("2015-11-30 23:50:48".to_string())), extract_from_meta(&document));
    }

    #[test]
    fn extracting_from_ldjson() {
        let mut response = reqwest::get("https://techcrunch.com/2015/11/30/atlassian-share-price/").unwrap();
        let mut body = String::new();
        response.read_to_string(&mut body).unwrap();
        let document = Document::from(body.as_str());

        assert_eq!(Some("2015-12-01T07:50:48Z".to_string()), extract_from_ldjson(&document));
    }

    #[test]
    fn extracting_from_html_tag() {
        let mut response = reqwest::get("http://edition.cnn.com/2015/11/28/opinions/sutter-cop21-paris-preview-two-degrees/index.html").unwrap();
        let mut body = String::new();
        response.read_to_string(&mut body).unwrap();
        let document = Document::from(body.as_str());

        assert_eq!(Some("2015-11-29T00:44:59Z".to_string()), extract_from_html_tag(&document));
    }

    #[test]
    fn extracting_article_published_date() {
        let link = "http://edition.cnn.com/2015/11/28/opinions/sutter-cop21-paris-preview-two-degrees/index.html";
        let mut response = reqwest::get("http://edition.cnn.com/2015/11/28/opinions/sutter-cop21-paris-preview-two-degrees/index.html").unwrap();
        let mut body = String::new();
        response.read_to_string(&mut body).unwrap();

        assert_eq!(NaiveDate::from_ymd(2015,11,28), extract_article_published_date(&link, None).unwrap());
        assert_eq!(NaiveDate::from_ymd(2015,11,28), extract_article_published_date(&link, Some(body)).unwrap());

        assert!((extract_article_published_date("", None)).is_err());
    }
}
<commit_msg>Add early return<commit_after>use regex::Regex;
use chrono::NaiveDate;
use reqwest;
use std::io::Read;
use select::document::Document;
use select::predicate::{Name, Attr};
use rustc_serialize::json::Json;
use errors::*;

// Some formats borrowed from https://github.com/amir/article-date-extractor
static FMTS: &'static [&str] = &["%A, %B %e, %Y", "%Y-%m-%dT%H:%M:%S%:z", "/%Y/%m/%d/", "/%Y/%d/%m/", "%Y-%m-%d", "%B %e, %Y", "%Y-%m-%d %H:%M:%S", "%Y-%m-%dT%H:%M:%SZ", "%B %k, %Y, %H:%M %p", "%Y-%m-%d %H:%M:%S.000000"];

// Use lazy_static to ensure we only compile the regex once
lazy_static! {
    // Regex by Newspaper3k  - https://github.com/codelucas/newspaper/blob/master/newspaper/urls.py
    static ref RE: Regex = Regex::new(r"([\./\-_]{0,1}(19|20)\d{2})[\./\-_]{0,1}(([0-3]{0,1}[0-9][\./\-_])|(\w{3,5}[\./\-_]))([0-3]{0,1}[0-9][\./\-]{0,1})").unwrap();
}

// Parse the date, trying out each format
fn parse_date(input: &str) -> Result<NaiveDate> {
    let mut result: Result<NaiveDate> = Err("None of the formats matched the date".into());

    'outer: for fmt in FMTS {
        if let Ok(v) = NaiveDate::parse_from_str(input, fmt) {
            { result = Ok(v);
              break 'outer; }
        }
    }

    result
}

// Extract date from a URL
fn extract_from_url(url: &str) -> Option<String> {
    if let Some(val) = RE.find(url) {
        return Some(val.as_str().to_string())
    } else {
        return None
    }
}

// Extract date from JSON-LD
fn extract_from_ldjson<'a>(html: &'a Document) -> Option<String> {
    let mut json_date: Option<String> = None;
    let mut ldjson = String::new();
    if let Some(ldj) = html.find(Attr("type", "application/ld+json")).next() {
        ldjson = ldj.text();
    }

    let decoded_ldjson = Json::from_str(ldjson.as_str()).unwrap();

    if let Some(date_published) = decoded_ldjson.search("datePublished") {
        if let Some(date) = date_published.as_string() {
            json_date = Some(date.to_string())
        }
    } else if let Some(date_created) = decoded_ldjson.search("dateCreated") {
        if let Some(date) = date_created.as_string() {
            json_date = Some(date.to_string())
        }
    }

    json_date
}

// Extract date from meta tags
fn extract_from_meta<'a>(html: &'a Document) -> Option<String> {
    let mut meta_date: Option<String> = None;

    'outer: for meta in html.find(Name("meta")) {
        let meta_name     = meta.attr("name").unwrap_or("").to_lowercase();
        let item_prop     = meta.attr("itemprop").unwrap_or("").to_lowercase();
        let http_equiv    = meta.attr("http-equiv").unwrap_or("").to_lowercase();
        let meta_property = meta.attr("property").unwrap_or("").to_lowercase();

        match meta_name.as_ref() {
            "pubdate"               | "publishdate"                  | "timestamp"       |
            "dc.date.issued"        | "date"                         | "sailthru.date"   |
            "article.published"     | "published-date"               | "article.created" |
            "article_date_original" | "cxenseparse:recs:publishtime" | "date_published"  => { if let Some(ct) = meta.attr("content") {
                                                                                meta_date = Some(ct.trim().to_string())
                                                                                }
                                                                                break 'outer; },
            _ => {},
        }

        match item_prop.as_ref() {
            "datepublished" | "datecreated" => { if let Some(ct) = meta.attr("content") {
                                                   meta_date = Some(ct.trim().to_string())
                                                 }
                                                 break 'outer; },
            _ => {},
        }

        match http_equiv.as_ref() {
            "date" =>  { if let Some(ct) = meta.attr("content") {
                           meta_date = Some(ct.trim().to_string())
                         }
                         break 'outer; },
            _ => {},
        }

        match meta_property.as_ref() {
            "article:published_time" | "bt:pubdate" => { if let Some(ct) = meta.attr("content") {
                                                           meta_date = Some(ct.trim().to_string())
                                                         }
                                                         break 'outer; },
            "og:image"                              => { if let Some(url) = meta.attr("content") {
                                                           meta_date = extract_from_url(url.trim())
                                                         }
                                                         break 'outer; },

            _ => {},
        }

    }

    meta_date
}

// Extract from html tags
fn extract_from_html_tag<'a>(html: &'a Document) -> Option<String> {
    let mut date: Option<String> = None;

    for time in html.find(Name("time")) {
        if let Some(dt) = time.attr("datetime") {
            date = Some(dt.to_string())
        } else if let Some("timestamp") = time.attr("class") {
            date = Some(time.text())
        }
    }

    if date.is_none() {
        for tag in html.find(Name("meta")) {
            if let Some("datePublished") = tag.attr("itemprop") {
                if let Some(v) = tag.attr("content") {
                    date = Some(v.to_string())
                } else {
                    date = Some(tag.text())
                }
            }
        }
    }

    date
}

// Try to extract the date by using each function one by one
pub fn extract_article_published_date(link: &str, html: Option<String>) -> Result<NaiveDate> {
    let mut body: String = String::new();
    let mut parsed_body: Option<Document> = None;

    if let Some(v) = extract_from_url(link) {
        return parse_date(v.as_str())
    }

    if html.is_none() {
        if let Ok(mut response) = reqwest::get(link) {
            response.read_to_string(&mut body).unwrap();
            let doc = Document::from(body.as_str());
            parsed_body = Some(doc);
        } else {
            return Err("Couldn't open the link".into())
        }
    } else {
        parsed_body = Some(Document::from(html.unwrap().as_str()))
    }

    if let Some(v) = extract_from_url(link) {
        return parse_date(v.as_str())
    } else if let Some(v) = extract_from_ldjson(parsed_body.as_ref().unwrap()) {
        return parse_date(v.as_str())
    } else if let Some(v) = extract_from_meta(parsed_body.as_ref().unwrap()) {
        return parse_date(v.as_str())
    } else if let Some(v) = extract_from_html_tag(parsed_body.as_ref().unwrap()) {
        return parse_date(v.as_str())
    } else {
        return Err("Couldn't find the date to parse".into())
    }
}

// Unit tests
#[cfg(test)]
mod test {
    use super::extract_from_url;
    use super::parse_date;
    use super::extract_from_meta;
    use super::extract_from_ldjson;
    use super::extract_from_html_tag;
    use super::extract_article_published_date;
    use chrono::NaiveDate;
    use reqwest;
    use std::string::String;
    use std::io::Read;
    use select::document::Document;

    #[test]
    fn parsing_date() {
        assert_eq!(NaiveDate::from_ymd(2015,11,30), parse_date("/2015/11/30/").unwrap());
        assert_eq!(NaiveDate::from_ymd(2015,11,30), parse_date("/2015/30/11/").unwrap());

        assert!(parse_date("bad_format").is_err());
    }

    #[test]
    fn extracting_from_url() {
        let link = "http://edition.cnn.com/2015/11/28/opinions/sutter-cop21-paris-preview-two-degrees/index.html";
        assert_eq!(Some("/2015/11/28/".to_string()), extract_from_url(link));

        let link = "";
        assert_eq!(None, extract_from_url(link));
    }

    #[test]
    fn extracting_from_meta() {
        let mut response = reqwest::get("https://techcrunch.com/2015/11/30/atlassian-share-price/").unwrap();
        let mut body = String::new();
        response.read_to_string(&mut body).unwrap();
        let document = Document::from(body.as_str());

        assert_eq!(Some(("2015-11-30 23:50:48".to_string())), extract_from_meta(&document));
    }

    #[test]
    fn extracting_from_ldjson() {
        let mut response = reqwest::get("https://techcrunch.com/2015/11/30/atlassian-share-price/").unwrap();
        let mut body = String::new();
        response.read_to_string(&mut body).unwrap();
        let document = Document::from(body.as_str());

        assert_eq!(Some("2015-12-01T07:50:48Z".to_string()), extract_from_ldjson(&document));
    }

    #[test]
    fn extracting_from_html_tag() {
        let mut response = reqwest::get("http://edition.cnn.com/2015/11/28/opinions/sutter-cop21-paris-preview-two-degrees/index.html").unwrap();
        let mut body = String::new();
        response.read_to_string(&mut body).unwrap();
        let document = Document::from(body.as_str());

        assert_eq!(Some("2015-11-29T00:44:59Z".to_string()), extract_from_html_tag(&document));
    }

    #[test]
    fn extracting_article_published_date() {
        let link = "http://edition.cnn.com/2015/11/28/opinions/sutter-cop21-paris-preview-two-degrees/index.html";
        let mut response = reqwest::get("http://edition.cnn.com/2015/11/28/opinions/sutter-cop21-paris-preview-two-degrees/index.html").unwrap();
        let mut body = String::new();
        response.read_to_string(&mut body).unwrap();

        assert_eq!(NaiveDate::from_ymd(2015,11,28), extract_article_published_date(&link, None).unwrap());
        assert_eq!(NaiveDate::from_ymd(2015,11,28), extract_article_published_date(&link, Some(body)).unwrap());

        assert!((extract_article_published_date("", None)).is_err());
    }
}
<|endoftext|>


<commit_before>pub trait TileResolver {
    type
}
<commit_msg>Removed unused file<commit_after><|endoftext|>


<commit_before>extern crate cards;
extern crate libthyme;
extern crate ncurses;

use super::{Action,UI};
use libthyme::board::{Position,HPosition,VPosition};
use libthyme::game::Game;
use libthyme::score::Scorer;
use std::cmp;

const CARD_WIDTH: i32 = 7;
const CARD_HEIGHT: i32 = 5;
const CARD_MARGIN: i32 = 2;
const BOARD_MARGIN: i32 = 2;
const STATUS_HEIGHT: i32 = 2;

const CARD_COLOR_BLACK: i16 = 1; // black on white
const CARD_COLOR_RED: i16 = 2; // red on white
const CARD_COLOR_EMPTY: i16 = 4; // white on black
const SELECTED_COLOR: i16 = 5; // yellow on black
const CURSOR_INFO_COLOR: i16 = 6; // cyan on black
const GAME_INFO_COLOR: i16 = 7; // green on black
const BG_COLOR: i16 = ncurses::COLOR_BLACK;
const CARD_BG_COLOR: i16 = ncurses::COLOR_WHITE;

/// Set up the UI
pub fn initialize_screen() {
    ncurses::setlocale(ncurses::LcCategory::ctype, "");
    ncurses::initscr();
    ncurses::noecho();
    ncurses::start_color();
    ncurses::keypad(ncurses::stdscr, true);
    ncurses::curs_set(ncurses::CURSOR_VISIBILITY::CURSOR_INVISIBLE);
    ncurses::init_pair(CARD_COLOR_BLACK, ncurses::COLOR_BLACK, CARD_BG_COLOR);
    ncurses::init_pair(CARD_COLOR_RED, ncurses::COLOR_RED, CARD_BG_COLOR);
    ncurses::init_pair(CARD_COLOR_EMPTY, ncurses::COLOR_WHITE, BG_COLOR);
    ncurses::init_pair(SELECTED_COLOR, ncurses::COLOR_YELLOW, BG_COLOR);
    ncurses::init_pair(CURSOR_INFO_COLOR, ncurses::COLOR_CYAN, BG_COLOR);
    ncurses::init_pair(GAME_INFO_COLOR, ncurses::COLOR_GREEN, BG_COLOR);
}

/// Redraw a UI in the current screen
pub fn redraw<T: Scorer>(ui: &UI, game: &mut Game<T>, refresh: bool) {
    if refresh {
        ncurses::clear();
        ncurses::bkgd(ncurses::COLOR_PAIR(CARD_COLOR_EMPTY));
    }
    if validate_screen_size() {
        write_title(game);
        draw_cards(ui, game);
        write_cursor_message(ui, game);
        write_message(&ui.message);
    }
    ncurses::refresh();
}

/// Tear down the UI
pub fn cleanup() {
    ncurses::endwin();
}

/// Process input from the user
///
/// Known inputs:
/// - Arrow keys/WASD: Move cursor between various positions on the board
/// - Q: Quit game
/// - H: Hint (unimplemented)
/// - ?: Help (unimplemented)
/// - Space: Toggle position selection
/// - Return: Play move, clear selection
pub fn get_action() -> Action {
    return match ncurses::getch() {
        ncurses::KEY_LEFT | 97 => Action::CursorLeft,
        ncurses::KEY_RIGHT | 100 => Action::CursorRight,
        ncurses::KEY_UP | 119 => Action::CursorUp,
        ncurses::KEY_DOWN | 115 => Action::CursorDown,
        ncurses::KEY_RESIZE => Action::Resize,
        ncurses::KEY_ENTER | 13 | 10 => Action::Play,
        32 =>  Action::ToggleSelection, // Space
        113 => Action::Quit, // Q
        104 => Action::Hint, // H
        63 =>  Action::Help, // ?
        _ =>   Action::Unknown
    }
}

/// Check that the content can fit
fn validate_screen_size() -> bool {
    let min_height = BOARD_MARGIN*2 + CARD_MARGIN*4 + STATUS_HEIGHT + CARD_HEIGHT*3;
    if ncurses::LINES < min_height || ncurses::COLS < 50 {
        write_message(&format!(
                "Please resize your terminal to be at least 50x{}",
                min_height));
        return false
    }
    return true
}

/// Print the game title and status info
fn write_title<T: Scorer>(game: &mut Game<T>) {
    printw_margin(0, 0);
    ncurses::attron(ncurses::A_BOLD());
    ncurses::printw("Thyme");
    ncurses::attroff(ncurses::A_BOLD());
    ncurses::printw(&format!(" - Score: {}", game.score()));
    printw_margin(0, 1);
    let (_, suit) = layout_suit(game.board.lucky_card);
    let info = ncurses::COLOR_PAIR(GAME_INFO_COLOR);
    ncurses::attron(info);
    ncurses::printw(&format!("Lucky Suit: {}  Discards Left: {}/{}", suit,
                             game.discards_allowed, game.discards_allowed_max));
    ncurses::clrtoeol();
    ncurses::attroff(info);
}

/// Print the message at the bottom of the window
fn write_message(message: &str) {
    printw_margin(0, ncurses::LINES - 1);
    ncurses::printw(message);
    ncurses::clrtoeol();
}

/// Print a message describing the stack above the cursor
fn write_cursor_message<T: Scorer>(ui: &UI, game: &Game<T>) {
    let stacked_cards = game.board.count_cards(ui.cursor_position);
    let all_cards = game.board.count_all_cards();
    let message = format!("*{}/{} cards in the stack", stacked_cards, all_cards);
    let color = ncurses::COLOR_PAIR(CURSOR_INFO_COLOR);
    printw_margin(0, ncurses::LINES - 2);
    ncurses::attron(color);
    ncurses::printw(&message);
    ncurses::clrtoeol();
    ncurses::attroff(color);
}

/// Print spaces for the width of the board margin
fn printw_margin(x: i32, y: i32) {
    ncurses::mv(y, x);
    printw_repeat(" ", BOARD_MARGIN, ncurses::COLOR_PAIR(CARD_COLOR_EMPTY));
}

/// Print the card values and empty stacks
fn draw_cards<T: Scorer>(ui: &UI, game: &mut Game<T>) {
    for position in game.board.positions() {
        let card = game.board.top(position);
        if card.is_some() {
            draw_card(position, card.unwrap());
        } else {
            draw_empty(game, position);
        }
        let (x, y) = card_location(position);
        toggle_highlight_card(x, y, ui.selection.contains(&position));
        if position == ui.cursor_position {
            let offset = (CARD_WIDTH as f32 /2.0).floor() as i32;
            ncurses::mvprintw(y + CARD_HEIGHT, x + offset, "*");
        }
    }
}

/// Draw a card on the board at a position
fn draw_card(position: Position, card: cards::card::Card) {
    let (x, y) = card_location(position);
    let (color, suit) = layout_suit(card);
    let value = layout_value(card);
    let black = ncurses::COLOR_PAIR(CARD_COLOR_BLACK);
    ncurses::attron(black);
    ncurses::mvprintw(y, x, &value);
    ncurses::attroff(black);
    ncurses::attron(color);
    ncurses::mvprintw(y, x + value.len() as i32, &suit);
    ncurses::attroff(color);
    let spacing = CARD_WIDTH - value.len() as i32 - 1;
    printw_repeat(" ", spacing, black);
    for i in 1..CARD_HEIGHT - 1 {
        ncurses::mv(y + i, x);
        printw_repeat(" ", CARD_WIDTH, black);
    }
    ncurses::mv(y + CARD_HEIGHT - 1, x);
    printw_repeat(" ", spacing, black);
    ncurses::attron(black);
    ncurses::printw(&value);
    ncurses::attroff(black);
    ncurses::attron(color);
    ncurses::printw(&suit);
    ncurses::attroff(color);
}

/// Draw empty slot for a card
fn draw_empty<T: Scorer>(game: &Game<T>, position: Position) {
    let color = ncurses::COLOR_PAIR(CARD_COLOR_EMPTY);
    let (x, y) = card_location(position);
    ncurses::attron(color);
    ncurses::mvprintw(y, x, "┌");
    printw_repeat("─", CARD_WIDTH - 2, color);
    ncurses::mvprintw(y, x + CARD_WIDTH - 1, "┐");
    ncurses::attroff(color);
    let gap_height = CARD_HEIGHT - 1;
    let bonus_height = gap_height/2;
    for i in 1..gap_height {
        ncurses::attron(color);
        ncurses::mvprintw(y + i, x , "│");
        if i == bonus_height {
            let bonus = format!("+{}", game.scorer.bonus(position));
            let available_width = cmp::max(0, CARD_WIDTH - 2 - bonus.len() as i32);
            let lede = available_width/2;
            printw_repeat(" ", lede, color);
            ncurses::printw(&bonus);
            printw_repeat(" ", available_width - lede, color);
        } else {
            printw_repeat(" ", CARD_WIDTH - 2, color);
        }
        ncurses::attroff(color);
        ncurses::attron(color);
        ncurses::mvprintw(y + i, x + CARD_WIDTH - 1, "│");
        ncurses::attroff(color);
    }
    ncurses::attron(color);
    ncurses::mvprintw(y + CARD_HEIGHT - 1, x, "└");
    printw_repeat("─", CARD_WIDTH - 2, color);
    ncurses::mvprintw(y + CARD_HEIGHT - 1, x + CARD_WIDTH - 1, "┘");
    ncurses::attroff(color);
}

/// Print a string repeatedly to fill a length
fn printw_repeat(content: &str, len: i32, color: u64) {
    ncurses::attron(color);
    for _ in 0..len {
       ncurses::printw(content);
    }
    ncurses::attroff(color);
}

/// Print or clear the border around a card
fn toggle_highlight_card(x: i32, y: i32, on: bool) {
    let color = ncurses::COLOR_PAIR(SELECTED_COLOR);
    ncurses::attron(color);
    ncurses::mvprintw(y - 1, x - 1, if on {"┌"} else {" "});
    ncurses::attroff(color);
    ncurses::mv(y - 1, x);
    printw_repeat(if on {"─"} else {" "}, CARD_WIDTH, color);
    ncurses::mv(y + CARD_HEIGHT, x);
    printw_repeat(if on {"─"} else {" "}, CARD_WIDTH, color);
    ncurses::attron(color);
    ncurses::mvprintw(y - 1, x + CARD_WIDTH, if on {"┐"} else {" "});
    for i in 0..CARD_HEIGHT {
        ncurses::mvprintw(y + i, x - 1, if on {"│"} else {" "});
        ncurses::mvprintw(y + i, x + CARD_WIDTH, if on {"│"} else {" "});
    }
    ncurses::mvprintw(y + CARD_HEIGHT, x - 1, if on {"└"} else {" "});
    ncurses::mvprintw(y + CARD_HEIGHT, x + CARD_WIDTH, if on {"┘"} else {" "});
    ncurses::attroff(color);
}

/// Printed representation of a card's suit
fn layout_suit(card: cards::card::Card) -> (u64, String) {
    let black = ncurses::COLOR_PAIR(CARD_COLOR_BLACK);
    let red = ncurses::COLOR_PAIR(CARD_COLOR_RED);
    return match card.suit {
        cards::card::Suit::Diamonds => (red, "\u{2666}".to_string()),
        cards::card::Suit::Clubs => (black, "\u{2663}".to_string()),
        cards::card::Suit::Spades => (black, "\u{2660}".to_string()),
        cards::card::Suit::Hearts => (red, "\u{2665}".to_string()),
    }
}

/// Printed representation of a card's value
fn layout_value(card: cards::card::Card) -> String {
    return match card.value {
        cards::card::Value::Ace => "A",
        cards::card::Value::Two => "2",
        cards::card::Value::Three => "3",
        cards::card::Value::Four => "4",
        cards::card::Value::Five => "5",
        cards::card::Value::Six => "6",
        cards::card::Value::Seven => "7",
        cards::card::Value::Eight => "8",
        cards::card::Value::Nine => "9",
        cards::card::Value::Ten => "10",
        cards::card::Value::Jack => "J",
        cards::card::Value::Queen => "Q",
        cards::card::Value::King => "K",
    }.to_string()
}

/// Location (x, y) for a card position
fn card_location(position: Position) -> (i32, i32) {
    let left = BOARD_MARGIN + CARD_MARGIN;
    let center = BOARD_MARGIN + CARD_MARGIN*2 + CARD_WIDTH;
    let right = BOARD_MARGIN + CARD_MARGIN*3 + CARD_WIDTH*2;
    let top = BOARD_MARGIN + CARD_MARGIN;
    let middle = BOARD_MARGIN + CARD_MARGIN*2 + CARD_HEIGHT;
    let bottom = BOARD_MARGIN + CARD_MARGIN*3 + CARD_HEIGHT*2;
    match (position.x, position.y) {
        (HPosition::Left, VPosition::Top) => (left, top),
        (HPosition::Left, VPosition::Middle) => (left, middle),
        (HPosition::Left, VPosition::Bottom) => (left, bottom),
        (HPosition::Center, VPosition::Top) => (center, top),
        (HPosition::Center, VPosition::Middle) => (center, middle),
        (HPosition::Center, VPosition::Bottom) => (center, bottom),
        (HPosition::Right, VPosition::Top) => (right, top),
        (HPosition::Right, VPosition::Middle) => (right, middle),
        (HPosition::Right, VPosition::Bottom) => (right, bottom),
    }
}
<commit_msg>Genericise char size<commit_after>extern crate cards;
extern crate libthyme;
extern crate ncurses;

use super::{Action,UI};
use libthyme::board::{Position,HPosition,VPosition};
use libthyme::game::Game;
use libthyme::score::Scorer;
use std::cmp;

const CARD_WIDTH: i32 = 7;
const CARD_HEIGHT: i32 = 5;
const CARD_MARGIN: i32 = 2;
const BOARD_MARGIN: i32 = 2;
const STATUS_HEIGHT: i32 = 2;

const CARD_COLOR_BLACK: i16 = 1; // black on white
const CARD_COLOR_RED: i16 = 2; // red on white
const CARD_COLOR_EMPTY: i16 = 4; // white on black
const SELECTED_COLOR: i16 = 5; // yellow on black
const CURSOR_INFO_COLOR: i16 = 6; // cyan on black
const GAME_INFO_COLOR: i16 = 7; // green on black
const BG_COLOR: i16 = ncurses::COLOR_BLACK;
const CARD_BG_COLOR: i16 = ncurses::COLOR_WHITE;

/// Set up the UI
pub fn initialize_screen() {
    ncurses::setlocale(ncurses::LcCategory::ctype, "");
    ncurses::initscr();
    ncurses::noecho();
    ncurses::start_color();
    ncurses::keypad(ncurses::stdscr, true);
    ncurses::curs_set(ncurses::CURSOR_VISIBILITY::CURSOR_INVISIBLE);
    ncurses::init_pair(CARD_COLOR_BLACK, ncurses::COLOR_BLACK, CARD_BG_COLOR);
    ncurses::init_pair(CARD_COLOR_RED, ncurses::COLOR_RED, CARD_BG_COLOR);
    ncurses::init_pair(CARD_COLOR_EMPTY, ncurses::COLOR_WHITE, BG_COLOR);
    ncurses::init_pair(SELECTED_COLOR, ncurses::COLOR_YELLOW, BG_COLOR);
    ncurses::init_pair(CURSOR_INFO_COLOR, ncurses::COLOR_CYAN, BG_COLOR);
    ncurses::init_pair(GAME_INFO_COLOR, ncurses::COLOR_GREEN, BG_COLOR);
}

/// Redraw a UI in the current screen
pub fn redraw<T: Scorer>(ui: &UI, game: &mut Game<T>, refresh: bool) {
    if refresh {
        ncurses::clear();
        ncurses::bkgd(ncurses::COLOR_PAIR(CARD_COLOR_EMPTY));
    }
    if validate_screen_size() {
        write_title(game);
        draw_cards(ui, game);
        write_cursor_message(ui, game);
        write_message(&ui.message);
    }
    ncurses::refresh();
}

/// Tear down the UI
pub fn cleanup() {
    ncurses::endwin();
}

/// Process input from the user
///
/// Known inputs:
/// - Arrow keys/WASD: Move cursor between various positions on the board
/// - Q: Quit game
/// - H: Hint (unimplemented)
/// - ?: Help (unimplemented)
/// - Space: Toggle position selection
/// - Return: Play move, clear selection
pub fn get_action() -> Action {
    return match ncurses::getch() {
        ncurses::KEY_LEFT | 97 => Action::CursorLeft,
        ncurses::KEY_RIGHT | 100 => Action::CursorRight,
        ncurses::KEY_UP | 119 => Action::CursorUp,
        ncurses::KEY_DOWN | 115 => Action::CursorDown,
        ncurses::KEY_RESIZE => Action::Resize,
        ncurses::KEY_ENTER | 13 | 10 => Action::Play,
        32 =>  Action::ToggleSelection, // Space
        113 => Action::Quit, // Q
        104 => Action::Hint, // H
        63 =>  Action::Help, // ?
        _ =>   Action::Unknown
    }
}

/// Check that the content can fit
fn validate_screen_size() -> bool {
    let min_height = BOARD_MARGIN*2 + CARD_MARGIN*4 + STATUS_HEIGHT + CARD_HEIGHT*3;
    if ncurses::LINES < min_height || ncurses::COLS < 50 {
        write_message(&format!(
                "Please resize your terminal to be at least 50x{}",
                min_height));
        return false
    }
    return true
}

/// Print the game title and status info
fn write_title<T: Scorer>(game: &mut Game<T>) {
    printw_margin(0, 0);
    ncurses::attron(ncurses::A_BOLD());
    ncurses::printw("Thyme");
    ncurses::attroff(ncurses::A_BOLD());
    ncurses::printw(&format!(" - Score: {}", game.score()));
    printw_margin(0, 1);
    let (_, suit) = layout_suit(game.board.lucky_card);
    let info = ncurses::COLOR_PAIR(GAME_INFO_COLOR);
    ncurses::attron(info);
    ncurses::printw(&format!("Lucky Suit: {}  Discards Left: {}/{}", suit,
                             game.discards_allowed, game.discards_allowed_max));
    ncurses::clrtoeol();
    ncurses::attroff(info);
}

/// Print the message at the bottom of the window
fn write_message(message: &str) {
    printw_margin(0, ncurses::LINES - 1);
    ncurses::printw(message);
    ncurses::clrtoeol();
}

/// Print a message describing the stack above the cursor
fn write_cursor_message<T: Scorer>(ui: &UI, game: &Game<T>) {
    let stacked_cards = game.board.count_cards(ui.cursor_position);
    let all_cards = game.board.count_all_cards();
    let message = format!("*{}/{} cards in the stack", stacked_cards, all_cards);
    let color = ncurses::COLOR_PAIR(CURSOR_INFO_COLOR);
    printw_margin(0, ncurses::LINES - 2);
    ncurses::attron(color);
    ncurses::printw(&message);
    ncurses::clrtoeol();
    ncurses::attroff(color);
}

/// Print spaces for the width of the board margin
fn printw_margin(x: i32, y: i32) {
    ncurses::mv(y, x);
    printw_repeat(" ", BOARD_MARGIN, ncurses::COLOR_PAIR(CARD_COLOR_EMPTY));
}

/// Print the card values and empty stacks
fn draw_cards<T: Scorer>(ui: &UI, game: &mut Game<T>) {
    for position in game.board.positions() {
        let card = game.board.top(position);
        if card.is_some() {
            draw_card(position, card.unwrap());
        } else {
            draw_empty(game, position);
        }
        let (x, y) = card_location(position);
        toggle_highlight_card(x, y, ui.selection.contains(&position));
        if position == ui.cursor_position {
            let offset = (CARD_WIDTH as f32 /2.0).floor() as i32;
            ncurses::mvprintw(y + CARD_HEIGHT, x + offset, "*");
        }
    }
}

/// Draw a card on the board at a position
fn draw_card(position: Position, card: cards::card::Card) {
    let (x, y) = card_location(position);
    let (color, suit) = layout_suit(card);
    let value = layout_value(card);
    let black = ncurses::COLOR_PAIR(CARD_COLOR_BLACK);
    ncurses::attron(black);
    ncurses::mvprintw(y, x, &value);
    ncurses::attroff(black);
    ncurses::attron(color);
    ncurses::mvprintw(y, x + value.len() as i32, &suit);
    ncurses::attroff(color);
    let spacing = CARD_WIDTH - value.len() as i32 - 1;
    printw_repeat(" ", spacing, black);
    for i in 1..CARD_HEIGHT - 1 {
        ncurses::mv(y + i, x);
        printw_repeat(" ", CARD_WIDTH, black);
    }
    ncurses::mv(y + CARD_HEIGHT - 1, x);
    printw_repeat(" ", spacing, black);
    ncurses::attron(black);
    ncurses::printw(&value);
    ncurses::attroff(black);
    ncurses::attron(color);
    ncurses::printw(&suit);
    ncurses::attroff(color);
}

/// Draw empty slot for a card
fn draw_empty<T: Scorer>(game: &Game<T>, position: Position) {
    let color = ncurses::COLOR_PAIR(CARD_COLOR_EMPTY);
    let (x, y) = card_location(position);
    ncurses::attron(color);
    ncurses::mvprintw(y, x, "┌");
    printw_repeat("─", CARD_WIDTH - 2, color);
    ncurses::mvprintw(y, x + CARD_WIDTH - 1, "┐");
    ncurses::attroff(color);
    let gap_height = CARD_HEIGHT - 1;
    let bonus_height = gap_height/2;
    for i in 1..gap_height {
        ncurses::attron(color);
        ncurses::mvprintw(y + i, x , "│");
        if i == bonus_height {
            let bonus = format!("+{}", game.scorer.bonus(position));
            let available_width = cmp::max(0, CARD_WIDTH - 2 - bonus.len() as i32);
            let lede = available_width/2;
            printw_repeat(" ", lede, color);
            ncurses::printw(&bonus);
            printw_repeat(" ", available_width - lede, color);
        } else {
            printw_repeat(" ", CARD_WIDTH - 2, color);
        }
        ncurses::attroff(color);
        ncurses::attron(color);
        ncurses::mvprintw(y + i, x + CARD_WIDTH - 1, "│");
        ncurses::attroff(color);
    }
    ncurses::attron(color);
    ncurses::mvprintw(y + CARD_HEIGHT - 1, x, "└");
    printw_repeat("─", CARD_WIDTH - 2, color);
    ncurses::mvprintw(y + CARD_HEIGHT - 1, x + CARD_WIDTH - 1, "┘");
    ncurses::attroff(color);
}

/// Print a string repeatedly to fill a length
fn printw_repeat(content: &str, len: i32, color: ncurses::chtype) {
    ncurses::attron(color);
    for _ in 0..len {
       ncurses::printw(content);
    }
    ncurses::attroff(color);
}

/// Print or clear the border around a card
fn toggle_highlight_card(x: i32, y: i32, on: bool) {
    let color = ncurses::COLOR_PAIR(SELECTED_COLOR);
    ncurses::attron(color);
    ncurses::mvprintw(y - 1, x - 1, if on {"┌"} else {" "});
    ncurses::attroff(color);
    ncurses::mv(y - 1, x);
    printw_repeat(if on {"─"} else {" "}, CARD_WIDTH, color);
    ncurses::mv(y + CARD_HEIGHT, x);
    printw_repeat(if on {"─"} else {" "}, CARD_WIDTH, color);
    ncurses::attron(color);
    ncurses::mvprintw(y - 1, x + CARD_WIDTH, if on {"┐"} else {" "});
    for i in 0..CARD_HEIGHT {
        ncurses::mvprintw(y + i, x - 1, if on {"│"} else {" "});
        ncurses::mvprintw(y + i, x + CARD_WIDTH, if on {"│"} else {" "});
    }
    ncurses::mvprintw(y + CARD_HEIGHT, x - 1, if on {"└"} else {" "});
    ncurses::mvprintw(y + CARD_HEIGHT, x + CARD_WIDTH, if on {"┘"} else {" "});
    ncurses::attroff(color);
}

/// Printed representation of a card's suit
fn layout_suit(card: cards::card::Card) -> (ncurses::chtype, String) {
    let black = ncurses::COLOR_PAIR(CARD_COLOR_BLACK);
    let red = ncurses::COLOR_PAIR(CARD_COLOR_RED);
    return match card.suit {
        cards::card::Suit::Diamonds => (red, "\u{2666}".to_string()),
        cards::card::Suit::Clubs => (black, "\u{2663}".to_string()),
        cards::card::Suit::Spades => (black, "\u{2660}".to_string()),
        cards::card::Suit::Hearts => (red, "\u{2665}".to_string()),
    }
}

/// Printed representation of a card's value
fn layout_value(card: cards::card::Card) -> String {
    return match card.value {
        cards::card::Value::Ace => "A",
        cards::card::Value::Two => "2",
        cards::card::Value::Three => "3",
        cards::card::Value::Four => "4",
        cards::card::Value::Five => "5",
        cards::card::Value::Six => "6",
        cards::card::Value::Seven => "7",
        cards::card::Value::Eight => "8",
        cards::card::Value::Nine => "9",
        cards::card::Value::Ten => "10",
        cards::card::Value::Jack => "J",
        cards::card::Value::Queen => "Q",
        cards::card::Value::King => "K",
    }.to_string()
}

/// Location (x, y) for a card position
fn card_location(position: Position) -> (i32, i32) {
    let left = BOARD_MARGIN + CARD_MARGIN;
    let center = BOARD_MARGIN + CARD_MARGIN*2 + CARD_WIDTH;
    let right = BOARD_MARGIN + CARD_MARGIN*3 + CARD_WIDTH*2;
    let top = BOARD_MARGIN + CARD_MARGIN;
    let middle = BOARD_MARGIN + CARD_MARGIN*2 + CARD_HEIGHT;
    let bottom = BOARD_MARGIN + CARD_MARGIN*3 + CARD_HEIGHT*2;
    match (position.x, position.y) {
        (HPosition::Left, VPosition::Top) => (left, top),
        (HPosition::Left, VPosition::Middle) => (left, middle),
        (HPosition::Left, VPosition::Bottom) => (left, bottom),
        (HPosition::Center, VPosition::Top) => (center, top),
        (HPosition::Center, VPosition::Middle) => (center, middle),
        (HPosition::Center, VPosition::Bottom) => (center, bottom),
        (HPosition::Right, VPosition::Top) => (right, top),
        (HPosition::Right, VPosition::Middle) => (right, middle),
        (HPosition::Right, VPosition::Bottom) => (right, bottom),
    }
}
<|endoftext|>


<commit_before>
use std::io;
use std::fs;
use std::net;

pub struct Usb {}
pub struct Serial {}

#[derive(Debug)]
pub struct Network {
    _host: String,
    _port: u16,
    stream: net::TcpStream
}

impl Network {
    pub fn new(host: &str, port: u16) -> Network {
        let stream = net::TcpStream::connect((host, port)).unwrap();
        Network{
            _host: host.to_string(),
            _port: port,
            stream: stream
        }
    }
}

impl io::Write for Network {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.stream.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.stream.flush()
    }
}

#[derive(Debug)]
pub struct File<W> {
    _path: String,
    fobj: W
}

impl <W: io::Write> File<W> {
    pub fn new(path: &str) -> File<fs::File> {
        let fobj = fs::OpenOptions::new()
            .write(true)
            .create(true)
            .open(path)
            .unwrap();
        File{_path: path.to_string(), fobj: fobj}
    }

    pub fn from(path: &str, fobj: W) -> File<W>{
        File{_path: path.to_string(), fobj: fobj}
    }
}

impl <W: io::Write> io::Write for File<W> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.fobj.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.fobj.flush()
    }
}
<commit_msg>Update device::File API<commit_after>
use std::io;
use std::fs;
use std::path;
use std::net;

pub struct Usb {}
pub struct Serial {}

#[derive(Debug)]
pub struct Network {
    _host: String,
    _port: u16,
    stream: net::TcpStream
}

impl Network {
    pub fn new(host: &str, port: u16) -> Network {
        let stream = net::TcpStream::connect((host, port)).unwrap();
        Network{
            _host: host.to_string(),
            _port: port,
            stream: stream
        }
    }
}

impl io::Write for Network {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.stream.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.stream.flush()
    }
}

#[derive(Debug)]
pub struct File<W> {
    _path: String,
    fobj: W
}

impl <W: io::Write> File<W> {
    pub fn new<P: AsRef<path::Path> + ToString>(path: P) -> File<fs::File> {
        let fobj = fs::OpenOptions::new()
            .write(true)
            .create(true)
            .open(&path)
            .unwrap();
        File{_path: path.to_string(), fobj: fobj}
    }

    pub fn from(path: &str, fobj: W) -> File<W>{
        File{_path: path.to_string(), fobj: fobj}
    }
}

impl <W: io::Write> io::Write for File<W> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.fobj.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.fobj.flush()
    }
}
<|endoftext|>


<commit_before>// Rust on BareMetal - Tested with Rust 0.13.0-nightly
// rustc -O --crate-type lib -o kernel64.o --emit obj kernel64.rs
// ld -T app.ld -o kernel64.sys kernel64.o

#![no_std]
#![allow(ctypes)]

#![feature(lang_items)]
#[lang="sized"]
trait Sized {}

enum Color {
    Black       = 0,
    Blue        = 1,
    Green       = 2,
    Cyan        = 3,
    Red         = 4,
    Pink        = 5,
    Brown       = 6,
    LightGray   = 7,
    DarkGray    = 8,
    LightBlue   = 9,
    LightGreen  = 10,
    LightCyan   = 11,
    LightRed    = 12,
    LightPink   = 13,
    Yello       = 14,
    White       = 15,
}

enum Option<T> {
    None,
    Some(T)
}

struct IntRange {
    cur: int,
    max: int
}

impl IntRange {
    fn next(&mut self) -> Option<int> {
        if self.cur < self.max {
            self.cur += 1;
            Some(self.cur - 1)
        } else {
            None
        }
    }
}

fn range(lo: int, hi: int) -> IntRange {
    IntRange { cur: lo, max: hi }
}

fn clear_screen(background: Color) {
    let mut r = range(0, 80 * 25);
    loop {
        match r.next() {
            Some(x) => {
                unsafe {
                   *((0xb8000 + x * 2) as *mut u16) = (background as u16) << 12;
                }
            },
            None => {break}
        }
    }
}

#[no_mangle]
#[no_stack_check]
pub fn main() {
    clear_screen(LightBlue);
    loop {
		// Loop forever
    }
}
<commit_msg>Allow dead_code<commit_after>// Rust on BareMetal - Tested with Rust 0.13.0-nightly
// rustc -O --crate-type lib -o kernel64.o --emit obj kernel64.rs
// ld -T app.ld -o kernel64.sys kernel64.o

#![no_std]
#![allow(ctypes)]
#![allow(dead_code)]
#![feature(lang_items)]
#[lang="sized"]
trait Sized {}

enum Color {
    Black       = 0,
    Blue        = 1,
    Green       = 2,
    Cyan        = 3,
    Red         = 4,
    Pink        = 5,
    Brown       = 6,
    LightGray   = 7,
    DarkGray    = 8,
    LightBlue   = 9,
    LightGreen  = 10,
    LightCyan   = 11,
    LightRed    = 12,
    LightPink   = 13,
    Yello       = 14,
    White       = 15,
}

enum Option<T> {
    None,
    Some(T)
}

struct IntRange {
    cur: int,
    max: int
}

impl IntRange {
    fn next(&mut self) -> Option<int> {
        if self.cur < self.max {
            self.cur += 1;
            Some(self.cur - 1)
        } else {
            None
        }
    }
}

fn range(lo: int, hi: int) -> IntRange {
    IntRange { cur: lo, max: hi }
}

fn clear_screen(background: Color) {
    let mut r = range(0, 80 * 25);
    loop {
        match r.next() {
            Some(x) => {
                unsafe {
                   *((0xb8000 + x * 2) as *mut u16) = (background as u16) << 12;
                }
            },
            None => {break}
        }
    }
}

#[no_mangle]
#[no_stack_check]
pub fn main() {
    clear_screen(LightBlue);
    loop {
		// Loop forever
    }
}
<|endoftext|>


<commit_before>use utils::bigint::{M256, U256, U512};
use utils::gas::Gas;
use utils::address::Address;
use super::Opcode;
use vm::{Machine, Memory, Stack, PC, Error, Result};
use transaction::Transaction;
use blockchain::Block;
use std::cmp::{min, max};

const G_ZERO: usize = 0;
const G_BASE: usize = 2;
const G_VERYLOW: usize = 3;
const G_LOW: usize = 5;
const G_MID: usize = 8;
const G_HIGH: usize = 10;
const G_EXTCODE: usize = 700;
const G_BALANCE: usize = 400;
const G_SLOAD: usize = 200;
const G_JUMPDEST: usize = 1;
const G_SSET: usize = 20000;
const G_SRESET: usize = 5000;
const R_SCLEAR: usize = 15000;
const R_SUICIDE: usize = 24000;
const G_SUICIDE: usize = 5000;
const G_CREATE: usize = 32000;
const G_CODEDEPOSITE: usize = 200;
const G_CALL: usize = 700;
const G_CALLVALUE: usize = 9000;
const G_CALLSTIPEND: usize = 2300;
const G_NEWACCOUNT: usize = 25000;
const G_EXP: usize = 10;
const G_EXPBYTE: usize = 10;
const G_MEMORY: usize = 3;
const G_TXCREATE: usize = 32000;
const G_TXDATAZERO: usize = 4;
const G_TXDATANONZERO: usize = 68;
const G_TRANSACTION: usize = 21000;
const G_LOG: usize = 375;
const G_LOGDATA: usize = 8;
const G_LOGTOPIC: usize = 375;
const G_SHA3: usize = 30;
const G_SHA3WORD: usize = 6;
const G_COPY: usize = 3;
const G_BLOCKHASH: usize = 20;

fn memory_cost(a: Gas) -> Gas {
    (Gas::from(G_MEMORY) * a + a * a / Gas::from(512u64)).into()
}

fn sstore_cost<M: Machine>(machine: &M) -> Result<Gas> {
    let index = machine.stack().peek(0)?;
    let value = machine.stack().peek(1)?;
    let address = machine.transaction().callee();

    if value != M256::zero() && machine.block().account_storage(address, index) == M256::zero() {
        Ok(G_SSET.into())
    } else {
        Ok(G_SRESET.into())
    }
}

fn call_cost<M: Machine>(machine: &M) -> Result<Gas> {
    Ok(gascap_cost(machine)? + extra_cost(machine)?)
}

fn callgas_cost<M: Machine>(machine: &M) -> Result<Gas> {
    let val = machine.stack().peek(2)?;
    if val != M256::zero() {
        Ok(gascap_cost(machine)? + G_CALLSTIPEND.into())
    } else {
        Ok(gascap_cost(machine)?)
    }
}

fn gascap_cost<M: Machine>(machine: &M) -> Result<Gas> {
    let base1 = machine.available_gas() - extra_cost(machine)?;
    let base2 = machine.stack().peek(0)?.into();

    if machine.available_gas() >= extra_cost(machine)? {
        Ok(min(base1 - base1 / Gas::from(64u64), base2))
    } else {
        Ok(base2)
    }
}

fn extra_cost<M: Machine>(machine: &M) -> Result<Gas> {
    Ok(Gas::from(G_CALL) + xfer_cost(machine)? + new_cost(machine)?)
}

fn xfer_cost<M: Machine>(machine: &M) -> Result<Gas> {
    let val = machine.stack().peek(2)?;
    if val != M256::zero() {
        Ok(G_CALLVALUE.into())
    } else {
        Ok(Gas::zero())
    }
}

fn new_cost<M: Machine>(machine: &M) -> Result<Gas> {
    let address: Address = machine.stack().peek(1)?.into();
    if address == Address::default() {
        Ok(G_NEWACCOUNT.into())
    } else {
        Ok(Gas::zero())
    }
}

fn suicide_cost<M: Machine>(machine: &M) -> Result<Gas> {
    let address: Address = machine.stack().peek(1)?.into();
    Ok(Gas::from(G_SUICIDE) + if address == Address::default() {
        Gas::from(G_NEWACCOUNT)
    } else {
        Gas::zero()
    })
}

// TODO: Implement C_SSTORE, C_CALL and C_SELFDESTRUCT
// TODO: Use a machine_state struct instead of u_ip, u_i and u_s
// TODO: Use machine_state to implement gas cost for EXP,
// CALLDATACOPY, CODECOPY, EXTCODECOPY, LOG0-4, SHA3

impl Opcode {
    fn gas_cost_memory<M: Machine>(&self, machine: &M) -> Result<Gas> {
        let ref stack = machine.stack();
        let ref memory = machine.memory();
        let opcode = self.clone();

        let current = memory_cost(machine.memory().active_len().into());
        let next = match opcode {
            Opcode::SHA3 | Opcode::CODECOPY | Opcode::RETURN => {
                let from: U256 = stack.peek(0)?.into();
                let len: U256 = stack.peek(1)?.into();
                memory_cost(max(Gas::from(from) + Gas::from(len),
                                machine.memory().active_len().into()))
            },
            Opcode::MLOAD | Opcode::MSTORE => {
                let from: U256 = stack.peek(0)?.into();
                memory_cost(max(Gas::from(from) + Gas::from(32u64),
                                machine.memory().active_len().into()))
            },
            Opcode::MSTORE8 => {
                let from: U256 = stack.peek(0)?.into();
                memory_cost(max(Gas::from(from) + Gas::from(1u64),
                                machine.memory().active_len().into()))
            },
            Opcode::CREATE => {
                let from: U256 = stack.peek(1)?.into();
                let len: U256 = stack.peek(2)?.into();
                memory_cost(max(Gas::from(from) + Gas::from(len),
                                machine.memory().active_len().into()))
            },
            _ => {
                memory_cost(machine.memory().active_len().into())
            }
        };
        Ok(next - current)
    }

    pub fn gas_cost<M: Machine>(&self, machine: &M) -> Result<Gas> {
        let ref stack = machine.stack();
        let ref memory = machine.memory();
        let opcode = self.clone();
        let self_cost: Gas = match opcode {
            Opcode::CALL | Opcode::CALLCODE |
            Opcode::DELEGATECALL => call_cost(machine)?,

            Opcode::SUICIDE => suicide_cost(machine)?,

            Opcode::SSTORE => sstore_cost(machine)?,

            Opcode::SHA3 => {
                let len = stack.peek(1)?;
                (Gas::from(G_SHA3) + Gas::from(G_SHA3WORD) * (Gas::from(len) / Gas::from(32u64))).into()
            },

            Opcode::LOG(v) => {
                let len = stack.peek(1)?;
                (Gas::from(G_LOG) + Gas::from(G_LOGDATA) * Gas::from(len) + Gas::from(G_LOGTOPIC) * Gas::from(v)).into()
            },

            Opcode::EXTCODECOPY => {
                let len = stack.peek(2)?;
                (Gas::from(G_EXTCODE) + Gas::from(G_COPY) * (Gas::from(len) / Gas::from(32u64))).into()
            },

            Opcode::CALLDATACOPY | Opcode::CODECOPY => {
                let len = stack.peek(2)?;
                (Gas::from(G_VERYLOW) + Gas::from(G_COPY) * (Gas::from(len) / Gas::from(32u64))).into()
            },

            Opcode::EXP => {
                if stack.peek(1)? == M256::zero() {
                    Gas::from(G_EXP)
                } else {
                    (Gas::from(G_EXP) + Gas::from(G_EXPBYTE) * (Gas::from(1u64) + Gas::from(stack.peek(1)?.log2floor()))).into()
                }
            }

            Opcode::CREATE => G_CREATE.into(),
            Opcode::JUMPDEST => G_JUMPDEST.into(),
            Opcode::SLOAD => G_SLOAD.into(),

            // W_zero
            Opcode::STOP | Opcode::RETURN
                => G_ZERO.into(),

            // W_base
            Opcode::ADDRESS | Opcode::ORIGIN | Opcode::CALLER |
            Opcode::CALLVALUE | Opcode::CALLDATASIZE |
            Opcode::CODESIZE | Opcode::GASPRICE | Opcode::COINBASE |
            Opcode::TIMESTAMP | Opcode::NUMBER | Opcode::DIFFICULTY |
            Opcode::GASLIMIT | Opcode::POP | Opcode::PC |
            Opcode::MSIZE | Opcode::GAS
                => G_BASE.into(),

            // W_verylow
            Opcode::ADD | Opcode::SUB | Opcode::NOT | Opcode::LT |
            Opcode::GT | Opcode::SLT | Opcode::SGT | Opcode::EQ |
            Opcode::ISZERO | Opcode::AND | Opcode::OR | Opcode::XOR |
            Opcode::BYTE | Opcode::CALLDATALOAD | Opcode::MLOAD |
            Opcode::MSTORE | Opcode::MSTORE8 | Opcode::PUSH(_) |
            Opcode::DUP(_) | Opcode::SWAP(_)
                => G_VERYLOW.into(),

            // W_low
            Opcode::MUL | Opcode::DIV | Opcode::SDIV | Opcode::MOD |
            Opcode::SMOD | Opcode::SIGNEXTEND
                => G_LOW.into(),

            // W_mid
            Opcode::ADDMOD | Opcode::MULMOD | Opcode::JUMP
                => G_MID.into(),

            // W_high
            Opcode::JUMPI => G_HIGH.into(),

            // W_extcode
            Opcode::EXTCODESIZE => G_EXTCODE.into(),

            Opcode::BALANCE => G_BALANCE.into(),
            Opcode::BLOCKHASH => G_BLOCKHASH.into(),
            Opcode::INVALID => Gas::zero(),
        };
        Ok(self_cost + self.gas_cost_memory(machine)?)
    }
}
<commit_msg>Problem: TODO is outdated Solution: remove those finished TODOs<commit_after>use utils::bigint::{M256, U256, U512};
use utils::gas::Gas;
use utils::address::Address;
use super::Opcode;
use vm::{Machine, Memory, Stack, PC, Error, Result};
use transaction::Transaction;
use blockchain::Block;
use std::cmp::{min, max};

const G_ZERO: usize = 0;
const G_BASE: usize = 2;
const G_VERYLOW: usize = 3;
const G_LOW: usize = 5;
const G_MID: usize = 8;
const G_HIGH: usize = 10;
const G_EXTCODE: usize = 700;
const G_BALANCE: usize = 400;
const G_SLOAD: usize = 200;
const G_JUMPDEST: usize = 1;
const G_SSET: usize = 20000;
const G_SRESET: usize = 5000;
const R_SCLEAR: usize = 15000;
const R_SUICIDE: usize = 24000;
const G_SUICIDE: usize = 5000;
const G_CREATE: usize = 32000;
const G_CODEDEPOSITE: usize = 200;
const G_CALL: usize = 700;
const G_CALLVALUE: usize = 9000;
const G_CALLSTIPEND: usize = 2300;
const G_NEWACCOUNT: usize = 25000;
const G_EXP: usize = 10;
const G_EXPBYTE: usize = 10;
const G_MEMORY: usize = 3;
const G_TXCREATE: usize = 32000;
const G_TXDATAZERO: usize = 4;
const G_TXDATANONZERO: usize = 68;
const G_TRANSACTION: usize = 21000;
const G_LOG: usize = 375;
const G_LOGDATA: usize = 8;
const G_LOGTOPIC: usize = 375;
const G_SHA3: usize = 30;
const G_SHA3WORD: usize = 6;
const G_COPY: usize = 3;
const G_BLOCKHASH: usize = 20;

fn memory_cost(a: Gas) -> Gas {
    (Gas::from(G_MEMORY) * a + a * a / Gas::from(512u64)).into()
}

fn sstore_cost<M: Machine>(machine: &M) -> Result<Gas> {
    let index = machine.stack().peek(0)?;
    let value = machine.stack().peek(1)?;
    let address = machine.transaction().callee();

    if value != M256::zero() && machine.block().account_storage(address, index) == M256::zero() {
        Ok(G_SSET.into())
    } else {
        Ok(G_SRESET.into())
    }
}

fn call_cost<M: Machine>(machine: &M) -> Result<Gas> {
    Ok(gascap_cost(machine)? + extra_cost(machine)?)
}

fn callgas_cost<M: Machine>(machine: &M) -> Result<Gas> {
    let val = machine.stack().peek(2)?;
    if val != M256::zero() {
        Ok(gascap_cost(machine)? + G_CALLSTIPEND.into())
    } else {
        Ok(gascap_cost(machine)?)
    }
}

fn gascap_cost<M: Machine>(machine: &M) -> Result<Gas> {
    let base1 = machine.available_gas() - extra_cost(machine)?;
    let base2 = machine.stack().peek(0)?.into();

    if machine.available_gas() >= extra_cost(machine)? {
        Ok(min(base1 - base1 / Gas::from(64u64), base2))
    } else {
        Ok(base2)
    }
}

fn extra_cost<M: Machine>(machine: &M) -> Result<Gas> {
    Ok(Gas::from(G_CALL) + xfer_cost(machine)? + new_cost(machine)?)
}

fn xfer_cost<M: Machine>(machine: &M) -> Result<Gas> {
    let val = machine.stack().peek(2)?;
    if val != M256::zero() {
        Ok(G_CALLVALUE.into())
    } else {
        Ok(Gas::zero())
    }
}

fn new_cost<M: Machine>(machine: &M) -> Result<Gas> {
    let address: Address = machine.stack().peek(1)?.into();
    if address == Address::default() {
        Ok(G_NEWACCOUNT.into())
    } else {
        Ok(Gas::zero())
    }
}

fn suicide_cost<M: Machine>(machine: &M) -> Result<Gas> {
    let address: Address = machine.stack().peek(1)?.into();
    Ok(Gas::from(G_SUICIDE) + if address == Address::default() {
        Gas::from(G_NEWACCOUNT)
    } else {
        Gas::zero()
    })
}

impl Opcode {
    fn gas_cost_memory<M: Machine>(&self, machine: &M) -> Result<Gas> {
        let ref stack = machine.stack();
        let ref memory = machine.memory();
        let opcode = self.clone();

        let current = memory_cost(machine.memory().active_len().into());
        let next = match opcode {
            Opcode::SHA3 | Opcode::CODECOPY | Opcode::RETURN => {
                let from: U256 = stack.peek(0)?.into();
                let len: U256 = stack.peek(1)?.into();
                memory_cost(max(Gas::from(from) + Gas::from(len),
                                machine.memory().active_len().into()))
            },
            Opcode::MLOAD | Opcode::MSTORE => {
                let from: U256 = stack.peek(0)?.into();
                memory_cost(max(Gas::from(from) + Gas::from(32u64),
                                machine.memory().active_len().into()))
            },
            Opcode::MSTORE8 => {
                let from: U256 = stack.peek(0)?.into();
                memory_cost(max(Gas::from(from) + Gas::from(1u64),
                                machine.memory().active_len().into()))
            },
            Opcode::CREATE => {
                let from: U256 = stack.peek(1)?.into();
                let len: U256 = stack.peek(2)?.into();
                memory_cost(max(Gas::from(from) + Gas::from(len),
                                machine.memory().active_len().into()))
            },
            _ => {
                memory_cost(machine.memory().active_len().into())
            }
        };
        Ok(next - current)
    }

    pub fn gas_cost<M: Machine>(&self, machine: &M) -> Result<Gas> {
        let ref stack = machine.stack();
        let ref memory = machine.memory();
        let opcode = self.clone();
        let self_cost: Gas = match opcode {
            Opcode::CALL | Opcode::CALLCODE |
            Opcode::DELEGATECALL => call_cost(machine)?,

            Opcode::SUICIDE => suicide_cost(machine)?,

            Opcode::SSTORE => sstore_cost(machine)?,

            Opcode::SHA3 => {
                let len = stack.peek(1)?;
                (Gas::from(G_SHA3) + Gas::from(G_SHA3WORD) * (Gas::from(len) / Gas::from(32u64))).into()
            },

            Opcode::LOG(v) => {
                let len = stack.peek(1)?;
                (Gas::from(G_LOG) + Gas::from(G_LOGDATA) * Gas::from(len) + Gas::from(G_LOGTOPIC) * Gas::from(v)).into()
            },

            Opcode::EXTCODECOPY => {
                let len = stack.peek(2)?;
                (Gas::from(G_EXTCODE) + Gas::from(G_COPY) * (Gas::from(len) / Gas::from(32u64))).into()
            },

            Opcode::CALLDATACOPY | Opcode::CODECOPY => {
                let len = stack.peek(2)?;
                (Gas::from(G_VERYLOW) + Gas::from(G_COPY) * (Gas::from(len) / Gas::from(32u64))).into()
            },

            Opcode::EXP => {
                if stack.peek(1)? == M256::zero() {
                    Gas::from(G_EXP)
                } else {
                    (Gas::from(G_EXP) + Gas::from(G_EXPBYTE) * (Gas::from(1u64) + Gas::from(stack.peek(1)?.log2floor()))).into()
                }
            }

            Opcode::CREATE => G_CREATE.into(),
            Opcode::JUMPDEST => G_JUMPDEST.into(),
            Opcode::SLOAD => G_SLOAD.into(),

            // W_zero
            Opcode::STOP | Opcode::RETURN
                => G_ZERO.into(),

            // W_base
            Opcode::ADDRESS | Opcode::ORIGIN | Opcode::CALLER |
            Opcode::CALLVALUE | Opcode::CALLDATASIZE |
            Opcode::CODESIZE | Opcode::GASPRICE | Opcode::COINBASE |
            Opcode::TIMESTAMP | Opcode::NUMBER | Opcode::DIFFICULTY |
            Opcode::GASLIMIT | Opcode::POP | Opcode::PC |
            Opcode::MSIZE | Opcode::GAS
                => G_BASE.into(),

            // W_verylow
            Opcode::ADD | Opcode::SUB | Opcode::NOT | Opcode::LT |
            Opcode::GT | Opcode::SLT | Opcode::SGT | Opcode::EQ |
            Opcode::ISZERO | Opcode::AND | Opcode::OR | Opcode::XOR |
            Opcode::BYTE | Opcode::CALLDATALOAD | Opcode::MLOAD |
            Opcode::MSTORE | Opcode::MSTORE8 | Opcode::PUSH(_) |
            Opcode::DUP(_) | Opcode::SWAP(_)
                => G_VERYLOW.into(),

            // W_low
            Opcode::MUL | Opcode::DIV | Opcode::SDIV | Opcode::MOD |
            Opcode::SMOD | Opcode::SIGNEXTEND
                => G_LOW.into(),

            // W_mid
            Opcode::ADDMOD | Opcode::MULMOD | Opcode::JUMP
                => G_MID.into(),

            // W_high
            Opcode::JUMPI => G_HIGH.into(),

            // W_extcode
            Opcode::EXTCODESIZE => G_EXTCODE.into(),

            Opcode::BALANCE => G_BALANCE.into(),
            Opcode::BLOCKHASH => G_BLOCKHASH.into(),
            Opcode::INVALID => Gas::zero(),
        };
        Ok(self_cost + self.gas_cost_memory(machine)?)
    }
}
<|endoftext|>


<commit_before>/// http://wiki.osdev.org/PIC
use cpuio::{Port, UnsafePort};
use spin::Mutex;

// PIC ports
const PIC1_COMMAND: u16 = 0x20;
const PIC2_COMMAND: u16 = 0xA0;
const PIC1_DATA: u16 = 0x21;
const PIC2_DATA: u16 = 0xA1;

// PIC commands
const PIC_CMD_EOI: u8 = 0x20;
const PIC_CMD_INIT: u8 = 0x11;
const PIC_CMD_READ_ISR: u8 = 0x0b;

struct Pic {
    offset: u8,
    command_port: UnsafePort<u8>,
    data_port: UnsafePort<u8>,
}

impl Pic {
    /// Test if an interrupt id is operated by this pic
    fn handles_interrupt(&self, interupt_id: u8) -> bool {
        self.offset <= interupt_id && interupt_id < self.offset + 8
    }

    /// Read ISR: https://wiki.osdev.org/8259_PIC#ISR_and_IRR
    unsafe fn read_isr(&mut self) -> u8 {
        self.command_port.write(PIC_CMD_READ_ISR);
        self.command_port.read()
    }

    /// Send end of interrupt signal to pic
    unsafe fn send_eoi(&mut self) {
        self.command_port.write(PIC_CMD_EOI);
    }
}

pub struct ChainedPics {
    pics: [Pic; 2],
}

impl ChainedPics {
    pub const unsafe fn new(pic1_offset: u8, pic2_offset: u8) -> ChainedPics {
        ChainedPics {
            pics: [
                Pic {
                    offset: pic1_offset,
                    command_port: UnsafePort::new(PIC1_COMMAND),
                    data_port: UnsafePort::new(PIC1_DATA),
                },
                Pic {
                    offset: pic2_offset,
                    command_port: UnsafePort::new(PIC2_COMMAND),
                    data_port: UnsafePort::new(PIC2_DATA),
                },
            ],
        }
    }
    /// Init - remap irqs
    /// http://wiki.osdev.org/PIC#Initialisation
    /// https://pdos.csail.mit.edu/6.828/2005/readings/hardware/8259A.pdf
    pub unsafe fn init(&mut self) {
        // Create local IO wait function
        let mut io_wait_port: Port<u8> = Port::new(0x80);
        let mut io_wait = || io_wait_port.write(0);

        // Initialization sequence
        for pic in &mut self.pics {
            pic.command_port.write(PIC_CMD_INIT);
            io_wait();
        }

        // Set interrupt offset
        for pic in &mut self.pics {
            pic.data_port.write(pic.offset);
            io_wait();
        }

        // Introduce PICs to each other
        self.pics[0].data_port.write(4);
        io_wait();
        self.pics[1].data_port.write(2);
        io_wait();

        // Use 8086/88 (MCS-80/85) mode (http://forum.osdev.org/viewtopic.php?f=1&t=12960&start=0)
        self.pics[0].data_port.write(1);
        io_wait();
        self.pics[1].data_port.write(1);
        io_wait();

        // Modify masks
        // http://wiki.osdev.org/IRQ#Standard_ISA_IRQs
        // http://wiki.osdev.org/8259_PIC#Masking
        // PIC1: PIT, Keyboard, Cascade
        // PIC2: Free IRQs (9,10,11), Primary ATA
        // Disable everything else
        let mask1 = 0b11111000;
        let mask2 = 0b10110001;

        // Restore / Set masks
        self.pics[0].data_port.write(mask1);
        self.pics[1].data_port.write(mask2);
    }

    /// Disable PICs by masking all interrupts.
    /// Used when transitioning to LAPIC.
    /// https://wiki.osdev.org/PIC#Disabling
    pub fn disable(&mut self) {
        for pic in &mut self.pics {
            unsafe {
                pic.data_port.write(u8::MAX);
            }
        }
    }

    /// Verify that an interrupt is produced by pics
    pub fn handles_interrupt(&self, interrupt_id: u8) -> bool {
        self.pics.iter().any(|p| p.handles_interrupt(interrupt_id))
    }

    /// Read ISR values from pics as a u16 bitmap
    pub unsafe fn read_isr(&mut self) -> u16 {
        let isr0 = self.pics[0].read_isr() as u16;
        let isr1 = self.pics[1].read_isr() as u16;
        (isr1 << 8) | isr0
    }

    /// Send end of interrupt notification
    pub unsafe fn notify_eoi(&mut self, interrupt_id: u8) {
        if self.handles_interrupt(interrupt_id) {
            // chain
            if self.pics[1].handles_interrupt(interrupt_id) {
                self.pics[1].send_eoi();
            }
            self.pics[0].send_eoi();
        }
    }

    /// Send end of interrupt notification to primary PIC only
    /// Used for spurious interrupt handling
    pub unsafe fn notify_eoi_primary(&mut self) {
        self.pics[0].send_eoi();
    }
}

// Rempap interrupts to 0x20..0x30
pub static PICS: Mutex<ChainedPics> = Mutex::new(unsafe { ChainedPics::new(0x20, 0x28) });

pub fn init() {
    unsafe {
        PICS.lock().init();
    }
}

pub fn disable() {
    unsafe {
        PICS.lock().disable();
    }
}
<commit_msg>Keep track of PIC enabled status<commit_after>/// http://wiki.osdev.org/PIC
use cpuio::{Port, UnsafePort};
use spin::Mutex;

// PIC ports
const PIC1_COMMAND: u16 = 0x20;
const PIC2_COMMAND: u16 = 0xA0;
const PIC1_DATA: u16 = 0x21;
const PIC2_DATA: u16 = 0xA1;

// PIC commands
const PIC_CMD_EOI: u8 = 0x20;
const PIC_CMD_INIT: u8 = 0x11;
const PIC_CMD_READ_ISR: u8 = 0x0b;

struct Pic {
    offset: u8,
    command_port: UnsafePort<u8>,
    data_port: UnsafePort<u8>,
}

impl Pic {
    /// Test if an interrupt id is operated by this pic
    fn handles_interrupt(&self, interupt_id: u8) -> bool {
        self.offset <= interupt_id && interupt_id < self.offset + 8
    }

    /// Read ISR: https://wiki.osdev.org/8259_PIC#ISR_and_IRR
    unsafe fn read_isr(&mut self) -> u8 {
        self.command_port.write(PIC_CMD_READ_ISR);
        self.command_port.read()
    }

    /// Send end of interrupt signal to pic
    unsafe fn send_eoi(&mut self) {
        self.command_port.write(PIC_CMD_EOI);
    }
}

pub struct ChainedPics {
    pics: [Pic; 2],
    enabled: bool,
}

impl ChainedPics {
    pub const unsafe fn new(pic1_offset: u8, pic2_offset: u8) -> ChainedPics {
        ChainedPics {
            pics: [
                Pic {
                    offset: pic1_offset,
                    command_port: UnsafePort::new(PIC1_COMMAND),
                    data_port: UnsafePort::new(PIC1_DATA),
                },
                Pic {
                    offset: pic2_offset,
                    command_port: UnsafePort::new(PIC2_COMMAND),
                    data_port: UnsafePort::new(PIC2_DATA),
                },
            ],
            enabled: false,
        }
    }
    /// Init - remap irqs
    /// http://wiki.osdev.org/PIC#Initialisation
    /// https://pdos.csail.mit.edu/6.828/2005/readings/hardware/8259A.pdf
    pub unsafe fn init(&mut self) {
        // Create local IO wait function
        let mut io_wait_port: Port<u8> = Port::new(0x80);
        let mut io_wait = || io_wait_port.write(0);

        // Initialization sequence
        for pic in &mut self.pics {
            pic.command_port.write(PIC_CMD_INIT);
            io_wait();
        }

        // Set interrupt offset
        for pic in &mut self.pics {
            pic.data_port.write(pic.offset);
            io_wait();
        }

        // Introduce PICs to each other
        self.pics[0].data_port.write(4);
        io_wait();
        self.pics[1].data_port.write(2);
        io_wait();

        // Use 8086/88 (MCS-80/85) mode (http://forum.osdev.org/viewtopic.php?f=1&t=12960&start=0)
        self.pics[0].data_port.write(1);
        io_wait();
        self.pics[1].data_port.write(1);
        io_wait();

        // Modify masks
        // http://wiki.osdev.org/IRQ#Standard_ISA_IRQs
        // http://wiki.osdev.org/8259_PIC#Masking
        // PIC1: PIT, Keyboard, Cascade
        // PIC2: Free IRQs (9,10,11), Primary ATA
        // Disable everything else
        let mask1 = 0b11111000;
        let mask2 = 0b10110001;

        // Restore / Set masks
        self.pics[0].data_port.write(mask1);
        self.pics[1].data_port.write(mask2);

        self.enabled = true;
    }

    /// Disable PICs by masking all interrupts.
    /// Used when transitioning to LAPIC.
    /// https://wiki.osdev.org/PIC#Disabling
    pub fn disable(&mut self) {
        for pic in &mut self.pics {
            unsafe {
                pic.data_port.write(u8::MAX);
            }
        }
        self.enabled = false;
    }

    /// Verify that an interrupt is produced by pics
    pub fn handles_interrupt(&self, interrupt_id: u8) -> bool {
        self.pics.iter().any(|p| p.handles_interrupt(interrupt_id))
    }

    /// Read ISR values from pics as a u16 bitmap
    pub unsafe fn read_isr(&mut self) -> u16 {
        let isr0 = self.pics[0].read_isr() as u16;
        let isr1 = self.pics[1].read_isr() as u16;
        (isr1 << 8) | isr0
    }

    /// Send end of interrupt notification
    pub unsafe fn notify_eoi(&mut self, interrupt_id: u8) {
        if self.handles_interrupt(interrupt_id) {
            // chain
            if self.pics[1].handles_interrupt(interrupt_id) {
                self.pics[1].send_eoi();
            }
            self.pics[0].send_eoi();
        }
    }

    /// Send end of interrupt notification to primary PIC only
    /// Used for spurious interrupt handling
    pub unsafe fn notify_eoi_primary(&mut self) {
        self.pics[0].send_eoi();
    }
}

// Rempap interrupts to 0x20..0x30
pub static PICS: Mutex<ChainedPics> = Mutex::new(unsafe { ChainedPics::new(0x20, 0x28) });

pub fn init() {
    unsafe {
        PICS.lock().init();
    }
}

pub fn disable() {
    unsafe {
        PICS.lock().disable();
    }
}

pub fn is_enabled() -> bool {
    PICS.lock().enabled
}
<|endoftext|>


<commit_before>#![cfg(feature = "use_std")]

use std::collections::HashMap;
use std::hash::Hash;
use std::iter::Iterator;

/// Creates a new `GroupingMap` from `iter`
pub fn new<I, K, V>(iter: I) -> GroupingMap<I>
where
    I: Iterator<Item = (K, V)>,
    K: Hash + Eq,
{
    GroupingMap { iter }
}

/// `GroupingMap` is an intermediate struct for efficient "group-and-fold" operations.
/// It groups elements by their key and at the same time fold each group
/// using some aggregating operation.
///
/// No method on this struct performs temporary allocations.
///
// See [`.into_grouping_map()`](../trait.Itertools.html#method.into_grouping_map)
// for more information.
pub struct GroupingMap<I> {
    iter: I,
}

impl<I, K, V> GroupingMap<I>
where
    I: Iterator<Item = (K, V)>,
    K: Hash + Eq,
{
    pub fn aggregate<FO, R>(mut self, mut operation: FO) -> HashMap<K, R>
    where
        FO: FnMut(Option<R>, &K, V) -> Option<R>,
    {
        let mut destination_map = HashMap::new();

        for (key, val) in self.iter {
            let acc = destination_map.remove(&key);
            if let Some(op_res) = operation(acc, &key, val) {
                destination_map.insert(key, op_res);
            }
        }

        destination_map
    }

    pub fn fold<FO, R>(self, init: R, mut operation: FO) -> HashMap<K, R>
    where
        R: Clone,
        FO: FnMut(R, &K, V) -> R,
    {
        self.aggregate(|acc, key, val| {
            let acc = acc.unwrap_or_else(|| init.clone());
            Some(operation(acc, key, val))
        })
    }

    pub fn fold_first<FO>(self, mut operation: FO) -> HashMap<K, V>
    where
        FO: FnMut(V, &K, V) -> V,
    {
        self.aggregate(|acc, key, val| {
            Some(match acc {
                Some(acc) => operation(acc, key, val),
                None => val,
            })
        })
    }

    pub fn collect<C>(self) -> HashMap<K, C>
    where
        C: Default + Extend<V>,
    {
        self.aggregate(|acc, _, v| {
            let mut acc = acc.unwrap_or_else(C::default);
            acc.extend(Some(v));
            Some(acc)
        })
    }

    pub fn count(self) -> HashMap<K, usize> {
        self.fold(0, |acc, _, _| acc + 1)
    }
}
<commit_msg>Remove useless mut modifier<commit_after>#![cfg(feature = "use_std")]

use std::collections::HashMap;
use std::hash::Hash;
use std::iter::Iterator;

/// Creates a new `GroupingMap` from `iter`
pub fn new<I, K, V>(iter: I) -> GroupingMap<I>
where
    I: Iterator<Item = (K, V)>,
    K: Hash + Eq,
{
    GroupingMap { iter }
}

/// `GroupingMap` is an intermediate struct for efficient "group-and-fold" operations.
/// It groups elements by their key and at the same time fold each group
/// using some aggregating operation.
///
/// No method on this struct performs temporary allocations.
///
// See [`.into_grouping_map()`](../trait.Itertools.html#method.into_grouping_map)
// for more information.
pub struct GroupingMap<I> {
    iter: I,
}

impl<I, K, V> GroupingMap<I>
where
    I: Iterator<Item = (K, V)>,
    K: Hash + Eq,
{
    pub fn aggregate<FO, R>(self, mut operation: FO) -> HashMap<K, R>
    where
        FO: FnMut(Option<R>, &K, V) -> Option<R>,
    {
        let mut destination_map = HashMap::new();

        for (key, val) in self.iter {
            let acc = destination_map.remove(&key);
            if let Some(op_res) = operation(acc, &key, val) {
                destination_map.insert(key, op_res);
            }
        }

        destination_map
    }

    pub fn fold<FO, R>(self, init: R, mut operation: FO) -> HashMap<K, R>
    where
        R: Clone,
        FO: FnMut(R, &K, V) -> R,
    {
        self.aggregate(|acc, key, val| {
            let acc = acc.unwrap_or_else(|| init.clone());
            Some(operation(acc, key, val))
        })
    }

    pub fn fold_first<FO>(self, mut operation: FO) -> HashMap<K, V>
    where
        FO: FnMut(V, &K, V) -> V,
    {
        self.aggregate(|acc, key, val| {
            Some(match acc {
                Some(acc) => operation(acc, key, val),
                None => val,
            })
        })
    }

    pub fn collect<C>(self) -> HashMap<K, C>
    where
        C: Default + Extend<V>,
    {
        self.aggregate(|acc, _, v| {
            let mut acc = acc.unwrap_or_else(C::default);
            acc.extend(Some(v));
            Some(acc)
        })
    }

    pub fn count(self) -> HashMap<K, usize> {
        self.fold(0, |acc, _, _| acc + 1)
    }
}
<|endoftext|>


<commit_before>#[feature(managed_boxes)];

extern mod extra;
extern mod common;

use std::{io, os};
use std::iter::Range;
use extra::{time, term};
use extra::term::Terminal;
use problem::Problem;

mod problem;

static NSEC_PER_SEC: u64 = 1000000000;
fn nanosec_to_str(nsec: u64) -> ~str {
    return format!("{}.{:09}",
         (nsec / NSEC_PER_SEC) as uint,
         (nsec % NSEC_PER_SEC) as uint);
}

fn bench<T>(f: proc() -> T) -> (u64, T) {
    let start_time = time::precise_time_ns();
    let result     = f();
    let end_time   = time::precise_time_ns();
    return (end_time - start_time, result);
}

fn color_print<T: Writer>(writer: T, color: term::color::Color, s: &str) {
    let mut term = Terminal::new(writer);
    match term { Ok(ref mut t) => { t.fg(color); }, _ => {}}
    print(s);
    match term { Ok(ref mut t) => { t.reset(); }, _ => {}}
}

fn print_result(correct: bool, name: &str, time: u64, comp_answer: &str) {
    print("[");
    if correct {
        color_print(io::stdout(), term::color::GREEN, "OK");
    } else {
        color_print(io::stdout(), term::color::RED, "NG");
    }
    println!("] {:5} {:13} {:20}", name, nanosec_to_str(time), comp_answer);
}

struct ArgIterator<'a> {
    args: &'a [~str],
    idx: uint,
    cur_range: Range<uint>,
}

impl<'a> ArgIterator<'a> {
    pub fn new<'a>(args: &'a [~str]) -> ArgIterator<'a> {
        let mut it = ArgIterator { args: args, idx: 0, cur_range: range(0u, 0) };
        it.update_range();
        return it;
    }

    fn update_range(&mut self) {
        self.cur_range = range(0u, 0);
        if self.idx >= self.args.len() { return; }

        if !self.args[self.idx].contains_char('-') {
            let n = from_str::<uint>(self.args[self.idx]);
            for &n in n.iter() {
                self.cur_range = range(n, n + 1);
            }
            return;
        }

        let mut ns = ~[];
        for ss in self.args[self.idx].split('-') {
            match from_str::<uint>(ss) {
                Some(n) => { ns.push(n); }
                None    => { return; }
            }
        }
        if ns.len() > 2 { return; }
        self.cur_range = range(ns[0], ns[1] + 1);
        println!("{:?}", ns);
    }
}

impl<'a> Iterator<uint> for ArgIterator<'a> {
    fn next(&mut self) -> Option<uint> {
        loop {
            if self.idx >= self.args.len() { return None; }
            match self.cur_range.next() {
                Some(n) => { return Some(n); }
                None    => {
                    self.idx += 1;
                    self.update_range();
                }
            }
        }
    }
}

fn solve_all<T: Iterator<&'static Problem<'static>>>(mut it: T) {
    let mut total_time  = 0;
    let mut solve_cnt   = 0;
    let mut all_correct = true;

    for p in it {
        let (time, answer) = do bench { (p.solve)() };
        let correct = p.answer == answer;
        print_result(correct, p.id.to_str(), time, answer);

        total_time += time;
        solve_cnt  += 1;
        all_correct &= correct;
    }

    if solve_cnt > 1 {
        print_result(all_correct, "TOTAL", total_time, "");
    }
}

fn main() {
    let args = os::args();
    let args = args.tail();

    if args.is_empty() {
        solve_all(range(0, problem::PROBLEMS.len())
                  .map(|i| problem::PROBLEMS[i]))
    } else {
        solve_all(ArgIterator::new(args)
                  .filter_map(|n| problem::PROBLEMS.bsearch(|&p| p.id.cmp(&n)))
                  .map(|i| problem::PROBLEMS[i]));
    };
}
<commit_msg>re-enable output colors<commit_after>#[feature(managed_boxes)];

extern mod extra;
extern mod common;

use std::{io, os};
use std::iter::Range;
use extra::{time, term};
use extra::term::Terminal;
use problem::Problem;

mod problem;

static NSEC_PER_SEC: u64 = 1000000000;
fn nanosec_to_str(nsec: u64) -> ~str {
    return format!("{}.{:09}",
         (nsec / NSEC_PER_SEC) as uint,
         (nsec % NSEC_PER_SEC) as uint);
}

fn bench<T>(f: proc() -> T) -> (u64, T) {
    let start_time = time::precise_time_ns();
    let result     = f();
    let end_time   = time::precise_time_ns();
    return (end_time - start_time, result);
}

fn color_print<T: Writer>(writer: T, color: term::color::Color, s: &str) {
    match Terminal::new(writer) {
        Ok(ref mut term) => {
            term.fg(color);
            term.write(s.as_bytes());
            term.reset();
        }
        Err(_) => { io::stdout().write(s.as_bytes()) }
    }
}

fn print_result(correct: bool, name: &str, time: u64, comp_answer: &str) {
    write!(&mut io::stdout() as &mut Writer, "[");
    if correct {
        color_print(io::stdout(), term::color::GREEN, "OK");
    } else {
        color_print(io::stdout(), term::color::RED, "NG");
    }
    writeln!(&mut io::stdout() as &mut Writer, "] {:5} {:13} {:20}", name, nanosec_to_str(time), comp_answer);
}

struct ArgIterator<'a> {
    args: &'a [~str],
    idx: uint,
    cur_range: Range<uint>,
}

impl<'a> ArgIterator<'a> {
    pub fn new<'a>(args: &'a [~str]) -> ArgIterator<'a> {
        let mut it = ArgIterator { args: args, idx: 0, cur_range: range(0u, 0) };
        it.update_range();
        return it;
    }

    fn update_range(&mut self) {
        self.cur_range = range(0u, 0);
        if self.idx >= self.args.len() { return; }

        if !self.args[self.idx].contains_char('-') {
            let n = from_str::<uint>(self.args[self.idx]);
            for &n in n.iter() {
                self.cur_range = range(n, n + 1);
            }
            return;
        }

        let mut ns = ~[];
        for ss in self.args[self.idx].split('-') {
            match from_str::<uint>(ss) {
                Some(n) => { ns.push(n); }
                None    => { return; }
            }
        }
        if ns.len() > 2 { return; }
        self.cur_range = range(ns[0], ns[1] + 1);
    }
}

impl<'a> Iterator<uint> for ArgIterator<'a> {
    fn next(&mut self) -> Option<uint> {
        loop {
            if self.idx >= self.args.len() { return None; }
            match self.cur_range.next() {
                Some(n) => { return Some(n); }
                None    => {
                    self.idx += 1;
                    self.update_range();
                }
            }
        }
    }
}

fn solve_all<T: Iterator<&'static Problem<'static>>>(mut it: T) {
    let mut total_time  = 0;
    let mut solve_cnt   = 0;
    let mut all_correct = true;

    for p in it {
        let (time, answer) = do bench { (p.solve)() };
        let correct = p.answer == answer;
        print_result(correct, p.id.to_str(), time, answer);

        total_time += time;
        solve_cnt  += 1;
        all_correct &= correct;
    }

    if solve_cnt > 1 {
        print_result(all_correct, "TOTAL", total_time, "");
    }
}

fn main() {
    let args = os::args();
    let args = args.tail();

    if args.is_empty() {
        solve_all(range(0, problem::PROBLEMS.len())
                  .map(|i| problem::PROBLEMS[i]))
    } else {
        solve_all(ArgIterator::new(args)
                  .filter_map(|n| problem::PROBLEMS.bsearch(|&p| p.id.cmp(&n)))
                  .map(|i| problem::PROBLEMS[i]));
    };
}
<|endoftext|>


<commit_before>// Copyright 2015 Till Höppner
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Traits and structs for conversion between various formats.
//! As the source format may not provide the same information as the
//! target format, all formats must allow for omittable information.

use std::io::{ BufRead, Write };
use std::borrow::Cow;

use event::Event;
use context::Context;

pub mod weechat3;
pub mod energymech;
//pub mod binary;

pub trait Encode<'a, W> where W: Write {
    fn encode(&'a self, context: &'a Context, output: W, event: &'a Event) -> ::Result<()>;
}

pub trait Decode<'a, R, O> where R: BufRead, O: Iterator<Item = ::Result<Event<'a>>> {
    fn decode(&'a mut self, context: &'a Context, input: R) -> O;
}

fn rejoin(s: &[&str], splits: &[char]) -> Cow<'static, str> {
    let len = s.iter().map(|s| s.len()).sum();
    let mut out = s.iter().zip(splits.iter()).fold(String::with_capacity(len),
        |mut s, (b, &split)| { s.push_str(b); s.push(split); s });
    out.pop(); Cow::Owned(out)
}

fn strip_one(s: &str) -> String {
    if s.len() >= 2 { s[1..(s.len() - 1)].to_owned() } else { String::new() }
}
<commit_msg>Re-enable binary<commit_after>// Copyright 2015 Till Höppner
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Traits and structs for conversion between various formats.
//! As the source format may not provide the same information as the
//! target format, all formats must allow for omittable information.

use std::io::{ BufRead, Write };
use std::borrow::Cow;

use event::Event;
use context::Context;

pub mod weechat3;
pub mod energymech;
pub mod binary;

pub trait Encode<'a, W> where W: Write {
    fn encode(&'a self, context: &'a Context, output: W, event: &'a Event) -> ::Result<()>;
}

pub trait Decode<'a, R, O> where R: BufRead, O: Iterator<Item = ::Result<Event<'a>>> {
    fn decode(&'a mut self, context: &'a Context, input: R) -> O;
}

fn rejoin(s: &[&str], splits: &[char]) -> Cow<'static, str> {
    let len = s.iter().map(|s| s.len()).sum();
    let mut out = s.iter().zip(splits.iter()).fold(String::with_capacity(len),
        |mut s, (b, &split)| { s.push_str(b); s.push(split); s });
    out.pop(); Cow::Owned(out)
}

fn strip_one(s: &str) -> String {
    if s.len() >= 2 { s[1..(s.len() - 1)].to_owned() } else { String::new() }
}
<|endoftext|>