<commit_before>use std::io::Cursor;

use kuchiki::NodeRef;
use kuchiki::NodeDataRef;
use kuchiki::NodeData;
use kuchiki::ElementData;

use model::ListTopicItem;
use model::ListTopicTitleItem;
use model::ListTopicAuthorItem;
use model::ShowItem;
use model::ShowReplyItem;
use model::UrlQueryItem;
use reply_model::*;

use regex::Regex;
use url::Url;
use std::collections::HashMap;

#[derive(Clone)]
pub struct Builder { }

impl Builder {
    pub fn new() -> Self {
        Builder {}
    }
    pub fn url_query_item(&mut self, url: &str) -> UrlQueryItem {
        parse_url_query_item(&url)
    }
}

impl Builder {
    pub fn show_item(&mut self, document: &NodeRef,  url: &str) -> Result<ShowItem, &'static str> {

        let url_query = parse_url_query_item(&url);

        let (title, reply_count) = match parse_title_and_reply_count(&document, &url) {
            Ok((title,reply_count)) => (title, reply_count),
            Err(e) =>  { return Err(e) }
        };

        let (page, max_page) = {

            let page_select = document.select("select[name='page']").ok().expect("fail to build page and max_page, reason: 'page_select' not found").last().expect("fail to build page and max_page, reason: 'page_select' not found");
            let page_str = page_select.as_node()
                                      .select("option[selected='selected']")
                                      .ok().expect("fail to build page and max_page, reason: 'page_str' not found")
                                      .next()
                                      .expect("fail to build page and max_page, reason: 'page_str' not found");
            let max_page_str = page_select.as_node().select("option").ok().expect("fail to build page and max_page, reason: 'max_page_str' not found").last().expect("fail to build page and max_page, reason: 'max_page_str' not found");

            let page = page_str.text_contents().trim().to_string().parse::<usize>().unwrap_or(0);
            let max_page = max_page_str.text_contents()
                                       .trim()
                                       .to_string()
                                       .parse::<usize>()
                                       .unwrap_or(0);

            (page, max_page)
        };

        let replies = parse_show_reply_items(&document);

        let show_item = ShowItem {
            url_query: url_query,
            replies: replies,
            page: page,
            max_page: max_page,
            reply_count: String::from(reply_count),
            title: String::from(title),
        };

        Ok(show_item)
    }
    pub fn default_show_item(&self) -> ShowItem {
        default_show_item()
    }
}

impl Builder {
    pub fn list_topic_items(&mut self, document: &NodeRef) -> Vec<ListTopicItem> {
        parse_list_topic_items(&document)
    }
    pub fn default_list_item(&self) -> ListTopicItem {
        default_list_item()
    }
}

fn default_show_item() -> ShowItem {
    ShowItem {
        url_query: UrlQueryItem { channel: "".to_string(), message: String::from("") },
        replies: vec![],
        page: 0,
        max_page: 0,
        reply_count: String::from(""),
        title: String::from(""),
    }
}

fn default_list_item() -> ListTopicItem {
    ListTopicItem {
        title: ListTopicTitleItem {
            url: "".to_string(),
            url_query: UrlQueryItem {
                channel: "".to_string(),
                message: "".to_string()
            },
            text: "".to_string(),
            num_of_pages: 0
        },
        author: ListTopicAuthorItem {
            url: "".to_string(),
            name: "".to_string()
        },
        last_replied_date: "".to_string(),
        last_replied_time: "".to_string(),
        reply_count: "".to_string(),
        rating: "".to_string()
    }
}

fn parse_list_topic_items(document: &NodeRef) -> Vec<ListTopicItem>{

    let trs = match document.select(".Topic_ListPanel tr[id]") {
            Ok(trs) => trs,
            Err(e) => panic!("{:?}", e)
    };

    trs.enumerate().map(|(i, tr)| {
        let items = match tr.as_node().select("td") {
            Ok(items) => items,
            Err(e) => panic!("{:?}", e)
        };
        let mut result = default_list_item();

        for (j, item) in items.enumerate().filter(|&(j, _)| j > 0 && j < 6) {
            match j {
                1 => { result.title = parse_list_topic_title_item(&item) },
                2 => { result.author = parse_list_topic_author_item(&item) },
                3 => {
                    let (date, time) = {
                        let text = item.text_contents().trim().to_string();
                        let map = text.split("\n").map(|x| x.trim().to_string()).collect::<Vec<_>>();
                        if map.len() < 2 {
                            panic!("length of map is invalid.");
                        }
                        (map[0].clone(), map[1].clone())
                    };
                    result.last_replied_date = date;
                    result.last_replied_time = time;
                },
                4 => {
                    let text = item.text_contents().trim().to_string();
                    result.reply_count = text
                },
                5 => {
                    let text = item.text_contents().trim().to_string();
                    result.rating = text
                },
                _ => {}
            }
        }
        result
    }).collect::<Vec<_>>()
}

fn parse_list_topic_title_item(item: &NodeDataRef<ElementData>) -> ListTopicTitleItem {
    let (first_link, links_count) = {
        let mut links = match item.as_node().select("a") {
            Ok(links) => links,
            Err(e) => panic!("ERR: {:?}", e)
        };

        let first_link_option = links.next();
        let last_link_option = links.last();

        let first_link = match first_link_option {
            Some(first_link) => first_link,
            None => { panic!("ERR: Can't find 'first_link'.") }
        };

        let max_page = match last_link_option {
            Some(last_link) =>
                last_link.text_contents().trim().to_string().parse::<usize>()
                .unwrap_or(0)
            ,
            None => { 1 }
        };

        (first_link, max_page)
    };

    let (url_str, url_query_item) = {
        let attrs = &(first_link.attributes).borrow();
        let href = attrs.get("href").unwrap_or("");

        let base_url = match Url::parse("http://forum1.hkgolden.com/view.aspx") {
            Ok(url) => url,
            Err(e) => { panic!(format!("fail to parse Base URL. Reason: {}", e)) }
        };
        let url = match base_url.join(&href) {
            Ok(url) => url,
            Err(e) => { panic!(format!("fail to build URL. Reason: {}", e)) }
        };
        let url_str = url.into_string();
        let url_query_item = parse_url_query_item(&url_str);
        (url_str, url_query_item)
    };

    let text = first_link.text_contents().trim().to_string();

     ListTopicTitleItem {
        url: url_str,
        url_query: url_query_item,
        text: text,
        num_of_pages: links_count
    }
}

fn parse_list_topic_author_item(item: &NodeDataRef<ElementData>) -> ListTopicAuthorItem {
    let (url, name) = {
        let mut links = match item.as_node().select("a") {
            Ok(links) => links,
            Err(e) => panic!("ERR: {:?}", e)
        };

        let first_link_option = links.next();

        let first_link = match first_link_option {
            Some(first_link) => first_link,
            None => { panic!("ERR: Can't find 'first_link'.") }
        };

        let attrs = &(first_link.attributes).borrow();
        let href = attrs.get("href").unwrap_or("").to_string();
        let text = first_link.text_contents().trim().to_string();
        (href, text)
    };

    ListTopicAuthorItem {
       url: url,
       name: name
    }
}

// Show Item
fn parse_title_and_reply_count (document: &NodeRef,  url: &str) -> Result<(String, String), &'static str> {

    return match document.select(".repliers tr") {
        Ok(mut trs) => {
            return match trs.next()  {
                Some(repliers_tr) => {

                    let repliers_header_option = repliers_tr.as_node().select(".repliers_header").ok().map_or(None, |x| x.last() );

                    let repliers_header = match repliers_header_option {
                        Some(x) => x,
                        None => { return Err("fail to build title and reply_count, reason: 'repliers_header' not found"); }
                    };

                    let divs = match repliers_header.as_node().select("div").ok() {
                        Some(x) => x.collect::<Vec<_>>(),
                        None => { return Err("fail to build title and reply_count, reason: 'divs' not found"); }
                    };

                    let topic_data = divs.iter().enumerate().map(|(index, div)| {
                                             let s_trimmed = div.text_contents().trim().to_string();
                                             if index == 1 {
                                                 let re = Regex::new(r"^(?P<count>\d+)個回應$").expect("fail to build title and reply_count, reason: invalid regex");
                                                 let cap = re.captures(&s_trimmed).expect("fail to build title and reply_count, reason: 'cap' not found");
                                                 // String::from(cap.name("count").unwrap_or("0"))
                                                 cap.name("count").unwrap_or("0").to_string()
                                             } else {
                                                 s_trimmed
                                             }
                                         })
                                         .collect::<Vec<_>>();

                    if topic_data.len() < 2 {
                        Err(&"length of topic_data is invalid.")
                    } else {
                        Ok(
                            (topic_data.get(0).expect("fail to build title and reply_count, reason: 'topic_data' not found").to_string(), // return as title
                            topic_data.get(1).expect("fail to build title and reply_count, reason: 'topic_data' not found").to_string() /* return as reply_count */)
                        )
                    }
                },
                None => Err(&"fail to build title and reply_count, reason: 'repliers_tr' not found")
            }
        },
        Err(e) =>  Err("fail to build title and reply_count, reason: 'repliers_tr' not found")
    };

}

fn parse_show_reply_items(document: &NodeRef) -> Vec<ShowReplyItem> {

    let replies_data = document.select(".repliers tr[userid][username]")
                               .ok().expect("fail to parse show reply items, reason: 'replies_data' not found")
                               .collect::<Vec<_>>();

    replies_data.iter()
                .enumerate()
                .map(|(index, tr)| {

                    let tr_attrs = (&tr.attributes).borrow();
                    let userid = tr_attrs.get("userid").expect("fail to parse show reply item, reason: 'userid' not found");
                    let username = tr_attrs.get("username").expect("fail to parse show reply item, reason: 'userame' not found");

                    let content_elm = tr.as_node()
                                        .select(".repliers_right .ContentGrid")
                                        .ok().expect("fail to parse show reply item, reason: 'content_elm' not found")
                                        .next()
                                        .expect("fail to parse show reply item, reason: 'content_elm' not found"); // first

                    let mut buff = Cursor::new(Vec::new());
                    let serialize_result = content_elm.as_node().serialize(&mut buff);
                    let vec = buff.into_inner();
                    let content = String::from_utf8(vec).expect("fail to parse show reply item, reason: 'content' invalid");

                    let datatime = tr.as_node()
                                     .select(".repliers_right span")
                                     .ok().expect("fail to parse show reply item, reason: 'datatime' not found")
                                     .last()
                                     .expect("fail to parse show reply item, reason: 'datatime' not found")
                                     .text_contents();

                    let mut vec: Vec<NodeType> = Vec::new();

                    vec = recursive(content_elm.as_node());

                    ShowReplyItem {
                        userid: String::from(userid),
                        username: String::from(username),
                        content: String::from(content),
                        body: vec,
                        published_at: String::from(datatime),
                    }
                })
                .collect::<Vec<_>>()
}

fn parse_url_query_item(url_str: &str) -> UrlQueryItem {
    let url = Url::parse(&url_str).expect("fail to parse url query item, reason: invalid url");
    let query = url.query().expect("fail to parse url query item, reason: invalid url query");
    let re = Regex::new(r"(\\?|&)(?P<key>[^&=]+)=(?P<value>[^&]+)").expect("fail to parse url query item, reason: invalid regex");

    let (channel, message) = {

        let mut map = HashMap::new();

        for cap in re.captures_iter(query) {
            let key = cap.name("key").unwrap_or("").to_string();
            let value = cap.name("value").unwrap_or("").to_string();
            map.entry(key).or_insert(value);
        }

        if map.len() < 2 {
            panic!("length of map is invalid.")
        }

        (
            map.get("type").expect("fail to parse url query item, reason: can not get value of 'type' attribute").to_string(),
            map.get("message").expect("fail to parse url query item, reason: can not get value of 'message' attribute").to_string()
        )
    };

    UrlQueryItem {
        channel: String::from(channel),
        message: String::from(message)
    }

}

fn recursive(elm: &NodeRef) -> Vec<NodeType> {

    let mut vec: Vec<NodeType> = Vec::new();

    for (index, child) in elm.children().enumerate() {
        // println!("[{}] => {:?}", index, child);
        let node_data = child.data().clone();

        match node_data {
            NodeData::Element(element_data) => {
                // println!("[{}] => [ELEMENT] => {:?}", index, element_data);
                // println!("[{}] => [ELEMENT] => {:?}", index, child);

                if element_data.name.local.trim().eq("blockquote") {

                    // println!("[{}] => [ELEMENT] => {:?}", index, child.children());
                    let subvec = recursive(&child);
                    let node = NodeType::BlockQuote(BlockQuoteNode { data: subvec });
                    vec.push(node);
                } else if element_data.name.local.trim().eq("br") {
                    let node = NodeType::Br(BrNode {});
                    vec.push(node);
                } else if element_data.name.local.trim().eq("img") {

                    let attrs = (&element_data.attributes).borrow();
                    let url = attrs.get("src").unwrap_or("");
                    let alt = attrs.get("alt").unwrap_or("");
                    let node = NodeType::Image(ImageNode { data: url.to_string(), alt: alt.to_string() });
                    vec.push(node);

                } else {
                    // println!("[{}] => [ELEMENT] => {:?}", index, child);
                    let mut subvec = recursive(&child);
                    vec.append(&mut subvec);
                }
            }
            NodeData::Text(rc) => {
                // println!("[{}] => [TEXT] => {:?}", index, rc);
                let d = rc.clone();
                let b = d.borrow();

                let s = b.trim().to_string();

                if s == "\n" {
                    // s = "\\n".to_string()
                    continue;
                }

                let node = NodeType::Text(TextNode { data: s });
                vec.push(node);
            }
            _ => {}
        }
    }
    vec
}
<commit_msg>builder - replace expect in parse_title_and_reply_count<commit_after>use std::io::Cursor;

use kuchiki::NodeRef;
use kuchiki::NodeDataRef;
use kuchiki::NodeData;
use kuchiki::ElementData;

use model::ListTopicItem;
use model::ListTopicTitleItem;
use model::ListTopicAuthorItem;
use model::ShowItem;
use model::ShowReplyItem;
use model::UrlQueryItem;
use reply_model::*;

use regex::Regex;
use url::Url;
use std::collections::HashMap;

#[derive(Clone)]
pub struct Builder { }

impl Builder {
    pub fn new() -> Self {
        Builder {}
    }
    pub fn url_query_item(&mut self, url: &str) -> UrlQueryItem {
        parse_url_query_item(&url)
    }
}

impl Builder {
    pub fn show_item(&mut self, document: &NodeRef,  url: &str) -> Result<ShowItem, &'static str> {

        let url_query = parse_url_query_item(&url);

        let (title, reply_count) = match parse_title_and_reply_count(&document, &url) {
            Ok((title,reply_count)) => (title, reply_count),
            Err(e) =>  { return Err(e) }
        };

        let (page, max_page) = {

            let page_select = document.select("select[name='page']").ok().expect("fail to build page and max_page, reason: 'page_select' not found").last().expect("fail to build page and max_page, reason: 'page_select' not found");
            let page_str = page_select.as_node()
                                      .select("option[selected='selected']")
                                      .ok().expect("fail to build page and max_page, reason: 'page_str' not found")
                                      .next()
                                      .expect("fail to build page and max_page, reason: 'page_str' not found");
            let max_page_str = page_select.as_node().select("option").ok().expect("fail to build page and max_page, reason: 'max_page_str' not found").last().expect("fail to build page and max_page, reason: 'max_page_str' not found");

            let page = page_str.text_contents().trim().to_string().parse::<usize>().unwrap_or(0);
            let max_page = max_page_str.text_contents()
                                       .trim()
                                       .to_string()
                                       .parse::<usize>()
                                       .unwrap_or(0);

            (page, max_page)
        };

        let replies = parse_show_reply_items(&document);

        let show_item = ShowItem {
            url_query: url_query,
            replies: replies,
            page: page,
            max_page: max_page,
            reply_count: String::from(reply_count),
            title: String::from(title),
        };

        Ok(show_item)
    }
    pub fn default_show_item(&self) -> ShowItem {
        default_show_item()
    }
}

impl Builder {
    pub fn list_topic_items(&mut self, document: &NodeRef) -> Vec<ListTopicItem> {
        parse_list_topic_items(&document)
    }
    pub fn default_list_item(&self) -> ListTopicItem {
        default_list_item()
    }
}

fn default_show_item() -> ShowItem {
    ShowItem {
        url_query: UrlQueryItem { channel: "".to_string(), message: String::from("") },
        replies: vec![],
        page: 0,
        max_page: 0,
        reply_count: String::from(""),
        title: String::from(""),
    }
}

fn default_list_item() -> ListTopicItem {
    ListTopicItem {
        title: ListTopicTitleItem {
            url: "".to_string(),
            url_query: UrlQueryItem {
                channel: "".to_string(),
                message: "".to_string()
            },
            text: "".to_string(),
            num_of_pages: 0
        },
        author: ListTopicAuthorItem {
            url: "".to_string(),
            name: "".to_string()
        },
        last_replied_date: "".to_string(),
        last_replied_time: "".to_string(),
        reply_count: "".to_string(),
        rating: "".to_string()
    }
}

fn parse_list_topic_items(document: &NodeRef) -> Vec<ListTopicItem>{

    let trs = match document.select(".Topic_ListPanel tr[id]") {
            Ok(trs) => trs,
            Err(e) => panic!("{:?}", e)
    };

    trs.enumerate().map(|(i, tr)| {
        let items = match tr.as_node().select("td") {
            Ok(items) => items,
            Err(e) => panic!("{:?}", e)
        };
        let mut result = default_list_item();

        for (j, item) in items.enumerate().filter(|&(j, _)| j > 0 && j < 6) {
            match j {
                1 => { result.title = parse_list_topic_title_item(&item) },
                2 => { result.author = parse_list_topic_author_item(&item) },
                3 => {
                    let (date, time) = {
                        let text = item.text_contents().trim().to_string();
                        let map = text.split("\n").map(|x| x.trim().to_string()).collect::<Vec<_>>();
                        if map.len() < 2 {
                            panic!("length of map is invalid.");
                        }
                        (map[0].clone(), map[1].clone())
                    };
                    result.last_replied_date = date;
                    result.last_replied_time = time;
                },
                4 => {
                    let text = item.text_contents().trim().to_string();
                    result.reply_count = text
                },
                5 => {
                    let text = item.text_contents().trim().to_string();
                    result.rating = text
                },
                _ => {}
            }
        }
        result
    }).collect::<Vec<_>>()
}

fn parse_list_topic_title_item(item: &NodeDataRef<ElementData>) -> ListTopicTitleItem {
    let (first_link, links_count) = {
        let mut links = match item.as_node().select("a") {
            Ok(links) => links,
            Err(e) => panic!("ERR: {:?}", e)
        };

        let first_link_option = links.next();
        let last_link_option = links.last();

        let first_link = match first_link_option {
            Some(first_link) => first_link,
            None => { panic!("ERR: Can't find 'first_link'.") }
        };

        let max_page = match last_link_option {
            Some(last_link) =>
                last_link.text_contents().trim().to_string().parse::<usize>()
                .unwrap_or(0)
            ,
            None => { 1 }
        };

        (first_link, max_page)
    };

    let (url_str, url_query_item) = {
        let attrs = &(first_link.attributes).borrow();
        let href = attrs.get("href").unwrap_or("");

        let base_url = match Url::parse("http://forum1.hkgolden.com/view.aspx") {
            Ok(url) => url,
            Err(e) => { panic!(format!("fail to parse Base URL. Reason: {}", e)) }
        };
        let url = match base_url.join(&href) {
            Ok(url) => url,
            Err(e) => { panic!(format!("fail to build URL. Reason: {}", e)) }
        };
        let url_str = url.into_string();
        let url_query_item = parse_url_query_item(&url_str);
        (url_str, url_query_item)
    };

    let text = first_link.text_contents().trim().to_string();

     ListTopicTitleItem {
        url: url_str,
        url_query: url_query_item,
        text: text,
        num_of_pages: links_count
    }
}

fn parse_list_topic_author_item(item: &NodeDataRef<ElementData>) -> ListTopicAuthorItem {
    let (url, name) = {
        let mut links = match item.as_node().select("a") {
            Ok(links) => links,
            Err(e) => panic!("ERR: {:?}", e)
        };

        let first_link_option = links.next();

        let first_link = match first_link_option {
            Some(first_link) => first_link,
            None => { panic!("ERR: Can't find 'first_link'.") }
        };

        let attrs = &(first_link.attributes).borrow();
        let href = attrs.get("href").unwrap_or("").to_string();
        let text = first_link.text_contents().trim().to_string();
        (href, text)
    };

    ListTopicAuthorItem {
       url: url,
       name: name
    }
}

// Show Item
fn parse_title_and_reply_count (document: &NodeRef,  url: &str) -> Result<(String, String), &'static str> {

    return match document.select(".repliers tr") {
        Ok(mut trs) => {
            return match trs.next()  {
                Some(repliers_tr) => {

                    let repliers_header_option = repliers_tr.as_node().select(".repliers_header").ok().map_or(None, |x| x.last() );

                    let repliers_header = match repliers_header_option {
                        Some(x) => x,
                        None => { return Err("fail to build title and reply_count, reason: 'repliers_header' not found"); }
                    };

                    let divs = match repliers_header.as_node().select("div").ok() {
                        Some(x) => x.collect::<Vec<_>>(),
                        None => { return Err("fail to build title and reply_count, reason: 'divs' not found"); }
                    };

                    let re = Regex::new(r"^(?P<count>\d+)個回應$").expect("fail to build title and reply_count, reason: invalid regex");
                    let topic_data = divs.iter().enumerate().map(|(index, div)| {
                                             let s_trimmed = div.text_contents().trim().to_string();
                                             if index == 1 {
                                                let cap_option = re.captures(&s_trimmed);
                                                if cap_option.is_none() {
                                                    None // return Err("fail to build title and reply_count, reason: 'cap' not found");
                                                } else {
                                                    Some(cap_option.unwrap().name("count").unwrap_or("0").to_string())
                                                }
                                             } else {
                                                 Some(s_trimmed)
                                             }
                                         })
                                         .collect::<Vec<_>>();

                    if topic_data.len() != 2 {
                        Err(&"length of topic_data is invalid.")
                    } else {

                        let title = topic_data.get(0).map_or(None, |x| x.clone());
                        let reply_count = topic_data.get(1).map_or(None, |x| x.clone());

                        if title.is_none() || reply_count.is_none() {
                            return Err(&"fail to build title and reply_count, reason: 'topic_data' not found");
                        }

                        Ok(
                            (
                                title.unwrap().to_string(), // return as title
                                reply_count.unwrap().to_string() /* return as reply_count */
                            )
                        )
                    }
                },
                None => Err(&"fail to build title and reply_count, reason: 'repliers_tr' not found")
            }
        },
        Err(e) =>  Err("fail to build title and reply_count, reason: 'repliers_tr' not found")
    };

}

fn parse_show_reply_items(document: &NodeRef) -> Vec<ShowReplyItem> {

    let replies_data = document.select(".repliers tr[userid][username]")
                               .ok().expect("fail to parse show reply items, reason: 'replies_data' not found")
                               .collect::<Vec<_>>();

    replies_data.iter()
                .enumerate()
                .map(|(index, tr)| {

                    let tr_attrs = (&tr.attributes).borrow();
                    let userid = tr_attrs.get("userid").expect("fail to parse show reply item, reason: 'userid' not found");
                    let username = tr_attrs.get("username").expect("fail to parse show reply item, reason: 'userame' not found");

                    let content_elm = tr.as_node()
                                        .select(".repliers_right .ContentGrid")
                                        .ok().expect("fail to parse show reply item, reason: 'content_elm' not found")
                                        .next()
                                        .expect("fail to parse show reply item, reason: 'content_elm' not found"); // first

                    let mut buff = Cursor::new(Vec::new());
                    let serialize_result = content_elm.as_node().serialize(&mut buff);
                    let vec = buff.into_inner();
                    let content = String::from_utf8(vec).expect("fail to parse show reply item, reason: 'content' invalid");

                    let datatime = tr.as_node()
                                     .select(".repliers_right span")
                                     .ok().expect("fail to parse show reply item, reason: 'datatime' not found")
                                     .last()
                                     .expect("fail to parse show reply item, reason: 'datatime' not found")
                                     .text_contents();

                    let mut vec: Vec<NodeType> = Vec::new();

                    vec = recursive(content_elm.as_node());

                    ShowReplyItem {
                        userid: String::from(userid),
                        username: String::from(username),
                        content: String::from(content),
                        body: vec,
                        published_at: String::from(datatime),
                    }
                })
                .collect::<Vec<_>>()
}

fn parse_url_query_item(url_str: &str) -> UrlQueryItem {
    let url = Url::parse(&url_str).expect("fail to parse url query item, reason: invalid url");
    let query = url.query().expect("fail to parse url query item, reason: invalid url query");
    let re = Regex::new(r"(\\?|&)(?P<key>[^&=]+)=(?P<value>[^&]+)").expect("fail to parse url query item, reason: invalid regex");

    let (channel, message) = {

        let mut map = HashMap::new();

        for cap in re.captures_iter(query) {
            let key = cap.name("key").unwrap_or("").to_string();
            let value = cap.name("value").unwrap_or("").to_string();
            map.entry(key).or_insert(value);
        }

        if map.len() < 2 {
            panic!("length of map is invalid.")
        }

        (
            map.get("type").expect("fail to parse url query item, reason: can not get value of 'type' attribute").to_string(),
            map.get("message").expect("fail to parse url query item, reason: can not get value of 'message' attribute").to_string()
        )
    };

    UrlQueryItem {
        channel: String::from(channel),
        message: String::from(message)
    }

}

fn recursive(elm: &NodeRef) -> Vec<NodeType> {

    let mut vec: Vec<NodeType> = Vec::new();

    for (index, child) in elm.children().enumerate() {
        // println!("[{}] => {:?}", index, child);
        let node_data = child.data().clone();

        match node_data {
            NodeData::Element(element_data) => {
                // println!("[{}] => [ELEMENT] => {:?}", index, element_data);
                // println!("[{}] => [ELEMENT] => {:?}", index, child);

                if element_data.name.local.trim().eq("blockquote") {

                    // println!("[{}] => [ELEMENT] => {:?}", index, child.children());
                    let subvec = recursive(&child);
                    let node = NodeType::BlockQuote(BlockQuoteNode { data: subvec });
                    vec.push(node);
                } else if element_data.name.local.trim().eq("br") {
                    let node = NodeType::Br(BrNode {});
                    vec.push(node);
                } else if element_data.name.local.trim().eq("img") {

                    let attrs = (&element_data.attributes).borrow();
                    let url = attrs.get("src").unwrap_or("");
                    let alt = attrs.get("alt").unwrap_or("");
                    let node = NodeType::Image(ImageNode { data: url.to_string(), alt: alt.to_string() });
                    vec.push(node);

                } else {
                    // println!("[{}] => [ELEMENT] => {:?}", index, child);
                    let mut subvec = recursive(&child);
                    vec.append(&mut subvec);
                }
            }
            NodeData::Text(rc) => {
                // println!("[{}] => [TEXT] => {:?}", index, rc);
                let d = rc.clone();
                let b = d.borrow();

                let s = b.trim().to_string();

                if s == "\n" {
                    // s = "\\n".to_string()
                    continue;
                }

                let node = NodeType::Text(TextNode { data: s });
                vec.push(node);
            }
            _ => {}
        }
    }
    vec
}
<|endoftext|>


<commit_before>use {Action, InputTranslator, InputRebind, MouseTranslationData, to_act_bt_hashmap};
use input::Button;
use window::Size;
use std::convert::Into;
use std::default::Default;

/// Convenience object for constructing an InputMap.
pub struct RebindBuilder<A: Action> {
    input_remappings: Vec<(Button, A)>,
    mouse_data: MouseTranslationData
}

impl<A: Action> RebindBuilder<A> {
    /// Creates a new `RebindBuilder` with the specified viewport size.
    pub fn new<S: Into<Size> + Sized>(size: S) -> Self {
        RebindBuilder {
            input_remappings: vec![],
            mouse_data: MouseTranslationData::new(size)
        }
    }

    /// Set whether the x scroll is inverted on the builder (and thus
    /// on the built object).
    pub fn x_scroll_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.x_axis_scroll_inverted = invert;
        self
    }

    /// Returns true if the x scroll is inverted on the builder (and thus
    /// on the built object).
    pub fn get_x_scroll_inverted(&self) -> &bool {
        &self.mouse_data.x_axis_scroll_inverted
    }

    /// Set whether the y scroll is inverted on the builder (and thus
    /// on the built object).
    pub fn y_scroll_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.y_axis_scroll_inverted = invert;
        self
    }

    /// Returns true if the y scroll is inverted on the builder (and thus
    /// on the built object).
    pub fn get_y_scroll_inverted(&self) -> &bool {
        &self.mouse_data.y_axis_scroll_inverted
    }

    /// Set whether the x axis motion is inverted on the builder (and thus
    /// on the built object).
    pub fn x_motion_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.x_axis_motion_inverted = invert;
        self
    }

    /// Returns true if the x axis motion is inverted on the builder (and thus
    /// on the built object).
    pub fn get_x_motion_inverted(&self) -> &bool {
        &self.mouse_data.x_axis_motion_inverted
    }

    /// Set whether the y axis motion is inverted on the builder (and thus
    /// on the built object).
    pub fn y_motion_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.y_axis_motion_inverted = invert;
        self
    }

    /// Returns true if the y axis motion is inverted on the builder (and thus
    /// on the built object).
    pub fn get_y_motion_inverted(&self) -> &bool {
        &self.mouse_data.y_axis_motion_inverted
    }

    /// Set the mouse sensitivity
    pub fn mouse_sensitivity(mut self, sensitivity: f64) -> Self {
        self.mouse_data.sensitivity = sensitivity;
        self
    }

    /// Returns the mouse sensitivity
    pub fn get_mouse_sensitivity(&self) -> &f64 {
        &self.mouse_data.sensitivity
    }

    /// Sets the viewport size used for mouse position calculations.
    pub fn viewport_size(mut self, size: Size) -> Self {
        self.mouse_data.viewport_size = size;
        self
    }

    /// Returns the currently set viewport size.
    pub fn get_viewport_size(&self) -> &Size {
        &self.mouse_data.viewport_size
    }

    /// Add an association between the Button and Action in the built object.
    pub fn with_action_mapping(mut self, button: Button, action: A) -> Self {
        self.input_remappings.push((button, action));
        self
    }

    /// Creates an `InputTranslator` from this builder object.
    pub fn build_translator(self) -> InputTranslator<A> { self.into() }

    /// Creates an `InputRebind` from this builder object.
    pub fn build_rebind(self) -> InputRebind<A> { self.into() }
}

/// Creates a new `RebindBuilder`. The viewport size is set to (800, 600).
impl<A: Action> Default for RebindBuilder<A> {
    fn default() -> Self {
        Self::new((800, 600))
    }
}

impl<A: Action> Into<InputTranslator<A>> for RebindBuilder<A> {
    fn into(self) -> InputTranslator<A> {
        let mut input_map = InputTranslator::new(self.mouse_data.viewport_size);

        input_map.mouse_translator.data = self.mouse_data;
        input_map.keymap = self.input_remappings.iter().cloned().collect();

        input_map
    }
}

impl<A: Action> Into<InputRebind<A>> for RebindBuilder<A> {
    fn into(self) -> InputRebind<A> {
        let mut input_rebind = InputRebind::new(self.mouse_data.viewport_size);

        input_rebind.mouse_data = self.mouse_data;
        input_rebind.keymap = to_act_bt_hashmap(self.input_remappings.iter().cloned());

        input_rebind
    }
}<commit_msg>`RebindBuilder` now returns its member vars as values, not references, to match `InputRebind`<commit_after>use {Action, InputTranslator, InputRebind, MouseTranslationData, to_act_bt_hashmap};
use input::Button;
use window::Size;
use std::convert::Into;
use std::default::Default;

/// Convenience object for constructing an InputMap.
pub struct RebindBuilder<A: Action> {
    input_remappings: Vec<(Button, A)>,
    mouse_data: MouseTranslationData
}

impl<A: Action> RebindBuilder<A> {
    /// Creates a new `RebindBuilder` with the specified viewport size.
    pub fn new<S: Into<Size> + Sized>(size: S) -> Self {
        RebindBuilder {
            input_remappings: vec![],
            mouse_data: MouseTranslationData::new(size)
        }
    }

    /// Set whether the x scroll is inverted on the builder (and thus
    /// on the built object).
    pub fn x_scroll_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.x_axis_scroll_inverted = invert;
        self
    }

    /// Returns true if the x scroll is inverted on the builder (and thus
    /// on the built object).
    pub fn get_x_scroll_inverted(&self) -> bool {
        self.mouse_data.x_axis_scroll_inverted
    }

    /// Set whether the y scroll is inverted on the builder (and thus
    /// on the built object).
    pub fn y_scroll_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.y_axis_scroll_inverted = invert;
        self
    }

    /// Returns true if the y scroll is inverted on the builder (and thus
    /// on the built object).
    pub fn get_y_scroll_inverted(&self) -> bool {
        self.mouse_data.y_axis_scroll_inverted
    }

    /// Set whether the x axis motion is inverted on the builder (and thus
    /// on the built object).
    pub fn x_motion_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.x_axis_motion_inverted = invert;
        self
    }

    /// Returns true if the x axis motion is inverted on the builder (and thus
    /// on the built object).
    pub fn get_x_motion_inverted(&self) -> bool {
        self.mouse_data.x_axis_motion_inverted
    }

    /// Set whether the y axis motion is inverted on the builder (and thus
    /// on the built object).
    pub fn y_motion_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.y_axis_motion_inverted = invert;
        self
    }

    /// Returns true if the y axis motion is inverted on the builder (and thus
    /// on the built object).
    pub fn get_y_motion_inverted(&self) -> bool {
        self.mouse_data.y_axis_motion_inverted
    }

    /// Set the mouse sensitivity
    pub fn mouse_sensitivity(mut self, sensitivity: f64) -> Self {
        self.mouse_data.sensitivity = sensitivity;
        self
    }

    /// Returns the mouse sensitivity
    pub fn get_mouse_sensitivity(&self) -> f64 {
        self.mouse_data.sensitivity
    }

    /// Sets the viewport size used for mouse position calculations.
    pub fn viewport_size(mut self, size: Size) -> Self {
        self.mouse_data.viewport_size = size;
        self
    }

    /// Returns the currently set viewport size.
    pub fn get_viewport_size(&self) -> Size {
        self.mouse_data.viewport_size
    }

    /// Add an association between the Button and Action in the built object.
    pub fn with_action_mapping(mut self, button: Button, action: A) -> Self {
        self.input_remappings.push((button, action));
        self
    }

    /// Creates an `InputTranslator` from this builder object.
    pub fn build_translator(self) -> InputTranslator<A> { self.into() }

    /// Creates an `InputRebind` from this builder object.
    pub fn build_rebind(self) -> InputRebind<A> { self.into() }
}

/// Creates a new `RebindBuilder`. The viewport size is set to (800, 600).
impl<A: Action> Default for RebindBuilder<A> {
    fn default() -> Self {
        Self::new((800, 600))
    }
}

impl<A: Action> Into<InputTranslator<A>> for RebindBuilder<A> {
    fn into(self) -> InputTranslator<A> {
        let mut input_map = InputTranslator::new(self.mouse_data.viewport_size);

        input_map.mouse_translator.data = self.mouse_data;
        input_map.keymap = self.input_remappings.iter().cloned().collect();

        input_map
    }
}

impl<A: Action> Into<InputRebind<A>> for RebindBuilder<A> {
    fn into(self) -> InputRebind<A> {
        let mut input_rebind = InputRebind::new(self.mouse_data.viewport_size);

        input_rebind.mouse_data = self.mouse_data;
        input_rebind.keymap = to_act_bt_hashmap(self.input_remappings.iter().cloned());

        input_rebind
    }
}<|endoftext|>


<commit_before>// Copyright 2021 TiKV Project Authors. Licensed under Apache-2.0.

use std::{iter, thread::sleep, time::Duration};

use rand::prelude::SliceRandom;
use test_util::alloc_port;
use tikv_util::config::ReadableDuration;
use tokio::time::Instant;

use super::test_suite::TestSuite;

#[test]
pub fn test_enable() {
    let mut test_suite = TestSuite::new(resource_metering::Config {
        receiver_address: "".to_string(),
        report_receiver_interval: ReadableDuration::millis(2500),
        max_resource_groups: 5000,
        precision: ReadableDuration::secs(1),
    });

    let port = alloc_port();
    test_suite.start_receiver_at(port);

    // Workload
    // [req-1, req-2]
    test_suite.setup_workload(vec!["req-1", "req-2"]);

    // | Address |
    // |   x     |
    sleep(Duration::from_millis(3000));
    assert!(test_suite.nonblock_receiver_all().is_empty());

    // | Address |
    // |   o     |
    test_suite.cfg_receiver_address(format!("127.0.0.1:{}", port));
    let res = test_suite.block_receive_one();
    assert!(res.contains_key("req-1"));
    assert!(res.contains_key("req-2"));

    // | Address |
    // |   x     |
    test_suite.cfg_receiver_address("");
    test_suite.flush_receiver();
    sleep(Duration::from_millis(3000));
    assert!(test_suite.nonblock_receiver_all().is_empty());

    // | Address |
    // |   o     |
    test_suite.cfg_receiver_address(format!("127.0.0.1:{}", port));
    let res = test_suite.block_receive_one();
    assert!(res.contains_key("req-1"));
    assert!(res.contains_key("req-2"));
}

#[test]
pub fn test_report_interval() {
    let port = alloc_port();
    let mut test_suite = TestSuite::new(resource_metering::Config {
        receiver_address: format!("127.0.0.1:{}", port),
        report_receiver_interval: ReadableDuration::secs(3),
        max_resource_groups: 5000,
        precision: ReadableDuration::secs(1),
    });
    test_suite.start_receiver_at(port);

    // Workload
    // [req-1, req-2]
    test_suite.setup_workload(vec!["req-1", "req-2"]);

    // | Report Interval |
    // |       3s        |
    let res = test_suite.block_receive_one();
    assert!(res.contains_key("req-1"));
    assert!(res.contains_key("req-2"));

    // | Report Interval |
    // |       1s        |
    test_suite.cfg_report_receiver_interval("1s");
    let res = test_suite.block_receive_one();
    let begin = Instant::now();
    assert!(res.contains_key("req-1"));
    assert!(res.contains_key("req-2"));
    let res = test_suite.block_receive_one();
    let duration = begin.elapsed();
    assert!(res.contains_key("req-1"));
    assert!(res.contains_key("req-2"));
    assert!(Duration::from_millis(800) < duration && duration < Duration::from_millis(1200));
}

#[test]
pub fn test_max_resource_groups() {
    let port = alloc_port();
    let mut test_suite = TestSuite::new(resource_metering::Config {
        receiver_address: format!("127.0.0.1:{}", port),
        report_receiver_interval: ReadableDuration::secs(4),
        max_resource_groups: 5000,
        precision: ReadableDuration::secs(2),
    });
    test_suite.start_receiver_at(port);

    // Workload
    // [req-{1..3} * 6, req-{4..5} * 1]
    let mut wl = iter::repeat(1..=3)
        .take(6)
        .flatten()
        .chain(4..=5)
        .map(|n| format!("req-{}", n))
        .collect::<Vec<_>>();
    wl.shuffle(&mut rand::thread_rng());
    test_suite.setup_workload(wl);

    // | Max Resource Groups |
    // |       5000          |
    let res = test_suite.block_receive_one();
    assert!(res.contains_key("req-1"));
    assert!(res.contains_key("req-2"));
    assert!(res.contains_key("req-3"));
    assert!(res.contains_key("req-4"));
    assert!(res.contains_key("req-5"));

    // | Max Resource Groups |
    // |        3            |
    test_suite.cfg_max_resource_groups(3);
    test_suite.flush_receiver();
    let res = test_suite.block_receive_one();
    assert_eq!(res.len(), 4);
    assert!(res.contains_key("req-1"));
    assert!(res.contains_key("req-2"));
    assert!(res.contains_key("req-3"));
    assert!(res.contains_key(""));
}

#[test]
pub fn test_precision() {
    let port = alloc_port();
    let mut test_suite = TestSuite::new(resource_metering::Config {
        receiver_address: format!("127.0.0.1:{}", port),
        report_receiver_interval: ReadableDuration::secs(3),
        max_resource_groups: 5000,
        precision: ReadableDuration::secs(1),
    });
    test_suite.start_receiver_at(port);

    // Workload
    // [req-1]
    test_suite.setup_workload(vec!["req-1"]);

    // | Precision |
    // |    1s     |
    let res = test_suite.block_receive_one();
    let (secs, _) = res.get("req-1").unwrap();
    for (l, r) in secs.iter().zip({
        let mut next_secs = secs.iter();
        next_secs.next();
        next_secs
    }) {
        let diff = r - l;
        assert!(diff <= 2);
    }

    // | Precision |
    // |    3s     |
    test_suite.cfg_precision("3s");
    test_suite.cfg_report_receiver_interval("9s");
    test_suite.flush_receiver();
    let res = test_suite.block_receive_one();
    let (secs, _) = res.get("req-1").unwrap();
    for (l, r) in secs.iter().zip({
        let mut next_secs = secs.iter();
        next_secs.next();
        next_secs
    }) {
        let diff = r - l;
        assert!((2..=4).contains(&diff));
    }
}
<commit_msg>test: stabilize resouce_metering::test_dynamic_config::test_report_interval (#12540)<commit_after>// Copyright 2021 TiKV Project Authors. Licensed under Apache-2.0.

use std::{iter, thread::sleep, time::Duration};

use rand::prelude::SliceRandom;
use test_util::alloc_port;
use tikv_util::config::ReadableDuration;
use tokio::time::Instant;

use super::test_suite::TestSuite;

#[test]
pub fn test_enable() {
    let mut test_suite = TestSuite::new(resource_metering::Config {
        receiver_address: "".to_string(),
        report_receiver_interval: ReadableDuration::millis(2500),
        max_resource_groups: 5000,
        precision: ReadableDuration::secs(1),
    });

    let port = alloc_port();
    test_suite.start_receiver_at(port);

    // Workload
    // [req-1, req-2]
    test_suite.setup_workload(vec!["req-1", "req-2"]);

    // | Address |
    // |   x     |
    sleep(Duration::from_millis(3000));
    assert!(test_suite.nonblock_receiver_all().is_empty());

    // | Address |
    // |   o     |
    test_suite.cfg_receiver_address(format!("127.0.0.1:{}", port));
    let res = test_suite.block_receive_one();
    assert!(res.contains_key("req-1"));
    assert!(res.contains_key("req-2"));

    // | Address |
    // |   x     |
    test_suite.cfg_receiver_address("");
    test_suite.flush_receiver();
    sleep(Duration::from_millis(3000));
    assert!(test_suite.nonblock_receiver_all().is_empty());

    // | Address |
    // |   o     |
    test_suite.cfg_receiver_address(format!("127.0.0.1:{}", port));
    let res = test_suite.block_receive_one();
    assert!(res.contains_key("req-1"));
    assert!(res.contains_key("req-2"));
}

#[test]
pub fn test_report_interval() {
    let port = alloc_port();
    let mut test_suite = TestSuite::new(resource_metering::Config {
        receiver_address: format!("127.0.0.1:{}", port),
        report_receiver_interval: ReadableDuration::secs(3),
        max_resource_groups: 5000,
        precision: ReadableDuration::secs(1),
    });
    test_suite.start_receiver_at(port);

    // Workload
    // [req-1, req-2]
    test_suite.setup_workload(vec!["req-1", "req-2"]);

    // | Report Interval |
    // |       3s        |
    let res = test_suite.block_receive_one();
    assert!(res.contains_key("req-1"));
    assert!(res.contains_key("req-2"));

    // | Report Interval |
    // |       1s        |
    test_suite.cfg_report_receiver_interval("1s");

    const RETRY_TIMES: usize = 3;
    let (_, mut first_recv_time) = (test_suite.block_receive_one(), Instant::now());
    for _ in 0..RETRY_TIMES {
        let (_, second_recv_time) = (test_suite.block_receive_one(), Instant::now());
        let duration = second_recv_time - first_recv_time;

        if Duration::from_millis(800) < duration && duration < Duration::from_millis(1200) {
            // test passed
            return;
        }
        first_recv_time = second_recv_time;
    }
    panic!("failed {} times", RETRY_TIMES)
}

#[test]
pub fn test_max_resource_groups() {
    let port = alloc_port();
    let mut test_suite = TestSuite::new(resource_metering::Config {
        receiver_address: format!("127.0.0.1:{}", port),
        report_receiver_interval: ReadableDuration::secs(4),
        max_resource_groups: 5000,
        precision: ReadableDuration::secs(2),
    });
    test_suite.start_receiver_at(port);

    // Workload
    // [req-{1..3} * 6, req-{4..5} * 1]
    let mut wl = iter::repeat(1..=3)
        .take(6)
        .flatten()
        .chain(4..=5)
        .map(|n| format!("req-{}", n))
        .collect::<Vec<_>>();
    wl.shuffle(&mut rand::thread_rng());
    test_suite.setup_workload(wl);

    // | Max Resource Groups |
    // |       5000          |
    let res = test_suite.block_receive_one();
    assert!(res.contains_key("req-1"));
    assert!(res.contains_key("req-2"));
    assert!(res.contains_key("req-3"));
    assert!(res.contains_key("req-4"));
    assert!(res.contains_key("req-5"));

    // | Max Resource Groups |
    // |        3            |
    test_suite.cfg_max_resource_groups(3);
    test_suite.flush_receiver();
    let res = test_suite.block_receive_one();
    assert_eq!(res.len(), 4);
    assert!(res.contains_key("req-1"));
    assert!(res.contains_key("req-2"));
    assert!(res.contains_key("req-3"));
    assert!(res.contains_key(""));
}

#[test]
pub fn test_precision() {
    let port = alloc_port();
    let mut test_suite = TestSuite::new(resource_metering::Config {
        receiver_address: format!("127.0.0.1:{}", port),
        report_receiver_interval: ReadableDuration::secs(3),
        max_resource_groups: 5000,
        precision: ReadableDuration::secs(1),
    });
    test_suite.start_receiver_at(port);

    // Workload
    // [req-1]
    test_suite.setup_workload(vec!["req-1"]);

    // | Precision |
    // |    1s     |
    let res = test_suite.block_receive_one();
    let (secs, _) = res.get("req-1").unwrap();
    for (l, r) in secs.iter().zip({
        let mut next_secs = secs.iter();
        next_secs.next();
        next_secs
    }) {
        let diff = r - l;
        assert!(diff <= 2);
    }

    // | Precision |
    // |    3s     |
    test_suite.cfg_precision("3s");
    test_suite.cfg_report_receiver_interval("9s");
    test_suite.flush_receiver();
    let res = test_suite.block_receive_one();
    let (secs, _) = res.get("req-1").unwrap();
    for (l, r) in secs.iter().zip({
        let mut next_secs = secs.iter();
        next_secs.next();
        next_secs
    }) {
        let diff = r - l;
        assert!((2..=4).contains(&diff));
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// This test makes sure that functions get annotated with the proper
// "target-cpu" attribute in LLVM.

// no-prefer-dynamic
// ignore-tidy-linelength
// only-x86_64
// compile-flags: -C no-prepopulate-passes -C panic=abort -Z cross-lang-lto -Cpasses=name-anon-globals

#![crate_type = "staticlib"]

// CHECK-LABEL: define {{.*}} @exported() {{.*}} #0
#[no_mangle]
pub extern fn exported() {
    not_exported();
}

// CHECK-LABEL: define {{.*}} @_ZN23target_cpu_on_functions12not_exported{{.*}}() {{.*}} #0
fn not_exported() {}

// CHECK: attributes #0 = {{.*}} "target-cpu"="x86-64"
<commit_msg>Relax the target-cpu-on-function codegen test so it just checks for presence of the attribute.<commit_after>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// This test makes sure that functions get annotated with the proper
// "target-cpu" attribute in LLVM.

// no-prefer-dynamic
// ignore-tidy-linelength
// compile-flags: -C no-prepopulate-passes -C panic=abort -Z cross-lang-lto -Cpasses=name-anon-globals

#![crate_type = "staticlib"]

// CHECK-LABEL: define {{.*}} @exported() {{.*}} #0
#[no_mangle]
pub extern fn exported() {
    not_exported();
}

// CHECK-LABEL: define {{.*}} @_ZN23target_cpu_on_functions12not_exported{{.*}}() {{.*}} #0
fn not_exported() {}

// CHECK: attributes #0 = {{.*}} "target-cpu"="{{.*}}"
<|endoftext|>


<commit_before>use futures::{future::ready, stream::futures_unordered::FuturesUnordered, StreamExt, TryFutureExt};
use reqwest::{header, Client};

use crate::consts::basic_runtime;
use std::time::Duration;

static HOSTS: &[&str] = &["https://myip.ipip.net/", "https://ipinfo.io/", "https://checkip.amazonaws.com"];
// curl https://ip.cn -v
static UA: &str = "curl/7.54.0";
static ACCEPT: &str = "Accept: */*";

pub fn call() {
    let mut rt = basic_runtime();

    let mut headers = header::HeaderMap::new();
    headers.insert(header::USER_AGENT, UA.parse().unwrap());
    headers.insert(header::ACCEPT, ACCEPT.parse().unwrap());

    let client = Client::builder()
        .default_headers(headers)
        .use_rustls_tls()
        .timeout(Duration::from_secs(10))
        .build()
        .expect("reqwest Client build failed");

    let futs = FuturesUnordered::new();
    HOSTS.iter().for_each(|host| futs.push(curl(host, &client)));

    rt.block_on(futs.for_each(|_| ready(())))
}

async fn curl(url: &str, client: &Client) {
    let res = client
        .get(url)
        .send()
        .map_err(|e| format!("Send request failed: {}", e))
        .and_then(|resp| {
            resp.text()
                .map_err(|e| format!("Read response's Body to string failed: {}", e))
        })
        .await;

    match res {
        Ok(body) => println!("{}\n{}", url, body),
        Err(e) => eprintln!("{}: {}", url, e),
    }
}
<commit_msg>ip: add https://api.ip.sb/ip<commit_after>use futures::{future::ready, stream::futures_unordered::FuturesUnordered, StreamExt, TryFutureExt};
use reqwest::{header, Client};

use crate::consts::basic_runtime;
use std::time::Duration;

static HOSTS: &[&str] = &[
    "https://myip.ipip.net",
    "https://ipinfo.io",
    "https://api.ip.sb/ip",
    "https://checkip.amazonaws.com",
];

// curl ip.sb -v
// curl https://api.ip.sb/geoip/185.222.222.222
static UA: &str = "curl/7.81.0";
static ACCEPT: &str = "Accept: */*";

pub fn call() {
    let mut rt = basic_runtime();

    let mut headers = header::HeaderMap::new();
    headers.insert(header::USER_AGENT, UA.parse().unwrap());
    headers.insert(header::ACCEPT, ACCEPT.parse().unwrap());

    let client = Client::builder()
        .default_headers(headers)
        .use_rustls_tls()
        .timeout(Duration::from_secs(10))
        .build()
        .expect("reqwest Client build failed");

    let futs = FuturesUnordered::new();
    HOSTS.iter().for_each(|host| futs.push(curl(host, &client)));

    rt.block_on(futs.for_each(|_| ready(())))
}

async fn curl(url: &str, client: &Client) {
    let res = client
        .get(url)
        .send()
        .map_err(|e| format!("Send request failed: {}", e))
        .and_then(|resp| {
            resp.text()
                .map_err(|e| format!("Read response's Body to string failed: {}", e))
        })
        .await;

    match res {
        Ok(body) => println!("{}\n{}", url, body),
        Err(e) => eprintln!("{}: {}", url, e),
    }
}
<|endoftext|>


<commit_before>use std::io::{Result, BufReader, BufRead, Write};
use std::os::unix::fs::PermissionsExt;
use std::fs::{metadata, set_permissions, remove_file};
use std::sync::mpsc::TryRecvError;
use std::cell::RefCell;

use mioco;
use mioco::sync::mpsc::{channel, Sender, Receiver};
use mioco::unix::UnixListener;
use serde_json;

use resolver::{RcResolver};
use utils::{WithTimeout, JsonRpcRequest};

pub struct ControlServer {
    #[allow(dead_code)]
    live_token: Sender<()>,
    notifier: RefCell<Option<Receiver<()>>>,
}

impl ControlServer {
    pub fn new() -> Self {
        let (send, recv) = channel::<()>();
        ControlServer {
            live_token: send,
            notifier: RefCell::new(Some(recv)),
        }
    }
    pub fn run(&self, resolver: &RcResolver) -> Result<()> {
        let notifier = self.notifier.borrow_mut().take()
        .expect("run() can only be called once");
        ControlServer::serve(resolver, notifier)
    }
    fn serve(resolver: &RcResolver, live_notifier: Receiver<()>) -> Result<()> {
        let sock_path = &resolver.config.control.sock_path;
        // Clean up old socket
        remove_file(sock_path).is_ok();
        let listener = try!(UnixListener::bind(sock_path));
        let mut perm = try!(metadata(sock_path)).permissions();
        perm.set_mode(**resolver.config.control.sock_permission);
        try!(set_permissions(sock_path, perm));
        let weak = resolver.to_weak();
        mioco::spawn(move || {
            loop {
                select!(
                    r:listener => {
                        /* Fall below */
                    },
                    r:live_notifier => {
                        match live_notifier.try_recv() {
                            Ok(_) => panic!("Nothing should be sent over this channel"),
                            Err(TryRecvError::Disconnected) => {
                                debug!("Resolver is dropped, closing control channel");
                                return;
                            },
                            Err(e) => panic!("Unexpected error: {:?}", e),
                        };
                    },
                );
                let resolver = match weak.upgrade() {
                    Some(x) => x,
                    None => {
                        debug!("Resolver is dropped, closing control channel");
                        return;
                    },
                };
                let result = listener.try_accept()
                .expect("Unable to accept from control socket");
                let sock = match result {
                    Some(sock) => sock,
                    None => continue,
                };
                let sock = sock.with_resetting_timeout(5000);
                let mut reader = BufReader::new(sock);
                let mut buf = String::new();
                if let Err(e) = reader.read_line(&mut buf) {
                    warn!("Failed to read from control socket: {:?}", e);
                    continue;
                }
                let req: JsonRpcRequest = match serde_json::from_str(&buf) {
                    Ok(x) => x,
                    Err(e) => {
                        warn!(
                            "Control socket received an invalid request: {}, {:?}",
                            buf, e,
                        );
                        continue
                    },
                };
                info!("Command: {}", req.method);
                let resp = match req.method.as_str() {
                    "ping" => req.result("Pong".into()),
                    _ => req.error("Unknown command".into()),
                };
                let mut resp_str = serde_json::to_string(&resp).unwrap();
                resp_str.push_str("\n");
                let mut sock = reader.into_inner();
                if let Err(e) = sock.write_all(resp_str.as_bytes()) {
                    warn!("Failed to write to control socket: {:?}", e);
                }
            }
        });
        Ok(())
    }
}
<commit_msg>No need to use RefCell<commit_after>use std::io::{Result, BufReader, BufRead, Write};
use std::os::unix::fs::PermissionsExt;
use std::fs::{metadata, set_permissions, remove_file};
use std::sync::mpsc::TryRecvError;

use mioco;
use mioco::sync::mpsc::{channel, Sender, Receiver};
use mioco::unix::UnixListener;
use serde_json;

use resolver::{RcResolver};
use utils::{WithTimeout, JsonRpcRequest};

pub struct ControlServer {
    #[allow(dead_code)]
    live_token: Sender<()>,
    notifier: Option<Receiver<()>>,
}

impl ControlServer {
    pub fn new() -> Self {
        let (send, recv) = channel::<()>();
        ControlServer {
            live_token: send,
            notifier: Some(recv),
        }
    }
    pub fn run(&mut self, resolver: &RcResolver) -> Result<()> {
        let notifier = self.notifier.take().expect("run() can only be called once");
        ControlServer::serve(resolver, notifier)
    }
    fn serve(resolver: &RcResolver, live_notifier: Receiver<()>) -> Result<()> {
        let sock_path = &resolver.config.control.sock_path;
        // Clean up old socket
        remove_file(sock_path).is_ok();
        let listener = try!(UnixListener::bind(sock_path));
        let mut perm = try!(metadata(sock_path)).permissions();
        perm.set_mode(**resolver.config.control.sock_permission);
        try!(set_permissions(sock_path, perm));
        let weak = resolver.to_weak();
        mioco::spawn(move || {
            loop {
                select!(
                    r:listener => {
                        /* Fall below */
                    },
                    r:live_notifier => {
                        match live_notifier.try_recv() {
                            Ok(_) => panic!("Nothing should be sent over this channel"),
                            Err(TryRecvError::Disconnected) => {
                                debug!("Resolver is dropped, closing control channel");
                                return;
                            },
                            Err(e) => panic!("Unexpected error: {:?}", e),
                        };
                    },
                );
                let resolver = match weak.upgrade() {
                    Some(x) => x,
                    None => {
                        debug!("Resolver is dropped, closing control channel");
                        return;
                    },
                };
                let result = listener.try_accept()
                .expect("Unable to accept from control socket");
                let sock = match result {
                    Some(sock) => sock,
                    None => continue,
                };
                let sock = sock.with_resetting_timeout(5000);
                let mut reader = BufReader::new(sock);
                let mut buf = String::new();
                if let Err(e) = reader.read_line(&mut buf) {
                    warn!("Failed to read from control socket: {:?}", e);
                    continue;
                }
                let req: JsonRpcRequest = match serde_json::from_str(&buf) {
                    Ok(x) => x,
                    Err(e) => {
                        warn!(
                            "Control socket received an invalid request: {}, {:?}",
                            buf, e,
                        );
                        continue
                    },
                };
                info!("Command: {}", req.method);
                let resp = match req.method.as_str() {
                    "ping" => req.result("Pong".into()),
                    _ => req.error("Unknown command".into()),
                };
                let mut resp_str = serde_json::to_string(&resp).unwrap();
                resp_str.push_str("\n");
                let mut sock = reader.into_inner();
                if let Err(e) = sock.write_all(resp_str.as_bytes()) {
                    warn!("Failed to write to control socket: {:?}", e);
                }
            }
        });
        Ok(())
    }
}
<|endoftext|>


<commit_before>use super::super::bus;
use super::cp0::CP0;
use super::instruction::Instruction;
use super::instruction::INSTRUCTION_SIZE;
use super::opcode::Opcode;
use super::opcode::OpcodeSpecial;
use super::opcode::OpcodeBAL;

use std::fmt;

const NUM_GPREG: usize = 32;
const NUM_FPREG: usize = 32;

const PIF_ROM_START: u64 = 0xffff_ffff_bfc0_0000;

pub struct Cpu {
    reg_gprs: [u64; NUM_GPREG],
    reg_fprs: [f64; NUM_FPREG],

    reg_pc: u64,

    reg_hi: u64,
    reg_lo: u64,

    reg_llbit: bool,

    reg_fcr0: u32,
    reg_fcr31: u32,

    cp0: CP0,

    bus: bus::Bus,
}

impl Cpu {
    pub fn new(bus: bus::Bus) -> Cpu {
        Cpu {
            reg_gprs: [0; NUM_GPREG],
            reg_fprs: [0.0; NUM_FPREG],

            reg_pc: PIF_ROM_START,

            reg_hi: 0,
            reg_lo: 0,

            reg_llbit: false,

            reg_fcr0: 0,
            reg_fcr31: 0,

            cp0: CP0::default(),

            bus: bus,
        }
    }

    pub fn run(&mut self) {
        loop {
            self.run_instruction();
        }
    }

    pub fn run_instruction(&mut self) {
        let instruction = self.read_instruction(self.reg_pc);
        println!("PC: {:#x}", self.reg_pc);
        let new_pc = self.reg_pc.wrapping_add(INSTRUCTION_SIZE as u64);
        self.change_pc(new_pc, false);

        self.execute_instruction(instruction);
    }

    fn execute_special(&mut self, instruction: Instruction) {
        match instruction.opcode_special() {
            OpcodeSpecial::SLL => {
                let shift = instruction.shift_amount();
                let res = self.read_gpr(instruction.target_immediate()) << shift;
                self.write_gpr(instruction.destination(), (res as i32) as u64);
            }
            OpcodeSpecial::SLLV => {
                let shift = self.read_gpr(instruction.source()) & 0x1F;
                let res = self.read_gpr(instruction.target_immediate()) << shift;
                self.write_gpr(instruction.destination(), (res as i32) as u64);
            }
            OpcodeSpecial::SRL => {
                let shift = instruction.shift_amount();
                let res = self.read_gpr(instruction.target_immediate()) >> shift;
                self.write_gpr(instruction.destination(), (res as i32) as u64);
            }
            OpcodeSpecial::SRLV => {
                let shift = self.read_gpr(instruction.source()) & 0x1F;
                let res = self.read_gpr(instruction.target_immediate()) >> shift;
                self.write_gpr(instruction.destination(), (res as i32) as u64);
            }
            OpcodeSpecial::OR => {
                let rs_val = self.read_gpr(instruction.source());
                let rt_val = self.read_gpr(instruction.target_register());
                self.write_gpr(instruction.destination(), rs_val | rt_val);
            }
            OpcodeSpecial::AND => {
                let rs_val = self.read_gpr(instruction.source());
                let rt_val = self.read_gpr(instruction.target_register());
                self.write_gpr(instruction.destination(), rs_val & rt_val);
            }
            OpcodeSpecial::XOR => {
                let rs_val = self.read_gpr(instruction.source());
                let rt_val = self.read_gpr(instruction.target_register());
                self.write_gpr(instruction.destination(), rs_val ^ rt_val);
            }
            OpcodeSpecial::MFHI => {
                let hi = self.reg_hi;
                self.write_gpr(instruction.destination(), hi);
            }
            OpcodeSpecial::MFLO => {
                let lo = self.reg_lo;
                self.write_gpr(instruction.destination(), lo);
            }
            OpcodeSpecial::MULTU => {
                // TODO: Deal with MFHI and MFLO
                let rs_val = self.read_gpr(instruction.source());
                let rt_val = self.read_gpr(instruction.target_register());

                // 64-bit mode
                let res = rs_val.wrapping_mul(rt_val);

                self.reg_lo = ((res & 0xffffffff) as i32) as u64;
                self.reg_hi = ((res >> 32) as i32) as u64;

            }
            OpcodeSpecial::ADDU => {
                let rs_val = self.read_gpr(instruction.source());
                let rt_val = self.read_gpr(instruction.target_register());
                println!("Add {:#x} - {:#x}", rs_val, rt_val);
                let sub_val = rs_val.wrapping_add(rt_val);
                self.write_gpr(instruction.destination(), (sub_val as i32) as u64);
            }
            OpcodeSpecial::SUBU => {
                let rs_val = self.read_gpr(instruction.source());
                let rt_val = self.read_gpr(instruction.target_register());
                println!("Sub {:#x} - {:#x}", rs_val, rt_val);
                let sub_val = rs_val.wrapping_sub(rt_val);
                self.write_gpr(instruction.destination(), (sub_val as i32) as u64);
            }
            OpcodeSpecial::JR => {
                println!("JUMPY");
                let new_pc = self.read_gpr(instruction.source());
                if new_pc & 0b11 != 0 {
                    panic!("Address error exception");
                }
                self.change_pc(new_pc, true);
            }
            OpcodeSpecial::SLTU => {
                let rs_val = self.read_gpr(instruction.source());
                let rt_val = self.read_gpr(instruction.target_register());
                self.write_gpr(instruction.destination(),
                               if rs_val < rt_val {
                                   1
                               } else {
                                   0
                               });
            }
        }
    }

    fn execute_bal(&mut self, instruction: Instruction) {
        match instruction.opcode_bal() {
            OpcodeBAL::BGEZAL => {
                let r31val = self.reg_pc + 4;

                self.branch(instruction, |rs, _, s| {
                    s.write_gpr(31, r31val);
                    (rs as i64) >= 0
                });
            }
        }
    }

    fn execute_instruction(&mut self, instruction: Instruction) {
        println!("Instr: {:?}", instruction);
        match instruction.opcode() {
            Opcode::SPECIAL => {
                self.execute_special(instruction);
            }
            Opcode::BAL => {
                self.execute_bal(instruction);
            }
            Opcode::ADDI => {
                let rs_val = self.read_gpr(instruction.source());
                self.write_gpr(instruction.target_immediate(), {

                    let rs_positive = (rs_val >> 31) & 1 == 0;
                    let imm = instruction.immediate_extend();
                    let imm_positive = (imm >> 31) & 1 == 0;
                    let res = rs_val.wrapping_add(imm);
                    let res_positive = (res >> 31 & 1) == 0;
                    match (rs_positive, imm_positive, res_positive) {
                        (true, true, false) => {
                            panic!("Integer overflow exception not implemented! (pp=n) {:016X}  \
                                    {:016X} != {:016X}",
                                   res,
                                   imm,
                                   res);
                        }
                        (false, false, true) => {
                            panic!("Integer overflow exception not implemented! (nn=p) {:016X}  \
                                    {:016X} != {:016X}",
                                   res,
                                   imm,
                                   res);
                        }
                        _ => {}
                    };
                    res
                });
            }
            Opcode::ADDIU => {
                let res = self.read_gpr(instruction.source())
                    .wrapping_add((instruction.immediate_extend()));
                self.write_gpr(instruction.target_immediate(), res);
            }
            Opcode::MTC0 => {
                let rt = instruction.target_register();
                let rd = instruction.destination();
                let data = self.read_gpr(rt);
                self.cp0.write_reg(rd, data);
            }
            Opcode::ANDI => {
                let res = self.read_gpr(instruction.source()) & (instruction.immediate() as u64);
                self.write_gpr(instruction.target_immediate(), res);
            }
            Opcode::ORI => {
                let res = self.read_gpr(instruction.source()) | (instruction.immediate() as u64);
                self.write_gpr(instruction.target_immediate(), res);
            }
            Opcode::LUI => {
                // assume 32 bit mode
                self.write_gpr(instruction.target_immediate(),
                               (((instruction.immediate() as u32) << 16) as i32) as u64);
            }
            Opcode::BEQ => {
                self.branch(instruction, |rs, rt, _| rs == rt);
            }
            Opcode::BEQL => self.branch_likely(instruction, |rs, rt, _| rs == rt),
            Opcode::BNE => {
                self.branch(instruction, |rs, rt, _| rs != rt);
            }
            Opcode::BNEL => self.branch_likely(instruction, |rs, rt, _| rs != rt),
            Opcode::LW => {
                // LW
                let base = self.read_gpr(instruction.source());
                let vaddr = base.wrapping_add((instruction.immediate() as i16) as u64);
                if vaddr & 0b11 != 0 {
                    panic!("Address error exception");
                }

                let word = self.read_word(vaddr);
                let mem = (word as i32) as u64;
                self.write_gpr(instruction.target_immediate(), mem);

            }
            Opcode::SW => {
                let base = self.read_gpr(instruction.source());
                let vaddr = base.wrapping_add((instruction.immediate() as i16) as u64);
                if vaddr & 0b11 != 0 {
                    panic!("Address error exception");
                }
                let value = self.read_gpr(instruction.target_immediate()) as u32;
                self.write_word(vaddr, value);
            }
        }

    }

    fn change_pc(&mut self, new_address: u64, with_delay: bool) {
        let delay_instuction = self.read_instruction(self.reg_pc);
        self.reg_pc = new_address;
        if with_delay {
            self.execute_instruction(delay_instuction);
        }
    }

    fn branch_likely<F>(&mut self, instruction: Instruction, f: F)
        where F: FnOnce(u64, u64, &mut Cpu) -> bool
    {
        if !self.branch(instruction, f) {
            self.reg_pc = self.reg_pc.wrapping_add(INSTRUCTION_SIZE as u64)
        }
    }

    fn branch<F>(&mut self, instruction: Instruction, f: F) -> bool
        where F: FnOnce(u64, u64, &mut Cpu) -> bool
    {
        let rs = self.read_gpr(instruction.source());
        let rt = self.read_gpr(instruction.target_immediate());
        let branch = f(rs, rt, self);

        if branch {
            let new_pc = self.reg_pc
                .wrapping_add(((instruction.immediate() << 2) as i16) as u64);
            self.change_pc(new_pc, true);
        }
        branch
    }

    fn read_word(&self, addr: u64) -> u32 {
        let paddr = vaddr_to_paddr(addr);
        self.bus.read_word(paddr as u32)
    }

    fn write_word(&mut self, addr: u64, value: u32) {
        let paddr = vaddr_to_paddr(addr);
        self.bus.write_word(paddr as u32, value);
    }

    fn read_instruction(&self, addr: u64) -> Instruction {

        Instruction(self.read_word(addr))
    }

    fn write_gpr(&mut self, index: usize, value: u64) {
        if index != 0 {
            self.reg_gprs[index] = value;
        }
    }

    fn read_gpr(&self, index: usize) -> u64 {
        match index {
            0 => 0,
            _ => self.reg_gprs[index],
        }
    }
}

impl fmt::Debug for Cpu {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        const REGS_PER_LINE: usize = 2;
        const REG_NAMES: [&'static str; NUM_GPREG] =
            ["r0", "at", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2", "t3", "t4", "t5",
             "t6", "t7", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1",
             "gp", "sp", "s8", "ra"];

        try!(write!(f, "\nCPU General Purpose Registers:"));
        for reg_num in 0..NUM_GPREG {
            if (reg_num % REGS_PER_LINE) == 0 {
                try!(writeln!(f, ""));
            }
            try!(write!(f,
                        "{reg_name}/gpr{num:02}: {value:#018X} ",
                        num = reg_num,
                        reg_name = REG_NAMES[reg_num],
                        value = self.reg_gprs[reg_num]));
        }

        try!(write!(f, "\n\nCPU Floating Point Registers:"));
        for reg_num in 0..NUM_FPREG {
            if (reg_num % REGS_PER_LINE) == 0 {
                try!(writeln!(f, ""));
            }
            try!(write!(f,
                "fpr{num:02}: {value:21} ",
                num = reg_num,
                value = self.reg_fprs[reg_num],));
        }

        try!(writeln!(f, "\n\nCPU Special Registers:"));
        try!(writeln!(f,
                      "\
            reg_pc: {:#018X}\nreg_hi: {:#018X}\nreg_lo: \
                       {:#018X}\nreg_llbit: {}\nreg_fcr0:  {:#010X}\nreg_fcr31: {:#010X}\n",
                      self.reg_pc,
                      self.reg_hi,
                      self.reg_lo,
                      self.reg_llbit,
                      self.reg_fcr0,
                      self.reg_fcr31));

        try!(writeln!(f, "{:#?}", self.cp0));
        writeln!(f, "{:#?}", self.bus)
    }
}

fn vaddr_to_paddr(vaddr: u64) -> u64 {
    let addr_bit_values = (vaddr >> 29) & 0b111;
    if addr_bit_values == 0b101 {
        // kseg1
        vaddr - 0xffff_ffff_a000_0000
    } else {
        panic!("Unrecognised virtual address {:#x}", vaddr);
    }
}
<commit_msg>Tidy up cpu<commit_after>use super::super::bus;
use super::cp0::CP0;
use super::instruction::Instruction;
use super::instruction::INSTRUCTION_SIZE;
use super::opcode::Opcode::*;
use super::opcode::OpcodeSpecial::*;
use super::opcode::OpcodeBAL::*;

use std::fmt;

const NUM_GPREG: usize = 32;
const NUM_FPREG: usize = 32;

const PIF_ROM_START: u64 = 0xffff_ffff_bfc0_0000;

pub struct Cpu {
    reg_gprs: [u64; NUM_GPREG],
    reg_fprs: [f64; NUM_FPREG],

    reg_pc: u64,

    reg_hi: u64,
    reg_lo: u64,

    reg_llbit: bool,

    reg_fcr0: u32,
    reg_fcr31: u32,

    cp0: CP0,

    bus: bus::Bus,
}

impl Cpu {
    pub fn new(bus: bus::Bus) -> Cpu {
        Cpu {
            reg_gprs: [0; NUM_GPREG],
            reg_fprs: [0.0; NUM_FPREG],

            reg_pc: PIF_ROM_START,

            reg_hi: 0,
            reg_lo: 0,

            reg_llbit: false,

            reg_fcr0: 0,
            reg_fcr31: 0,

            cp0: CP0::default(),

            bus: bus,
        }
    }

    pub fn run(&mut self) {
        loop {
            self.run_instruction();
        }
    }

    pub fn run_instruction(&mut self) {
        let instruction = self.read_instruction(self.reg_pc);
        println!("PC: {:#x}", self.reg_pc);
        let new_pc = self.reg_pc.wrapping_add(INSTRUCTION_SIZE as u64);
        self.change_pc(new_pc, false);

        self.execute_instruction(instruction);
    }

    fn execute_special(&mut self, instruction: Instruction) {
        match instruction.opcode_special() {
            SLL => {
                let shift = instruction.shift_amount();
                let res = self.read_gpr(instruction.target_immediate()) << shift;
                self.write_gpr(instruction.destination(), (res as i32) as u64);
            }
            SLLV => {
                let shift = self.read_gpr(instruction.source()) & 0x1F;
                let res = self.read_gpr(instruction.target_immediate()) << shift;
                self.write_gpr(instruction.destination(), (res as i32) as u64);
            }
            SRL => {
                let shift = instruction.shift_amount();
                let res = self.read_gpr(instruction.target_immediate()) >> shift;
                self.write_gpr(instruction.destination(), (res as i32) as u64);
            }
            SRLV => {
                let shift = self.read_gpr(instruction.source()) & 0x1F;
                let res = self.read_gpr(instruction.target_immediate()) >> shift;
                self.write_gpr(instruction.destination(), (res as i32) as u64);
            }
            OR => {
                let rs_val = self.read_gpr(instruction.source());
                let rt_val = self.read_gpr(instruction.target_register());
                self.write_gpr(instruction.destination(), rs_val | rt_val);
            }
            AND => {
                let rs_val = self.read_gpr(instruction.source());
                let rt_val = self.read_gpr(instruction.target_register());
                self.write_gpr(instruction.destination(), rs_val & rt_val);
            }
            XOR => {
                let rs_val = self.read_gpr(instruction.source());
                let rt_val = self.read_gpr(instruction.target_register());
                self.write_gpr(instruction.destination(), rs_val ^ rt_val);
            }
            MFHI => {
                let hi = self.reg_hi;
                self.write_gpr(instruction.destination(), hi);
            }
            MFLO => {
                let lo = self.reg_lo;
                self.write_gpr(instruction.destination(), lo);
            }
            MULTU => {
                // TODO: Deal with MFHI and MFLO
                let rs_val = self.read_gpr(instruction.source());
                let rt_val = self.read_gpr(instruction.target_register());

                // 64-bit mode
                let res = rs_val.wrapping_mul(rt_val);

                self.reg_lo = ((res & 0xffffffff) as i32) as u64;
                self.reg_hi = ((res >> 32) as i32) as u64;

            }
            ADDU => {
                let rs_val = self.read_gpr(instruction.source());
                let rt_val = self.read_gpr(instruction.target_register());
                println!("Add {:#x} - {:#x}", rs_val, rt_val);
                let sub_val = rs_val.wrapping_add(rt_val);
                self.write_gpr(instruction.destination(), (sub_val as i32) as u64);
            }
            SUBU => {
                let rs_val = self.read_gpr(instruction.source());
                let rt_val = self.read_gpr(instruction.target_register());
                println!("Sub {:#x} - {:#x}", rs_val, rt_val);
                let sub_val = rs_val.wrapping_sub(rt_val);
                self.write_gpr(instruction.destination(), (sub_val as i32) as u64);
            }
            JR => {
                println!("JUMPY");
                let new_pc = self.read_gpr(instruction.source());
                if new_pc & 0b11 != 0 {
                    panic!("Address error exception");
                }
                self.change_pc(new_pc, true);
            }
            SLTU => {
                let rs_val = self.read_gpr(instruction.source());
                let rt_val = self.read_gpr(instruction.target_register());
                self.write_gpr(instruction.destination(),
                               if rs_val < rt_val {
                                   1
                               } else {
                                   0
                               });
            }
        }
    }

    fn execute_bal(&mut self, instruction: Instruction) {
        match instruction.opcode_bal() {
            BGEZAL => {
                let r31val = self.reg_pc + 4;

                self.branch(instruction, |rs, _, s| {
                    s.write_gpr(31, r31val);
                    (rs as i64) >= 0
                });
            }
        }
    }

    fn execute_instruction(&mut self, instruction: Instruction) {
        println!("Instr: {:?}", instruction);
        match instruction.opcode() {
            SPECIAL => {
                self.execute_special(instruction);
            }
            BAL => {
                self.execute_bal(instruction);
            }
            ADDI => {
                let rs_val = self.read_gpr(instruction.source());
                self.write_gpr(instruction.target_immediate(), {

                    let rs_positive = (rs_val >> 31) & 1 == 0;
                    let imm = instruction.immediate_extend();
                    let imm_positive = (imm >> 31) & 1 == 0;
                    let res = rs_val.wrapping_add(imm);
                    let res_positive = (res >> 31 & 1) == 0;
                    match (rs_positive, imm_positive, res_positive) {
                        (true, true, false) => {
                            panic!("Integer overflow exception not implemented! (pp=n) {:016X}  \
                                    {:016X} != {:016X}",
                                   res,
                                   imm,
                                   res);
                        }
                        (false, false, true) => {
                            panic!("Integer overflow exception not implemented! (nn=p) {:016X}  \
                                    {:016X} != {:016X}",
                                   res,
                                   imm,
                                   res);
                        }
                        _ => {}
                    };
                    res
                });
            }
            ADDIU => {
                let res = self.read_gpr(instruction.source())
                    .wrapping_add((instruction.immediate_extend()));
                self.write_gpr(instruction.target_immediate(), res);
            }
            MTC0 => {
                let rt = instruction.target_register();
                let rd = instruction.destination();
                let data = self.read_gpr(rt);
                self.cp0.write_reg(rd, data);
            }
            ANDI => {
                let res = self.read_gpr(instruction.source()) & (instruction.immediate() as u64);
                self.write_gpr(instruction.target_immediate(), res);
            }
            ORI => {
                let res = self.read_gpr(instruction.source()) | (instruction.immediate() as u64);
                self.write_gpr(instruction.target_immediate(), res);
            }
            LUI => {
                // assume 32 bit mode
                self.write_gpr(instruction.target_immediate(),
                               (((instruction.immediate() as u32) << 16) as i32) as u64);
            }
            BEQ => {
                self.branch(instruction, |rs, rt, _| rs == rt);
            }
            BEQL => self.branch_likely(instruction, |rs, rt, _| rs == rt),
            BNE => {
                self.branch(instruction, |rs, rt, _| rs != rt);
            }
            BNEL => self.branch_likely(instruction, |rs, rt, _| rs != rt),
            LW => {
                // LW
                let base = self.read_gpr(instruction.source());
                let vaddr = base.wrapping_add((instruction.immediate() as i16) as u64);
                if vaddr & 0b11 != 0 {
                    panic!("Address error exception");
                }

                let word = self.read_word(vaddr);
                let mem = (word as i32) as u64;
                self.write_gpr(instruction.target_immediate(), mem);

            }
            SW => {
                let base = self.read_gpr(instruction.source());
                let vaddr = base.wrapping_add((instruction.immediate() as i16) as u64);
                if vaddr & 0b11 != 0 {
                    panic!("Address error exception");
                }
                let value = self.read_gpr(instruction.target_immediate()) as u32;
                self.write_word(vaddr, value);
            }
        }

    }

    fn change_pc(&mut self, new_address: u64, with_delay: bool) {
        let delay_instuction = self.read_instruction(self.reg_pc);
        self.reg_pc = new_address;
        if with_delay {
            self.execute_instruction(delay_instuction);
        }
    }

    fn branch_likely<F>(&mut self, instruction: Instruction, f: F)
        where F: FnOnce(u64, u64, &mut Cpu) -> bool
    {
        if !self.branch(instruction, f) {
            self.reg_pc = self.reg_pc.wrapping_add(INSTRUCTION_SIZE as u64)
        }
    }

    fn branch<F>(&mut self, instruction: Instruction, f: F) -> bool
        where F: FnOnce(u64, u64, &mut Cpu) -> bool
    {
        let rs = self.read_gpr(instruction.source());
        let rt = self.read_gpr(instruction.target_immediate());
        let branch = f(rs, rt, self);

        if branch {
            let new_pc = self.reg_pc
                .wrapping_add(((instruction.immediate() << 2) as i16) as u64);
            self.change_pc(new_pc, true);
        }
        branch
    }

    fn read_word(&self, addr: u64) -> u32 {
        let paddr = vaddr_to_paddr(addr);
        self.bus.read_word(paddr as u32)
    }

    fn write_word(&mut self, addr: u64, value: u32) {
        let paddr = vaddr_to_paddr(addr);
        self.bus.write_word(paddr as u32, value);
    }

    fn read_instruction(&self, addr: u64) -> Instruction {

        Instruction(self.read_word(addr))
    }

    fn write_gpr(&mut self, index: usize, value: u64) {
        if index != 0 {
            self.reg_gprs[index] = value;
        }
    }

    fn read_gpr(&self, index: usize) -> u64 {
        match index {
            0 => 0,
            _ => self.reg_gprs[index],
        }
    }
}

impl fmt::Debug for Cpu {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        const REGS_PER_LINE: usize = 2;
        const REG_NAMES: [&'static str; NUM_GPREG] =
            ["r0", "at", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2", "t3", "t4", "t5",
             "t6", "t7", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1",
             "gp", "sp", "s8", "ra"];

        try!(write!(f, "\nCPU General Purpose Registers:"));
        for reg_num in 0..NUM_GPREG {
            if (reg_num % REGS_PER_LINE) == 0 {
                try!(writeln!(f, ""));
            }
            try!(write!(f,
                        "{reg_name}/gpr{num:02}: {value:#018X} ",
                        num = reg_num,
                        reg_name = REG_NAMES[reg_num],
                        value = self.reg_gprs[reg_num]));
        }

        try!(write!(f, "\n\nCPU Floating Point Registers:"));
        for reg_num in 0..NUM_FPREG {
            if (reg_num % REGS_PER_LINE) == 0 {
                try!(writeln!(f, ""));
            }
            try!(write!(f,
                "fpr{num:02}: {value:21} ",
                num = reg_num,
                value = self.reg_fprs[reg_num],));
        }

        try!(writeln!(f, "\n\nCPU Special Registers:"));
        try!(writeln!(f,
                      "\
            reg_pc: {:#018X}\nreg_hi: {:#018X}\nreg_lo: \
                       {:#018X}\nreg_llbit: {}\nreg_fcr0:  {:#010X}\nreg_fcr31: {:#010X}\n",
                      self.reg_pc,
                      self.reg_hi,
                      self.reg_lo,
                      self.reg_llbit,
                      self.reg_fcr0,
                      self.reg_fcr31));

        try!(writeln!(f, "{:#?}", self.cp0));
        writeln!(f, "{:#?}", self.bus)
    }
}

fn vaddr_to_paddr(vaddr: u64) -> u64 {
    let addr_bit_values = (vaddr >> 29) & 0b111;
    if addr_bit_values == 0b101 {
        // kseg1
        vaddr - 0xffff_ffff_a000_0000
    } else {
        panic!("Unrecognised virtual address {:#x}", vaddr);
    }
}
<|endoftext|>